94
Helló Window! GTK alapú felhasználói felületek C, C++, Python nyelv˝ u fejlesztése és tesztelése Pfeiffer Szilárd 1 2013. január 3. 1 A könyv létrejöttét a FSF.hu Alapítvány a Szabad Szoftver Pályázat[5] keretében támogatta

Helló Window!

Embed Size (px)

DESCRIPTION

GTK alapú felhasználói felületek C, C++, Python nyelvű fejlesztése és tesztelése

Citation preview

Page 1: Helló Window!

Helló Window!GTK alapú felhasználói felületek C, C++, Python nyelvu fejlesztése és tesztelése

Pfeiffer Szilárd 1

2013. január 3.

1A könyv létrejöttét a FSF.hu Alapítvány a Szabad Szoftver Pályázat[5] keretében támogatta

Page 2: Helló Window!

Tartalomjegyzék

I Bevezetés 1

1. Gimp Tool Kit 21.1. Általánosságok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

1.1.1. Története . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.1.2. Elérhetosége . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

1.2. Részegységek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31.2.1. GTK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31.2.2. GDK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31.2.3. GLib . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31.2.4. Cairo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41.2.5. Pango . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

1.3. Nyelvi változatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41.3.1. GTK minus minus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41.3.2. PyGobject . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51.3.3. Összehasonlítás . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

1.4. Kapcsolódó projektek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51.4.1. Automata tesztelés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

2. Alapveto ismeretek 72.1. A fejlesztés fogalmai . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

2.1.1. A GTK+ objektum-orientáltsága . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72.1.2. A GTK alapfogalmai . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82.1.3. A GTK+ muködési sajátosságai . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

2.2. A tesztelés fogalmai . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102.2.1. Az ATK koncepciója . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102.2.2. A Dogtail muködése . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

3. A fejlesztés menete 133.1. Gimp Tool Kit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

3.1.1. Beszerzése . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133.1.2. Fordítása . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14

3.2. Saját alkalmazások . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143.2.1. Fordítás és linkelés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143.2.2. Futtatás . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

4. Elso ablakunk 164.1. Kódolási alapismeretek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

4.1.1. Forráskód formázása . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164.1.2. Elnevezési konvenciók . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164.1.3. Fejlécfájlok és importálás . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

4.2. Minimálisan alkalmazás . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174.2.1. Forráskód . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174.2.2. Fordítás és futtatás . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184.2.3. Eredmény . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

4.3. Tesztelés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194.3.1. Forráskód . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194.3.2. Futtatás . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

i

Page 3: Helló Window!

5. Szignálkezelés dióhéjban 215.1. Fogalmak . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215.2. Szignálkezelés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22

5.2.1. C, illetve C++ nyelvu változat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225.2.2. Python nyelvu változatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265.2.3. Fordítás és futtatás . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275.2.4. Eredmény . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

II Alapveto widgetek 28

6. Ablakok 296.1. Bevezetés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

6.1.1. Popup és toplevel ablakok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296.1.2. Window és dialóg . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296.1.3. Modalitás . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296.1.4. Tranziencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

6.2. Használat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306.2.1. Létrehozás . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306.2.2. Minimális példa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326.2.3. Tartalmi elemek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326.2.4. Vezérlo elemek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336.2.5. Megjelenítés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 366.2.6. Bezárás . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 366.2.7. Eseménykezelés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386.2.8. Saját eseménykezelo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

6.3. Platformfüggo sajátosságok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 406.3.1. Ablakkezelo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 406.3.2. Vezérlo elemek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

6.4. A kód . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416.4.1. Fordítás és linkelés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416.4.2. Futtatás . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416.4.3. Eredmény . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

6.5. Tesztelés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416.5.1. Keresés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416.5.2. Státuszok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 436.5.3. Interfészek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 436.5.4. Tulajdonságok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44

7. Konténerek 457.1. Fogalmak . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45

7.1.1. Konténerek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457.1.2. Méretezés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457.1.3. Elrendezés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46

7.2. Alapmuveletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467.2.1. Létrehozás . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467.2.2. Elem hozzáadása . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 477.2.3. Elem eltávolítása . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

7.3. Pa(c)kolás . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 487.3.1. Elemek elhelyezkedése . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 487.3.2. Térköz, pányvázás és szegély . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

7.4. A kód . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 517.4.1. Fordítás és linkelés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 517.4.2. Futtatás . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 517.4.3. Eredmény . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

7.5. Tesztelés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 517.5.1. Gyerekek keresése . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52

ii

Page 4: Helló Window!

8. Megjeleníto eszközök 538.1. Fogalmak . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53

8.1.1. Igazítás és helykitöltés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 538.1.2. Widgetek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 538.1.3. Szövegformázás . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 548.1.4. Widgetek összefüggései . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54

8.2. Alapmuveletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 548.2.1. Létrehozás . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 548.2.2. Megjelenítés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 568.2.3. Kezelés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57

8.3. Haladó muveletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 578.3.1. GtkLabel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 578.3.2. GtkTooltip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57

8.4. Tesztelés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 588.4.1. Objektum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 588.4.2. Állapotok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 588.4.3. Interfészek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 588.4.4. Viszonyok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59

9. Egysoros beviteli mezok 609.1. Fogalmak . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

9.1.1. Beviteli mezok típusai . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 609.1.2. Interfész . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

9.2. Alapmuveletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 619.2.1. Létrehozás . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 619.2.2. Tartalom kezelése . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 629.2.3. Csak olvasható mód . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 629.2.4. Jelszavak kezelése . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 629.2.5. Szignálok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62

9.3. Haladó muveletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 629.3.1. Ikonok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 629.3.2. Folyamatindikátor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 639.3.3. Iránymutató szöveg . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 639.3.4. Buffer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 639.3.5. Formázás . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64

9.4. Tesztelés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 659.4.1. Keresés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 659.4.2. Interfészek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 659.4.3. Állapotok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669.4.4. Tulajdonságok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669.4.5. Akciók . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

10. Választáson alapuló adatbevitel 6710.1. Fogalmak . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67

10.1.1. Beviteli mezok típusai . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6710.2. Alapmuveletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68

10.2.1. Létrehozás . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6810.2.2. Kezelés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6910.2.3. Szignálok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70

10.3. Haladó muveletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7010.4. Tesztelés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70

10.4.1. Keresés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7010.4.2. Státuszok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7010.4.3. Akciók . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7010.4.4. Viszonyok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7110.4.5. Interfészek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71

iii

Page 5: Helló Window!

III Összetett widgetek 72

11. Táblázatos megjelenítés alapjai 7311.1. Fogalmak . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73

11.1.1. Modell-nézet-vezérlo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7311.1.2. Modell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7311.1.3. Nézet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7311.1.4. Elemek elérése . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73

11.2. Alapmuveletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7411.2.1. Létrehozás . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7411.2.2. Kezelés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7711.2.3. Szignálok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80

11.3. Tesztelés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8011.3.1. Keresés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8011.3.2. Interfészek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8011.3.3. Állapotok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8011.3.4. Akciók . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80

A. Licencelési feltételek 81

Tárgymutató 82

Táblázatok jegyzéke 86

Ábrák jegyzéke 87

Kódrészletek jegyzéke 88

Irodalomjegyzék 89

iv

Page 6: Helló Window!

rész I

Bevezetés

1

Page 7: Helló Window!

1. fejezet

Gimp Tool Kit

1.1. Általánosságok

1.1.1. TörténeteMint megannyi szoftver a nyílt forrás több, mint negyedszázados történetében a GTK+ is felhasználói elégedetlenségeredményeként született. Peter Mattis és Spencer Kimball, a Kaliforniai Egyetem (Berkeley) hallgatói – az azóta is anyílt forrású szoftverek egyik zászlóshajójának számító képszerkeszto program, a GIMP fejlesztése közben – szembe-sültek az akkori idokben amúgy is csak kis számban rendelkezésre álló grafikus felhasználói felület készíto eszközökhiányosságival. Ezek leküzdésére döntöttek úgy – egyébiránt a Motif használata helyett –, hogy belekezdenek egy sajátfüggvénykönyvtár fejlesztésébe.

Az azóta nagykorúvá lett GTK+ több ízben is komoly átalakuláson ment keresztül. Ezek közül talán a legmeghatá-rozóbb, hogy az eredetileg GUI eszközkészletként indult projekt jócskán túllépett eredeti keretein. Ennek lehetoségét amoduláris felépítés teremtette meg, mely késobb még több ízben hasznára vált a projektnek. Azon részek, melyek nemközvetlenül függenek össze a grafikus felhasználói felületek fejlesztésével, vagy másutt is hasznosak lehetnek, külön mo-dulokban kaptak helyet, egy flexibilis eszközkészletet hozva így létre, melybol mindenki pontosan annyit és csak annyithasznál fel, amennyire feltétlenül szüksége van.

A legutóbbi foverzió – vagyis a GTK+ 3 –, melyrol a továbbiak szó lesz majd, nem csupán folytatja a hagyományokat,de igyekszik mindinkább kiszélesíteni a modularitás adta elonyok alkalmazási területeit.

1.1.2. ElérhetoségeA GTK+ többféle formában, többféle operációs rendszerre és grafikus szerverre is elérheto. A forma alatt ez esetben azértendo, hogy a függvénykönyvtár nem csupán forráskódként, hanem bináris változatban is letöltheto. Ez nyilván nemkülönösebb meglepetés, már csak azért sem, mert nyílt forrású szoftverrol van szó. Itt érdemes megjegyezni, hogy a licenca GTK+ – annak függoségei, illetve számos kapcsolódó függvénykönyvtár esetén – a GNU Lesser General Public License,rövidítve az LGPL, ami összefoglalva annyit tesz, hogy nyílt, illetve zárt forrású szoftverek egyaránt fejleszthetoek aGUI eszközkészlet használatával, azzal a kitétellel, hogy a függvénykönyvtár általunk használt – esetleges módosításokatis tartalmazó – változatának forrását ügyfeleinknek kérésre át kell adnunk. A szokásjog, illetve a saját érdekünk aztdiktálja ugyanakkor, hogy a javítások mielobb bekerüljenek a fejlesztok által karbantartott változatba. Visszatérve azelérhetoséghez a forráskód mind verziókezelo rendszeren (Git) keresztül, mind archív állományok formájában letöltheto.

A bináris változatok tekintetében elmondható, hogy mindaddig, amíg GNU/Linux alapú rendszereken dolgozunk,különösebb nehézségekbe nem fogunk ütközni, hiszen nagy valószínuséggel az általunk használt disztribúció mind afejlesztéshez (devel), mind a hibajavításhoz (debug) szükséges csomagokat tartalmazza. Ugyanakkor a GTK+ egy multi-platform eszköz, vagyis joggal várható el, hogy több operációs rendszeren is muködjenek az általunk megírt kódok. A máremlített GNU/Linux disztribúciókon túl – melyet a továbbiakban elsodlegesen, de közel sem kizárólagosan használunkmajd – a GTK+ mind a Microsoft Windows, mind pedig az Apple Mac OS X rendszerein elérheto.

A használható grafikus szerverekrol elöljáróban annyit érdemes megemlíteni, hogy a GTK+ portabilitásának két alap-pillére közül az elso az a megoldás, mely a felhasználó felület építoköveinek – a GTK+ által használt terminológiávalélve widget – implementációját elválasztja az azok megjelenítésére szolgáló rajzoló primitívek megvalósításától. Ennekrévén biztosítható, hogy a GTK+ forráskód túlnyomó részének változatlansága mellett – csupán egy újabb, úgynevezettbackend hozzáadásával – alkalmassá teheto egy újabb grafikus szerver, vagy alrendszer (pl: X11, frame buffer, HTML5,. . . ) alá.

2

Page 8: Helló Window!

1.2. RészegységekA GTK1 szervezésérol fontos elöljáróban megemlíteni, hogy példaértékuen választja szét a funkcionalitás egyes elemeit,jól elhatárolt implementációs részegységre, melyek fejlesztése egymástól függetlenül, a GNOME projekttel együttmukö-désben folyik.

Ez a megoldás – számos elonye mellett – jár természetesen néhány nehézséggel is. A modulok csak publikus interfé-szeiken keresztül tudnak egymással kommunikálni. Ez egyrészrol függetlenséget jelent a modulok belügynek tekinthetoimplementációs részleteinek terén, viszont komoly kötöttség a publikus felület oldalán, lévén annak változatlanságátólnem csak a külso projektek, de az egyes GTK modulok is függenek.

A hosszan fenntartandó állandóság hatásait jól példázza, hogy a GTK+ csak hat év után indított új foverziót (3.x),mely felszámolta a korábbi változattal való – helyenként csaknem teljesen felesleges – kompatibilitást. Az azóta elteltidoben2 viszont vajmi kevés projekt döntött úgy, hogy átáll az új verzióra, még azzal együtt sem, hogy az egyszerubbalkalmazások tekintetében ez különösebben komoly eroforrást nem igényel. Célszeruen tehát ezek az inkompatibilisváltások nem lehetnek túl gyakoriak.

A következokben sorra vesszük, mik azok a részegységek, melyek együtt a GTK C nyelvu változatát alkotják, smelyek természetesen a további nyelvi változatok alapjául szolgálnak.

1.2.1. GTKA GTK+ a grafikus felhasználó felületek (GUI) fejlesztéséhez szükséges felületi elemek (beviteli mezok, rádiógombok,listák, dialógus ablakok, . . . ), azaz widgetek tárháza, vagyis a GTK keretrendszer lelke. A függvénykönyvtár a korábbanleírtaknak megfeleloen csak a közvetlenül szükséges implementációt, vagyis a widgetek kirajzolásához, interakcióinak,adattárolásának megvalósításához szükséges kódok összességét jelenti. Ettol persze némiképp többet, de errol késobb(1.4.1) esik szó.

1.2.2. GDKA GIMP Drawing Kit (GDK) – a GTK+ részeként terjesztett – alacsony szintu rajzolási és ablakkezelési feladatok meg-valósítására, egyszersmind a magasabb szintu rutinok elöl történo elfedésére szolgáló függvénykönyvtár. A GDK fontosszerepet tölt be a GTK különbözo platformok közötti hordozhatóságának megteremtésében, lévén az általa nyújtott –viszonylag szuk köru – funkcionalitást újraimplementálva a GTK+ alkalmas lehet egy újabb grafikus környezetben valófutásra. Ezen funkciók alatt a már említett rajzolási primitíveken túl többek között rasztergrafikai feladatok, kurzor meg-jelenítése, illetve alacsony szintu ablakesemények implementálása értendo.

A fentieknek köszönheto, hogy az eredetileg csak az X Window System3 (X11) felett muködni képes GDK, máranemcsak egyéb Linux alapú szerveren (Wayland), de más operációs rendszereken (Windows, Mac OS X), sot akár web-böngészoben is muködni képes4. A grafikus alrendszer portolása mellett felmerülo problémák jelentékeny részét a GLibfüggvénykönyvtár oldja meg.

1.2.3. GLibA GLib a GNOME projekt egyik fundamentális eleme, mely történetileg ugyan kötodik a GTK+-hoz, mára azonban tel-jesen önállóvá vált. Eredendoen a GTK+ által használt, de attól függetlenül is létjogosultsággal bíró, platformfüggetlenkódok kiemelése egy külön függvénykönyvtárba, melyet számos – a GNOME, vagy a GTK projektekhez akár nem iskapcsolódó – szoftver5 alkalmaz. A meglehetosen szerteágazó funkcionalitáscsomag melyet a Glib megtestesít, rövi-den a következoben foglalható össze; C programozási nyelven írt standard függvénykönyvtárak nem, vagy csak részbenelérheto, esetleg nehézkesen használható eszközök összessége.

A teljesség igénye nélkül megemlítendoek a GLib adattárolói (láncolt listák, fák, dinamikus tömbök, hash táblák,. . . ), hordozható adattípusai (guint32, gboolean, . . . ), memória allokációs függvényei (g_new, g_allocator_new,g_slice_alloc, . . . ), gyakran használt formátumok (dátum, ido, URI, fájl- és könyvtárnév, . . . ) kezelésére szolgálóeszközei, konverziós algoritmusai (base64, endianness, karakterkódolás, . . . ) széles körben alkalmazott módszerek imp-lementációi (XML, Glob, Regex, . . . ). Mindezeken túl a GLib tartalmaz néhány külön is említésre méltó alrendszert.

1a GTK rövidítés alatt a továbbiakban az általánosságban vett grafikus felhasználói felület fejlesztoi eszközt, míg GTK+ alatt ennek C nyelvuváltozatát értjük

2a 3.0.0 verzió megjelenése idopontja 2011. február3a Linux alapú rendszerek, a GDK születésének idejében, gyakorlatilag kizárólagos grafikus szervere4ezen szolgáltatáshoz eléréséhez websocket támogatásra van szükség a böngészoben, illetve a Broadway elnevezésu backend bekapcsolására a GTK+

fordításakor5példának okáért a Compiz, a Midnight Commander, vagy a syslog-ng

3

Page 9: Helló Window!

GObject

A GLib Object System egy C nyelven írt objektumorientált keretrendszer, mely megvalósít számos olyan funkciót (szár-maztatás, virtuális függvények, típus, objektumok memória menedzsmentje, . . . ) melyek például a C++, vagy a Javaesetén nyelvi szinten adottak. A GObject, mint osztály szolgál például alapjául a GTK+ által implementált minden egyeswidgetnek. A kép ezzel azonban még közel sem teljes.

A GObject implementál számos alapveto fontosságú egyszeru (gdouble, gint, . . . ) és összetett típust, illetve tá-mogatja ezek felhasználását saját típusok létrehozásakor, illetve a GObject-bol származó osztályokban adattagként valóelhelyezését. Emellett megvalósít egy – az objektumok állapotváltozásainak követésére szolgáló – kommunikációs al-rendszert (signal). Fentiek létrehozásakor kifejezett cél volt a rugalmas bovíthetoség és a könnyu adaptálhatóság másnyelvekre. Ez utóbbi a késobb (1.3) említésre kerülo nyelvi változatok egyszeru megvalósíthatóságának elofeltétele.

GModule

A GModule egy dinamikus modulok betöltésére szolgáló függvénykönyvtár, mely rendelkezésre áll mindazon rendszere-ken, ahol a GLib is, elfedve ez egyes operációs rendszer különbözoségeit ezen a területen.

GThread

A GThread célja erosen hasonlatos az imént említett GModule-éhoz, vagyis biztosítani egy, a platformok sajátosságaitólfüggetlen megoldást ezúttal nem a dinamikus modulbetöltés, hanem a szálkezelés tekintetében.

GIO

Az eddigieket folytatva a GIO is egy, a multiplatformos programozás során gyakran felmerülo probléma – jelesül a fájlok,fájlrendszerek, meghajtók kezelése – megoldására született. A megfelelo POSIX hívásokkal eddig is megvalósíthatóvolt egy kvázi platformfüggetlen fájlkezelés, így ennek önmagában nem lenne számottevo haszna. A GIO ugyanakkortöbb, mint egy egyszeru POSIX hívásokat burkoló függvényhalmaz. A GObject-re támaszkodva egy magasabb szintu,dokumentumközpontú interfészt valósít meg.

1.2.4. CairoA Cairo egy eszközfüggetlen6 kétdimenziós vektorgrafikai függvénykönyvtár, melyet kifejezetten a hardveres gyorsítók-kal való együttmuködésre terveztek, s mellyel a GDK a kétdimenziós rajzolási feladatait végzi. Érdemes megemlíteni,hogy a Cairo nem a GNOME, hanem a freedesktop.org projekt része.

1.2.5. PangoA Pango szövegek képi formában történo eloállításáért (rendering) és megjelenítésért (lay out) felelos a GTK-n belül,de természetesen a GTK-tól függetlenül is használható, lévén a függvénykönyvtár az elobbiekhez hasonlóan számosplatformot támogat.

1.3. Nyelvi változatok

1.3.1. GTK minus minusA gtkmm, illetve annak függoségei adják a GTK projekt C++ nyelvu változatát. Ezek a függvénykönyvtárak wrapperek azeredeti C változat fölött, az ebbol fakadó elonyökkel és korlátokkal együtt. Ezen kódok jelentékeny része wrapper mivol-tukból következoen generált, ugyanakkor számos helyen – ahol ez funkcionalitáshoz a programozási nyelvhez leginkábbilleszkedo megvalósításához szükséges – eredeti kódot is tartalmaz.

A C, illetve C++ nyelvu változatok a leheto legkisebb mértékben térnek el egymástól. Ez egyben azt is jelenti, hogyaz egyes nyelvi változatok nem tartalmaznak a többihez képest többlet funkcionalitást. Nem lehet azonban eltekinteni azegyes programozási nyelvek adta lehetoségek elonyeitol, hátrányaitól, melyek könnyebbé vagy nehezebbé teszik a GTKadott nyelven való használatát.

Libsigc++

A gtkmm implementációjánál használt függvénykönyvtár, ami lehetové teszi a szignálkezelés típusbiztos megvalósítását,mely a C változatnál – a nyelvi sajátosságok okán – nem adott.

6értsd hardvereszközöktol független

4

Page 10: Helló Window!

GTK+ gtkmm PyGObject

Nyelv: C C++ Python

Implementáció módja: natív wrapper binding

Objektumorinentálttechnikák használata: közvetett natív natív

Licenc: LGPL LGPL LGPL

Ismertebb projektek: Evolution, Firefox, Gimp, . . . GParted, Inkscape, . . . gedit

1.1. táblázat. A GTK+, gtkmm, PyGObject összehasonlítása

1.3.2. PyGobjectA Python változat – ahogy számos más egyéb nyelvi variáció is – alapjai gyökeresen megváltoztak a GTK+ új fover-ziójának megjelenésével. A korábbi – a gtkmm által is alkalmazott – módszer, az eredeti változatot rejti el, burkolja be(wrap), vagy egy köztes réteget képez a C, illetve a cél nyelv – esetünkben a Python – között. Ezen réteg többé-kevésbétermészetesen automaták (pl: generátor szkriptek) révén jön létre, ugyanakkor igaz az, hogy nem közvetlenül az eredetikódbázist használja a burkoló réteg létrehozására. Következésképpen a GTK+ publikus felületében bekövetkezo válto-zásokat az egyes wrappereknek rendre követniük kell, holott a GTK+ kódjának írásakor is adottak azok a metaadatok,melyek mondjuk egy Perl, vagy Python elkészítéséhez szükségesek.

GObject Introspection

Az elobbi gondolatot tovább fuzve juthatunk el ahhoz a kézenfekvo kérdéshez, hogy miért nincsenek az említett meta-adatok rögtön a GTK+ – illetve a GLib, Pango és a többi függoség – kódja mellett, ahonnan kinyerve azokat az egyesnyelvi változatokat egyszeruen generálni lehetne. A GObject Introspection pontosan ezt célozza. Egy binding elkészíté-séhez szükséges adatok a C nyelvu változatok – kódjában egy erre a célra meghatározott formátumban – megjegyzéskéntszerepelnek, a különbözo nyelvu változatok pedig ezt felhasználva jönnek létre.

1.3.3. ÖsszehasonlításAz egyes változatoknak megvannak a maguk – jellemzoen a programozási nyelv sajátosságaiból következo – elonyei.Ilyenek lehetnek például a C nyelv, illetve a fordítók széles köru elterjedtsége, a C++ azon sajátossága, hogy a nyelvnyújtotta módszereket, mint például az örököltetés, itt közvetlenül használhatjuk ki, vagy a Python nyelvu fejlesztéssebessége. Míg mondjuk a C nyelv esetében bizonyos funkciók kissé nehézkesen használhatóak, addig a C++ objektum-orientált megközelítése mellett ugyanez a funkció játszi könnyedséggel elérheto, vagy éppen a Python nyújtotta szkriptkörnyezet ad könnyebb, rugalmasabb kezelhetoséget. Az említett három változat tekintetében a fobb ismérveket a 1.1táblázat tartalmazza.

1.4. Kapcsolódó projektek

1.4.1. Automata tesztelésA grafikus felületek kapcsán sajnálatosan elhanyagolt terület az automata tesztelés, azon nyilvánvaló tény ellenére is, hogya felhasználó épp ezeken a felületeken keresztül éri el az érdemi funkcionalitást és nyeri elso benyomásait a szoftverrelkapcsolatban, így ennek megjelenése, valamint helyes muködése dönto az alkalmazás késobbi sikerességének tekinte-tében. Ezzel együtt igaz továbbá, hogy teljes köru (end-to-end) megvalósított tesztelés mindenképpen a felhasználófelületrol indított akcióval kell induljon és az ugyanott tapasztalt reakció ellenorzésével kell végzodjön.

A GTK felhasználásával fejlesztett felületek tesztelésénél rendelkezésünkre áll a megfelelo keretrendszer, mely lehe-toséget teremt, hogy az elkészült felületi elemek muködését automaták segítségével teszteljük. A késobbiek során bemu-tatásra kerülo mintapéldák esetén mindenütt kitérünk majd az azok kapcsán felmerülo tesztelési feladatok megoldásánakmikéntjére. Most azonban lássuk nagy vonalakban hogyan is muködik ez a tesztelési keretrendszer.

5

Page 11: Helló Window!

Accessibility Tool Kit

Elsore talán egymástól távoli területnek tunik a szoftverek akadálymentesítése (accessibility), valamint a felhasználófelületek automata tesztelése, egy valami mégis összeköti oket. Ez pedig az a követelmény, aminek a szoftver mindkét célelérése érdekében eleget kell tegyen, ami nem más, mint az alkalmazás vezérelhetosége bizonyos felhasználói interakciókkizárása mellett is. Az automata tesztelés esetén ez gyakorlatilag az összes eszköz (billentyuzet, egér, . . . ) kizárásátjelenti, hiszen a felhasználót ez esetben teljes egészében a tesztelést végzo szoftver helyettesíti.

A szoftverek akadálymentesítésének biztosítása egy speciális megközelítést igényel, mely a fogyatékossággal élo em-berek szoftverekkel végzett munkájának megkönnyítését helyezi elotérbe. Ehhez az ATK csupán annyit tesz, hogy definiálegy interfészt, melyen keresztül az adott szoftvert el lehet érni. Indulva onnan, hogy egy adott alkalmazást ki lehet válasz-tani az összes aktuálisan futó alkalmazás közül, folytatva azzal, hogy le lehet kérdezni az általa megnyitott ablakokat, azabban lévo felületi elemeket (widget), egészen odáig, hogy az általuk tárolt értékeket (egy beviteli mezo szövege, folya-matindikátor értéke, . . . ), illetve állapotokat (rádiógomb kiválasztott állapota, beviteli mezo szerkeszthetosége, . . . ) írniolvasni lehet, rajtuk akciókat (gomb lenyomása, menüelem kiválasztása) végezhetünk.

Gail

Lévén az ATK lényegében csak egy interfész definíció, ahhoz minden esetben7 tartozik egy implementáció, mely az adottfelületfejlesztoi rendszer muködését megfelelteti az ATK által definiáltaknak. A GTK esetén ez az implementáció a Gail.

Dogtail

A Dogtail egy Python nyelven írt és Python nyelven használható tesztautomatizációs eszköz, illetve keretrendszer. Se-gítségével létrehozhatók felhasználói felületek – a már említett ATK interfészen keresztül – tesztelo szkriptek, többféleformában és módon is. Ami a módot illeti, lehetoségünk van egyrészrol effektíve forráskód – azaz egy Python szkript– formájában létrehozni a tesztjeinket, vagy úgymond ”felvételt” készíteni magáról a tesztelésrol, majd az így rögzítetteseményeket mint tesztet visszajátszani. Ha az elobbi módszernél maradunk – amirol a további részekben is szó esik –,akkor is két lehetoség adódik, hiszen a Dogtail rendelkezik egy procedurális, illetve egy objektumorientált megközelítésuAPI-val, melyek tetszés szerint használhatóak a tesztek elkészítésekor.

Accerciser

Mind az automata tesztelo szkriptek megírásakor, mind egy konkrét alkalmazás felületének feltérképezésére, mind pedigaz ATK interfésszel történo ismerkedésre alkalmas eszköz az Accerciser, mely az akadálymentesített szoftverek feltér-képezésére szolgáló eszköz és mint ilyen pontosan azon adatok megjelenítésére és módosítására, valamint azon akciókvégrehajtására alkalmas, amire a Dogtail szkriptek révén képesek vagyunk.

7már amennyiben az adott grafikus felületfejlesztoi rendszer – mint amilyen a GTK+, vagy mondjuk a Qt – biztosítani kívánja az ATK-n keresztülielérést

6

Page 12: Helló Window!

2. fejezet

Alapveto ismeretek

2.1. A fejlesztés fogalmai

2.1.1. A GTK+ objektum-orientáltságaEbben a fejezetben arra próbálunk mélyebben is rávilágítani, hogy bár a GTK+ ugyan C nyelven íródott, mégis számos azobjektum-orientált nyelvek esetén megszokott terminológiát használ, sot ezeket a nyelvi eszközök adta mértékben meg isvalósítja. Ezért az objektum-orientált fejlesztés fogalmait, kifejezéseit joggal használjuk még akkor is, ha GTK+ nyelvufejlesztésrol esik szó.

Azzal együtt, hogy az objektum-orientált mechanizmusokat nyelvi szinten a C nem, csak a C++, illetve a Python tá-mogatja lehetséges ezekkel élni ezen változat esetén is. Lássuk mik lennének ezek és hogyan válik lehetové alkalmazásuka GTK+ esetén.

Egységbezárás

Az objektum-orientált alapelvek közül C nyelven is viszonylag jól biztosítható elvrol beszélünk. A GTK+ meg is teszi,amit ebben a tekintetben meg lehet. Az adatstruktúrákat – jelen esetben widgeteket – és az azokon muveleteket végzofüggvényeket a lehetoségekhez mérten egységként kezeli, valamint elrejti oket a külvilág elol.

A GTK+ minden saját makrót/függvényt GTK/gtk prefixszel lát el (a GLib esetén ez pusztán csak egy kis, illetve nagyg betu). Egy adott részterület – például egy widget – saját „névtérrel” is rendelkezhet, azaz újabb prefixet vezethet be1.Ezeket egymástól, illetve a „valódi” funkciót jelölo nevektol _ (aláhúzás) jellel választjuk el. A C++ wrapper esetén –kihasználva a kézenfekvo nyelvi lehetoséget – a prefixek szerepét természetesen a névterek, illetve az osztályok veszikát2.

Ezen prefixelt makrók/függvények elso paramétere minden esetben a prefix által meghatározott típusú objektum,elosegítve ezzel is ezen függvények, illetve az általuk kezelt objektumok egy egységként való kezelését. A privát adatokkülvilág elöl való elrejtésére a C nyelven erre a célra széles körben alkalmazott átlátszatlan mutatókat3 (opaque pointer)használja a GTK+, ami lehetové teszi az objektum által használt adatszerkezetek, implementációs módszerek elfedését apublikus interfész használó kódok elöl.

Öröklodés

Megoldott a widgetek egymásból történo származtatása, sot felhasználói widgetek is definiálhatóak a már meglévoekretámaszkodva. Az kód újrahasznosítását jól mutatja az a tény, hogy a GObject osztály – mely önmagában is számoshasznos funkcióval rendelkezik – minden widget, illetve számos más a GTK-ban használt nem vizuális elemnek ose. Megkell jegyezni, hogy a gtkmm, illetve a PyGObject esetén – lévén ezen esetekben az objektum-orientáltságot támogatja maga nyelv – természetesen a származtatás nagyságrenddel egyszerubb, de mintapéldákat felhasználva némi rutinnal a GTK+esetén sem igényel különösebb erofeszítést.

A widgetek öröklési fájáról már itt érdemes megjegyezni, hogy az nem csupán két szintu. A GObject típus a facsúcspontja, de közte és az egyes widgettípusok között adott esetben még számos csomópont található az öröklési fában.A hasonló funkcionalitású, következésképp rendszerint hasonló megjelenésu widgetek (9.1) értelemszeruen egymásbólszármaznak. Ez még a leghétköznapibb esetekben is igaz. A számok kezelésére is alkalmas widget (spin button) ose azegyszeru, egy sornyi szöveg befogadására alkalmas beviteli mezot megvalósító widget (entry).

1a GtkWindow típushoz tartozó függvények prefixe gtk helyett gtk_window2a Window típus a gtkmm esetén Gtk névtéren belül szereplo Window nevu osztály3a módszer egyebek mellett pimpl (pointer to implementation idiom) néven is ismert

7

Page 13: Helló Window!

(a) Egysoros beviteli mezo[9] (b) Számbeviteli mezo[9]

2.1. ábra. Öröklodés hasonló funkciójú widgetek között

A mechanizmus további elonye az interfészek4 kialakításának lehetosége. A GTK+ a 3-as foverziót megelozoentörekedett arra, hogy a különbözo widgetek azonos funkciót megvalósító részeit (kattintható, szerkesztheto, görgethetoelemek) egységes programozási felületen keresztül érhetjük el, ezt követoen ez a tendencia tovább erosödik.

Polimorfizmus

Hasonlóan az öröklodéshez – pusztán nyelvi szinten – itt sem érheto el teljes köru megoldás C nyelv esetén. Ugyanakkora fo momentum, vagyis a származási hierarchia egyes osztályainak specifikus viselkedése egy adott funkciót megvalósítómetódusok tekintetében elérheto. A widgeteket leíró struktúrákban ugyanis a származtatott osztályokból felülírhatóakaz egyes funkciókat implementálható függvények mutatói5. Az így létrejövo többalakúság bár közel sem tökéletes, ámszámos gyakorlati problémát megold.

Ezen felül a GTK+ minden widgettípushoz – mondhatni osztályhoz – definiál egy-egy makrót, melyek segítségé-vel futásidoben ellenorizheto egy adott widget, azaz objektum tényleges típusa, hasonlóan ahhoz, amire a dynamic_casthasználata ad lehetoséget a C++-ban. Azt a mechanizmust, melynek révén lehetové válik a GTK+-ban a futás ideju típus-ellenorzés, a már említett GObject osztály implementálja, az ebbol származó saját osztályainknak nem csupán lehetséges,de szükséges is a használata.

2.1.2. A GTK alapfogalmaiWidget

A fogalmat egyrészt, mint gyujtofogalmat használjuk a grafikus felhasználói felületek programozása során a felhasználófelületek egyes grafikai elemeinek megnevezésére6, mint amilyen például egy rádiódomb, egy szövegbeviteli mezo, vagyakár egy kép. Másrészrol a GtkWidget minden – ez elobbi értelemben vett widget – ososztálynak a neve is – még ha azszármaztatás a C esetében nyelvi szinten nem is támogatott – melybol minden egyes elem származik.

A GtkWidget osztály, mint a widget fogalom objektum-orientált leképezése a felhasználó felület egyes elemeinektulajdonságait, illetve az azokhoz kapcsolódó muveleteket zárja egységbe, biztosítva egyúttal az általa implementált funk-ciók újrahasznosíthatóságát éppúgy, mint a felülbírálhatóságukat. Lássuk, hogy ezen általánosságban megfogalmazottelvek mögött mi is rejtozik.

Tulajdonságok

A mindennapi felhasználás – ez esetben ugye a mindennapi szoftverfejlesztés – során talán a leggyakrabban felmerülokérdés – nyilván csak azt követoen, hogy megismerkedtünk milyen tulajdonságokkal bírnak a különbözo widgettípusok–, hogy mik ezen tulajdonságok aktuális értéki konkrét objektumaink esetén. Ezen tulajdonságok (property), illetveezek értékei határozzák meg widgeteink megjelenését, a felhasználói interakciókkal, illetve más widgetekkel összefüggoviselkedését.

Ezek a tulajdonságok lehetnek egészen kézenfekvoek, mint amilyen például egy beviteli mezoben szereplo szöveg,egy folyamatindikátor százalékban vett értéke, egy rádiógomb be-, kikapcsolt állapota. Lehetnek teljesen általánosak,mint amilyen widgetek neve, láthatósága, méretei, a tartalmazó konténerben elfoglalt helyzetük, igazításuk. Tükrözhetnekvalamilyen állapotot, mint hogy a widget fókuszban van-e, fogad-e a felhasználói interakciókat és természetesen számos– csak az adott widgettípusra vonatkozó – tulajdonságot.

Szignál

Lévén egy alapvetoen eseményvezérelt eszközrol beszélünk a fent említett tulajdonságok értékeinél már csak a widgetáltal definiált események (event) bekövetkezésérol, vagy éppen elmaradásáról való értesülés lehet fontosabb. Különösenazon esetekben mikor az esemény számunkra valamilyen szempontból jelentoséggel bírnak, következésképp arra reflek-tálni szeretnénk. Ez persze nem mindig van így, hiszen mondjuk adatok bevitelére szolgáló ablakban egy rádiógomb

4a kifejezés alatt a Java nyelv interfész, illetve a C++ absztrakt osztálya értendo5ami hasonló eredményre vezet, mint a C++ virtual kulcsszavának használata esetén6ebben az értelemben a ”window gadget” kifejezés rövidítése

8

Page 14: Helló Window!

állapotának változása nem feltétlenül érdekes, inkább csak annak akkori értéke, mikor az ablakon található nyugtázógombot (pl: Ok, Alkalmaz, . . . ) lenyomtuk és az ablakban megadott adatoknak megfeleloen szeretnénk eljárni. Ellenbenez utóbbi eseményrol – mármint hogy a gomb lenyomásra került – csaknem minden esetben értesülni szeretnénk.

A widgetek eseményeinek bekövetkeztérol a GObject osztály által implementált értesítési rendszer, a szignálok (sig-nal), révén áll módunkban tudomást szerezni. Ezen mechanizmus keresztül valósítható meg az eseményekhez kezelofügg-vények (callback) kapcsolása, ahol a felhasználó akcióra a megfelelo reakciót válthatjuk ki. Az imént említett nyugtázógomb lenyomásának hatására példának okáért bezárhatjuk az ablakot, vagy épp hibaablakot dobhatunk fel, ha a bevittadatok a validáció során nem bizonyultak helyesnek. Itt érdemes megjegyezni, hogy a GObject nem csupán a GtkWidgetosztály ose, hanem más GTK-s elemeknek is, melyek jellemzoen nem hagyományos widgetek, ugyanakkor ki szeretnékhasználni a GObject osztály szolgáltatásait. Az olyan elemeknek is lehetnek tehát szignáljai, melyek nem jelennek megközvetlenül a felületen, amire egy adattároló (pl: GtkTreeModel, TextBuffer) objektum lehet jó példa. Ezen osztályokpéldányai is szignálokon keresztül kommunikálnak, így adnak például jelzést arról, hogy a tárolt elemekben változás álltbe.

A GTK szignál kulcsszavának jelentése (jel, jelzés) tehát jól tükrözi funkcióját. Minden widgethez tartoz(hat)nak kü-lönbözo események – mint amilyen egy gomb esetén annak lenyomása (vagy éppen felengedése), egy beviteli mezonél azabba történo írás – melyekrol a widgetek – jellemzoen GDK révén, egy alacsony szintu7 esemény formájában – tudomástszereznek, majd elvégzik a megfelelo muveleteket – gomb újrarajzolás, beviteli mezobe karakter írása a karakter megje-lenítése – majd értesítést küldenek a program többi része felé, immár egy magasabb szinten8 értesítést. Ezt az értesítést,avagy jelzést nevezzük szignálnak.

A szignálokhoz alapvetoen három tevékenységhez kapcsolódik. Ezek közül ketto leginkább csak a saját widgetekfejlesztése során kerül elo, míg a harmadik gyakorlatilag még a legegyszerubb esetekben is nélkülözhetetlen. Ez utóbbi afüggvények kapcsolása (connect) az eseményekhez, ezt azonban meg kell elozze a a másik két említett muvelet. Idorendisorrendben ez a szignálok regisztrációja register) – ahol meg kell adunk az eseményünk nevét, illetve paramétereit –,illetve a szignálok küldése, kibocsátása (emit), ahol a regisztráció során megadott nevet, valamint paramétereket szükségesmegadni. Az eseménykezelok kapcsolásakor ezt a nevet használjuk fel, meghívásukkor pedig ezek a paramétereket kapjukmeg.

Callback

Amennyiben egy adott widgethez kapcsolódó valamilyen eseményrol (event) tudomást kívánunk szerezni a program fu-tása során, ezt úgy tehetjük meg, hogy a widget megfelelo típusú jelzéséhez (signal) eseménykezelo függvényt (callback)kapcsolunk. Itt minden olyan muvelet elvégezheto, ami nem a widgethez, hanem annak programunkban betöltött szerepé-hez kötodik. A korábbi példánál maradva ha egy adatok bevitelére szolgáló ablak Ok gombjának lenyomásánál szükségeslehet a felhasználó által megadott adatok szintaktikai, illetve szemantikai ellenorzése, az ellenorzött adatok mentésére,majd az ablak bezárására, probléma esetén hibaablak feldobására, valamint a beviteli folyamat újrakezdésére, akkor azt akezelo függvényben mind megtehetjük. Egyébiránt az egyes tulajdonságok (property) megváltozása szintén eseményekminosül, vagyis ezekhez is módunk van függvényeket csatolnunk.

2.1.3. A GTK+ muködési sajátosságaiMain Loop

Az események kezelése kapcsán megválaszolandó az a triviálisan adódó kérdés, hogy az egyes widgetek hogyan szerez-nek tudomást a rajtuk – a felhasználók, vagy éppen az automata tesztelo eszközök által – végrehajtott akciókról. A válaszpedig épp ugyanabban rejlik, mint bármely más felhasználói felületek fejlesztésére szolgáló eszközkészlet esetén, azazaz eseményvezérelt muködési modellben. Ez nem jelent más mint, hogy a GTK+ mindaddig várakozik, amíg valamilyenforrásból esemény nem érkezik (pl: egér mozgatása, gombjainak lenyomása, billentyu felengedése, . . . ). Amennyibenegy esemény bekövetkezik meghatározza, melyik widgetet érintett az esemény által, meghívja a widget megfelelo ese-ménykezelo függvényét, majd újabb várakozásba kezd.

Ennek a várakozási ciklusnak az implementációja main loop. A GTK+ tulajdonképpeni fociklusa a Glib függvény-könyvtárban implementált, a GTK+ ezt újra felhasználva ciklizál, várva a grafikus szerver – legyen az a Linux, a MacOS X, vagy a Windows megfelelo alrendszere – üzeneteire. Mindezt a GDK-n keresztül teszi, mely – mint azt az elozorészben is említettük – egy vékony burkoló réteg az ablakozó rendszer köré. A main loop tehát az ami az imént említettkapcsolaton át eljuttatja az ablakozó rendszer alacsony szintu eseményeit a GDK által standardizált formában az egyeswidgetekhez, hogy ezek a feldolgozást követoen egy magasabb szintu eseményt váltsanak ki a többi widget, illetve azapplikáció más részei felé.

7billentyu lenyomása, felengedése, egérkattintás, . . .8beviteli mezo értékének változása, kattintás, . . .

9

Page 15: Helló Window!

Referencia-számlálás

A GTK+ segítségével létrehozott felületek – ahogy azt a késobbiekben látni fogjuk – nem widgetek szórvány halmazát,hanem egymással szoros összefüggésben álló (szülo-gyerek, modell-nézet-vezérlo kapcsolat) elemek hálózatát jelentik.Ennek okán az megoldandó feladat, hogy az egymáshoz valamilyen szempont alapján kötodo elemek egymás oly módontudják hivatkozni, hogy hivatkozások létrejötte, illetve megszunése egyúttal a widgetek memória menedzsmentjére ismegoldást adjon. Ennek bevett módszere a referenciák tartása a hivatkozott elemekre, mellyel a GTK+ is él.

Minden GObjectbol származó osztály – így a GtkWidget is – rendelkezik referencia-számmal, mely tulajdonképpenazt fejezi ki, hogy hányan hivatkoznak az adott elemre. A GTK+ – pontosabban ez esetben a GLib – ”lebego” referenciát(”floating” reference) alkalmaz, mely azt jelenti, hogy az objektum létrejöttekor annak referenciája 1 lesz, bár a widgetreekkor még nem hivatkozik semelyik másik widget sem, azaz ezt a referenciát úgymond nem birtokolja senki. Amikoregy widgetre megszületik ez elso valódi hivatkozás, például egy konténer osztályba – mint amilyen egy közönséges ablakis – tesszük a widgetet, vagyis létrejön az elso valódi hivatkozás az elemre, akkor az a hivatkozó birtokába kerül. Areferencia-érték változatlanul 1 marad, viszont a lebego referencia elsüllyesztésre (sink) kerül. Minden ezt követo esetbena konténerbol történo eltávolítás csökkenti, ahhoz való hozzáadás pedig növeli a referencia értékét. Érdemes felhívni afigyelmet arra, hogy az elmondottak alapján, ha hozzáadtuk widgetünket egy konténerhez, majd pedig eltávolítjuk beloleazt, akkor annak referenciája 0-ra csökken, ami maga után vonja a widget destruktorának lefutását. Ezt elkerülhetjük, haaz eltávolítás elott explicit módon növeljük a referenciát, amit aztán csökkentenünk kell, ha egy másik osztály „birtokába”adjuk a widgetet.

Szülo-gyerek kapcsolat

A szülo-gyerek kapcsolat a már említett konténerek – azaz a GtkContainer, illetve az abból származó osztályok – vi-szonylatában merül fel. Ezen elemek teszik lehetové a widgetek felületen való elrendezését (ablakok, táblázatok, gombok,. . . ), egymásba ágyazását. A konténer tehát az a widget, amely további widgetet, vagy widgeteket tartalmazhatnak. Ilyenértelemben egy szülo-gyerek kapcsolatot valósítanak meg, ahol minden szülonek lehetnek gyermekei, de egy gyermekwidgetnek minden esetben csak egy szüloje van, vagyis a szülok és a gyerekek egy fa hierarchiát alkotnak.

Ez a szerkezet több szempontból is fontos szerepet játszik a GTK+ muködése során. Egyrészrol a referencia-számlálásnálmár említett módon, azaz ha egy widgetet hozzáadunk egy konténerhez, akkor az úgymond tart rá egy referenciát – vagya referenciaszám növelésével, vagy ”lebego” referencia elsüllyesztésével –, majd elereszti azt a konténerbol való eltávo-lításakor. Másrészrol egy még nem ismertetett – a szülo- és gyerekwidgetek viszonyának tulajdonságait rögzíto – mecha-nizmust tesz lehetové, melyrol a késobbiekben még részletesebben esik szó. Elöljáróban csak annyit, a widgetek sajáttulajdonságain (property) túl, léteznek olyanok is melyek szülo widgetekkel való kapcsolatára jellemzoek, mint például agyerek widgetek elhelyezkedése a konténerben (pozíció, térköz, kiterjedés, . . . ).

Interfészek

A GTK+, erosítve az objektum-orientált megközelítést, olyan absztrakciós rétegeket definiál, amiket az adott funkciót(szöveg bevitel, aktiválhatóság, igazítás) betölto widgetek implementálnak. Az ilyen típusú általánosítások komoly ha-szonnal bírnak, mikor az adott funkciót egységes felületen keresztül, a konkrét implementáció részleteivel nem törodve,szeretnénk kezelni.

Függetlenül attól, hogy például egy, vagy többsoros beviteli mezorol legyen szó, a karaktereket épp úgy szeretnénk ki-olvasni mindkét esetben. Éppúgy igaz ez az elrendezés (orientation) tekintetében, hisz amennyiben a megfelelo widgetek– jellemzoen a konténerek – megvalósítják ezt, az elrendezésre vonatkozó interfészt, a vízszintes, illetve függoleges ori-entáció futás közben is könnyedén váltható (flip). Van egy olyan interfész, amit minden egyes felülettel rendelkezo widget(GtkWidget) és számos felülettel nem rendelkezo objektum (GObject) is implementál (GtkBuildable). Ezen osztá-lyon keresztül valósulnak meg a legalapvetobb funkciók, mint amilyenaz objektumok nevének, tulajdonságok értékéneklekérdezése, beállítása, a gyerek widgetek létrehozása9.

2.2. A tesztelés fogalmaiA tesztelési feladatok ellátása kapcsán a GTK ismerete bizonyos esetekben nem árt, míg más esetekben nem sokat használ.Ez azon egyszeru oknál fogva van így, mivel a tesztelés során a megközelítés meroben eltéro, lévén a teszteléshez használtrendszer nem közvetlenül a GTK+-ra , hanem az ATK-ra épít.

2.2.1. Az ATK koncepciójaA már említett ATK koncepciójában némiképpen különbözik a GTK+-tol. Lévén ezt az interfészt a fogyatékossággal eloemberek szükségleteinek kielégítésére tervezték, elsosorban nem magukra a widgetekre, vagy azok kapcsolataira, felületi

9mára a Glade elnevezésu felhasználói felületek tervezésére szolgáló alkalmazás is ezt az interfészt használja

10

Page 16: Helló Window!

megjelenésére, hanem az általuk hordozott információkra koncentrál. Ezen információk rejtozhetnek természetesen awidgetek által megjelenített szövegekben, vagy számszeru értékekben éppúgy, mint a widgetek aktuális állapotában (aktív,szerkesztheto, látható, . . . ), ugyanakkor persze az egymás közi viszonyok (tartalmazás, vezérlés, . . . ) is meghatározóaklehetnek.

Ezen interfészek, állapotok, illetve viszonyok függetlenek a konkrét implementációtól. Bármely widgetkészlet szá-mára implementálhatóak, sot implementálandóak, ami annyit tesz, hogy az egyes widgetkészletek logikája még ha nagy-jából egyezik is az ATK logikájával, számos ponton kisebb-nagyobb eltérések tapasztalhatóak, amiket szükséges valami-lyen, a widgetkészlet és az ATK között elhelyezkedo, azokat összeköto (bridge) implementációval áthidalni.

Interfészek

A kifejezetten a funkcionalitáson alapuló megközelítés egyenes következményei az ATK interfészei. Ezek gyakorlatilaga grafikus felhasználói felület elemeit legfobb funkcióik szerint csoportosító eszközök. Egy adott GTK widget természe-tesen megvalósíthat több interfész is, lévén többféle funkciót is betölthet. Egy egyszeru példával megvilágítva a helyzetetegy közönséges gomb (GtkButton) egyszerre valósítja meg a szöveg (AtkText), illetve a képek (AtkImage) lekérdezé-sére szolgáló interfészeket, hiszen a gombon kép és felirat egyaránt elhelyezheto.

Állapotok

Bizonyos esetekben nem a widget által tartalmazott adat – legyen az szám, vagy szöveg, esetleg kép – hordozza azinformációt, hanem widget valamilyen szempont szerinti állapota. Általánosságban véve ilyen állapot lehet egy widgetláthatósága, egy ablak átméretezhetosége, egy beviteli mezo szerkeszthetosége, vagy akár egy rádiógomb aktív mivolta.Az állapotok mindegyike bináris, azaz igaz vagy hamis értékkel írható le. Ennek megfeleloen az ATK által definiáltállapotok egy bithalmazt alkotnak, melyek leírják egy adott widget konkrét idopillanatban vett állapotát.

Viszonyok

Tesztelési szempontokból létezik még egy jelentoséggel bíró tulajdonságtípus, mely azonban nem konkrét widgetek pa-ramétereit, hanem azok egymáshoz kötodo viszonyát írják le. Úgyis, mint egy felirat (label) és a hozzá kötodo widgetösszetartozását, a vezérlo és vezérelt widget között fennálló viszonyt, vagy éppen a fák megjelenítésére használt widgetesetén a az elemek szülo-gyerek kapcsolatait. Az egyes viszonyok (relation) – hasonlóan az imént említett állapotokhoz– szintén két értékeket vehetnek fel, bár ellentétben azokkal az állapotokkal az egyes viszonyok csak egy másik widgettelegyütt van értelmük. Az ellentétes elojelu viszonyok, mint a vezérlo és vezérelt widget egyidejuleg is igazak lehetnek,más-más widgetekkel összefüggésben.

2.2.2. A Dogtail muködéseA Dogtail a szoftverek akadálymentesítésének megvalósítására használta technológiai módszereket (assistive technolo-gies) alkalmazza a tesztelendo alkalmazások vezérlésére.

2.2. ábra. Akadálymentesített szoftverek elérése[1]

11

Page 17: Helló Window!

A muködés modell (2.2 ábra) végeredményben nem túl bonyolult. Az applikáció közvetlenül nem szólítható meg.Ahogy ezt hang alapú vezérlést megvalósító, illetve képernyoolvasó szoftverek is teszik, az AT SPI-n (Assistive Technolo-gies Service Provider Interface) keresztül szólítják meg a megfelelo szoftvert. Amennyiben ez egy GTK+ felhasználásávalfejlesztett alkalmazás, akkor a kérésre a Gail (1.4.1) nevu alrendszert futtatva válaszol, az ATK interfészben leírtaknakmegfeleloen.

Ez a mechanizmus adja az alapját az automata tesztelésnek is. Maga a teszt is az AT-SPI interfészt használja arra,hogy a tesztelendo applikációval kommunikáljon, ezen keresztül kérdezi le a korábban említett állapotokat, viszonyokat,illetve szólítja meg az egyes widgetek által implementált interfészeket. Az így vezérelés alá vont szoftver egyes elemeinekállapotát, illetve tulajdonságait követve vonhatóak le következtetések arra nézvést, hogy az adott szoftver a kívánalmaknakmegfeleloen muködik-e.

12

Page 18: Helló Window!

3. fejezet

A fejlesztés menete

3.1. Gimp Tool Kit

3.1.1. BeszerzéseA GTK+ beszerzésére alapvetoen két módszer kínálkozik. Az egyik megoldás, hogy hagyatkozunk az általunk használtoperációs rendszerre és az általa biztosított, vagy legalábbis arra elérheto változatot telepítjük. A másik lehetoség, hogyletöltjük a GTK+ és a függoségek forráskódját és némi nehézséget vállalva ezeket fordítjuk le. Elobbi eset boségesenmegfelel amennyiben még csak most ismerkedünk a GTK+ függvénykönyvtárral, illetve nem akarunk túlságosan belebo-nyolódni az grafikus alkalmazások fejlesztésébe. Elkerülhetetlenül az utóbbit kell azonban választanunk, ha az átlagnáljobban el szeretnénk merülni a GTK+ rejtelmeiben, ha esetleg hibákat javítanánk, vagy változásokat eszközölnénk magána grafikus eszközkészleten.

Bináris változat

A GTK változatok beszerzése nem jelent különösebb feladatot, amennyiben valamelyik népszeru Linux disztribúciót hasz-náljuk, hiszen azok nagy valószínuséggel már amúgy is telepítve vannak az általunk használt rendszeren, lévén vélhetolegmár használunk ezen eszközök segítségével fejlesztett szoftvereket. A fejlesztéshez, illetve teszteléshez a bináris vál-tozatokon túl fejlesztoi csomagokra is szükséges lesz, amit rpm, illetve deb alapú disztribúciók esetén rendre az alábbiparancsok kiadásával tehetünk meg:

sudo yum install gtk-devel-package

sudo apt-get install gtk-dev-package

GTK+. A GTK+ fejlécfájlokat és egyéb állományokat – melyekrol a késobbiekben (3.2.1) még részletesebben is esikszó – a Debian/Ubuntu, illetve Fedora rendszereken a libgtk-3-dev, illetve a gtk3-devel csomagok tartalmazzák.

gtkmm. A fenti csomagok természetesen csak a C nyelvu változat – azaz a GTK+ – használatához elegendoek, amennyi-ben a C++ nyelvet – ezzel együtt a gtkmm függvénykönyvtárat – kívánjuk használni, további csomagokra (libgtkmm-3.0-dev,vagy gtkmm3-devel) is szert kell tennünk.

PyGObject. Amennyiben a GTK alapú fejlesztéssel a Python nyelv révén ismerkednénk a már említett fordított nyelvekhelyett, akkor a fenti csomagokat nem, a python-gi, vagy a python-gobject csomagokat viszont be kell szereznünk.

Dogtail. Az automata tesztek készítéséhez szükséges Python függvénykönyvtár – a Dogtail – állományait az azonosnevu csomag tartalmazza, melyek installálása a fentiekhez hasonlóan történik.

Forráskód

A GTK forrásának beszerzésére szintén több módszer kínálkozik. Egyrészrol az általunk használt Linux disztribúcióbiztosít eszközöket forráscsomagok telepítésére. A korábbi deb, illetve rpm alapú rendszerek példájánál maradva ezrendre az alábbiak szerint történik.

apt-get source gtk-src-package

yumloader --source gtk-src-package

Lehetoség van természetesen az egyes verziók letöltésére a GNOME projekt weboldaláról is,

13

Page 19: Helló Window!

wget http://ftp.gnome.org/pub/gnome/sources/gtk+/major.minor/gtk+-major.minor.micro.tar.gz

wget http://ftp.gnome.org/pub/gnome/sources/gtk+/major.minor/gtk+-major.minor.micro.tar.bz2

valamint használhatóak e célra egyes projektek verziókeloi is, ha szeretnénk mindig az aktuális forráskóddal dolgozni.

git clone git://git.gnome.org/gtk+

Ha azonban magunk szeretnénk a teljes GTK-t fordítani – legyen szó a C, vagy a C++ nyelvu változatról – számol-nunk kell azzal, hogy számos egyéb komponens (GLib, Pango, Cairo, ATK, . . . ) fordítására, illetve az frissítéseket követoújrafordítására válik szükségessé, ami meglehetosen idoigényes és fáradságos feladat, amit a Linux disztribúción össze-állítói már megtettek helyettünk. Így célszeru kezdetben ezt kihasználni és a saját fordításba csak akkor belekezdeni, haarra feltétlenül szükségünk.

3.1.2. FordításaAmennyiben a korábban említett nehézségek ellenére mégis nekivágunk a GTK saját fordításának, akkor sem vagyunkmagunkra hagyva. A GNOME projekt része egy JHBuild elnevezésu szoftver1, amit a GNOME projekt moduljaibólösszeállított halmazok – mint amilyen a GTK és annak függoségei – letöltésére, frissítésére, fordítására és fordítás ered-ményeként létrejött futtatási környezetben való munkára használhatunk.

Mindenekelott azonban szükségünk lesz néhány olyan eszközre, amik a Linux alapú rendszereken fordítási feladatokellátására kvázi szabványnak számítanak. A GNOME – sok más fejlesztési projekthez hasonlóan – az Autotoolst2 hasz-nálja moduljainak fordításához. Ennek részleteibe nem célunk ezen dokumentum keretében elmerülni, már csak azértsem, mert a JHBuild ezen, fordításhoz szükséges, függoségek telepítését megoldja helyettünk.

jhbuild sanitycheck

jhbuild bootstrap

Az parancs futtatásának hatására ellenorzésre kerül a konfigurációban megadott könyvtárak írhatósága, a szükségesfordítási eszközök telepített mivolta. Ezt követoen kerülhet sor a forráskódok letöltésére, a fordítás elotti konfigurálásra,magára fordításra, valamint az elkészült bináris állományok telepítésére. Ehhez a következo parancsokat használhatjuk.

jhbuild update

jhbuild make

A fordítás végeztével lehetoségünk van az újólag létrejött környezetbe úgymond belépni, vagy közvetlenül parancso-kat futtatni. Ez gyakorlatilag ennyit tesz, hogy számos környezeti változó beállításának eredményeként saját a fordítandóalkalmazásaink és a fordítás eredményeként létrejött futtatandó állományok is a az új környezetben létrehozott függvény-könyvtárakat fogják használni a rendszeren található változatuk helyett.

jhbuild run

jhbuild shell

Ennek akkor van leginkább haszna, ha szeretnénk az általunk fejlesztett alkalmazást a rendszeren elérheto GTK vál-tozaton kívül a legfrissebb verzión is kipróbálni, GTK valamely modulján szeretnénk változtatni és ennek hatását látnialkalmazásunkra, vagy éppen csak nyomon követnénk a GTK változásaik, aktuális fejlesztéseit még mielott azok az álta-lunk használt disztribúcióban is megjelenik.

3.2. Saját alkalmazásokMivel a továbbiakban részletesen foglalkozunk majd a GTK alapú alkalmazások létrehozásával, itt most csak a legfonto-sabb parancsokat vesszük számba, melyek révén a C, illetve C++ nyelvu forrásfájlokból futtatható bináris állományokathozhatunk létre.

3.2.1. Fordítás és linkelésAkár a rendszeren található, akár JHBuild, vagy más eszköz révén létrehozott környezetben lévo GTK változatot is hasz-nálunk, az alábbi parancssorok segítségével fordíthatóak le forrásfájljaink.

gcc gtk_sourcefile.c -o gtk_binary ‘pkg-config --cflags --libs gtk+-3.0‘

g++ gtkmm_sourcefile.cc -o gtkmm_binary ‘pkg-config --cflags --libs gtkmm-3.0‘

1telepítése a korábbiaknak leírtaknak megfelelon történik2a GNU fordítási rendszere, mely tulajdonképpen az Automake, Autoconf, Libtool együttese.

14

Page 20: Helló Window!

Ahhoz, hogy a GTK+, illetve gtkmm fordítási függoségeit ne magunknak kelljen megadnunk a pkg-config parancsothívhatjuk segítségül, hogy a GCC részére a megfelelo paramétereket meg tudjuk adni. A -cflags paraméter hatásáraa fordításhoz, míg a -libs eredményeképp a linkeléshez szükséges opciókat kapjuk vissza. A parancs két ` (backtick)közé zárt. aminek hatására a program kimenete része lesz a fordító parancssorának, amivel pont az kívánt hatást érjük el.

3.2.2. FuttatásEzek után már csak az örömteli pillanat van hátra, mikor a két különbözo nyelven és függvénykönyvtárral lekódolt teljesenazonos funkciójú programunkat lefuttatjuk a ./gtk_binary, illetve a ./gtkmm_binary paranccsal.

Amennyiben a Python nyelvu változat mellett tesszük le voksunkat a fordítás, mint lépés kimarad, a futtatás történhetközvetlenül (./gtk_script.py), amennyiben van az adott fájlon futtatási jog, vagy a Python interpreternek paraméter-ként (python gtk_sctipt.py). A továbbiakban ezt az utóbbi sémát követjük.

Ezzel túl is vagyunk azon a rövid áttekintésen ami után már épp ideje nekilátnunk elso ablakunk implementálásának,futtatásának és tesztelésének.

15

Page 21: Helló Window!

4. fejezet

Elso ablakunk

4.1. Kódolási alapismeretekA nagyobb nyílt forrású projektek a kódolás, kódszervezés során egy meghatározott konvenciót követnek, bár egy olyanméretu projekt esetén, mint a GNOME az egyes részterületeken lehetnek eltérések, azzal együtt is, hogy az azonosságoknyilván eros többségben vannak. Lássuk mik ezek a GNOME, illetve a GTK projektek esetén.

4.1.1. Forráskód formázásaA GTK fejlesztoi a GNU coding standard[2], illetve a Linux kernel coding style[3] irányelveit alkalmazzák, ami mind-addig csak az olvasást, megértését elosegíto módszertani eszköz, amíg nem áll szándékunkban a GTK fejlesztésébe,javításába belefogni, ugyanakkor két oknál fogva mégis érdemes megemlíteni. Ha még nem ismerkedtünk meg egyet-len kódolási konvencióval sem, akkor az említett ketto – mind népszeruségük, mind letisztult mivoltuk okán – alkalmasválasztás lehet. A másik ok, hogy néhány a fejlesztés során hasznos információ ezen konvenciókból következik.

4.1.2. Elnevezési konvenciókA GNOME projekten belül nem csak formázási, de elnevezési konvenciók is használatosak. Ezek az egyes funkciótmegvalósító szoftverelemekre (pl: függvények, makrók, . . . ) vonatkoznak, amik közül a az alábbiak már a legegyszerubbpéldák esetén is feltunnek.

• az egyes nevek részekre oszthatóak,

• a részek meghatározott sorrendben követik egymást, ahol

– kezdve a GNOME megfelelo projektjével (pl: atk, gtk, . . . ),

– folytatva a vonatkozó osztály nevével (pl: entry, label, . . . ),

– befejezve a megvalósított muvelettel (pl: get, set, . . . ),

– illetve a muvelet tárgyával (pl: text, value, . . . ),

• a részeket aláhúzásjel (’_’) választja el,

• az egyes részek rendszerint vagy csak kis-, vagy csak nagybetuket, illetve számokat tartalmaznak.

Fentieknek megfeleloen egy – a GTK+ által implementált – rádiógomb aktív mivoltát lekérdezo függvény neve a gtkelotaggal kezdodik, amit a osztálynév, vagyis a radio_button, majd lekérdezésrol lévén szó get akciónév követ, végülpedig a tulajdonság neve (actvie) zár. Aláhúzás jelekkel összefuzve gtk_radio_button_get_active.

A C++, illetve a Python nyelvu változatok esetén is hasonló az elnevezés módszertana a nyelvbol fakadó sajátosságokokozta eltéréssel természetesen. A gtkmm esetén a projektek nevét tartalmazó prefixum szerepét a Gtk névtér veszi át,míg az osztályok neve a C++ osztályok neve lesz. A tagfüggvények az elobbi két elotag (pl: gtk_entry) nélküli neveklesznek (pl: set_text). A PyGobject esetén a névtér szerepét a Gtk modulnév veszi át, a Python osztályok nevei ugyanazta szerepet töltik be, mint a C++ esetén.

16

Page 22: Helló Window!

4.1.3. Fejlécfájlok és importálásSzakítva az elozo foverziónál (2.x) megszokottaktól az új foverzió (3.x) esetén, a C, illetve a C++ változat egyaránt csakegyetlen állomány beszerkesztésére (include) van lehetoség és szükség. A korábbiakban az egyes widgetekhez tartozófejlécállományok (pl: gtk/gtkentry.h) beszerkesztésére külön-külön volt lehetoség függoen attól, melyekre van, illetvemelyekre nincs szükségünk. Most azonban közvetlenül csak a gtk/gtk.h szerkesztheto be, a többi fejlécállomány eseténhibaüzenetet kapunk. Így mind a C, mind a C++ változat azonos módon muködik, ami egyébiránt hasonlít a Pythonmodul importjára.

4.2. Minimálisan alkalmazásEnnyi bevezeto után lássuk egymás mellett (4.1. Kódrészlet) a három nyelvi változat (C, C++, illetve Python) kódjátszámba véve azok hasonlóságait és különbözoségeit. Ami talán elso látásra is feltuno, hogy messze a GTK+ változat”kódsurusége” a legnagyobb, vagyis a C nyelvu változat igényli ugyanazon funkcionalitás mellett a legtöbb kódsor (18sor) begépelését, és egyben a legtöbb munkát is. Ez persze nem jelenthet különösebb meglepetést ha van némi tapaszta-latunk az interpretált, illetve a fordított, a procedurális, illetve az objektum központú nyelvek esetén elérheto fejlesztésisebesség terén.

4.2.1. ForráskódElso közelítésben a már említett formai különbségek lehetnek szembeötloek, ugyanakkor számos, a tartalmat, megvalósí-tást érinto eltérés is felfedezheto ebben a még oly kevést kódsort tartalmazó példában. Ezek megértése nagyban könnyítiaz egyes nyelvi változatok közötti átjárást és ne utolsó sorban a GTK koncepcionális sajátosságaira is rávilágít. Lássuktehát sorról sorra az imént olvasott kódok magyarázatát.

1 #include<gtk/gtk.h>23 int4 main(intargc,char*argv[])5 {6 GtkWidget*window;78 gtk_init(&argc, &argv);9

10 window=gtk_window_new(GTK_WINDOW_TOPLEVEL);11 g_signal_connect(G_OBJECT(window), "delete-event",12 G_CALLBACK(gtk_main_quit),NULL);13 gtk_widget_show(window);1415 gtk_main();1617 return0;18 }

#include<gtkmm.h>

intmain(intargc,char*argv[]){

Gtk::Mainkit(argc,argv);

Gtk::Windowwindow;

window.show();

Gtk::Main::run();

return0;}

1fromgi.repositoryimportGtk234if__name__== "__main__":5678910window=Gtk.Window()11window.connect("delete-event",12Gtk.main_quit)13window.show()1415Gtk.main()161718

Kódrészlet 4.1. Minimálisan szükséges kód GTK+, gtkmm, illetve PyGobject használata mellett

1. sor A header fájlok beszerkesztésének különbözoségeirol az elozoekben esett szó, így erre itt csak azt megemlítendotérünk ki, hogy a Python változat import parancsának azt a változatát alkalmazzuk, ami a legerosebb hasonlóságoteredményezi a másik két nyelvi változattal, már ami a gtk kulcsszó forráskódban történo megjelenéseinek helyétilleti.

4. sor Ez a sor a programjaink belépési pontja, azaz itt kezdodik meg a futtatás, már legalábbis ami a C/C++ változa-tot illeti. A Python nyelv esetén az import parancs már végrehajtásra került mire ide jutunk, sot erre a kódsorravoltaképpen nincs is feltétlenül szükséges[4, fej. 27.4.], leginkább csak a másik két példához való még erosebb ha-sonlóság végett került be ez a kódsor. Fontossá a main függvények tulajdonképpen csak a parancssori paraméterekGTK-nak történo átadás8 szempontjából válnak.

6. sor A C nyelvi verzióban kénytelenek vagyunk blokk elején deklarálni azt a változót – ami ez esetben widgetünk címéttartalmazza majd – mivel az ISO C90 szabvány még nem, majd csak az ISO C99 támogatja a blokkon belül kifejezésután elhelyezett változó deklarációkat. Ezt viszont sem 3.0-nál korábbi GCC, sem pedig a Microsoft Visual StudioC fordítója nem támogatja, vagyis problémába ütköznénk a C++ példában használt módszerrel, így a biztonságkedvéért maradunk a hagyományoknál, azaz lokális változók deklarációja csak blokkok kezdetén szerepel.

8. sor Eljutottunk végre az elso GTK specifikus híváshoz, mely a Python változatból teljesen hiányzik, míg a C, illetve aC++ verzióban funkciójuk azonos, mégis van köztük egy árnyalatnyi különbség. A GTK+ esetén az argc, valamintaz argv változók címeit adjuk át, biztosítandó, hogy az init függvény a GTK saját paramétereit 1 el tudja távolítania tömbbol és azok számával csökkenteni tudja argc értékét. Erre a C++-os változat esetén erre azért nincs szükség,

1a GTK által értelmezett parancssori paramétereket összefoglalója itt olvassható.

17

Page 23: Helló Window!

mert még ha nem is látszik, mindkét változóra referencia adódik át a Gtk::Main konstruktorának. A Python változatesetén nincs init jellegu függvényhívás, mivel az inicializálás a modul (gi.repository.Gtk) importálásával implicitmódon megtörténik, másrészrol a parancssori paraméterek a sys modulon keresztül bárhol hozzáférhetoek, azokparaméterként való átadása így felesleges.

10. sor Elso widgetünk létrehozása a már említett nevezéktan szerinti függvények meghívásával történik. Minden widgettípushozlétezik egy, mondjuk úgy konstruktor, melyet meghívva egy új – az adott típushoz tartozó – widgetet kapunk vissza.A hívás mikéntje természetesen függ a nyelvtol magától, illetve attól is, hogy a nyelv esetén használható-e, il-letve használjuk-e az objektumok-orientált megközelítést. A C nyelvi változat esetén a gtk_widgettípusnév_newforma használatos, addig a C++ esetében a prefixek szerepét a névterek veszik át, tehát általános formában aGtk::WidgetTípusNév::WidgetTípusNév írható le a konstruktor. A Python szintén névterekkel operál, aholazok határait a modulok jelentik, melyek neveit egymástól, illetve függvényeiktol pont (.) választja el, vagyis akonstruktor Gtk.WidgetTípusNév formában írható le.

A különbség nem is annyira a nevekben, mint inkább a memória kezelésében rejlik, hiszen egy újólag létreho-zott objektum felszabadításáról C esetén magunknak kell gondoskodnunk, míg a C++ a tole megszokott módonfelszabadítja a lokális változókat. Ugyanakkor érdemes itt visszautalni a korábbiakban már említett referenciaszámlálásra, illetve a ”lebego” referenciára, melyek révén a különbözo nyelvi változat esetén mód van arra, hogycsak a legfelso szintu elemrol kelljen ebben a tekintetben magunknak gondoskodnunk, a GTK a többi elem memó-riakezelését maga menedzseli.

Az ablak létrehozásában meg egy különbség fedezheto fel a C, illetve a másik két változat között.Ez pedig a para-méterkezelés mikéntje. Elobbi esetben meg kell mondanunk az ablak típusát (toplevel) – hiszen a C nyelv nem teszlehetové a paraméterek esetén alapértelmezett értékét –, míg a másik két esetben erre nincs szükség. Ez a paraméterugyan mindkét esetben létezik alapértelmezett értékük, pont az, amit a C változat esetén használunk.

11. sor Anélkül, hogy a szignálok kezelésének rejtelmeiben elmerülnénk egy gondolatnyi kitérot érdemes ezen kódsor akapcsán tenni. Elsoként azt érdemes tisztázni mire szolgál a delete-event szignál. Ez a szignál akkor váltódikki, amikor az ablakunk felhasználó interakció hatására záródik be. Ez többféle interakciót is jelenthet – operációsrendszertol és ablakkezelotol függoen –, de alapvetoen az ablak jobb felso sarkában2 lévo X gomb, vagy az Alt+F4billentyuk lenyomására kell gondolnunk.

A C, illetve a Python nyelvu változat ezen esemény bekövetkeztekor a GTK fociklusát szeretné leállítani, amiannak révén ér el, hogy a delete-event szignálra a nyelvi változatnak megfelelo main_quit függvényt köti fel.Figyelembe véve, hogy a delete-event alapértelmezett szignálkezeloje megszünteti (destroy) az ablakot, ez azeljárás logikus, lévén egy programnak foablak nélkül nincs igazán sok értelme. A C++ változat viszont ugyanebbena tekintetben látszólag semmilyen lépést sem tesz, ugyanakkor megfigyelhetjük, hogy néhány sorral lejjebb (15. sor)a run függvények paraméterként átadja azt ablakot, ami nagyon hasonló eredményre vezet. Egészen pontosan nemcsak az átadott ablak megszunésekor, de az eltüntetésekor (hide) is ki fog lépni a fociklus.

13. sor Ezek után nem érhet meglepetésként bennünket, hogy miért nincs szükség a C++ változat esetén az megjelenítofüggvény meghívására, ezt az átadott ablakra a fociklust elindító run függvény megteszi, ami logikus is hiszenha nincs egyetlen látható ablakunk sem, akkor nehéz olyan felhasználó interakció kezdeményezni – legalábbis afelhasználói felületen keresztül –, ami a fociklus kilépését eredményezné.

15. sor A hívások a – korábban már részletezett – GTK main loopot indítják, azaz itt kezdodik meg az az eseményvezéreltszakasz, mely a választott nyelvtol függoen a gtk_main_quit, a Gtk::Main::quit, vagy a Gtk.main_quit meg-hívásáig tart. Ezekben a minimális példákban erre az egyetlen mód a futtatáskor megjeleno ablak bezárása, hiszenaz imént említett függvényeket az ennek hatására kiváltódó delete-event szignálhoz rendeltük. Miután a szignált”kezelo” függvény lefutott a GTK fociklusa (main loop) kilép, azaz a run függvény futása befejezodik, a programfuttatása az azt követo soron folytatódhat.

17. sor Visszatérési értékünk mindhárom esetben 0, amit a rendelkezésre álló nyelvi módszerek legegyszerubbikévelérünk el, ezzel jelezvén a hívó félnek, hogy a futás rendben lezajlott.

4.2.2. Fordítás és futtatásA korábbiakban már említett metodika mellet a mostani forráskódok3 fordítása a követlezoképp néz ki:

gcc gtk_minimal.c -o gtk_minimal ‘pkg-config --cflags --libs gtk+-3.0‘

g++ gtkmm_minimal.cc -o gtkmm_minimal ‘pkg-config --cflags --libs gtkmm-3.0‘

A futtatás tekintetében próbálkozzunk a ./gtk_minimal, illetve a ./gtkmm_minimal, illetve a python3 gtk_minimal.pyparancsokkal abban a könyvtárban, ahol a forrásállományaink is találhatóak.

2Mac OS X, illetve az újabb Ubuntu verziók esetén bal felso sarok3természetesen csak a C, illetve a C++ változat fordítandó a Python nem

18

Page 24: Helló Window!

4.2.3. EredményNagy meglepetést nem várhatunk egy ilyen méretu applikációtól, viszont az azért kell tudnunk értékelni, hogy a leg-rosszabb esetben is alig másfél tucat kódsorból egy muködo grafikus felhasználó felületet lehet létrehozni, melynek ered-ménye az alábbi képeken látható.

4.1. ábra. Minimális mintapéldák képernyoképiC, C++, Python változat

Tulajdonképpen csak egy puszta ablakot kapunk, bármilyen gomb, vagy egyéb elem nélkül. Minden díszítés – azablak fejléce, a címsor szövege, a minimalizáló, maximalizáló és a bezáró gombok – egyaránt az ablakkezelonek és nema GTK-nak köszönhetoek. Ez utóbbi gombhoz kötodik az egyetlen – GTK szempontjából is érdemleges4 – muvelet, amia már több ízben is említett delete-event szignált fogja kiváltani, ami a végso soron a program futásának befejezéséhezvezet.

4.3. TesztelésElsore talán azt gondolhatjuk, hogy a fenti ablakon igazán nincs mit tesztelni, egy feladat mégis akad, mégpedig az, hogya futó applikációt, illetve annak egyetlen ablakát megtaláljuk, majd bezárjuk, ami mint látni fogjuk azért ez is jelent némifeladatot.

4.3.1. ForráskódAz ablakok tesztelésére szolgáló programok forráskódjai (6.16. Kódrészlet) mindössze két sorban térnek el egymástól,ami a gyakorlatban nem jelent érdemi különbséget, ugyanakkor rámutat arra, hogy a Dogtail használatával két API isrendelkezésünkre áll az applikációk teszteléséhez. Ezek a tree, illetve a procedural API. Elobbi egy objektumorientáltmegközelítést alkalmazva teszi lehetové, hogy a felhasználó felület egyes elemeit – gombok, ablakok, menük – elérjük,azokon muveleteket végezzünk, illetve a köztük fennálló összefüggéseket feltárjuk. Utóbbi az ablakokra, illetve azokelemeire – melyeket nevükkel hivatkozhatunk – ad fókuszt, illetve végez egyéb muveleteket, aminek révén egyszeruenvezérelhetjük a tesztelendo alkalmazást.

1 fromdogtailimportprocedural23 importunittest45 classGtkDemoTest(unittest.TestCase):6 defsetUp(self):7 fromdogtailimportutils8 self.pid=utils.run(’gtk3-demo’)9 self.app=procedural.focus.application(’gtk3-demo’)

1011 deftearDown(self):12 importos,signal,time13 os.kill(self.pid,signal.SIGTERM)14 time.sleep(0.5)1516 deftestGtkDemo(self):17 pass1819202122 if__name__==’__main__’:23 unittest.main()

1fromdogtailimporttree23importunittest45classGtkDemoTest(unittest.TestCase):6defsetUp(self):7fromdogtailimportutils8self.pid=utils.run(’gtk3-demo’)9self.app=tree.root.application(’gtk3-demo’)1011deftearDown(self):12importos,signal,time13os.kill(self.pid,signal.SIGTERM)14time.sleep(0.5)1516deftestGtkDemo(self):17pass1819if__name__==’__main__’:20unittest.main()

Kódrészlet 4.2. Minimálisan szükséges teszt tree, illetve procedural API használata mellett

4a minimalizálása és a maximalizálás az ablakkezelo hatáskörébe tartoznak

19

Page 25: Helló Window!

A következo kód – a fejlesztési példától némiképpen eltéroen – nem szorítkozik a minimálisan szükséges kódsorokismertetésére, ennél egy kicsit tovább megy. Ennek oka kettos. Egyrészrol minimálisan alig néhány sorra van szükség,másrészrol igyekszünk egy, a valós életben is használható kóddal szolgálni, aminek része egy tesztelési kertrendszer (fra-mework), ebben az esetben a Python unittest modulja. Nem mellesleg a Dogtail saját tesztjei is ezt a modult alkalmazzák,a következokben ismertetettekhez nagyon hasonló, esetenként teljesen azonos módon. Ahogy a korábbiakban, úgy itt semismertetjük a nyelvi eszközökbol adódó sajátosságokat, hacsak annak nincs kifejezett hatása a tesztelésre, így a unittestmodul is csak olyan mértékben kerül ismertetésre, amennyire az a megértés szempontjából szükséges. Tesztelendo alkal-mazásnak a GTK demó programját választottuk, ami csaknem minden widgetre ad példát és része azon fejlesztoi korábbanmár említett csomagnak, mely a C nyelvu fordításhoz szükséges.

1. sor A két különbözoséget adó sor egyike, ahol az Dogtail elobbiekben említett procedural, illetve tree moduljaitimportáljuk. Az egyes példákban az importált modulnak megfelelo módszereket, illetve eszközöket alkalmazzuk.

1. sor A tesztelésre szolgáló osztályunk a Python beépített unittest nevu moduljának TestCase osztályából származik,vagyis ezen modult használjuk a tesztek implementálására. Maga a modul természetesen nem feltétlenül szükségesa Dogtail alapú teszteléshez, ugyanakkor számos olyan eszközt nyújt melynek a késobbiekben még hasznát látjuk.

2. sor A unittest modul TestCase osztályából származó osztályok setUp, illetve tearDown (11. sor) nevu függvényei atesztfuttatása során automatikusan meghívódnak minden egyes teszteset elott, illetve után, lehetoséget adva a összestesztesetre nézve közös elokészíto, illetve utómunkák elvégzésére. Ebben az esetben az elokészíts nem áll másból,mint hogy a utils modul megfelelo függvényének segítségével elindítjuk a GTK+ demó alkalmazását, amit azegyes tesztelési feladatok bemutatására használunk majd fel.

4. sor Itt történik a GTK+ demó alkalmazásának tényleges futtatása, ahol a visszatérési értékként kapott folyamatazono-sítót (PID) eltesszük késobbi használatra (13. sor).

11. sor Ahogy arról szó esett a setUp függvényhez hasonlóan egy speciális függvény, amit Python unittest moduljaautomatikusan hív meg, minden egyes teszteset lefutását követoen.

13. sor A korábban elmentett (4. sor) folyamatazonosítót felhasználva küldünk kilépésre (termination) felszólító szig-nált a GTK+ demó programjának, mivel annak foablaka nem tartalmaz olyan elemet (pl: menüpont, gomb, . . . ),melynek segítségével a kilépést el lehetne érni.

14. sor Levezetésként 5 másodperc várakozás következik minden tesztesetet követoen elkerülendo az AT-SPI túlterhelé-sét.

16. sor Ez a függvény voltaképpen csak demonstrációs jelleggel kapott helyet ebben a példaprogramban, bemutatandó,hogy a nevükben test elotaggal rendelkezo függvényeket a Python unittest modulja tesztnek tekinti és ennekmegfeleloen futtatja oket.

20. sor A unittest modul main függvénye példányosítja a TestCase osztály leszármazottjait – azaz a teszteset imple-mentációkat tartalmazó objektumokat hoz létre –, majd futtatja az azokban lévo – az elobbiekben említett nevezék-tan szerinti – tesztfüggvényeket. Bizonyos körülmények megfeleloen – például, hogy a függvények futása soránkeletkezett-e kivétel – hibásnak, vagy sikeresnek tekinti e teszteket. A unittest modul számos funkció révénkönnyíti meg a teszteloi munkát, melynek részleteirol a modul dokumentációjában olvashatunk, illetve a továbbirészekben lesz szó.

4.3.2. FuttatásAz imént ismertetett tesztfuttatása rendkívül egyszeru eredményre vezet, lévén mindösszesen egy tesztesetet (GtkDemoTest)és azon belül is csak egyetlen tesztet (testGtkDemo) tartalmaz, mely reményeink szerint sikeresen fut majd le. A szkriptfuttatása még ebben az egyszeru esetben is többféleképp lehetséges, már ami a megadható paramétereket illeti. A paramé-terek nélkül a szkript az összes tesztesetének összes tesztfüggvényét futtatja, ugyanakkor lehetoség van paraméterként egyteszteset, vagy akár egy azon belüli tesztfüggvény megadására is. Elobbi révén a megadott teszteset összes tesztfüggvényefuttatható, míg az utóbbi eset egy konkrét tesztfüggvény futtatására használható.

python dogtail_minimal_tree.py

python dogtail_minimal_tree.py GtkDemoTest

python dogtail_minimal_tree.py GtkDemoTest.testGtkDemo

A tesztesetek lefutásának mikéntjérol maga a teszt szolgáltat információt futtatáskor megjelenítve az összes futtatottteszteset számát, a futtatás idejét, a sikeresen, a sikertelenül, illetve a hibásan lefutott teszteket. Utóbbi két esetben a hibaokával, illetve a hozzájuk tartozó híváslistával (backtrace) együtt, ami alapjául szolgálhat a hibakeresésnek.

20

Page 26: Helló Window!

5. fejezet

Szignálkezelés dióhéjban

Ebben a részben a szignálok kezelésének elméleti kérdéseirol, illetve gyakorlatáról esik szó, amihez egy új példaprogramotveszünk górcso alá, ami forráskódját tekintve némiképp ugyan bonyolultabb a korábbiakban tárgyaltaktól, muködésérenézve azonban nem sokban különbözik attól.

5.1. FogalmakEloször is a korábban már megismert alapfogalmakat vesszük újra elo, most azonban általános ismertetésük helyett atémánkhoz konkrétan kapcsolódó specifikumaikat vázoljuk fel.

Main Loop. Ahogy arról az elozo részekben már szó esett – más felületprogramozási nyelvekhez teljesen hasonlóan – aGTK is eseményvezérelt (event-driven) módon muködik. Ez annyit tesz, hogy felhasználói interakciók bekövetkeztéig –figyelmen kívül hagyva az ütemezett eseményeket és néhány, a késobbi részekben részletezendo funkciót –, a GTK a sajátfociklusában (main loop) várakozik, lényegében tehát a szoftver futását maguk a bekövetkezo események vezérlik, hiszenezek híján a várakozás sosem ér véget. Felhasználói interakció lehet például az egér megmozdítása, vagy egy kattintás,esetleg egy billentyu lenyomása, vagy éppen felengedése.

Ahhoz, hog az alkalmazás az eseményvezérelt szakasza megkezdodhessen be kell lépni a GTK fociklusába. Erre, azelozo részben taglalt minimalista alkalmazások forráskódjából már ismeros gtk_main(), gtkmm esetén a Gtk::Main::run(),illetve a Python változat esetén a Gtk.main() függvény szolgál. Ha az imént említett események közül bármelyik bekö-vetkezik, az addig „alvó” fociklus úgymond „felébred”, azonosítja az eseményhez tartozó felületi elemet, majd a bekö-vetkezett eseményt továbbítja (propagate) az azonosított widget, vagy widgetek felé. A vezérlés ezt követoen a fociklusbatér vissza, ahol folytatódik a várakozás a következo eseményre.

Signal. A szignál voltaképpen egy névvel azonosított, a bekövetkezett esemény továbbítására szolgáló üzenet, amit azosztály definiál, hogy példányai értesíthessék az események bekövetkeztérol az az iránt érdeklodoket. Ilyen üzenetbolszámos létezik, hisz az egyes widgettípusokon különbözo események lehetnek értelmezettek. Gomb esetén a rá történokattintás, egy legördülo menünél az egér menüelem fölé történo mozgatása, míg egy widgetnél például annak átmére-tezése. Minden ilyen esemény rendelkezik saját névvel, melynek révén hivatkozni tudunk rá (pl: button-pressed,enter-notify-event, size-request). A szignálok öröklodnek, azaz egy specifikus widget, mint amilyen mondjukegy RadioButton, vagy egy CheckButton, minden olyan szignállal rendelkezik, amivel ose a Button, vagy akár annak azose az a Widget típus rendelkezett.

A szignálok egyrészrol arra szolgálnak, hogy a GTK rendszerén belül az egyes widgetek egymással kommunikálhas-sanak. Ha például egy gombot lenyomunk, akkor azt (illetve annak részeit) újra kell rajzolni, ha egy menüelemet kivá-lasztunk, azt át kell színezni, illetve az esetleges almenüpontokat ki kell rajzolni, míg átméretezésnél az egyes widgetekhelyigényét újra ki kell számolni. Másfelol ha a program írói valamely esemény bekövetkezésérol értesülni szeretnének,megadhatnánk eseménykezelo függvényeket, melyek ezen esetekben meghívódnak.

Callback. Ezen eseménykezelo függvények elnevezése a GTK terminológiában callback. Az egyes eseményekhez tar-tozó kezelofüggvények prototípusai a szignál fajtájától függenek. A C nyelvu változat esetén elso paraméterük jellemzoenaz a Widget – pontosabban szólva Object, hiszen a szignálkezelés ezen a szinten került implementálásra a Glib-ben – me-lyen az esemény kiváltódott. Ezt a paramétert követik a szignálhoz kapcsolódó egyéb jellemzok, az utolsó pedig a szignálbekötésekor megadott, úgynevezett user data, amirol a példaprogram kapcsán részletesebben szólunk. Elöljáróban csakannyit, hogy ez egy meglehetosen kényelmetlen és gyakorta nehézkesen használható megoldás, melyre a C++, illetvePython nyelvu változatok kínálnak kényelmes alternatívát.

21

Page 27: Helló Window!

5.2. SzignálkezelésAz elozo szám módszertanától eltérve az alábbiak szerint elemezzük a kódokat:

• külön-külön vesszük számba ez egyes nyelvi változatok sajátosságait

• eloször a C, illetve a C++ nyelvu verziónak fogunk neki, ezt követoen bemutatjuk mennyiben más a helyzet, Pythonnyelvu változatokban

• a kódot nem sorfolytonosan, hanem a futás logikája szerint követjük, lévén egy kicsit is bonyolultabb esetben –mint amilyennek az alábbi példa is mondható – már ez a logikusabb

5.2.1. C, illetve C++ nyelvu változat

1 #include<gtk/gtk.h>234 staticvoidon_button_clicked(GtkWidget*widget,gpointerdata)5 {6 g_print("%s\n", (constchar*)data);7 }89

101112 staticgbooleanon_delete_event(GtkWidget*widget,13 GdkEvent *event,14 gpointer data)15 {16 g_print("deleteeventoccurred\n");17 returnTRUE;18 }192021 GtkWidget*my_window_new()2223 {24 GtkWidget*window=gtk_window_new(GTK_WINDOW_TOPLEVEL);25 GtkWidget*button=gtk_button_new_with_label("HelloWindow!");2627 g_signal_connect(G_OBJECT(window), "delete-event",28 G_CALLBACK(on_delete_event),NULL);29 g_signal_connect(G_OBJECT(window), "destroy",30 G_CALLBACK(gtk_main_quit),NULL);3132 g_signal_connect(G_OBJECT(button), "clicked",33 G_CALLBACK(on_button_clicked),34 "HelloWindow!");35 g_signal_connect_swapped(G_OBJECT(button), "clicked",36 G_CALLBACK(gtk_widget_destroy),37 G_OBJECT(window));3839 gtk_container_add(GTK_CONTAINER(window),button);4041 gtk_widget_show(button);4243 returnwindow;44 }454647484950 intmain(int argc,char*argv[])51 {52 GtkWidget*window;5354 gtk_init(&argc, &argv);5556 window=my_window_new();57 gtk_widget_show(window);5859 gtk_main();6061 return0;62 }

1#include<gtkmm.h>2#include<iostream>34voidon_button_clicked(constGlib::ustring&hello_msg)5{6std::cout<<hello_msg<< std::endl;7}89classMyWindow:publicGtk::Window10{11protected:12virtualboolon_delete_event(GdkEventAny*event)131415{16std::cout<< "deleteeventoccurred"<<event<< std::endl;17returntrue;18}1920public:21MyWindow() :22button("HelloWindow!")23{242526272829303132button.signal_clicked().connect(33sigc::bind(sigc::ptr_fun(on_button_clicked),34"HelloWindow!"));35button.signal_clicked().connect(36sigc::mem_fun(*this, &MyWindow::hide));373839add(button);4041button.show();424344}4546private:47Gtk::Buttonbutton;48};4950intmain(intargc,char*argv[])51{525354Gtk::Mainkit(argc,argv);5556MyWindowwindow;575859Gtk::Main::run(window);6061return0;62}

Kódrészlet 5.1. Szignálok kezelése C, illetve C++ nyelven

Általánosságok

1 - 2 sor Az fejlécállományok beszerkesztésének sajátosságairól már esett szó, így az egyedüli specifikum itt a C++változat által beszerkesztett iostream fejlécfájl, amire csupán a képernyore történo íráshoz lesz szükség. A GTK+esetén – mivel a gtk.h minden szükséges fejléc állományt maga felsorol – használni tudjuk a Glib erre a célrahasználatos függvénylét (g_print).

22

Page 28: Helló Window!

50 - 62 sor A main függvényben alkalmazottak gyakorlatilag teljesen azonosak a korábbi minimális példánál ismertetet-tekkel, így itt errol leginkább csak annyit érdemes megjegyezni, hogy a C++ változat – élve a nyelv adta leheto-ségekkel – egy saját osztály segítségével zárja egységbe egy gombbal kiegészített ablakunkat, míg a C változatbanegy – a saját ablak létrehozására szolgáló – függvény segítségével igyekeztünk ezt módszert valamelyest követni.Természetesen a GTK+ esetén is van mód származtatásra, de nem oly kézenfekvo módon, mint amikor a C++nyelvet használjuk, ennél fogva ennek ismertetése még várat magára.

Szignálok a GTK+ nyelvu kódban

24 - 25 sor A létrehozott ablak, illetve gomb tárolására szolgáló változók típusa GtkWidget, a specifikusabb GtkWindow,illetve GtkButton helyett. Ennek magyarázata, hogy minden olyan függvény a GTK+-ban, melynek segítségévelegy új widgetet hozhatunk létre – gyakorlatilag a _new végu metódusok – egy GtkWidget típusú objektumra muta-tóval tér vissza. Ennek több oka is van. Egyrészrol kényelmi, hogy elkerülhessük a folytonos típuskényszerítéseket,hisz számos esetben olyan függvényeket használunk, melyek amúgy is GtkWidgeteket kezelnek, tehát ilyen típusramutatót vesznek át elso paraméterként. Másrészrol ha egy specifikus – mondjuk GtkButton-t kezelo – függvénytakarunk hívni, akkor vagy fordítási – vagy ami inkább javasolt – futás ideju típuskényszerítés alkalmazandó (pl:GTK_BUTTON, GTK_WINDOW), aminek megvan az a komoly elonye, hogy ha sikerült a mutatónkat, vagy a mutatottobjektumot valamilyen módon korrumpálni, akkor arra viszonylag hamar fény tud derülni.

Szignálkezelo függvények felkötése.

27. sor Az elso szignálbekötés. Viszonylagos egyszerusége ellenére számos apróságra érdemes figyelmet fordítani. Azelso maga a g_signal_connect kulcsszó, ami függvénynek tunhet, pedig ugyanúgy, mint a g_signal_connect_swapped,makró, amik a g_sinal_connect_data függvényt burkolják. A soron következo érdekesség a G_OBJECT makró,ami futás ideju típusellenorzést hajt végre a neki megadott paraméteren, majd egy GObject típusra mutatóval térvissza. A megrögzött C++ programozók joggal kérdezhetik, mi szükség erre, hisz egyfelol majd elvégzi a típusel-lenorzést a fordító, meg hát a GtkWindow típus úgy is leszármazottja a GObject ”osztálynak„. Ez így is lenne, na deez itt C, tehát os-, illetve származtatott osztályokról csak logikai értelemben lehet szó, a típusellenorzés tehát nemvégezheto, sot minden esetben a hívott függvénynek megfelelo típuskényszeríto makrót célszeru alkalmazni.

A második paraméter a szignál neve, amivel azt adjuk meg, hogy az elozo paraméterként megadott object melyikszignáljára is szeretnénk kezelo függvényt (callback) kötni. A harmadik paraméter azon függvény címe, aminekmeghívódását ki szeretnénk váltani az esemény bekövetkezésekor. A függvénynevet itt is egy makró segítségéveladjuk át, ami az elozoekhez hasonlóan C nyelvi hiányosságokra vezetheto vissza. Mivel a meghívandó callbackekprototípusai igen sokfélék lehetnek (ami magából a példából is látszik valamelyest és ezek mind külön típusnakminosülnek a C nyelvben ezért ahányféle callback variáció létezik, annyiféle g_signal_connect függvényre lenneszükség. Könnyen belátható, hogy a jogos lustaság más irányba vitte a GTK+ fejlesztoit. A G_CALLBACK tulajdon-képpen egy fordítási ideju típuskényszerítés egy általános függvénytípusra, amivel ugyan megoldottuk, hogy csakegyetlen g_signal_connect_data függvényre legyen szükség, de elvesztettünk minden nemu típusbiztosságot.Ha például egy az adott szignálnak nem megfelelo típusú függvényt adunk meg paraméterként, amit a példabelifüggvénynevek felcserélésével könnyen megtehetünk, csúnya meglepetésekben lesz részünk, de csak futásidoben.Nem hagyhatjuk továbbá figyelmen kívül a C nyelv azon sajátosságát sem, hogy az átadott függvényparaméterekátvétele nemkötelezo, azaz ha kevesebb paraméterrel definiálunk egy függvény, mit amennyivel hívni szeretnénkvoltaképpen nem követünk el bunt, ez viszont tovább bonyolítja a helyzetet.

Az utolsó paraméter az úgynevezett user data, ami arra szolgál, hogy az eseménykezelo függvényünknek olyanadatokat adjunk át, amik az esemény megtörténtébol nem következnek. Ilyenek lehetnek például más widgetekcímei, ahogy azt látni is fogjuk. Ez esetben az átadott paraméter NULL, ami szintén egy makró ami egy jól nevelt((void*) 0) kifejezésre fejtodik ki C kód esetén. Zárszóként ehhez a sorhoz csak annyit, hogy a delete_eventeseményt az ablakkezelo váltja ki, akkor, amikor az ablakot valamilyen módon (billentyuzet, menü, egér) bezárjuk.

A delete-event szignál blokkolása.

12. sor Ez a delete_event szignálkezelo függvénye, aminek – néhány más szignálkezelo függvényhez hasonlóan – egygboolean értékkel kell visszatérnie, ami azt határozza meg, hogy az általunk a szignálkezeloben végrehajtottakután a GTK lefuttassa-e saját szignálkezelo rutinját, vagy sem. Jelentése voltaképpen tehát az, hogy a saját magunka szignált mindenre kiterjedoen kezeltük-e. Ennek megfeleloen ha a visszatérési érték 0 – azaz logikai hamis –, akkor végrehajtódik a GTK+ adott szignálhoz kapcsolódó alapértelmezett kezelo függvénye, ellenkezo esetbenértelemszeruen arra utasítjuk a GTK-t, hogy a szignál további feldolgozásától tekintsen el. Itt érdemes felhívni afigyelmet arra, hogy mivel a C nyelvben – a C++-al ellentétben –, nincs bool típus annak analógiájára definiáltáka gboolean típust (ami tulajdonképpen egy int) és a két megfelelo logikai értéket makróként (TRUE, FALSE).

Ebben a konkrét esetben (delete_event szignál) az alapértelmezett szignálkezelo a gtk_widget_destroy függ-vény, vagyis ha nem kötünk fel saját szignálkezelo függvényt, vagy logikai hamis értékkel térünk vissza a kezelo

23

Page 29: Helló Window!

függvénybol, akkor a window objektum megsemmisül, az ablak bezárul. Logikai igaz érték visszaadásával elérheto,hogy hiába próbáljuk akár a jobb felso sarok x gombjának megnyomásával, akár valamilyen billentyukombinációrévén bezárni az ablakot ez a próbálkozás sikertelen lesz, ellenben minden ilyen próbálkozás egy újabb sor kiírásteredményezi.

Az ablak bezárása.

29. sor Az elozohöz teljesen hasonló módon itt a destroy szignálra kötünk be eseménykezelot, ami a widget életciklu-sának végén váltódik ki. Egy konténerben lévo widget esetén ez a konténerbol való eltávolítás következménye –már ha valaki nem tart külön referenciát az eltávolított widgetre –, legfelso szintu widgetek (toplvel) esetén – mintamilyenek az ablakok is – ez jellemzoen a gtk_widget_destroy függvény meghívásának folyománya, lévén azilyen widgetek automatikusan nem semmisülnek meg, errol nekünk explicit módon kell gondoskodnunk (35. sor).

A destroy szignál kezelése általánosságban nézve ritka, jelen esetben is csak az a szerepe, hogy a program vala-milyen módon ki tudjon lépni. Az ablak bezárása alapértelmezetten ugyan ezt eredményezné, de a delete-eventszignálra kötött kezelofüggvényben nem hogy ezt nem tesszük meg, de még az ablak bezáródásást is meggátoljuk.Mikor a destroy szignálra kötött kezelofüggvény meghívódik, ablakunk épp megszunofélben van, ugyanakkor haez az eset áll is fenn a programunk futása annak ellenére sem érne véget, hogy az ablakunk bezáródik, hiszen amain loopból nem lépnénk ki. Ezen helyzet elkerülésére eseménykezeloként a main loopból való kilépésre szolgálógtk_main_quit függvényt kötjük fel.

Érdemes megjegyezni, hogy bár a gtk_main_quit függvény definíciója (void (*) ()) nem felel meg tökéletesena destroy szignál által elvártaknak (void (*) (GtkWidget *, gpointer)) ez voltaképpen nem jelent problé-mát, hiszen a típusok különbözoségét a G_CALLBACK makró által alkalmazott típuskényszerítés elrejti a fordítóelol, futásidoben pedig a gtk_main_quit egész egyszeruen nem veszi át a szignálkezelot meghívó kódtól a kétfüggvényparamétert.

35. sor A 32. sortól csak a meghívandó eseménykezelo függvényben, illetve az annak átadandó paraméterekben sor-rendjében tér el. Ahogy az a függvény nevébol (g_signal_connect_swapped) következik, arról van szó, hogy agomb lenyomásakor meghívandó callback – jelen esetben a gtk_widget_destroy – paramétereiben a user_data,illetve az az object, amin az esemény kiváltódik, felcserélésre kerül. Kicsit konkrétabban fogalmazva a user_datalesz a callback elso paramétere és a gomb a második. Mivel itt a callback a gtk_widget_destroy függvény, amiparaméterként mondjuk úgy, a törlendo widgetet várja, a user_data pedig az ablakunk, nem nehéz kitalálni, hogya gombra való kattintás eredményeként az ablak meg fog szunni, de csak azután, hogy a „Helló Window!” üzenetmegjelent a konzolban.

Gomb lenyomásának kezelése.

32. sor Eseménykezelo függvény bekötése a gomb clicked szignáljára, ami a gomb lenyomásakor hívódik meg, aminekegyetlen különlegessége, hogy itt a szignálkezelo definíciója pontosan megfelel a szignál által elvártaknak, ugyan-akkor a G_CALLBACKmakró mégis szükséges, mivel a g_signal_connect azt a típust várja, amire az on_button_clickedfüggvényt a makró kényszeríti.

4. sor A fenti állítás – miszerint az ablak csak a kiírást követoen szunik meg – csak azért igaz, mert a on_button_clickedfüggvény, mint eseménykezelo elobb kerül felkötésre, mint a gtk_widget_destroy, valamint azért, mert az ese-ménykezelok alapvetoen a felkötés sorrendjében kerülnek meghívásra. Fordított esetben elobb hívódna meg adestroy az ablakra, ami – sok egyéb mellett – leköti az eseménykezelo függvényeket, így a kiírást nem is látnánk.

Egyebek.

39. sor A nyomógomb hozzáadása az ablakhoz.

41 - 57 sor A létrehozott widgetek megjelenítése.

43. sor Belépés az eseményvezérelt szakaszba.

Fentiek ismeretében nagy biztonsággal jósolhatjuk meg példaprogramunk muködését. Az elindított alkalmazás egyablakot jelenít meg, melyben egy Helló Window! feliratú gomb lesz. Az ablak bezárásával hiába próbálkozunk egér, vagybillentyuzet segítségével, ezen kísérletek eredmény csupán egy-egy ”delete event occurred„ sor a konzolban. Ha azonbanle találnák nyomni gombunkat az ablak hirtelen eltunik a konzolban egy ”Helló Window!„ felirat jelenik meg és a programkilép. Lássuk, hogy érhetünk ehhez teljesen hasonló funkcionalitást C++-ban.

24

Page 30: Helló Window!

Szignálok a gtkmm nyelvu kódban

Szignálkezelo függvények felkötése.

50 - 62 sor Ahogy azt az általánosságokat taglaló részben említettük Gtk::Window helyett MyWindow típust használunkfoablakunk létrehozásához. Mivel azonban a MyWindow publikusan származik a Gtk::Window típusból ez a gtkmmszámára nem jelent különbséget. A C változathoz képest a származtatás itt nem csupán ”logikai”, vagyis minden aC++-an megszokott elony könnyedén realizálható. Erre példa, hogy a származtatás miatt nincs szükség semmilyentípuskényszerítésre mikor a Gtk::Main::run függvényt hívjuk, ami pedig egy Gtk::Window referenciát vesz átparaméterként.

21 - 44 sor Saját osztályunk konstruktorában megtehetjük mindazokat a lépéseket, melyeket a C nyelvu változat esetén amy_window_new függvényben implementáltunk. Úgy is mint a szignálok felkötése, a gomb hozzáadása az ablakhoz,a widgetek megjelenítése. Az egységbezárás ezen elonyén túl a származtatásból fakadó örömöket is élvezhetjük,ugyanakkor persze az ebbol fakadó kötelességeknek is eleget kell tenni. Ez esetben ez a konstruktor meghívásátjelenti, ami rejtett módon megy végbe. Az ososztály konstruktorának explicit hívásának hiányában a Gtk::Windowazon konstruktora fut le, ami paraméterek nélkül is hívható. Másrészrol viszont az adattagként tárolt GtkButtont(button) is inicializálnunk kell. Itt is lehetne közvetve, implicit módon hívni a paraméter nélküli konstruktort,azonban kézenfekvobb azt a változatot használni, amivel egyszerre a gomb feliratát (label) is megadhatjuk, így egyhívás a késobbiekben megspórolható.

Külön szót érdemelnek a szignálok bekötései. Különösebb programozó géniusz nem kell, hogy felfedezzük a szig-nálok eléréséhez egy signal_szignálnév szerkezetu hívását használjuk fel. Az ilyen hívások egy, a Glib::SignalProxyBaseosztályból származó objektumot adnak vissza, amik connect nevu metódusai valósítják meg azt, amit a GTK+esetén a g_signal_connect makró tett meg, vagyis egy adott widget, adott szignáljára eseménykezelo felkötését.Elonye ennek a módszernek, hogy típusbiztos, azaz a connect paraméterként csak olyan függvényt (slot) fogadel, melynek típusa megfelel az adott szignálnál leírtakkal. További elony, hogy a slotokhoz nem csupán egy userdata csatolható, hanem tetszés szerinti számú, s ezek típusa is ellenorzésre kerül fordításkor. Amennyiben azonbansikerül csupán egy apróságot is elírnunk a szignál bekötésénél, vagy a slot típusának megadásánál – a sablonokkal(template) történo megvalósításnak hála –, akkor jellemzoen több oldalas, nehezen kibogarászható hibaüzenetteltalálhatjuk szemben magunkat.

32 - 35 sor Lássuk akkor miként is érheto el ugyanaz gtkmm esetén, mint ami korábban GTK+ használatával. Elsopillantásra is szembeszöko, hogy mindkét sorban találunk olyan hívást, amik nem a Gtk névtérben definiáltak.Ennek az az oka, hogy a gtkmm a szignálkezelést egy külso – libsigc++ nevu – függvénykönyvtárral valósítjameg. A két eseménykezelo felkötése közötti különbséget az eseménykezelo függvények típusa adja lássuk eztrészletesebben.

32. sor Ha a megadni kívánt függvény nem kötodik objektumhoz – legyen ez egy osztály statikus tagfüggvénye, vagyakár egy tisztán C nyelvu kódból származó függvény – slot létrehozásához a sigc::ptr_fun alkalmazandó. Ebbena konkrét esetben a slot létrehozásán túl, paramétereket is hozzákapcsolunk a clicked esemény bekövetkeztekormeghívandó függvényhez. Ennek eszköze a sigc::bind, melynek elso paramétere egy slot, a továbbiak pedig acsatolandó paraméterek. Itt csupán egy ilyen van, a gomb lenyomásának hatására kiírandó üzenet szövege. Ezpersze kissé kényszeredett, hiszen a paraméter értéke soha nem változik, így ennek igazi hasznát ezen a példaalapján még nehéz belátni.

4. sor Eseménykezelo függvényünk a leheto legegyszerubb, csupán azt szemlélteti miként is kell az átadott paraméterekethasználni. Ez esetben annak értékét a standard kimenetre kiírni. Muködését és funkcióját tekintve a C-s változatazonos nevu függvényével analóg.

35. sor Ha a megadni kívánt eseménykezelo egy osztály tagfüggvénye, akkor a sigc::mem_fun használható arra, hogyslotot hozzunk létre az osztály egy példányából, illetve az osztály tagfüggvényébol, ebben a sorrendben átadva oketa függvénynek, utóbbit a teljes névtérlistával együtt. Természetesen az imént említett sigc::bind, az elobbiekhezhasonlóan módon itt is alkalmazható.

Ez a hívás épp ugyanazt a célt szolgálja, mint a GTK+ változat azonos sorszámú sora, azaz hogy az alkalmazá-sunkból annak ellenére is ki lehessen lépni, hogy az ablak bezáró gombjának hatására a itt sem történik semmiegyéb, mint kiíródik a ”delete event occurred„ szöveg a standard kimenetre. Míg legutóbb a gtk_widget_destroyfüggvényt kötöttük fel eseménykezeloként, itt a Gtk::Widget osztály hide függvényét használjuk, aminek hatá-sára a GTK fociklusa kilép, mivel annak futtatásakor (59. sor) megadtuk ablakunkat paraméterként. Átgondolvaa muködést jogosnak tekintheto, hiszen látható foablak hiányában a további muködésnek nem sok értelme van,ugyanakkor a C változat gtk_widget_destroy függvénye helyett a C++ változatban a delete hívással lehetneúgymond jelezni, hogy az ablakunkra nincs tovább szükség, viszont ez nem célszeru, hiszen az a main függvénybenegy lokális változó.

25

Page 31: Helló Window!

A delete-event szignál blokkolása.

12. sor A GTK+ változathoz képesti komoly különbség, hogy itt a delete-event szignál blokkolása nem egy felkötötteseménykezelon keresztül valósul meg – ezért is nincs a kódban olyan sor, ami erre a szignálra vonatkozna –, hanemaz alapértelmezett eseménykezelo kerül felülírásra. A muködés megértésének kulcsa a virtual kulcsszóban rejlik.Minden szignálhoz tartozik ugyanis egy – az adott widget által implementált – alapértelmezett eseménykezelo függ-vény, ami alkalmasint felülbírálható (ovverride). Ha ezt megtesszük, azzal a szignál kezelésének teljes folyamatátmi irányítjuk, ami mellett komoly érvek szólhatnak, de nem árt körültekintonek lenni. Ám ebben az esetben a célpont annak a demonstrálása, hogy a gtkmm szabad kezet ad abban, hogy egy származtatott widget miként kívánjakezelni az ososztály eseményeit. A visszatérési érték szerepe ugyanaz, így a muködés is azonos az elozo – GTK+nyelvu – példáéval.

A szignálkezelésrol összegzésképpen annyit, hogy alapvetoen két lehetoség kínálkozik arra, hogy az egyes widgetekeseményei kezeljük:

• Callbackeket kapcsolni azon widgetek azon eseményihez, melyek számunkra érdekesek és ezekben megtenni amegfelelo lépéseket

• Felülbírálni a widget saját eseménykezelojét az öröklodés mechanizmusai útján. Erre mindkét változat esetén vanlehetoség, ám a GTK+ megoldása kissé körülményes és nehezebben megértheto, így annak ismertetése valmelykésobbi részre marad. A C++ nyelvi eszközeit kihasználva a gtkmm viszont ezt oly könnyedén oldja meg, hogykár lett volna kihagyni a bemutatást annak ellenére is, hogy a módszerre ritkán van szükség, hiszen többnyirearról van szó, hogy a különbözo widgetpéldányok azonos szignáljainak kiváltódásakor más-más irányba szeretnénkterelni a program futását. A felülbírálás révén viszont arra nyílik lehetoség, hogy a szignál kezelésének módjátváltoztassuk meg. Ha nem kívánunk egyebet tenni, mint ami amúgy is történne, hívjuk meg a felülbírált függvényszüloosztálybeli változatát. Ha azonban ez elott, vagy után még valami másra is szükségünk van, megtehetjük, hogycsak a függvény közepérol hívjuk a szülo metódusát, vagy akár el is hagyhatjuk az ha tudjuk mit és foként hogyanszeretnénk kezelni.

5.2.2. Python nyelvu változatok

1 fromgi.repositoryimportGtk23 defon_button_clicked(button,hello_msg):4 print(hello_msg)567 defon_delete_event(my_window,event):8 print("deleteeventoccurred")9 returnTrue

1011 defmy_window_new():12 my_window=Gtk.Window()13 my_window.connect("destroy", Gtk.main_quit)14 my_window.connect("delete-event",on_delete_event)1516 button=Gtk.Button("HelloWindow")17 button.connect("clicked",on_button_clicked, "HelloWindow")18 button.connect_object("clicked", Gtk.Widget.destroy,my_window)19 my_window.add(button)2021 returnmy_window222324 if__name__== "__main__":2526 my_window=my_window_new()27 my_window.show_all()2829 Gtk.main()

1fromgi.repositoryimportGtk23defon_button_clicked(button,hello_msg):4print(hello_msg)56classMyWindow(Gtk.Window):7defdo_delete_event(self,event):8print("deleteeventoccurred")9returnTrue1011def__init__(self):12Gtk.Window.__init__(self)13self.connect("destroy", Gtk.main_quit)141516self.button=Gtk.Button("HelloWindow")17self.button.connect("clicked",on_button_clicked, "HelloWindow")18self.button.connect_object("clicked", Gtk.Widget.destroy,self)19self.add(self.button)2021222324if__name__== "__main__":2526my_window=MyWindow()27my_window.show_all()2829Gtk.main()

Kódrészlet 5.2. Szignálok kezelése Python nyelven

Elöljáróban annyit érdemes megjegyezni a Python változat kapcsán, hogy az itt alkalmazott megoldások a C, illetveC++ változatból már ismertek, logikájuk hol a GTK+, hol pedig a gtkmm megoldásaira emlékeztetnek – függoen termé-szetesen attól is, hogy kihasználjuk-e a Python nyelv adta objektum-orientáltság lehetoségeit –, ötvözve azok elonyeit.Kihasználva a korábbiakban leírtakat, itt már csak a kifejezett nyelv specifikus részeket ismertetjük, a csupán szintaktikaielemekben eltéro részletekre nem térünk ki.

Általánosságok.

1. sor A Gtk szimbólumok importálása, csakúgy, mint a korábbi, minimális példában.

26

Page 32: Helló Window!

24 - 29 sor Ez a rész gyakorlatilag azonos a korábbi, minimális példánál ismertetettekkel, azzal a különbséggel, hogya delete-event szignál helyet az ablakunk destroy szignáljára kötjük rá a programból való kilépéshez vezetomain_quit függvényt, melynek magyarázat épp az, mint a korábban C változatnál, errol azonban még esik szó.

A delete-event szignál blokkolása.

14. sor A korábbiakhoz hasonlóan a delete-event szignál elnyomására – függoen attól, hogy objektum-orientált meg-közelítést alkalmazunk-e vagy sem – két lehetoség kínálkozik. Vagy egy szignálkezelo függvényt kötünk fel, ami-ben logikai igaz értékkel (True) térünk vissza, azt mondva ezzel a GTK szignált feldolgozó kódjának, hogy ezt azeseményt kezeltük, vagy saját osztályunkban írjuk felül az alapértelmezett eseménykezelot (do_delete_event),amiben hasonlóképpen járunk el. Mivel a Python nyelvben gyakorlatilag minden függvény virtuális ezt mindentovábbi nélkül megtehetjük.

7. sor A kezelo függvény a nyelvi különbségektol eltekintve teljesen azonos a C, illetve C++ nyelvu változatokéval,vagyis mindkét függvény paraméterként megkapja a kezelt eseményt leíró adatstruktúrát, illetve azt az objektumotamin az esemény kiváltódott. Kicsit korrektebbül fogalmazva az objektum-orientált változat valójában azon objek-tumra kap referenciát, melynek osztályához a kezelo függvény tartozik, de ebben az esetben ez a ketto egybeesik.

Gomb lenyomásának kezelése.

17. sor Mivel a Python esetén a widgetek – a C++ változathoz hasonlóan – valódi objektumok, az eseménykezelo függ-vények bekötése az objektumon keresztül történik. Az eseménykezelo függvények elso paramétere maga az objek-tum lesz. A szignál bekötésekor megadott további paraméterek a kezelo függvénynek adódnak át. A C változattalellentétben – ahol csak egy paraméter adható meg – a Python képes egyszerre több paraméter átadására is – épp-úgy, mint a gtkmm – ugyanakkor itt természetesen olyan szigorúan vett típusellenorzésrol nem beszélhetünk, minta C++ nyelv esetén.

Az ablak bezárása.

18. sor Az ablak bezárásának logikája azonos a C változatnál elmondottakkal, vagyis az ablak destroy szignáljánakkezelojeként a main_quit függvényt adjuk meg, azaz az esemény bekövetkeztekor a Gtk fociklusa, egyszersminda programunk is kilép.

13. sor Ezt úgy érjük el, hogy gombunk clicked szignáljának kiváltására meghíjuk az ablak destroy függvényét. Ha-sonlóan a C változathoz, itt is alkalmazunk némi cselt. Mivel ott csak egy paraméter adható át – pontosabban ketto,hiszen az elso maga az objektum, aminek a szignáljára a kezelofüggvényt felkötöttük –, a paramétereket meg kellfordítanunk, hogy az általunk megadott adat kerüljön az elso helyre, vagyis ez legyen a gtk_widget_destroyáltal megkapott egyetlen paraméter. Voltaképpen a connect_object ugyanezt a célt szolgálja, így ellentétben aconnect függvénnyel ennek csak egy paramétere van, mely ez esetben maga az ablakunk, amit a destroy függ-vénynek adunk át.

5.2.3. Fordítás és futtatásA korábbiakhoz hasonlóan az alábbi parancssorok segítségével fordíthatóak elemzett programjaink:

gcc gtk_signal.c -o gtk_signal ‘pkg-config --cflags --libs gtk+-3.0‘

g++ gtkmm_signal.cc -o gtkmm_signal ‘pkg-config --cflags --libs gtkmm-3.0‘

Próbálkozzunk ezúttal a ./gtk_signal, ./gtkmm_signal, illetve python gtk_signal.py parancsokkal abban akönyvtárban, ahol a fordítást elkövettük, illetve a Python forrást tartjuk.

5.2.4. EredményBármily hihetetlen ezúttal sem történik sok egyéb, mint a korábbi minimális példa esetén. A különbség remélhetolegannyi, hogy a meglepetéssel teli borzongást legutóbb ablakunk váratlan felbukkanása, míg most a bennünk szikrakéntfelvillanó megértés okozza.

27

Page 33: Helló Window!

rész II

Alapveto widgetek

28

Page 34: Helló Window!

6. fejezet

Ablakok

6.1. BevezetésEbben a részében a GTK-ban létrehozható különbözo ablaktípusok közös vonásait, valamint eltéréseit, illetve ezek okaitvesszük sorra. Kitérünk egyrészrol az egyes ablaktípusok létrehozásának sajátosságaira, azok widgetekkel való feltölté-sére, másrészrol a felhasználó interakciók kezelésére, ezzel együtt az ablakok bezárásának módjaira is, szem elott tartvatermészetese a C, C++, illetve Python nyelvu változat azonosságait, különbözoségeit.

6.1.1. Popup és toplevel ablakokA popup ablakra, mint típusra ugyan ritkán lesz közvetlenül szükségünk, érdemes tudni, hogy a GTK ebben a tekintet-ben két fajta ablakot különböztet meg. A popup (felugró, felbukkanó) ablakokat, melyekre – valamilyen speciális céltszolgáló saját készítésu widgetektol eltekintve – csak néhány példa létezik (menu, tooltip), valamint a toplevel (legkülso,legfelso szintu) ablakokat, melyek csaknem minden GTK-s, illetve saját fejlesztésu ablak alapjául szolgálnak. Ha tehát azablakra, illetve a hozzá kapcsolódó fogalmakra gondolunk, többségében egy toplevel ablakra gondolunk, és nem a popup1

típusúakra, melyekrol talán nem is feltételeznénk elso ránézésre, hogy ablakok.Az ablakkezelo ezt az információt használja fel annak eldöntésére, hogy az adott ablakot milyen kerettel, dekorációval

lássa el, illetve hogy általánosságban menedzselje e ablakot. Utóbbi – azaz a toplevel ablakok – esetben alapértelmezettenaz ablakkezelo keretet, illetve a beállításoktól függoen azon például bezáró, teljes mértre váltó, minimalizáló gombotjelenít meg. A popup típusú ablakokat az ablakkezelo nemcsak hogy nem dekorálja, de nem is menedzseli azokat, kö-vetkezésképp tehát számos – az ablakkezelo hatáskörébe tartozó – funkció, mint amilyen például a minimalizálás, vagy amaximalizálás nem is érheto el. Bár kézenfekvo megoldásnak látszik a popup típus arra, ha egy dekoráció nélküli ablakotkészítsünk, mégse ezt tegyük, az ilyen típusú megjelenésbeli sajátosságok beállítására léteznek külön függvények.

Minden GtkWindow egyben konténer is, pontosabban fogalmazva egy GtkBin, azaz tartalmazhat egy további elemetgyerekként, ami természetesen szintén lehet egy konténer, így biztosítva, hogy számos elemet helyezhessünk el az elké-szített ablakon belül.

6.1.2. Window és dialógA window típus – azon belül is ahogy tárgyaltuk a toplevel window – közvetlen szüloje a dialog típusnak, számottevo kü-lönbség tulajdonképpen nincs is a ketto között. Egy dialog nem más, mint egy olyan window, melybe a GTK+ fejlesztoinéhány hasznos elemet helyeztek el. Konkrétabban fogalmazva minden dialógba egy függoleges elrendezésu konténerwidget (GtkBox), abba pedig egy, a gombok elhelyezésére szolgáló konténer (GtkButtonBox típusú action_area), va-lamint egy szeparátor (GtkSeparator) kerül, ebben a sorrendben mindkét esetben a konténer aljára helyezve2. Ebbolkövetkezik, hogy minden, amit egy dialógusba – annak elkészült után – tenni akarunk az a gombsor, valamint a vízszintesszeparátor fölött jelenik meg, függetlenül attól, hogy azt a pack_start(), vagy a pack_end() függvény segítségévelhelyezzük el a konténerben.

A dialog típus tehát – a szeparátor által – függolegesen ketté osztott window, ahol az alsó rész (action_area),ami általában a gombokat tartalmazza (pl.: Ok, Mégse, Súgó, . . . ), a felso (content_area) pedig azokat az elemekettartalmazza, amik a felhasználói számára a szükséges akcióhoz (pl.: adatbevitel, hibaüzenet megjelenítése, . . . ) szükséges.

6.1.3. ModalitásA több ablakkal történo párhuzamos interakció tiltására szolgál a window modal tulajdonsága. Amennyiben egy ablak„modális” csak az abban az ablakban elhelyezkedo widgetekbe történhet például bevitel, csak azokon váltódhat ki vala-

1Más eszközkészletek a „popups” gyujtofogalom alá sorolják a dialógusokat, a GTK esetén azonban egy dialógus ablak mindig egy toplevel2A GtkBox típus pack_end() függvényét hívva.

29

Page 35: Helló Window!

milyen felhasználó által kezdeményezett esemény. Ezt kihasználva biztosíthatjuk például, hogy egy adatbeviteli ablak3

bezárásáig ne változzon semmilyen, a felhasználó által módosítható, widget tartalma a háttérben.Modalitásnak két formáját különböztetjük meg. Egyrészrol – amirol eddig is szó esett – a csak az applikációra vonat-

kozó modalitást, mely lehetové teszi, hogy más applikációk ablakaihoz minden további nélkül hozzáférhetünk. Másrészrola teljes rendszerre érvényes modalitást, ahol a modális ablakon kívüli ablakokkal folytatott minden nemu felhasználó in-terakció tiltott. Ez utóbbi módszert csak a legszükségesebb esetben – már ha van ilyen – célszeru alkalmazni és az elobbiis csak akkor fogadható el felülettervezési szempontból, ha az applikáció egyéb részeihez való hozzáférés adatvesztést,vagy más komoly hibát okozna. Amennyiben mégis a modalitás mellett döntünk, ami nem ritka, hiszen az adatbevitelre,módosításra használt ablakok majd mindegyike ilyen, fontos egyértelmuvé tenni a felhasználó számára, hogy mikénthagyhatja el azt az ablakot, ami korlátozza az applikáció más részeihez való hozzáférését. Egy ilyen menekülo útvonalbiztosításának kézenfekvo módja lehet például egy Mégse feliratú gomb.

6.1.4. TranzienciaA dialógusok rendszerint „tranziensek” arra az ablakra, melybol származnak, azaz arra az ablakra melyen azt a muveletetváltottuk ki, aminek hatására a dialógus megjelent. Ezen beállítás alapján az ablakkezelo képes a dialógusunkat elotérben,a szüloablak fölött tartani4, valamint ha arra kérjük, akkor a szülo ablakhoz képest középen megjeleníteni (6.2.1).

Ez a funkció azonban nem csak az ablakok helyes megjelenítéséhez szükséges, a megszüntetésükkor is hasznos,hiszen a destroy-with-parent tulajdonságon (property) keresztül lehetoség van arra utasítani a GTK-t, hogy egy ablakmegszunésekor azokat az ablakokat is szüntesse meg, melyek erre a szüloablakra nézve tranziensek. Ez leginkább akkorhasznos, hogy ha egy bizonytalan ideig létezo ablakra szeretnénk tranziensek lenni5. Így nem kell törodnünk azzal, hogyablakaink esetleg „árván” maradnak.

6.2. Használat

6.2.1. LétrehozásMind a GtkDialog, mind pedig a GtkWindow típus létrehozása – már ami formai részt illeti –, teljesen hasonló az összestöbbi widgetéhez, van azonban egy érdemi különbség, amire érdemes kitérni. Az ablakok – igaz ez természetesen azösszes többi GtkWindow típusból származó widgetre is (pl.: GtkMessageDialog, GtkAboutDialog, . . . ) – természetük-nél fogva nem kerülnek bele más konténerbe, hiszen pont ezek azok a típusok, amik widgeteket tartalmaznak. Ellentétbenazonban a többi típussal, ahol a referenciaszámlálás megoldja a problémát, itt a létrejött widgetek felszabadításáról ma-gunknak kell gondoskodnunk.

Paraméterek

A GtkDialog létrehozásában már valamivel nagyobb a különbség, függoen attól, hogy GTK+-t, vagy gtkmmet hasz-nálunk, bár így sem számottevo. Minkét esetben meg kell adnunk a címsor szövegét, valamint azt az ablakot, amiretranziensek kívánunk lenni. GTK+ esetén – ahogy látszik – lehetoségünk van NULL érték megadására, ami azt jelenti,hogy nem kívánunk ezzel a lehetoséggel élni. A gtkmm is lehetséges ez, ha az alább látható konstruktort helyett azthívjuk, amelybol hiányzik a parent paraméter.

GtkWidget*gtk_window_new(GtkWindowTypetype);

explicitWindow(WindowTypetype=WINDOW_TOPLEVEL);

classWindow(Gtk.Window):def__init__(self,type=Gtk.WindowType.TOPLEVEL, **kwds):

Kódrészlet 6.1. Window létrehozása

Ahogy arról szó esett (6.1.1) a GTK két típust különböztet meg – a popup és toplevel window – melyek közül az elobbiolyannyira ritkán használt, hogy a C++ nyelvu változat esetén az alapértelmezett paramétere is van a típus konstruktorá-nak, ahol a típus alapértelmezett értéke toplevel.

3Ilyen lehet például a szerkesztés menüpontok beállítások almenüjének hatására megjeleno ablak.4Helytelen beállítások esetén – ha rosszul, vagy egyáltalán nem adjuk meg a szüloablakot – elofordulhat, hogy egy újonnan létrehozott és megjele-

nített dialógusunk a már létezo ablakok alatt, vagy között kerül megjelenítésre, ami felhasználói szempontból roppant zavaró5Erre lehet példa egy nem modális ablak, ami a programfutása során is megszunhet

30

Page 36: Helló Window!

A C, illetve a Python változat flags paramétere egyben tartalmazza a gtkmm modal (6.1.3) és a destroy-with-parent(6.1.4) értéket egy bitmask értékben. Ez utóbbi beállítására ugyan van lehetoség gtkmm esetén is, a C++ nyelvi eszköze-inek korrekt használata mellett nemigen van szükség6.

GtkWidget*gtk_dialog_new_with_buttons(constgchar*title,GtkWindow*parent,GtkDialogFlagsflags,constgchar*first_button_text,...);

Gtk::Dialog::Dialog(constGlib::ustring& title,Gtk::Window&parent,boolmodal= false,booluse_separator=false);

classDialog(Gtk.Dialog,Container):def__init__(self,

title=None,parent=None,flags=0,buttons=None,_buttons_property=None,**kwars):

Kódrészlet 6.2. Dialog létrehozása

Pozíció

Egy ablak képernyon elfoglalt pozíciójának megadására alapvetoen két lehetoség kínálkozik. Az egyik, ha elore – mégaz ablak megjelenítése elott – megadjuk, a kívánt elhelyezkedést. Ehhez a GTK annyiban tud segítségünkre lenni, hogyválaszthatunk néhány elore definiált elhelyezkedési pozíció közül, így nem szükséges a pixelben megadott koordinátákkiszámítására idot és energiát pazarolni7.

A set_position függvény még a megjelenítést megelozoen – azaz window esetén a show, daialog esetén pedig arun meghívása elott – módunkban áll az alábbi elhelyezkedési sémák közül a megfelelot kiválasztani.

WIN_POS_NONE Nincs befolyással a megjelenítést ablak pozíciójára nincs.

WIN_POS_CENTER A megjelenítendo ablak a teljes képernyohöz képest középen jelenik meg.

WIN_POS_MOUSE A megjelenítendo ablak az egér aktuális pozíciója alatt jelenik meg.

WIN_POS_CENTER_ALWAYS A megjelenítendo ablak a teljes képernyohöz képest középen jelenik meg és átméretezéstkövetoen is ott marad8.

WIN_POS_CENTER_ON_PARENT A megjelenítendo ablak – a set_transient_for függvénnyel beállított – szülojé-hez képest középen jelenik meg.

Ha úgy látjuk a fenti lehetoségek nem felelnek meg maradéktalanul céljainknak, akkor lehetoségünk van arra, hogyablakunkat a kívánt pozícióra mozgassuk. Megjegyzendo, hogy ez a mozgatás csupán egy kérés az ablakkezelo felé, amitaz figyelmen kívül is hagyhat. Az ablakkezelok jelentékeny része ezt meg is teszi amennyiben ezzel a módszerrel kívánjukaz ablak kezdeti pozícióját meghatározni, viszont honorálja kérésünket, ha az ablak korábban már megjelenítésre került.Az ablak elhelyezkedésének megadása x, y koordinátákkal történik egy választott referenciaponthoz képest, mely lehet azablak bármely sarokpontja, az élek középpontja és az ablak középpontja egyaránt. A mozgatás maga a move függvénnyeltörténik, a referenciapontot pedig a gravity értéke határoz meg.

Egy ablak pozíciójának nem csak a beállítására, de lekérdezésére is szükség lehet, ugyanakkor ebben a tekintetbenadott egy komoly megszorítás, amivel mindenképp szükséges számolni. A get_position függvény által visszaadottértékek a már korábban említett módon függenek egyrészrol a gravity értékétol, másrészrol pedig az ablakkezelotol.Elméletben, ha a visszakapott x, illetve y értéket átadnánk a set_position függvények azt kellene tapasztalnunk, hogyaz ablak egy helyben marad, gyakorlatban viszont azt tapasztalhatjuk, hogy az ablak valamennyit elmozdul. Ennek okaaz ablakkezelo által az ablak köré rajzolt dekoráció, illetve annak geometriája, amit a GTK+ csak jó közelítéssel tudbecsülni. Ez például akkor okozhat gondot, ha programunk ablakainak méretét és elhelyezkedését menteni szeretnénk,majd azt visszaállítanánk a következo futtatásnál. Érdemes tehát körültekintonek lenni.

Méret és arány

A konténerek méretének meghatározásáról és elemeik (children) elhelyezkedésérol leírtak (7.3) a belso elrendezésükarányait átméretezéskor is megtartó ablakok kialakításakor válnak igazán fontossá.

Egy ablak méretét, ha más erre vonatkozó beállítást nem teszünk – épp úgy mint mint minden más konténerét – abenne lévo elemek méretigény határozza meg, ugyanakkor lehetoség van ennek az alapértelmezés szerinti muködésnek amódosítására. Egyrészrol a set_default_size függvény révén, melynek megadható az ablak alapértelmezett vízszintesés függoeges mérete pixelben. Ennek hatása, hogy az ablak elso megjelenítéskor9 legalább ilyen méretu lesz. Ha azon-ban az ablakban tárolt widgetek méretigénye azt indokolja, akkor a megadott szélesség és magasság értékeknél nagyon

6Ha egy ablakra egy tranziens dialógust akarunk megjeleníteni, az nyugodtan lehet adattag, aminek megszüntetésérol az ablak destruktorábangondoskodhatunk.

7Ami nem minden esetben kézenfekvo feladat, hiszen adott esetben nem csak a képernyo felbontásával, saját ablakunk méreteivel, de a szüloablak,vagy éppen a desktop szélesség és magasság értékeivel is foglalkozni kell.

8A legtöbb esetben ez a választás nem szerencsés, lévén nem feltétlenül muködik ez a mód minden ablakkezelo rendszer esetén.9Egy esetleges eltüntetéskor hide az ablak mérete úgymond mentésre kerül, azaz az alapértelmezett méret nem kerül újra alkalmazásra, ha az ablakot

eltüntetjük hide, majd újra megjelenítjük.

31

Page 37: Helló Window!

méretben kerül megjelenítésre. Ezt a muködést természetesen a size request megadása révén is elérhetnénk, de az alapér-telmezett méret beállítása esetén a felhasználó csökkenteni tudja az ablak méretét, ha megadottak szerinti méretre nincsfeltétlenül szükség.

Az ablak átméretezése kapcsán két felhasználói szempontból érdemleges kérdés merül fel. Az egyik, hogy engedjük-eaz ablak átméretezését a felhasználónak. Erre a kérdésre adott válasz leginkább azon múlik, hogy mennyi idot kívánunk azablak tervezésével tölteni, illetve mennyire van a felhasználónak igénye az átméretezésre. Ha idonk korlátos és valójábannincs szükség a méret megváltoztatására, akkor szerencsénk van, nyugodtan tilthatjuk ezt az interakció. Ha viszont eznem lehetséges – például azért, mert az alkalmazásunk foablakáról, vagy egy olyan dialógusról van szó, melyben egylista jelenik meg, aminek hasznos lehet a leheto legtöbb helyet biztosítani –, akkor idot és energiát kell szálnunk az egyeswidgetek viselkedésének megtervezésére. Át kell gondolnunk mely elemeknek foglalják el (expand, fill) azt a helyet,ami az átméretezés révén rendelkezésre áll majd. Egy-egy feleslegesen megnyúló widget – mondjuk egy teljes képernyotelfoglaló beviteli mezo, amibe mondjuk csak egy IP címet szeretnénk írni – épp annyira szerencsétlenül mutat, mintamennyire zavaró az, ha hiába növeljük az ablak méretét, a lista, amibol több sort szeretnénk látni, mégsem no. Ha mégisaz átméretezés tiltása mellett döntenénk, akkor ezt a set_resizable függvény hívásával tudjuk elérni. A másik érdemlegeskérdés a programból történo átméretezés, amire ugyan megoldható (resize), de erosen ellenjavallt usability szempontból.

A fentieknél is részletesebb beállítások a set_geometry_hints függvénnyel tehetok meg. Az ablak minimális és amaximális vízszintes, illetve függoeges irányban külön-külön állíthatóak, ahogy az átméretezés lépésköze is, sot az ablakméretarányának 10 (aspect ratio) lehetséges legkisebb és legnagyobb értéke is megadható.

6.2.2. Minimális példaEnnyi bevezeto után lássuk egy olyan példát, ami a leheto legkevesebb kódsor mellet, még ha meglehetosen korlátozottfunkcionalitás bíró, de muködo alkalmazást eredményez. Az alábbi C, C++, illetve Python nyelvu kód nem tesz egyebet,létrehoz egy ablakot, amit meg is jelenít azáltal, majd átadja a vezérlést a GTK-nak azáltal, hogy futtatja a main loopot,

1 #include<gtk/gtk.h>23 int4 main(intargc,char*argv[])5 {6 GtkWidget*window;78 gtk_init(&argc, &argv);9

10 window=gtk_window_new(GTK_WINDOW_TOPLEVEL);11 g_signal_connect(G_OBJECT(window), "delete-event",12 G_CALLBACK(gtk_main_quit),NULL);13 gtk_widget_show(window);1415 gtk_main();1617 return0;18 }

#include<gtkmm.h>

intmain(intargc,char*argv[]){

Gtk::Mainkit(argc,argv);

Gtk::Windowwindow;

window.show();

Gtk::Main::run();

return0;}

1fromgi.repositoryimportGtk234if__name__== "__main__":5678910window=Gtk.Window()11window.connect("delete-event",12Gtk.main_quit)13window.show()1415Gtk.main()161718

Kódrészlet 6.3. Minimál példa GtkWindowhoz

A változatok bár egyformának tunnek, néhány apróságban mégis eltérnek egymástól. Ezek egy része, mint a windowváltozó deklarálásának helye, vagy paraméterezése, a programozási nyelv sajátosságaiból következik. Mások viszont,mint a main loop futtatásának módja, illetve a függvény elnevezése már a nyelvi változat megalkotóinak belátásán mulik.A programok muködése azonos, csupán a technikai megvalósításban vannak eltérések.

Ezekrol korábban már szó esett, így itt ezeket nem részletezzük, inkább sorra vesszük miként vehetünk használatbaegy frissen létrehozott ablakot, mit kell tennünk, ha elemeket szeretnénk elhelyezni az ablakban, ha vezérlo gombokkalszeretnénk látnánk el, majd a megjelenítést után a felhasználó interakciókat szeretnénk követni, illetve azokra reagálni.

6.2.3. Tartalmi elemekEgy ablak típusa szerint nem más, mint egy konténer, pontosabban fogalmazva egy GtkBin (7.1.1), amibe további ele-meket tehetünk. Praktikusan ez az elem egy újabb konténer, rendszerint egy GtkBox. A GtkDialog esetén, mint azt akorábbiakban (6.1.2) kifejtettük, a konténerbe helyezett GtkBox már adott.

Az így egy ablakba kerülo widgetekre nem csak abban az értelemben tekintünk csoportként, hogy mindegyikük szüloje– toplevel widget szinten – ugyanaz az ablak, de vannak bizonyos tulajdonságok, melyek ugyan konkrétan a widgetrevonatkoznak, de összefüggésben állnak az ablak más widgeteinek bizonyos tulajdonságaival, de mindig csak az azonosablakban lévo más widgetekéivel, vagyis erre csoport nézve zártak.

10szélesség/magasság lebegopontos számként adott értéke

32

Page 38: Helló Window!

Fókusz widget

Egy adott ablakban11 egy adott pillanatban legfeljebb egy olyan widget lehet, melyen fókuszban (keyboard focus) van. Havan ilyen widget, akkor minden – az ablak által fogadott – billentyuzet esemény (billentyu lenyomása, felengedése, . . . )hozzá kerül továbbításra. Így értheto is, hiszen ha például gépelünk valamit a billentyuzeten, akkor annak eredményétértelemszeruen csak egy beviteli mezoben szeretnénk látni.

A widgetek többsége valamilyen látható módon is jelzi azt az állapotot, hogy aktuális fókuszban van. Ez az szöveg-bevitelre szolgáló widgetek (GtkEntry, GtkTextView, . . . ) esetén abban nyilvánul meg, hogy a kurzort látjuk villogni abeviteli mezoben, egyéb esetekben ezt egy vékony fekete keret jelzi. A fókusz egyik widgetrol a másikra történo mozgatá-sára a szokások módszer, azaz a tab, illetve a kurzormozgató billentyuk használhatóak. Az egyes widgetek között történováltás is testre szabható a GtkContainer set_focus_chain függvényével, de erre valóban ritkán lehet szükség.

6.1. ábra. Billentyuzet fókusz jelzése a widgeten[6]

Az egyes widgetekre külön-külön engedheto, vagy tiltható, hogy fókuszba kerülhessenek, a can-focus tulajdonságállításával. Az egyes widgettípusok esetében az alapértelmezés szerinti érték rendszrint megfelel a céljainknak12. Hakódból szeretnénk átmozgatni a fókuszt az egyik widgetrol a másikra, vagy csak azt kívánjuk elérni, hogy az ablakmegjelenítésekor legyen olyan widget ami fókuszban van, akkor a grab_focus függvényt kell alkalmaznunk, aminekelofeltétele a can-focus tulajdonság igaz értéke, azaz fókuszálhatónak kell lennie, ami viszont bizonyos widgetek (pl.:GtkFrame) esetén nem lehetséges.

Alapértelmezett widget

esetén csaknem mindig használt tulajdonság az alapértelmezett widget. Ez ellentétben a fókusszal, ami inkább egy logikaitulajdonság, a felületre nincs, csak a muködésre van hatással. Egy ablakon belül – nevébol is következoen – legfeljebb egyolyan widget lehet, mely rendelkezik ezzel a tulajdonsággal, ez rendszerint a dialógus jóváhagyó (affirmative) gombja,jellemzoen az Ok gomb.

6.2. ábra. Gombok szokások sorrendje egy dialógusban[6]

Hatása abban áll, hogy az alapértelmezett widget – vagyis ami esetén a has-default tulajdonság értéke igaz – akti-válódik akkor, ha egy egysoros beviteli mezo van fókuszban (GtkEntry) és akkor Entert nyomunk, ez is csak akkor, haaz GtkEntry activates-default tulajdonsága szintén igaz értéku. Többsoros beviteli mezo (GtkTextView) esetén eznem muködoképes, hiszen ott az Enter lenyomása soremelést jelent. Az alapértelmezett widgetnek a billentyuzetrol valóhasználat kényelmesebbé tételében van szerepe, hiszen ha minden szükséges mezot kitöltöttünk egy dialógusban, akkornem kell a megfelelo gombig – egérrel, vagy Tab billentyu(k) lenyomásával – elnavigálnunk, csak egyszeruen az Enterleütésére aktiválódik az alapértelmezett widget.

Ahhoz, hogy egy widget egyáltalán számításba kerüljön, mint potenciális alapértelmezett widget, ahhoz eloször acan-default tulajdonságának kell igaznak lenni, ilyen widget több is lehet egy ablakon belül, melyek közül aktuáli-san alapértelmezetté GtkWidget osztály garb_default függvényével tehetünk egyet, így annak has-default értékkeligazzá válik, míg az ablak korábbi alapértelmezett widgete, már ha volt ilyen, esetén a tulajdonság értéke természetesenhamis lesz.

6.2.4. Vezérlo elemekAmennyiben a szükséges tartalmi elemeket elhelyeztük az ablakban, a vezérlo elemekkel is hasonlóan kell eljárnunk. Akülönbözo célokra használt ablakok különbözo vezérlo elemeket kívánnak meg, amik az egyes típusokként eros hasonló-ságot mutatnak. Egy foablak csak kivételes esetekben tartalmaz az eddigiekben tárgyalt gombokat, a vezérlés általábanmenükkel, a toolbaron elhelyezett gombokkal történik (?? ábra).

11Itt ablak alatt a toplevel és nem a popup ablakokat értjük.12Ez az érték egy GtkEntry esetén igaz, míg egy Gtkabel esetén hamis alapértelmezés szerint.

33

Page 39: Helló Window!

(a) Foablak (b) Dialógus

6.3. ábra. Tipikus ablakszerkezetek[6]

A foablakból nyíló különbözo célú ablakok, melyek például egy adott elem tulajdonságainak beállítására, egy bo-nyolultabb funkció lépésenként történo megvalósítására, az alkalmazás egészének konfigurálására, egy folyamat nyom-követésére, esetleg a felhasználó informálására, figyelmeztetésére szolgálnak, közvetlenül, vagy közvetve a GtkDialogtípusból szármáznak, saját gombsorral látjuk el oket. Tipikus példa erre egy elem tulajdonságainak szerkesztésére használtdialógus (?? ábra).

GtkDialog

A C, illetve a Python nyelvu változat mutat némi különbözoséget a C++ változathoz képest a gombok hozzáadásánakmikéntjében. Elobbiek esetén ugyanis több gombot is hozzáadhatunk egyszerre a dialógoshoz, egymás után sorolvaa button_text és a response_id paramétereket. A paraméterlistát a C változat esetén NULL értékkel kell zárnunk,különben változatos programhibákkal fogunk szembesülni, erre a Python esetén nincs szükség, mivel itt a hívott féltisztában van az átadott paraméterek számával.

GtkWidget*gtk_dialog_add_button(GtkDialog*dialog,constgchar*button_text,gintresponse_id);

voidgtk_dialog_add_buttons(GtkDialog*dialog,constgchar*first_button_text,...);

Button*Gtk::Dialog::add_button(constGlib::ustring&button_text,intresponse_id);

Button*Gtk::Dialog::add_button(constGtk::StockID&stock_id,intresponse_id);

defadd_button(self,textresponse):

defadd_buttons(self,*args):

Kódrészlet 6.4. Gombok hozzáadása GtkDialoghoz[6]

A választott nyelvi változattól függetlenül igaz, hogy a button_text paramétere vagy az általunk vágyott feliratszövege lehet, vagy egy stock ID (Ok gomb esetén például GTK_STOCK_OK).

6.4. ábra. Egy tipikus gombsor

Egy fenti elrendezésu – amúgy meglehetosen szokványos – gombsor az alábbi kódrészletekkel hozható létre az egyesnyelvek esetén. Az eltérés nem számottevo, nem tartalmaz semmilyen olyan különbözoséget, ami a korábbiakban már nekerült volna ismertetésre.

34

Page 40: Helló Window!

cancel_button=gtk_dialog_add_button(GTK_DIALOG(dialog),GTK_STOCK_CANCEL,GTK_RESPONSE_CANCEL);

ok_button=gtk_dialog_add_button(GTK_DIALOG(dialog),GTK_STOCK_OK,GTK_RESPONSE_OK);

gtk_widget_grab_default(ok_button);

help_button=gtk_dialog_add_button(GTK_DIALOG(dialog),GTK_STOCK_HELP,GTK_RESPONSE_HELP);

cancel_button=dialog->add_button(

Gtk::Stock::Cancel,Gtk::Response::Cancel);

ok_button=dialog->add_button(

Gtk::Stock::Ok,Gtk::Response::Ok);

ok_button->grab_default();

help_button=dialog->add_button(

Gtk::Stock::Help,Gtk::Response::Help);

cancel_button=dialog.add_button(

Gtk.STOCK_CANCEL,Gtk.ResponseType.CANCEL);

ok_button=dialog.add_button(

Gtk.STOCK_OK,Gtk.ResponseType.OK);

ok_button.grab_default();

help_button=dialog.add_button(

Gtk.STOCK_HELP,Gtk.ResponseType.HELP);

Kódrészlet 6.5. Tipikus gombsor hozzáadása GtkDialoghoz[6]

GtkMessageDialog

Létezik a GtkDialog típusnak egy – a gombok hozzáadása szempontjából érdekes sajátossággal bíró – specializált vál-tozata, melyet a felhasználóval történo kommunikáció céljaira használunk, s mely ennek megfeleloen rendszerint csak azüzenet szövegét, illetve a válasz megadásához szükséges vezérlo elemeket tartalmazza.

(a) Információs üzenetablak (b) Hiba üzenetablak

6.5. ábra. Tipikus üzenetablakok

Az ábrákon látható dialógusokat – a szövegezéstol eltekintve – csak típusuk különbözteti meg egymástól. A bal oldali(??) egy információs ablak, míg a jobb oldali (??) egy hibadialógus. Az egyik szembeötlo különbség az ablakok között azikon, amit az üzenetablak típusa határoz meg. A fenti két típuson kívül még két saját ikonnal rendelkezo típus (questionés warning) létezik, illetve készíthetünk ikon nélküli változatot, aminél módunk van saját ikon megadására.

Az üzenetablak típus eltérése, vagy inkább specialitása a GtkDialog típushoz képest nem csak a típus megadásá-nak lehetoségére korlátozódik. A kifejezetten a szoftver és a felhasználó közötti „üzenetváltás” célját szolgáló widgetrendelkezik beépített elemekkel az üzenet megjelenítésére. A felhasználóval közölni kívántakat egy elsodleges és egymásodlagos (primary-text, secondary-text) részre bonthatjuk, ahol az elobbi egy rövid, csak a helyzet leglényege-sebb elemit tartalmazó, egy mondatos összefoglalója a közölni kívánt információnak, vagy a javasolt kívánt muveletnek,míg az utóbbi ennek mélyebb, részletekbe meno kifejtése leírása, ami tájékoztatja a felhasználót a felmerült helyzet oka-iról, esetleges mellékhatásairól. Az esetek többségében a felhasználónak már az elsodleges szöveg elolvasását követoenmeg kell tudni hoznia döntését, a másodlagos szöveg a döntés alátámasztására, az esetleges kétségek eloszlatására szolgál.

A harmadik specialitás – a gombok felhelyezésének mikéntje – a vezérlo elemek szempontjából is említésre méltó.Azzal együtt, hogy dialog típusnál ismertetett módszer az öröklodés okán természetesen itt is használható, mivel azonban aleggyakrabban használt gombkombinációk száma erosen korlátos, így ezek közül létrehozáskor választhatunk. Lehetségesértékek eredményeként vagy egyedüliként a Bezárás, Ok, Mégse gombok, vagy a Ok/Cancel, Igen/Nem párosok kerülneka dialógusra.

35

Page 41: Helló Window!

GtkWidget*gtk_message_dialog_new(GtkWindow *parent,GtkDialogFlags flags,GtkMessageType type,GtkButtonsType buttons,constgchar *message_format,...)G_GNUC_PRINTF(5,6);

GtkWidget*gtk_message_dialog_new_with_markup(GtkWindow *parent,GtkDialogFlags flags,GtkMessageType type,GtkButtonsType buttons,constgchar *message_format,...)G_GNUC_PRINTF(5,6);

explicitMessageDialog(constGlib::ustring&message,booluse_markup= false,MessageTypetype=MESSAGE_INFO,ButtonsTypebuttons=BUTTONS_OK,boolmodal=false);

MessageDialog(Gtk::Window&parent,constGlib::ustring&message,booluse_markup= false,MessageTypetype=MESSAGE_INFO,ButtonsTypebuttons=BUTTONS_OK,boolmodal=false);

def__init__(self,parent=None,flags=0,message_type=Gtk.MessageType.INFO,buttons=Gtk.ButtonsType.NONE,message_format=None,**kwars):

Kódrészlet 6.6. MessageDialog létrehozása

A C, illetve a Python nyelvu változatok, ellentétben a C++-os megvalósítással nem egyetlen paraméterként várják azüzenetablak elsodleges szövegét, hanem egy printf-stílusú formátumleírót és az annak megfelelo paramétereket vesz-nek át. Ennek típusbiztosságáról a C változat esetén a G_GNUC_PRINTF makró gondoskodik, már amennyiben a GNUC fordítót használjuk. Ebben az esetben fordítási ideju figyelmeztetést kapunk ha paramétereink nem felelnek meg aformátumleíróban megadottaknak.

6.2.5. MegjelenítésTöbb lehetoség kínálkozik, ha egy ablakot, illetve annak tartalmát szeretnénk megjeleníteni. Használhatjuk egyrészrol, aGtkWidget, a GtkWindow, illetve a GtkDialog típus által adott módszereket.

GtkWidget

A korábban (??) már tárgyalt show függvényt, ami megjeleníti a windowt, de csak a windowt, annak gyerekeit nem.Lévén a window egy konténer típus, helyezhetünk el további widgeteket benne, amiknek a megjelenítésérol vagy már ko-rábban gondoskodnunk kell – mondjuk egy show hívással –, vagy megtehetjük a szülo és az összes gyerek megjelenítésétegyszerre a show_all (??) függvénnyel.

GtkWindow

Egy ablak esetén nem csupán a puszta megjelenítés lehet szempont, hanem az is, hogy a felhasználó az ablakot észre isvegye. Ez az estek túlnyomó többségében adott, hiszen valamilyen felhasználói interakció révén jelenik meg az új ablak.Ha viszont nem errol van szó akkor szükséges lehet az megjelenítésen túl más ablakok általi takarás megszüntetésére,a tálcáról való felhozatalra, az aktuális desktopra történo mozgatásra, a fókusz (6.2.3) átadására, mely muveletek mindfügghetnek mind a platformtól, mind az ablakkezelotol, mind pedig a felhasználói beállításoktól. Erre használható apresent függvény. Kezeljük azonban kello óvatossággal ezt a függvényt, hiszen mindannyian bosszankodtunk már egykello indok nélkül, váratlanul megjeleno ablak miatt.

GtkDialog

Egy window típusú ablak esetén – mivel jellemzoen nincsenek az ablakon gombok és nem modálisak – nincs igazánszükségünk arra, hogy – a kód futásának szempontjából helyben – kivárjuk a felhasználó reakciót. A dialog típus ezzelszemben általában egy felhasználói interakció révén jelenik meg (pl.: egy elem tulajdonságait, vagy az applikáció be-állításait szerkeszto ablak) és jelentos részben valamilyen döntés elé állítja a felhasználót (különösen igaz ez az üzenetablakoknál, ahol kérést teszünk fel), melynek eredményérol szeretnénk értesülni. A GtkDialog típus run függvénye– ahogy azt a következoekben (6.2.7) részletesen is tárgyaljuk – pontosan ezt a célt szolgálja. Egyrészrol várakozik afelhasználó interakció – amihez természetesen szükséges a main loop futtatása – majd visszatérési értékként az ablak„futtatásának” eredményét, vagyis a kiválasztott vezérlo elem – az ablak elkészítésekor megadott – response id-t adjavissza. Mint látható, a függvény nem kifejezetten a dialógus megjelenítését szolgálja, az hasznos mellékhatásként mégismegtörténik.

6.2.6. BezárásA window tehát leginkább a bezárás kapcsán állít kihívást elénk, melyet függoen attól, mit szeretnénk elérni annak hatá-sára, hogy a felhasználó az ablak bezárását kezdeményezte, több lehetséges megoldás, az egyes megoldásokra, pedig többmódszer is kínálkozik.

36

Page 42: Helló Window!

Blokkolás

Kezdjük a legegyszerubbnek látszó esettel, vagyis azzal, hogy semmilyen hatása ne legyen annak ha felhasználó az ablakbezárását kezdeményezi. Nyilván megfontolandó, hogy ezt tegyük, hiszen a felhasználó sem véletlenül akarja, amit akar,de ha mondjuk azt szeretnénk kikényszeríteni, hogy a foablakunkból csak a Fájl menüpont „Kilépés” almenüpontjárakattintva lehessen bezárni, akkor ez egy lehetséges megoldás13.

A feladat megoldása a korábban (??) már tárgyalt delete-event szignál kezelésében rejlik. Ahogy arról szó esett eza szignál váltódik minden ablakon (toplevel window), illetve minden az ablakban lévo widgeten, mikor a felhasználó azablak bezárását kezdeményezi. A szignál két külön említésre is méltó sajátossággal is rendelkezik:

1. A szignált kezelni kívánó függvénynek egy bool értékkel kell visszatérnie, ami azt jelzi a GTK felé, hogy azadott függvény kezelte-e az eseményt, egyszersmind nincs szükség a további kezelo függvény meghívására. AGTK következésképp addig hívja sorra az szignálra „feliratkozott” függvényeket (signal handler) – köztük ha vanilyen, akkor az alapértelmezett szignálkezelo függvényt (default signal handler) amíg egyikük true értékkel nemtér vissza.

2. Ha minden ilyen függvény false értékkel tér vissza, azaz a szignál további propagálását kéri a GTK-tól, akkorkonkrétan a delete-event esetében a GDK alapértelmezett eseménykezeloje (default event handler) fut le, amimeghívja az ablak destruktorát.

Fentiek alapján ahhoz, hogy megelozzük az ablak bezárását – vagyis hogy ne történjen semmi – el kell érnünk,hogy az alapértelmezett eseménykezelo ne hívódjon meg, azaz az általunk felkötött szignálkezelo true értékkel kell hogyvisszatérjen, jelezve azt, hogy az eseményt kezeltük, a további szignálkezelo függvények meghívására nincs szükség.Ebben az esetben ez azt is jelenti, hogy az alapértelmezett eseménykezelo sem hívódik meg. Ehhez definiálnunk kell aszignálkezelo függvényeket, ami a korábbi (6.3. kódrészlet14) példát kiegészítve az alábbihoz hasonló módon tehetünkmeg,

234 gboolean5 on_delete_event(GtkWidget*widget,6 GdkEvent*event,7 gpointeruser_data)8 {9 g_print("on_delete_event\n");

1011 returnTRUE;12 }

#include<iostream>

boolon_delete_event(GdkEventAny*event)

{std::cout<< "on_delete_event"<< std::endl;

returntrue;}

2345defon_delete_event(widget,6event):789print("on_delete_event")1011returnTrue12

Kódrészlet 6.7. Szignálkezelo függvény perzisztens ablakhoz

majd ezeket a függvényeket a delete-event szignálra be is kell kötnünk15.

12 g_signal_connect(G_OBJECT(window), "delete-event",13 G_CALLBACK(on_delete_event),NULL);

window.signal_delete_event().connect(sigc::ptr_fun(on_delete_event));

12window.connect("delete-event",13on_delete_event)

Kódrészlet 6.8. Szignálkezelo függvény bekötése perzisztens ablakhoz

Ha az adott szignál alapértelmezett szignálkezelo függvénnyel is rendelkezik – ami a widget osztály-leírójában kerülmegadásra – és magunk akarjuk a szignált kezelni, akkor szükségessé válik, hogy a saját kezelo függvényünk még azalapértelmezett elott hívódjék meg, ami további praktikák bevetését igényli, amirol egy másik részben esett részletesebbenszó.

Ha a saját szignálkezelo függvény írását kissé túlzónak találjuk egy olyan egyszeru feladat ellátására, mint egy trueértékkel való visszatérés, akkor nem tévedünk, sot a GTK+ fejlesztoi is gondoltak erre és megalkották a gtk_true,illetve a gtk_false nevu függvényeket, melyek semmi egyebet nem tesznek, mint a nevüknek megfelelo értékkel térnekvissza, így a fenti példa szignálkezelo függvényei elhagyhatók, hiszen azok GTK+ által adott – ekvivalens funkciójú –függvényekkel helyettesíthetoek.

g_signal_connect(G_OBJECT(window), "delete-event",G_CALLBACK(gtk_true),NULL);

Kódrészlet 6.9. Egyszerusített szignálkezelo függvény bekötése

13Ne becsüljük azonban alá se a felhasználói találékonyságot, se a felhasználói környezetek változatosságát. Semmiképp ne hagyatkozzunk arra,hogy egy adott módszer a felhasználó számára véleményünk szerint nem érheto el.

14A sorszámozás az eredeti példába való beillesztés pontját mutatja.15A sorszámozás az eredeti példába való beillesztés pontját mutatja.

37

Page 43: Helló Window!

Eltüntetés

Folytassuk másodikként azzal az eshetoséggel, ha el szeretnénk tüntetni az ablakot, aminek a bezárását a felhasználókezdeményezte. Az elozo szituációhoz hasonlóan most is több módszer adódik a feladat megoldására.

A fent tárgyalt perzisztens ablakot eredményezo szignálkezelok (6.7. kódrészlet) ismeretében a legnyilvánvalóbbmegoldás, hogy még mielott visszatérnénk az adott függvényekbol a hide függvény segítségével eltüntetjük az adottablakot. A megoldás jó és muködoképes megoldás lehet, ugyanakkor számolnunk kell azzal, hogy az ablak csak eltunik,de nem feltétlenül semmisül meg. A már korábban is használt minimális példa azon kiegészítését is figyelembe vesszük,ahol a delete-event szignálra a main loop futását megszakító függvényt hívjuk, akkor azt fogjuk tapasztalni, hogy azablakunk eltunik ugyan, de a mögöttes muködés már nagyban attól függ a két implementációt (6.7. kódrészlet) mikéntvontuk össze. Ha a saját szignálkezelo függvényünket kötjük be elobb a delete-event szignálra, akkor az – a visszatérésiértéke révén – leállítja a szignál további kezelését, vagyis a szignálkezelo függvények sorának meghívását is, így azablak a saját kezelo függvényünk (on_delete_event), a hide hívással kiegészítve eltünteti az ablakot, de a következoszignálkezelo – ami kiléptetné a main loopot – már nem hívódik meg. Ha a felkötés sorrendje fordított, akkor elobbkilép a main loop és csak ezután hívódik meg saját függvényünk, ami egyrészrol elrejti az ablakot, másrészrol blokkoljaa további kezelést, aminek végso soron nem lehet hatása, hiszen a main loop már kilépett.

Amennyiben a célunk csupán az ablak eltüntetése egyszerubben is elérhetjük ugyanezt, hiszen a hide függvény köz-vetlenül – pontosabban egy beépített GTK+ keresztül – is bekötheto a delete-event szignálra. Ezt kényelmi funkcióta gtkmm esetén elveszítjük – hasonlóan az elozo példához –, mivel az általunk használni kívánt szignálkezelo függvénydeklarációja nem egyezik meg az eloírttal, így tehát ezt a „kényelmet” a típusbiztosság oltárán fel kell áldozni.

g_signal_connect_swapped(G_OBJECT(window), "delete-event",G_CALLBACK(gtk_widget_hide_on_delete_event),window);

Kódrészlet 6.10. Egyszerusített szignálkezelo függvény bekötése

Ha nem ragadunk le a könnyen értheto, ám nem túl életszeru minimális példánknál, akkor az mondható el, hogy ab-lakokat – legyenek azok GtkWindow, vagy GtkDialog típusúak –, valamilyen felhasználói interakcióra reagálva hozunkfel, valamilyen kezelo függvényben. Az bezáráskori elrejtés akkor lehet hasznos számunkra, ha nem akarjuk újra és újraelkészíteni az ablakot az adott felhasználói muveletre. Erre lehet példa mondjuk egy névjegy (about) ablak, aminek atartalma nem változik a program futása során, így azt az alkalmazás indulásakor, vagy az elso megtekintéskor létrehoz-zuk, utána már elegendo csak elrejteni, vagy újra megjeleníteni. Másik példa lehet egy státusz jellegu információkatmegjeleníto ablak, amiben úgymond gyujteni tudjuk az adatokat és ha a felhasználó be is zárja az ablakot, mi az elrejtésután az adatgyujtést és az ablak tartalmának frissítését tovább fojtatjuk, majd az újbóli megjelenítéskor már a naprakészinformációk jelennek meg.

Megszüntetés

Mint az a fentiekbol már kiderült – függetlenül attól, hogy GtkWindow, GtkDialog, vagy ezekbol származó típusokrólvan-e szó –, a delete-event szignál kiváltódását – ha egyebet nem teszünk – az ablak destruktorának meghívás fogjaautomatikusan követni, ami az esetek túlnyomó többségében meg is felel a céljainknak.

6.2.7. EseménykezelésSzinkron

A GtkDialog és a GtkWindow típusok közötti eltérések (6.2.4) közül a legszámottevobb – mivel ehhez tartozik a legtöbbbeépített szolgáltatás –, a vezérlo elemek, azaz a gombok kezelése. A GtkDialog nem csupán arra ad lehetoséget, hogy agombokat egy erre a célra készült konténerbe helyezzük el – ezt magunk is megtehetnénk minden különösebb erofeszítésnélkül –, hanem az ezeken végzett felhasználói interakciókat is egyszeruen nyomon követhetjük. Választhatunk az adott(kód)környezetben számunkra kényelmesebb – szinkron, illetve aszinkron eseménykezelés közül. Elobbi esetén helyben16

tudjuk kezelni az eseményeket, utóbbi viszont eseménykezelo függvények megírását és bekötését teszi szükségessé.

16Az ablak létrehozásának helyén.

38

Page 44: Helló Window!

1 #include<gtk/gtk.h>23 int4 main(intargc,char*argv[])5 {6 GtkWidget*dialog;78 gtk_init(&argc, &argv);9

10 dialog=gtk_dialog_new();1112 gtk_dialog_run(GTK_DIALOG(dialog));1314 return0;15 }

#include<gtkmm.h>

intmain(intargc,char*argv[]){

Gtk::Mainkit(argc,argv);

Gtk::Dialogdialog;

dialog.run();

return0;}

1fromgi.repositoryimportGtk234if__name__== "__main__":5678910dialog=Gtk.Dialog()1112dialog.run()131415

Kódrészlet 6.11. Minimál példa GtkDialoghoz

A run függvény (12.) egyrészrol függvényeket köt be a szükséges szignálokra (response, unmap, delete-event,destroy), majd egy saját main loopot futtatásán belül kezeli az említett eseményeket. Ha ezek közül bármelyik bekövet-kezik, akkor a main loop futása megszakad, és a run függvény a megfelelo értékkel visszatér. Ennek kezelése tipikusanegy if, vagy egy switch szerkezeten belül történik.

Ha a fenti minimális példát a korábbi, gombok hozzáadását tartalmazó forrással (6.5. kódrészlet) egészítjük ki17,mondjuk az alábbihoz hasonló módon kezelhetjük a felhasználói döntés eredményeként kapott választ (response).

12 gintresult=gtk_dialog_run(GTK_DIALOG(dialog));13 switch(result)14 {15 caseGTK_RESPONSE_OK:16 do_application_specific_something();17 break;18 default:19 do_nothing_since_dialog_was_cancelled();20 break;21 }22 gtk_widget_destroy(dialog);

intresult=dialog.run();switch(result){caseGtk::RESPONSE_OK:do_application_specific_something();break;

default:do_nothing_since_dialog_was_cancelled();break;

}

12result=dialog.run()131415ifresult==Gtk.ResponseType.OK:16do_application_specific_something()1718else:19do_nothing_since_dialog_was_cancelled();202122

Kódrészlet 6.12. Minimál példa GtkDialoghoz

Itt az egyszeruség kedvéért csak az Ok, illetve a többi gomb kerültek megkülönböztetésre, így ha a Mégse, illetvea Súgó gomb aktiválódott, akkor is a switch szerkezet default ága (18) fut le. Kérdés azonban, hogy ha az iméntmegismert delete-event szignál váltódik ki az ablak bezáródásának hatására, annak mi lesz az eredménye. A válaszegyszeru, hiszen a switch elso ágára (15) nem futhatunk rá, tehát marad itt is a default ág. Ha külön szeretnénk kezelniezt az esetet, akkor a RESPONSE_DELETE_EVENT konstans kezelésére kell egy új ágat beillesztenünk.

Nem minden esetet fedtünk azonban le, elképzelheto ugyanis, hogy a dialóg felszabadul, míg a run függvény fut.Ebben az esetben a visszatérési érték RESPONSE_NONE lesz, így ezt az esetet is meg lehet különböztetni a többitol, azon-ban mégsem tanácsos. Helyette, ha mindenképpen meg akarjuk szakítani a run futását, akkor a resposne függvénythívhatjuk, aminek eredményeként a run a resposne-nak paraméterként átadott értékkel tér vissza.

Az élelmesebbek megfigyelhetik, hogy dialog példaprogramból (6.11. kódrészlet) a window hasonló példájához (6.3.kódrészlet) kimaradt a show függvény hívása, ami annak tudható be, hogy a run ezt megteszi helyettünk, ahogy a dialó-gusunkat is modálissá teszi a run futásának idejére.

Aszinkron

Ha valamilyen oknál fogva lemondanák a run adta kényelemrol, lehetoségünk van az aszinkron kezelésre. Ebben azesetben a run helyett a show függvényt hívjuk, illetve egy saját függvényt – melyben elvégezzük a számunkra szükségesmuveleteket – kötünk be a response szignálra. Ezt a módszert használva ebben a függvényben kell gondoskodnunk az ab-lak felszabadításáról is, már amennyiben nem csak a delete-event esemény hatására szeretnénk, hogy ez megtörténjen.A response szignál paraméterei között a response_id is szerepel, így a függvény tartalma hasonló lehet a run hívástköveto kódhoz (6.12. kódrészlet). Muködésben azonban van némi különbség, hiszen a delete-event alapértelmezettmuködésének megváltoztatására például nincs mód.

Az aszinkron a megoldásra lehet egy másik példa, ha a lehetséges választások mindegyike ugyanazzal az eredménnyelkell járjon, például be kell záródjon az ablak. Ennek olyan leginkább helyzetekben van realitása, ahol csupán egy (pl.:Bezárás) gomb jelenik meg az ablakon. Erre lehet példa egy olyan üzenetablak (MessageDialog), amiben nem egy kérdéstteszünk fel, hanem csupán informáljuk a felhasználót.

g_signal_connect_swapped(G_OBJECT(dialog), "response",G_CALLBACK(gtk_widget_destroy),dialog);

Kódrészlet 6.13. Egyszerusített response-kezelo függvény bekötése

17A változók deklarációinak hozzáadása szükséges a fordíthatóság érdekében.

39

Page 45: Helló Window!

6.2.8. Saját eseménykezeloHa a fentiekben vázoltak valamilyen oknál fogva nem elégítenék ki igényeinket, vagy már egyébként is alkalmasabb mód-szernek látszik egy saját widget implementálása akkor nem kell egyebet tennünk, minthogy az ososztály eseménykezelofüggvényét felüldefiniáljuk a nyelvi változatnak megfelelo módon.

1 staticgboolean2 my_window_delete_event(GtkWidget*widget,3 GdkEventAny*event)4 {5 returnTRUE;6 }78 staticvoid9 my_window_class_init(MyWindowClass*class)

10 {11 GtkWidgetClass*widget_class;1213 widget_class= (GtkWidgetClass*)class;1415 widget_class->delete_event=my_window_delete_event;16 }

classMyWindow:publicGtk::Window{virtualboolon_delete_event(GdkEventAny*event){returntrue;

}};

1classMyWindow(Gtk.Window):23defon_delete_event(self,widget,event):4Gtk.main_quit()5678910111213141516

Kódrészlet 6.14. Eseménykezelo felüldefiniálása saját widgetosztályban

Az objektum-orientált megvalósítások esetén ez egy lényegesen kisebb erofeszítést igénylo feladat. Ahogy ezt a fentikódrészlet is mutatja a C változatból épp csak a leglényesebb rész emelheto ki egy jó tucatnyi sorban18, addig a csaknemteljes értéku C++, Python kódok ennek a felét sem teszi ki.

6.3. Platformfüggo sajátosságokBár a GTK – a GDK19, illetve a Glib függvénykönyvtárakon keresztül – komoly platformfüggetlenséget biztosít, mégisszámolnunk kell a különbségekkel, különösen az ablakok kezelése kapcsán. Ezek közül itt csak a két legfontosabbatemeljük ki.

6.3.1. AblakkezeloBizonyos értelemben maga a az ablakkezelo is egy platform, hisz ahogy az operációs rendszerek a tolük elvárt funkciókat– mint amilyen például a fájlkezelés – a maguk módján valósítják meg, úgy az ablakkezelo rendszerek is saját sziszté-májuk szerint teszik ezt. Bizonyos funkciók csak néhány ablakkezelo implementál, addig másokat csaknem minden ilyenrendszer megvalósít, bár arra nem számíthatunk, hogy az egyes megvalósítások minden részletben megegyeznek.

Az ablakkezelok különbözoségei fejlesztési oldalról azzal a következménnyel járnak, hogy még akkor is szembekell néznünk a platformok sajátosságai által okozott nehézségekkel, ha egyébként alkalmazásunkat nem használjuk többkülönbözo20 operációs rendszer alatt.

Még az olyan egyszeru és széles körben megvalósított funkciók kapcsán, mint amilyen maximalizálás, vagy a minima-lizálás (maximize, iconify) kételkednünk kell a mögöttes implementáció meglétében, illetve figyelembe kell vennünkaz egyes megvalósítások különbözoségeit, vagyis nem alapozhatunk ezen függvények meghívását követoen arra, hogy azablak abba az állapotba kerül, amire számítottunk. Akár az elobbiek említett funkciókra, akár mondjuk az ablak elotér-ben tartására (keep_above), akár teljes képernyoméretre nagyításra (fullscreen), akár az összes munkaterület (desktop)való egyszerre történo meg megjelenítésre (stick) kérjük az ablakkezelot, nem bizonyos, hogy kérésünk teljesül. Ennekcsak az egyik oka az, hogy az ablakkezelo nem támogatja az adott funkciót, a másik pedig, hogy kérésünket követoenvalaki más az elozo, vagy éppen mindkettotol eltéro állapot állít be. Ennél fogva ügyelnünk kell arra, hogy egy ilyenhelyzetre programunk fel legyen készülve.

Az ablak dekorációja a másik olyan terület, ahol kéréseket (hint) intézhetünk az ablakkezelo felé. Hasonlóan azonbana fent tárgyaltakhoz itt is igaz, hogy célszeru ellenorizni feltételezéseinket arra vonatkozólag, hogy kérésünk úgy és abbana formában hajtódik végre, ahogy azt mi elgondoltuk. Számos olyan eset lehetséges az ablak dekorációján található bezárógomb megjelenítésének tiltásától (deletable), az ablak taskbarból történo kihagyásáig (skip_taskbar_hint) melynekimplementációs részletei teljes egészében az ablakkezelotol függenek.

6.3.2. Vezérlo elemekA GTK alapértelmezés szerint a gombokat a GNOME Human Interface Guideline[6] (HIG) által javasolt elrendezésétalkalmazza, vagyis a jóváhagyó (affirmative) gomb a jobb oldalon a szélso pozícióba kerül, míg a menekülo gomb (cancel)ettol balra kerül. Ha eltéro – úgymond alternatív elrendezést – szeretnénk, akkor azt a set_alternative_button_orderfüggvény segítségével egy korábbi példát (6.5. kódrészlet) kiegészítve az alábbi módon állíthatjuk be.

18A teljes C kód egy ?? soros .c, illetve egy ?? soros .h állományból áll.19GIMP Drawing Kit20Ebben a tekintetben a Linux alapú rendszereket közel azonosnak tekinthetjük

40

Page 46: Helló Window!

gtk_dialog_set_alternative_button_order(GTK_DIALOG(dialog),GTK_RESPONSE_OK,GTK_RESPONSE_CANCEL,GTK_RESPONSE_HELP,-1);

intalt_resp[] = {Gtk::RESPONSE_OK,Gtk::RESPONSE_CANCEL,Gtk::RESPONSE_HELP};

size_talt_resp_size=sizeof(alt_resp) /sizeof(int);std::vector<int>alt_resp_vector(alt_resp,

alt_resp+alt_resp_size);dialog->set_alternative_button_order_from_array(alt_resp_vector);

dialog.set_alternative_button_order_from_array(

[Gtk.ResponseType.OK,Gtk.ResponseType.CANCEL,Gtk.ResponseType.HELP])

Kódrészlet 6.15. Alternatív gombsorrend beállítása

Amennyiben az üzenetablakoknál megismert (6.2.4) páros gombok (Ok/Cancel, Igen/Nem) valamelyikét használjuk,akkor ezt a sorrendállítást a GTK megteszi helyettünk.

6.4. A kód

6.4.1. Fordítás és linkelésA korábbiakhoz hasonlóan az alábbi parancssorok segítségével fordíthatóak programjaink.

gcc gtk_sourcefile.c -o gtk_binary ‘pkg-config --cflags --libs gtk+-3.0‘

g++ gtkmm_sourcefile.cc -o gtkmm_binary ‘pkg-config --cflags --libs gtkmm-3.0‘

6.4.2. FuttatásA futtatással ezúttal is a forrásfájlok – egyszersmind a fordítás – könyvtárában érdemes próbálkoznunk, a példaprogramnevétol függoen a ./futtatható_bináris parancs kiadásával.

6.4.3. EredményBármily hihetetlen ezúttal sem történik semmi egyéb, mint a korábbiakban. Remélhetoleg azonban a különbség mégisérzékelheto annyiban, hogy legutóbb a meglepetéssel teli borzongást ablakunk váratlan felbukkanása, míg most a bennünkszikraként felvillanó megértés okozza.

6.5. Tesztelés

6.5.1. KeresésApplikáció keresése

Fejlesztés közben – már amennyiben a felhasználói felületet kódból és nem egy felülettervezo program segítségévelhozzuk létre – az egyes widgetek létrehozáskor módunkban áll egyúttal valamilyen változóval hivatkoznunk rájuk, így akésobbiekben nincs szükségünk arra, hogy az egyes widgeteket a felhasználói felületen belül keresgessük. Teszteléskorugyanakkor a felületet készen kapjuk így az elso feladat azon elemek megtalálása, amiken késobb valamilyen muveleteketszeretnénk végezni.

classGtkDemoTest(unittest.TestCase):defsetUp(self):

fromdogtailimportutilsself.pid=utils.run(’gtk3-demo’)self.app=procedural.focus.application(’gtk3-demo’)

classGtkDemoTest(unittest.TestCase):defsetUp(self):

fromdogtailimportutilsself.pid=utils.run(’gtk3-demo’)self.app=tree.root.application(’gtk3-demo’)

Kódrészlet 6.16. Alapveto elemek keresése teszt tree, illetve procedural API használata mellett

Ahogy az a korábbi minimális tesztelési példában is látszott az elso lépés a tesztelés során, hogy a kívánt applikációtmegtaláljuk a sok egyéb futó alkalmazás között. Ha ezzel megvagyunk, akkor az applikáción belül kereshetjük meg annakablakait, azokon belül pedig az egyes widgeteket. Elobbire a célra szolgál a Dogtail tree moduljának applicationfüggvénye.

A függvény mindössze egy paraméter vesz át, a keresett applikáció nevét. Ez a név jellemzoen – bár nem mindenesetben – megegyezik annak az applikáció indításához futtatott állomány nevével. Mint a késobbekben is csaknem mindig,a kísérletezés segít leginkább az applikáció felderítésében, a kereséshez használandó nevek meghatározásában. Ehhezegyrészrol használhatjuk a korábban már említett Accercisert, ami voltaképpen egy grafikus felhasználói felület, amiaz akadálymentesítéhez használt API által megszerezheto információkat jeleníti meg struktúrált formában. Másrészrolhasználhatjuk a Dogtailt, a konkrét esetben a Root osztály applications függvényét, ami az összes – az accessibiltyalrendszer számára látható – applikációval tér vissza.

41

Page 47: Helló Window!

Az application függvény a tree modul Application osztályának egy, az applikációhoz tartozó példányával térvissza, vagy ha az applikációt az újrapróbálkozásokat követoen – melyek számát a Config osztály searchCutoffCounttulajdonsága, míg a próbálkozások között tartandó szünet mértékét ugyanezen osztály searchBackoffDuration tulaj-donsága határoz meg – nem találja SearchError kivételt dob.

Általános keresés

A findChild függvény elso paramétere egy feltételhalmaz (predicate). Amennyiben a keresés során ennek a faltétel-halmaznak megfelelo elemet találunk a közvetlen vagy közvetett leszármazottak között – függoen a recursive paraméterértékétol –, akkor a függvény az elso ilyennel visszatér. Amennyiben nem talál megfelelo elemet és a retry paraméterértéke True, akkor újra próbálkozik a config objektumban foglalt beállításoknak megfeleloen. Amennyiben a retryértéke False értelem szeruen összesen egy próbálkozás történik. Ha a requireResult paraméter értéke True, akkorSearchError kivételt dob, ha nem akkor egyszeruen None értékkel tér vissza. Megjegyzendo, hogy mindkét paraméteralapértelmezett értéke True, azaz a Dogtail többször is próbálkozik és sikertelenség esetén SearchError kivételt dob.

classGenericPredicate(Predicate):def__init__(self,name= None,roleName= None,description= None,label= None,debugName=None):

Kódrészlet 6.17. Node keresése GenericPredicate, illetve findChild függvény segítségével

Ahogy látszik a findChild függvénynek átadott GenericPredicate objektum voltaképpen a keresési feltételeketzárja egy egységbe. Ezek a feltételek a név (name), a szerep neve (roleName) , leírás (description), illetve felirat(label). A muködésrol fontos megjegyezni, hogy a feltételhalmazban az utolsó feltétel (label) elonyt élvez a többivelszemben, vagyis amennyiben ezt feltételt megadjuk a másik három nem érvényesül. Ha viszont csak az elso háromparaméter használjuk azok egymással és kapcsolatba kerülnek, vagyis csak olyan elem lehet a keresés eredmény, amiminden feltételnek megfelel.

A feltételhalmazban megadott paraméterek értékeinek kísérleti úton meghatározását a már több alkalommal említettAccerciser alkalmazás tud segíteni. Egyrészrol megjeleníti az egyes applikációkat, azok ablakait, illetve a további gye-rekelemeket fa hierarchiába szervezve. Másrészrol az egyes – amik mind egy-egy Node típusú objektumot jelentenek –kapcsán megmutatja mik az objektum tulajdonsági, állapotai, a rajta végrehajtható akciók. Természetesen a késobbiekbenaz egyes widgettípusok ismertetésekor ezen értékekre mi is ki fogunk térni.

Az általános keresés szempontjából az imént említett néhány paraméter, illetve a Node osztály ezeknek megfeleloparaméterei érdemlegesek. A név attribútum (name) a widget típusától – amit a role, illetve annak szöveges formája aroleName reprezentál –, függoen vesz fel értéket. Ablakok esetén annak címsorát, címkék esetén azok szövegét, olyanwidgetek esetén, amikhez címkét kapcsoltunk szintén a címke szövegét. A nevek felvett értékeinek részleteire, valamint atípusnevekre az egyes widgettípusok tárgyalásakor térünk. Maga a név explicit módon is beállítható a GTK, pontosabbanaz ATK révén, hiszen ez utóbbi interfészen keresztül történik az accessibilty réteggel történo kapcsolattartás. MindenGtkWidget objektumhoz tartozik egy AtkObject objektum, amit a get_accessible függvény segítségével kérhetünk.Az AtkObject a set_name függvény révén adható olyan név, amire a Dogtail használata során is hivatkozni tudunk.

A már említett Predicate osztálynak példányait felhasználhatjuk arra is, hogy csupán annyit tudjuk egy adott Nodegyerekeként, ami megfelel a predicate által megfogalmazott feltételhalmaznak. Ehhez a Predicate osztály satisfiedByNodefüggvényét kell hívnunk, arra azzal a Node objektummal paraméterként, amire a vizsgálatot el szeretnénk végezni. Egyadott Node objektumról ugyanez a döntés a satisfies függvény hívásával hozható meg aminek viszont a predicate aparamétere.

Specializált keresés

Létezik számos specializációja, leszármazottja a Predicate osztálynak, amik a gyakran használt keresések egyszerusíté-sére szolgálnak. Ezek közül minden részben azokat vesszük sorra, amik az adott rész szempontjából érdekesek. Itt tehátaz applikációk és ablakok keresésére szolgálok származtatásokat ismertetjük.

Applikáció. A Root osztály application függvénye az Application osztály a tree modul Node osztályából szárma-zik, ami gyakorlatilag minden olyan osztálynak ose, melyet egy keresés eredményeként visszakaphatunk (Application,Root, Window).

defapplication(self,appName):returnroot.findChild(predicate.IsAnApplicationNamed(appName), recursive=False)

Kódrészlet 6.18. Applikáció keresése application függvénnyel, illetve IsAnApplicationNamed objektummal

Ahogy látszik az application függvény nem más, mint egy specializáció, ami tulajdonképpen Node osztály findChildáltalános keresofüggvényét hívja paraméterként egy IsAnApplicationNamed osztály egy objektumával, ami a Predicateosztályból származik és példányosításkor a keresendo applikáció nevét veszi át paraméterként.

42

Page 48: Helló Window!

Ablak. Hasonlóan az applikáció kereséséhez az ablakok keresésére is létezik a Predicate osztályból származó sajátosztály. Mind a IsAWindowNamed, mind a IsADialogNamed konstruálásához csak az ablak címsorát kell megadnunk.A két külön osztályra mindössze azért van szükség, mert a GtkWindow és a GtkDialog típusok a Dogtail reprezentációmás roleName értékkel rendelkeznek (frame, dialog), ahogy az a egyezést vizsgáló függvénnyek implementációjából islátszik.

classIsAWindowNamed(Predicate):def__init__(self,windowName):

...

def_genCompareFunc(self):defsatisfiedByNode(node):

returnnode.roleName==’frame’ andstringMatches(self.windowName,node.name)returnsatisfiedByNode

classIsADialogNamed(Predicate):def__init__(self,dialogName):

...

def_genCompareFunc(self):defsatisfiedByNode(node):

returnnode.roleName==’dialog’ andstringMatches(self.dialogName,node.name)returnsatisfiedByNode

Kódrészlet 6.19. Ablak keresése specializált Predicate objektummal

6.5.2. StátuszokAz ablakok kapcsán – függetlenül attól, hogy GtkWindow, vagy GtkDialog típusról van szó – van néhány tulajdonság,amit ebben a részben a fejlesztési oldalról már tárgyaltunk és most a visszaellenorzésükre is kitérünk. Az státuszok alap-vetoen két értékuek és mint ilyenek egy állapot halmazt alkotnak, ami egy adott Node objektumra egy adott pillanatbanjellemzo. Ez az állapot-, vagy státuszhalmaz a getState függvény segítségével kérdezheto le, majd ezt kell megvizsgál-nunk, hogy a számunkra aktuálisan érdekes állapot része-e a halmaznak.

@propertydefstatename(self):

returnself.getState().contains(pyatspi.STATE_STATENAME)

Kódrészlet 6.20. Node státuszvizsgálatához szükséges függvény sémája

Létezik néhány olyan – a késobbiekben részletezett – státusz, amikre a Node osztály implementálja a megfelelo lekér-dezo függvényt, azon esetekben azonban, ahol ez nem áll rendelkezésre magunknak kell a megoldásról gondoskodnunk.Az ablakok szempontjából az átméretezhetoség (resizable), a modalitás (modal), valamint az az állapot, hogy a vizsgáltablak aktuálisan az aktív ablak (active) ablak-e az érdemleges állapotok.

6.5.3. InterfészekA GTK koncepcióját taglaló részben esett néhány szó róla, a GTK akadálymentesítéhez szükséges implementációt az ATKáltal definiált interfésznek megfeleloen nyújtja. A Dogtail voltaképpen egy Python nyelven íródott absztrakció ezen rétegfölé, ami elrejti annak részleteit és egy magasabb szinten, a felhasználói felületek teszteléséhez megfelelo módon kezeliaz akadálymentesítési réteg által nyújtott funkcionalitásokat.

Komponens

Az egyik ATK által definiált interfész a AtkComponent, aminek segítségével az egyes objektumok pozíciójáról, illetveméretérol szerezhetünk információkat. A Dogtail ezen interfész részleteit elrejti elölünk, kihasználva a Python nyelvadottságait egy-egy tulajdonság (property) kiolvasásával férhetünk hozzá az AtkComponent osztály által szolgáltatottértékekhez. Ezek az értékek a Node pozíciója (position), mérete (size), illetve ezek összességét jelento kiterjedés(extents), ami mind az x, y, mind pedig a szélesség, magasság értékeket tartalmazza. Bár ezek az értékek minden Nodeesetén elérhetoek, az ablakokon kívül – ahol ezen értékek révén ellenorizni tudjuk az alapértelmezett méretet, illetve aszülo ablakhoz képesti pozíciót –, különösebb jelentoségük nincs.

Akciók

Vannak esetek, amikor sem nem információkat kinyerni, sem nem információkat bevinni nem akarunk a tesztelendoalkalmazásba, ehelyett inkább a mozgásban szeretnénk azt tartani, bizonyos akciók révén. Példának okáért ilyen lehetegy gomb megnyomása, ami tovább mozdítja a tesztelendo alkalmazást. A végrehajtható akciókat, illetve azok kezelésétaz AtkAction interfész fogja össze. Ezt az interfészt a queryAction függvény segítségével kérdezheto le.

43

Page 49: Helló Window!

Az interfész abban nyújt segítséget, hogy az nActions attribútumból az adott elemen keresztül kiolvashatjuk a vég-rehajtható akciók számát, bár ez következik az akciókat tartalmazó dict (actions) számosságából is, ami az akciókneveihez magukat az akciókat reprezentáló osztályok (dogtail.tree.Action példányait rendeli. Ezek tartalmazzák aaz akció nevét (name), leírását (description) attribútumként, illetve egy paraméter nélküli függvényt (do), ami révén azakciót végrehajthatjuk. Ez utóbbi a Node osztály doActionNamed függvénye révén is megteheto, ha ismerjük az akciónevét, mivel ez a függvénynek átadandó egyetlen paraméter. A függvények visszatérési értéke, hogy az akciót sikerült-evégrehajtani.

A GtButton típus objektumaira, azaz jelen esetben a dialógusok kapcsán tárgyalt gombokra igaz, hogy végrehajthatórajtuk a click akció, ami a gombra való kattintás kiváltódását eredményezi. Ennek hasznát természetesen akkor látjuk,amikor egy dialógus ablak beviteli mezoinek kitöltésével végeztünk és szeretnénk a normál ügymenetnek megfeleloenaz Ok gombot megnyomni, ekkor használhatjuk a doActionNamed(’click’), vagy a do(’click’) függvényhívástfüggoen attól, hogy a gombhoz tartozó Node, vagy annak action interfésze áll rendelkezésünkre.

6.5.4. TulajdonságokAzon információk számára, amik sem a különbözo ATK által definiált interfészeken, sem az státuszok révén, sem másmódokon nem érhetoek el, adott egy név érték párokat tartalmazó adatszerkezet. Az adatszerkezet lekérdezheto dict(get_attributes), illetve list (getAttributes) formájában is, ahol a a lista elemei a nevek és az értékek szöve-ges változatainak kettosponttal (:) elválasztott értékei, míg a szótár értelemszeruen a neveket kulcsként, az értékeket akulcsokhoz rendelt értékként tartalmazza.

Az attribútumlista minden Node esetén tartalmazza annak a grafikus eszközkészletnek nevét, aminek révén a teszteltalkalmazást létrehozták. A GTK esetén tehát a toolkit névhez nem meglepo módon a gtk érték párosul. Egyébirántaz eszközkészlet neve elérheto a Node toolkitName nevu tulajdonságán keresztül, ehhez hasonlóan az eszközkészletverziója pedig a toolkitVersion tulajdonságon keresztül.

44

Page 50: Helló Window!

7. fejezet

Konténerek

Már a legegyszerubb felhasználói felület láttán is könnyen belátható, hogy a korábbi példaprogramjaink kissé sántítanak,mégpedig abban a tekintetben, hogy nincs olyan valós életben is használt program amiben egy-egy widget lenne csupán.Ha viszont több widgetet szeretnénk elhelyezni egy ablakban kézenfekvo kérdés, hogy miként tudnák oket a felületencsoportokba rendezni. Erre a kérdésre keressük a választ ebben a részben.

7.1. FogalmakA korábbiakhoz hasonlóan itt is érdemes eloször tisztázni néhány alapfogalmat és csak utána kezdeni bele az érdemimunkába.

7.1.1. KonténerekA widgetek a felületen történo csoportokba csoportokba rendezése konténerek (container) segítségével valósul meg. Ezekolyan láthatatlan widgetek, melyekbe más widgeteket helyezhetünk (pack).

A GtkContainer egy önmagában nem használható absztrakt osztály, ami csupán osül szolgál minden olyan származ-tatott osztálynak, melyet widgetek tárolására lehet használni.

Alapvetoen két olyan ososztállyal találkozhatunk a késobbiekben, melyek további származtatás alapjául szolgálnak.Ezek a GtkBin és a GtkBox. A GtkBin ráadásul absztrakt osztály, azaz csak további származtatás céljára használható,példányosítani nem lehet. Bár a GtkBin és a GtkBox osztályok csupán a bennük tárolható elemek számosságában kü-lönböznek egymástól, szerepük mégis gyökeresen eltéro. Elobbi mindössze egy elem tárolására alkalmas, vagyis nemklasszikus tárolóként használatos, hanem rendszerint valamilyen dekorátor funkciót ad hozzá a benne tárolt elemhez (pl:GtkWindow, GtkFrame, GtkButton, . . . ). Utóbbi konténerek a widgetek vízszintesen, illetve függoleges rendezett tárolá-sát teszik lehetové.

Egy elemu konténerek

A GtkBin jelentosége a további származtatásoknál jelentkezik majd, hisz az olyan nélkülözhetetlen típusoknak, mint azablak (GtkWindow), a gomb (GtkButton), vagy a frame (GtkFrame) mind a GtkBin az ososztálya.

Több elemu konténerek

A felületi elrendezés kialakításakor játszanak fontos szerepet, a bennük található widgetek – amit a GTK konténer gyere-keknek (children) nevez – elrendezésén túl azok méretét és konténeren belüli pozícióját is meghatározza. Ilyen típusokpéldául maga a GtkBox, vagy a táblázatos megjelenítést szolgáló GtkGrid.

7.1.2. MéretezésA GtkContainer osztály legfontosabb funkcionalitása – amit a származtatott osztályok is felhasználnak – az, hogy megtudja határozni a benne található elemek méretét. Ezt persze nem teljesen önállóan teszi, hanem megkérdezni a benne ta-lálható widgeteket, hogy mekkora helyre lenne szükségük. Minden egyes widget saját hatáskörben állapíthatja meg, hogymekkora az a vízszintes, illetve függoleges kiterjedés, ami az igényeinek legjobban megfelelne, illetve amire minimálisanszüksége van. Ez a mechanizmus fentrol lefelé, azaz a gyökértol a levelek felé terjed abban a fa hierarchiában, melynekgyökere az ablak, közbülso elemeit a konténerek, leveleit pedig a widgetek alkotják.

A legegyszerubb módszer nyilván az, hogy a konténerek – amilyen maga az ablak is – összeadják a gyermekeik(a fában közvetlenül alattuk lévo elemek) méretigényét és azt sajátjukként propagálják. A muködési modell azonbannem ilyen egyszeru, ugyanis az egyes widgetek vertikális helyigénye változhat annak függvényében, hogy mennyi hely

45

Page 51: Helló Window!

áll rendelkezésre horizontálisan. Kézenfekvo példa erre egy többsoros címke, ami a vízszintes hely növekedése eseténkevesebb sorban – azaz kisebb függoleges helyen – is elfér. Az egyes widgetek végleges méretének meghatározása ennekokán több ütemben történik az alábbiak szerint.

A konténer eloször lekérdezi közvetlen gyermekei által preferált minimális (minimal) és szokványos (natural) széles-ségigényét (size request), majd ezt összegezve adja tovább, mint saját vízszintes helyigényét. Ezt követoen a minimálishorizontális helyigényt, mint rendelkezésre álló helyet megadva a konténer lekérdezi gyermekei függoleges helyigényét.Ezzel meghatározásra került a minimális vízszintes, illetve az ahhoz tartozó függoleges helyigény, aminél kisebb helyenaz adott konténer nem fér el.

Ezt követoen a legfelso szintu konténer, vagyis maga az ablak alapértelmezett, vagy a minimális mérte alapján –ha az utóbbi nagyobb az elobbinél – megfeleloen újrakezdi a függoleges helyigén lekérdezését. Ezek után a konténera szétosztja a rendelkezésre álló vízszintes helyet az általa tartalmazott widgetek között, majd ennek megfeleloen újramegkezdodik a függoleges helyigény lekérdezése. Néhány ciklust követoen adott az egyes widgetek a helyzethez szabotthelyigénye.

7.1.3. ElrendezésA legtöbb esetben egy ablak több helyet tud rendelkezésre tudja bocsájtani mint, amit a benne lévo widgetek igényeltek.A kérdés tehát abban áll, hogy mi legyen akkor miként jelenítse meg a konténer a saját widgeteit, ha több a hely, mintamennyire feltétlenül szükség volna. Ilyen eset például akkor állhat elo, ha ez ablak átméretezheto és azt a felhasználónagyobbra nyújtja, mint amekkora hely a benne lévo widgetek kirajzolásához elégséges.

Ezt az esetet szabályozzák, a konténer és a tárolt elem viszonyát meghatározó tulajdonságok (pl: fill, expand, pack-type). Ezek a tulajdonságok voltaképpen nem a konténerhez, hanem a benne tárolt widgethez nem kötodnek, a widgetviszonyát a konténerhez határozzák meg. Megadásuk akkor történik, amikor egy widgetet szeretnénk egy konténerben –például egy GtkBoxban – elhelyezni, tárolásukról pedig a konténer gondoskodik.

GtkBox

A boxokat, leginkább úgy képzelhetjük el, mint egy mindkét végén zárt dobozokat amikbe középrol a két vége felé lehetpakolni. A hasonlat már csak azért is helytálló, mert az egyes elemek a dobozban történo elhelyezése csak egymástkövetoen, úgymond egymásra pakolva lehetséges. Annyiban viszont sántít a példa, hogy a GTK esetén rendkívül ritka –bár egyáltalában nem lehetetlen – hogy elemeket vegyünk ki egy konténerbol.

GtkGrid

A grid egy rács – vagy ha úgy tetszik táblázatos formát – megvalósító konténer, aminek segítségével widgeteinket rácsalakzatban sorokba és oszlopokba rendezhetjük. Az egyes oszlopok, illetve sorok azonos szélességuek, illetve magassá-gúak, ennek révén alakul ki egy táblázatos forma, ahol az egyes ”cellákban” a táblázatba rakott widgetek helyezked-nek el. Hasonlóan a boxokhoz az oszlopok és a sorok között megadható térköz (column-spacing, row-spacing),illetve megoldható, hogy az összes oszlop, illetve sor azonos szélességu, illetve magasságú (column-homogeneous,row-homogeneous)legyen .

A fentiekbol talán nem következik elso látásra, de ez egy oszlopos, illetve egy soros GtkGrid tökéletesen megvalósítjaa függoleges, illetve a vízszintes elrendezésu GtkBox funkcionalitását.

GtkOrientable

A korábban említett konténertípusok mindegyik implementál egy, az orientációra vonatkozó interfészt, aminek révén lehe-tové válik az orientáció flexibilis kezelése, akár futásidoben történo megváltoztatása. Ennek elonyeit a mobilalkalmazásokterjedésének fényében nem igazán kell részletezni.

7.2. Alapmuveletek

7.2.1. LétrehozásA GtkContainer, illetve a GtkBin absztrakt osztályok, így ebben a formájukban nem, csak a származtatott osztályok ré-vén példányosíthatóak. A származtatott osztályok közül ebben a részben a legnépszerubbet – a vízszintes vagy függolegeselrendezést és megvalósító – GtkBox, illetve a rács, formát implementáló GtkGrid osztályt vesszük górcso alá.

46

Page 52: Helló Window!

GtkBox

Létrehozáskor a doboz elrendezését1 (orientation) túl még egy paramétert kell megadunk (spacing), ami az elemek közötthagyandó térköz méretét adja meg pixelben. Létezik a két lehetséges orientációnak megfelelo saját típus is (GtkHBox,GtkVBox), amik létrehozáskor nem várják az orientációt, mint paraméter, viszont megadandó egy bool típusú paraméter(homogeneous), aminek TRUE értéke esetén minden egyes elem azonos helyet foglal majd el a konténerben, ahol a méreta értelemszeruen a legnagyobb helyigényu widget mérete lesz. Ezek a típusok újólag írt kódokban már nem javallottak,helyettük a GtkBox típus ajánlott, ami ugyanolyan egyszeruen használható, mint a két orientációnak megfeleloen speci-alizált típus, ugyanakkor generikusabb megoldást kínál. A létrehozást követoen természetesen ezen típus objektumainakis megadható a homogeneous tulajdonság.

GtkGrid

Létrehozáskor a GtkGrid a egyetlen paramétert sem vár. Míg a vízszintes elrendezésu boxoknál a widgetek szélessége, afüggolegeseknél a magassága, addig a táblázatoknál mindketto azonos ha a homogeneous paraméter értéke TRUE.

7.2.2. Elem hozzáadásaEzen függvények közös sajátossága, hogy paraméterként átveszik azt a widgetet, melyet a konténerbe kívánunk helyezni.A korábban említett szülo-gyerek viszonyok által kialakított fa hierarchiából sajátosságaiból következik, hogy egy elemnem lehet több szülonek gyermeke – különben erdo szerkezetrol beszélnénk fa hierarchia helyett –, azaz egy widgetetösszesen egy konténerben helyezhetünk el. Ha esetleg ezt másodszor is megpróbálnánk – még mielott a korábbi konténe-rébol eltávolítottuk volna – akkor futás ideju hibaüzenetet kapunk.

Ne feledjük, a GTK rendelkezik referenciaszámlálási metódussal, azaz minden egyes objektum (GtkObject) – ese-tünkben GtkWidget – rendelkezik egy referenciaszámmal. Ha egy widgetet egy konténerbe helyezünk, annak referenci-áját a konténer, annak rendje és módja szerint, növeli eggyel. Ez a referencia mindaddig megmarad, míg a widgetet elnem távolítjuk, vagy a konténer valamilyen oknál fogva meg nem szunik, ami jellemzoen akkor következik be ha az egészablakot megszüntetjük (destroy).

Itt érdemes visszatérni a lebego referencia (floating reference) fogalmához. Egy widget referenciaszámlálójának értékelétrehozáskor egy. Ezt a konténer – amibe a widgetet helyezzük – nem növeli meg, csupán a lebego referenciát süllyesztiel, vagyis a következo referenciát növelo muvelet ténylegesen növelni fogja a referenciaszámláló értékét, növelés hiányá-ban a következo referencia csökkento muvelet – például a konténerbol való eltávolítás – a widget megsemmisüléséhez(destroy) vezet. Ez hasznos abban a szokványos esetben ha egy adott widgettel együtt annak minden gyerekét is megszeretnénk semmisíteni, ugyanakkor odafigyelést igényel abban a ritka esetben, amikor egy konténerbol úgy szeretnénkeltávolítani egy elemet, hogy az ne semmisüljön meg.

GtkContainer

Az add nevu függvény egyetlen paramétert, az elhelyezni kívánt widgetet veszi át. Ritkán, leginkább csak egyszeru kon-ténereknél alkalmazott hívás, lévén olyan alapértelmezett paraméterek használ a widget elhelyezésére, amik a felhasználócéljainak a legtöbb esetben nem felelnek meg. Használható ugyan a származtatott, bonyolultabb konténerek esetén is (pl:GtkBox, GtkGrid), de célszerubb ezen esetekben az azokhoz tartozó, specifikus függvényt alkalmazni, lévén az sokkalrugalmasabban paraméterezhetoek.

GtkBin

Ebbe a típusba elemet csak a GtkContainer add függvényével tehetünk. Ha többször hívjuk meg a függvényt anél-kül, hogy a korábban elhelyezett gyereket eltávolítottuk volna futási hibát kapunk, hiszen a GtkBin csak egyetlen elemtárolására képes.

GtkBox

Ahogy arról a bevezetoben szó volt a GtkBox típus olyan, mint egy két végén zárt doboz, amibe középrol pakolhatunkkét irányba. Ennek megfeleloen két olyan függvény van, amivel elemeket – akár többet is – helyezhetünk a boxba, azegyikkel az egyik iránya, elrendezéstol függoen fel, illetve balra, a másikkal a másik irányba, elrendezéstol függoen le,illetve jobbra. A pack_start felülrol lefelé, illetve balról jobbra haladva tölti meg a konténert úgy, hogy az egymást utánelhelyezett elemek egymás alatt, illetve balról-jobbra egymás mellett jelennek meg a boxba történo behelyezés sorrendjé-ben. A pack_end hívás ezekkel épp ellentétesen, alulról felfelé, illetve jobbról balra haladva helyez elemeket a tárolóba,szintén a hívás sorrendjében.

Ahogy a GtkContainer esetén, itt is megadandó a boxba helyezendo widget, de ezen túl itt a konténeren belülielhelyezkedést meghatározó értékek is. Az expand és fill bool típusú paraméterek, amik a korábban már említett

1értsd vízszintes vagy függoleges orientációjú dobozról van-e szó

47

Page 53: Helló Window!

”felesleges” hely kitöltésére vonatkoznak. Elobbi azt határozza meg, hogy a widget a konténeren belül rendelkezésre állóhelyet kitöltse-e, vagyis ha egyáltalában van szabad hely, akkor azt igényelje-e magának (TRUE), vagy lemondjon róla(FALSE) a többi – a konténerben lévo – widget javára.

Az expand paraméter annak beállítására szolgál, hogy a rendelkezésre álló – illetve az expand okán elnyert – helyremi módon rajzolja ki magát a widget. Ha a paraméter értéke TRUE, akkor a widget maga tölti ki ezt a helyet, azaz afeltétlenül szükségesnél nagyobb helyen rajzolódik ki, míg ha az érték FALSE, akkor csak a minimálisan szükséges helyrerajzolódik és a maradék részt úgymond üresen hagyja. A pack függvények utolsó paramétere a padding nevet viseli, amia widget körül (függoleges elrendezés esetén felül és alul, függoleges elrendezés esetén jobbról és balról) hagyandó üreshely értékét adja meg pixelben.

Ha elsore nem is teljesen egyértelmu, mit jelent ez a gyakorlatban, a következo fejezet illusztrációjából minden vilá-gossá válik.

GtkGrid

Az attach függvény – ami a táblázatok esetén elem elhelyezésére szolgál – bizonyos szempontból bonyolultabb, bizonyszempontból egyszerubb, mint a korábbi függvények. A táblázatnál természetesen vízszintes, illetve függoleges pozíciótis meg kell adnunk, ahová a widget szánjuk, viszont sem fill, sem expand paramétereket nem kell megadni, lévén ezeka paraméterek widgetenként külön-külön nem állíthatóak. Másrészrol lehetoség van arra, hogy egy adott widget többoszlopot illetve több sort is elfoglaljon. Ez esetben a left és right, illetve a top és a bottom paraméterek értékénekkülönbsége nem egynél nagyobb.

7.2.3. Elem eltávolításaA származtatott típusok, legalábbis azok, amelyekkel ebben a részben foglalkozunk (GtkGrid, GtkBin, GtkBox) nemigényelnek az eltávolítás során semmilyen extra muveletet, így a GtkContainer funkcionalitására támaszkodnak.

GtkContainer

A remove függvény értelemszeruen az eltávolítani kívánt widgetet várja paraméterként és ahogy azt említettük az általatartott referenciát meg is szünteti. Ez egyben azt is jelenti egyben, hogy az eltávolított widgetre az utolsó – hisz többnyirecsak a konténere tart referenciát egy widgetre – referencia és ezzel maga a widget is megszunik.

Ha ezt az esetet el akarjuk kerülni, akkor még az eltávolítás elott a referenciaszám növelésérol magunknak kell gon-doskodnunk. Vagyis, ha két lépésben (remove és add) akarunk egy widgetet áthelyezni egyik konténerbol a másikba,akkor az eltávolítás elott növelnünk, a hozzáadás után pedig csökkentenünk kell a referenciát. Utóbbira azért van szük-ség, mert az új szüloelem maga is növel egyet a referencián, így ha mi, az általunk korábban megnövelt referenciát nemcsökkentenénk, a widget soha nem szunne meg.

Hasonlóan a GtkBinhez itt sincs specifikus függvény az eltávolításra, hanem a GtkContainer remove függvényéthívjuk.

7.3. Pa(c)kolás

7.3.1. Elemek elhelyezkedéseFogalmak

Lássuk mire jó végül is ez a három opció (homogeneous, expand, fill) és mikét függenek össze egymással.

Homogenitás. A konténer tulajdonsága, ami a gyerekek méretének egymáshoz való viszonyát határozza meg. Jelentéseegyenloség abban az értelemben, hogy minden egyes elem a konténerben pontosan ugyanakkora helyet foglal majd el.Ez a hely a természetesen a legnagyobb helyigénnyel rendelkezo widget mérete lesz, hiszen csak így biztosítható, hogyminden elem kiférjen, ugyanakkor azonos helyet foglaljon el.

Terjeszkedés. A terjeszkedés (expand) azt határozza meg, hogy az adott widget megpróbál-e helyet szerezni magá-nak a konténerben a többi widget rovására. Gyakorlatban ez annyit tesz, hogy a konténer által elfoglalt hely szabadonmaradt része – vagyis amennyivel a konténer nagyobb, mint a benne lévo elemek minimális méretének összege –, ezentulajdonsággal rendelkezo widgetek között kerül elosztásra.

Kitöltés. A gyerek által elfoglalható terület kitöltésének (fill) módját leíró tulajdonság. Gyakorlatilag azt határozzamegy, hogy az adott widget megrajzolásakor a rendelkezésére álló teljes helyet kihasználja a rendszer, függetlenül attól,hogy ehhez a mennyiségu helyhez miként jutott a widget (expand, homogeneous, . . . ), vagy csak a minimális szükségeshelyet használja fel, míg a maradékot üresen hagyja.

48

Page 54: Helló Window!

Igazítás. A igazítás (align) azt határozza megy, hogy a konténerbe helyezett elem GtkBox esetén a konténeren belül,GtkGrid esetén a cellán belül merre igazodjék. Az igazítani lehet elore (start), vagy hátra (end), ami természetesenfüggoen az orientációtól – vízszintes, vagy függoleges – bal, vagy jobb oldalt, illetve az alsó vagy a felso részt jelenti.

GtkBox

A következo ábra azt szemlélteti, hogy miként változtatja meg a homogenitás az expand, fill) tulajdonságok függvényébenaz elemek elhelyezkedését a konténeren belül.

7.1. ábra. Méretarányos és homogén elhelyezésének a konténeren belül[8]

Ahogy korábban a kódsorokat, most a widgetek sorait vesszük sorra a minél jobb megértés kedvéért.

Méretarányos elhelyezkedés.

expand = FALSE, fill = FALSE A konténerben lévo elemek – ahogy fentiekben fogalmaztunk – nem akarnak egy-más rovására helyet szerezni (epxand), így a rendelkezésre álló vízszintes helyet nem is töltik ki, vagyis ezzel amegoldással az egész elemsorra nézve a szélekre történo igazítást (pack_start esetén balra zárt, míg pack_endesetén jobbra zárt) alakítható ki.

expand = TRUE, fill = FALSE Az összkép, leginkább az alatta található sor miatt kissé csalóka, mivel az elemekkissé rendezetlennek tunnek, ehelyett viszont arról van szó, hogy minden egyes elem megszerezte magának – asaját minimális méretigényének arányában – az expand paraméter okán rendelkezésre álló plusz helyet és az ígyallokált (size allocation) térrészen belül középen helyezkedik el.

expand = TRUE, fill = TRUE Az egyes elemek nem csak hogy kiterjeszkednek (expand), hanem a korábban fel nemhasznált területre, de ki is töltik (fill) azt, azaz annak teljes méretében rajzolják is ki magukat. Ezzel a megol-dással az elemsorra igaz, hogy az elemek között – a konténer spacing, illetve a konténerbe helyezéskori paddingparaméter értékével meghatározottaktól eltekintve – térköz nincs.

Ahogy az az ábrából – és talán a magyarázatból is – kitunik az fill opció állításának semmi teteje anélkül, hogyaz expand be ne lenne kapcsolva, hisz e nélkül nincs semmilyen plusz terült, amire a widget magát megnagyobbítvarajzolhatná.

Homogén elhelyezkedés.

expand = TRUE, fill = FALSE A konténerben lévo elemek – a már használt kifejezéssel élve – hiába akarnak egymásrovására helyet szerezni (epxand) a konténerben, a konténer a rendelkezésre álló vízszintes helyet egyenloen osztjael közöttük. Ezzel a megoldással az egész elemsorra igaz, hogy az elemek azonos helyet foglalnak el, de ezenhelyen belül csak a minimálisan szükséges területre rajzolják ki magukat.

expand = TRUE, fill = TRUE A fentiekhez képest az eltérés csupán annyi, hogy az egyes elemek ki is használják– ha úgy tetszik kitöltik (fill) az egyenloen kiporciózott térrészt, maximális méretben rajzolva ki magukat. Akülönbség az elozo sorhoz képes épp az, mint a nem homogén konténerek esetén ugyanazon paraméterezés esetén.

49

Page 55: Helló Window!

GtkGrid

A GtkGrid típus szempontjából ugyanaz a két saját tulajdonság (homogeneous, spacing) számottevo, mint az iméntGtkBox típus esetén amiket a fentiekhez hasonlóan befolyásol másik két paraméter. Ezek a paraméterek viszont ugyanúgya konténer és a widget viszonyát határozzák meg, mint az expand és a fill és azokhoz hasonló módon is muködnek,ugyanakkor a GtkWidget tulajdonságai, tehát ott is tárolódnak.

Kiterjedés. Az expand önálló tulajdonságként is létezik és szerepe gyakorlatilag ugyanaz, mint a GtkBox típus esetén,ugyanakkor mégis van egy számottevo különbség. Az expand voltaképpen másik két tulajdonság (hexpand, vexpand)– amik a vízszintes és a függoleges kiterjedés szabályozzák –, együttes kezelésére szolgál, amik külön-külön is létezo ésértelmezett tulajdonságai a widgetnek.

Igazítás. A fill gyerek-tulajdonság ebben a formában nem létezik, mint a GtkWidget típus tulajdonsága, viszonthelyette két tágabban értelmezett tulajdonságot is használhatunk. Ez a tulajdonság az igazítás (align), amit szintén meg-adhatunk külön vízszintes és függoleges (halign, valign) értelmezésben. A tulajdonság négy értéket vehet fel, amikközül egy a FILL, ami megegyezik a korábban részletezett fill gyerek-tulajdonság muködésével. A mássága, illetverugalmassága abban áll, hogy a másik három érték (START, END, CENTER) révén nem csak azt érhetjük el, hogy a widgeta rendelkezésre álló, felesleges vagy éppen szándékoltan megszerzett (expand) helyet kitöltse, hanem azt is, hogy annakelején, végén, vagy éppen közepén helyezze el magát. A pozíció természetesen függ az orientációtól, így a START értékjelenthet bal oldalt, vagy éppen felso pozíciót, ahogy az END jobb oldalt, vagy az üres térrész alját.

7.3.2. Térköz, pányvázás és szegélyFogalmak

Térköz. A konténer tulajdonsága, ami az benne elhelyezett elemek egymástól vett távolságát, vagyis ez egyes elemekközötti térközt határozza meg. Fontos megjegyezni, hogy a konténer elso eleme elott és utolsó elem után ez a térköznem jelenik meg, csakis az elemek között. Ennek megfeleloen az egy elemu konténerek ezen tulajdonsággal nem isrendelkeznek.

Pányvázás és szegély. Ha a konténerben az egyes elemek között nem egyenlo térközt szeretnénk megadni, akkor lehe-toség van az egyes widgetek esetén külön-külön, csak az adott widgetre vonatkozó, a widgetet körülvevo térközt megadni.Ez a fajta térköz minden esetben megjelenik a widget megfelelo oldalán és a már említett térközhöz hasonlóan szinténfügg az orientációtól.

A GtkBox típus esetén csak egy – az orientációtól függo irányban értelmezett – térköz megadására van lehetoség, amitpányvázásnak (padding) nevezünk és a widget mindkét oldalán megjelenik. A GtkGrid típus ettol eltéroen lehetoségetad az oldalankénti (top, bottom, left, right) szegélyérték (margin) megadásra külön-külön, de kezelhetjük az összes oldalegyben is.

GtkBox

7.2. ábra. Tér az elemek között és körül a konténerben[8]

Tér az elemek között.

50

Page 56: Helló Window!

expand = TRUE, fill = FALSE Ez a példa nem mutatja meg igazán jól azt, hogy az elemek között jelenik meg aza térköz, amit a konténer létrehozásakor megadtunk, hiszen elemek nem töltik ki (fill) maximálisan a számukrarendelkezésre álló teret.

expand = TRUE, fill = TRUE Mivel itt mindkét érték TRUE, a widgetek a rendelkezésre álló teret teljes egészében ki-használják maguk megrajzolására, eltekintve természetesen a közöttük megjeleno 10 pixelnyi térköztol (spacing).Érdemes külön figyelmet fordítani a két szélso elemre, azoknak is az ablak széléhez közelebb eso részére a követ-kezo megoldással való összehasonlításhoz.

Tér az elemek körül.

expand = TRUE, fill = FALSE A padding megadásával a térköz nem az elemek között, hanem azok körül jelenikmeg. Ez azt jelenti, hogy minden elem jobb és bal oldalán (függoleges elrendezés esetén felül és alul) egyarántjelentkezik a megadott térköz, ennek okán közöttük annak – függoen a fill értékétol – a kétszerese.

expand = TRUE, fill = TRUE Ez az az eset amikor igazán jól látható a widgetek között és az azok mellett megjelenotérköz 2:1 aránya. Az elobb – a szélso widgetek elhelyezkedésénél megfigyelteket – most hasznosíthatjuk, ha ész-revesszük itt a szélso widgetek nem tudnak a konténer széléig kiterjeszkedni, lévén két oldalról ki vannak párnázva(padding) 10-10 pixellel.

GtkGrid

Az imént leírtak a GtkGrid, illetve a GtkWidget típus saját tulajdonságainak használatával annyiban változnak, hogy apányvázás (padding) helyett, szegélyezést alkalmazhatunk, vagyis minden oldal esetében külön-külön is megadhatjuk awidgetet körülvevo térközt (margin-top, margin-bottom, margin-left, margin-right), amiket szintén kezelhetünkegyütt (margin), ami íráskor az összes értéket felülírja, míg olvasáskor a legnagyobb értéket adja vissza.

7.4. A kódA fenti példaprogramok forrása a GTK+ oldalán érhetok el.

7.4.1. Fordítás és linkelésA korábbiakhoz hasonlóan az alábbi parancssor segítségével fordítható a példaprogram:

gcc gtk_packbox.c -o gtk_packbox ‘pkg-config --cflags --libs gtk+-3.0‘

7.4.2. FuttatásPróbáljuk ezúttal a ./gtk_packbox 1|2|3 paranccsal abban a könyvtárban, ahol a fordítást elkövettük, ahol a paramétera teszt sorszáma, abban a sorrendben, ahogy azokat itt is ismertettük (a 3. természetesen csak ráadás).

7.4.3. EredményHa netán úgy érezzük mégsem világos mi is történik, mikor és miért a konténerekbe pakolás kapcsán, ne adjuk fel.Elsore talán az egész mechanizmus jelentosége sem szembetuno, ugyanakkor érdemes próbálkozni, azaz venni a forrástés játszani a különbözo értékekkel (fill, expand, spacing, padding), illetve a létrehozott ablak átméretezésével.

7.5. TesztelésAmint azt már megállapítottuk, a konténerek segítségével alakíthatjuk ki a felhasználói felület szerkezetét. Ezen eszközökteszik lehetové, hogy a widgeteket egymásba ágyazzunk, ezek határozzák meg a widgetek konténerekben való elhelyez-kedését, más widgetekhez való viszonyát. Az így létrejövo szülo-gyerek viszonyok fa hierarchiát határoznak meg, aminekgyökerében maga az applikáció áll, második szintjén az applikáció egyes ablakai, ezek alatt pedig az ablakokon belüliwidgetek, a felületi elrendezésnek megfeleloen.

A konténerek által kialakított szerkezet természetesen a tesztelés során is tetten érheto, bár nem szabad megfeledkez-nünk arról, hogy a felhasználói felület tesztelésének alapjául szolgáló – a szoftverek akadálymentesítéséhez (accessibility)megalkotott – réteg némiképp másként tekint a widgetekre és a widgetek közötti összefüggésekre, mint azt a GTK teszi.Utóbbi természetesen erosen szoftverfejlesztoi szemszöget képviseli, míg az elobbi a felhasználói gondolatvilágból in-dul ki, a felhasználók által értelmezett fogalmakból, szerkezetbol, összefüggésekbol, tulajdonságokból építkezik, ebboladódnak azok az eltérések, amiket a késobbikben folyamatosan számba veszünk.

51

Page 57: Helló Window!

7.5.1. Gyerekek kereséseAz elozo részben említett GenericPredicate nem csak szurt keresések lebonyolítására alkalmas. Az alapértelmezettparaméterekkel létrehozott objektum használatának eredménye a gyakorlatban pont az, hogy az így létrejött elvárásoknakminden elem megfelel. Egy adott Node findChild függvénynek átadva az elobbi módszerrel létrehozott predicate ob-jektumot, annak gyerekeit kaphatjuk vissza. Amennyiben a findChild függvény recursive paramétere True, akkor azösszeg gyereket, amennyiben False, akkor csak a közvetlen gyerekeket.

Azt már elöljáróban is érdemes megjegyezni, hogy a GTK widgetek által létrehozott hierarchia, illetve az accessibilityeszközök által lekérdezheto elemek hierarchiája nem egyezik meg tökéletesen. Bizonyos elemek esetén eltérések lehetnek,amiket azt adott helyen részletezünk.

52

Page 58: Helló Window!

8. fejezet

Megjeleníto eszközök

sA szöveges adatbevitel legegyszerubb módjairól ugyan volt szó az elozo részben a kép azonban közel sem teljes, hiszenablakok rendszerint nem csupán beviteli mezokbol állnak, hiszen magukból a mezokbol igencsak nehezen lehetne rájönniarra, hogy voltaképpen a mezokbe mit is kellene írnunk. Ebben a részben néhány nélkülözhetetlen, teljesen általánosanhasznált megjeleníto widgetet veszünk górcso alá, úgy is mint a címkék, képek, súgó-, vagy leíróbuborékok.

8.1. Fogalmak

8.1.1. Igazítás és helykitöltésA GTK+ definiál egy olyan osként szolgáló osztályt (GtkMisc), ami önmagában nem példányosítható, csupán a beloleszármazó osztályok (GtkLabel, GtkImage, GtkArrow) közös beállításait fogja össze. Mindössze két tulajdonságról –igazítás (align), helykitöltés (padding) van szó tulajdonképpen, amik a widget tartalmának elhelyezkedését befolyásoljákmagán a widgeten belül, azaz függetlenül a widget konténerben elfoglalt helyétol és annak paramétereitol. Ez talánnémiképp ködösen hangzik, de a származtatott osztályok ismeretében hamar világossá válik.

Fontos már itt megjegyezni, hogy a GtkMisc némiképp idejétmúlt, szolgáltatásai egyszerusített – ugyanakkor a gya-korlati alkalmazás szempontjából mégis ugyanannyira kielégíto – formában a GtkWidget típus által is implementáltak,így ezen típus tulajdonságainak használata újólag írt kódokban ellenjavallt. Két oknál fogva mégis célszeru ezzel a tí-pussal foglalkozni. Az egyik a kézenfekvo ok, hogy a GtkMisc még mindig része a GTK+ függvénykönyvtárnak ésmeglehetosen régen az, ennek okán pedig számos korábbi kódban találkozhatunk vele.

8.1.2. WidgetekCímkék

Talán a legfontosabb és leggyakrabban használt kiegészíto widgetek a címkék, ahol értelemszeru az igazítás (alignment)jelentése,ami azonos azzal, amit a szövegszerkeszto szoftverek esetén megszokhattunk. A helykitöltés (padding) mukö-dése a konténereknél már tárgyaltakkal egyezik meg.

8.1. ábra. Címke[9]

Képek

A GTK – mint minden más felhasználó felület fejlesztéséhez használt eszközkészlet – lehetové teszi képek megjelenítéséta felhasználó felületek részeként. Az igazítás és a helykitöltés funkciója ebben az esetben is értelemszeru.

Képek természetesen több forrásból is származhatnak. A felhasználói felületen természetesen megjeleníthetünk külsoforrásból származó képeket, ikonokat, animációkat, melyeket fájlból tölthetünk be, ugyanakkor a GTK maga is szállítszámos olyan ikont, ami nehezen nélkülözheto még a legegyszerubb felületeken sem. Ilyenek például a leggyakrabbaneloforduló gombok ( Ok, Mégsem, Alkalmaz, . . . ), az üzenetablakok ( hiba, figyelmezteto, információs, . . . ) ikonjai.

53

Page 59: Helló Window!

8.2. ábra. Kép[9]

Beépített ikonok. Ezek a beépített (stock) ikonok a széles körben használt menüelemek, illetve eszközkészletek ikon-jait jelentik, amikre azonosítók segítségével (id) hivatkozhatunk képek, gombok, dialógusok létrehozásánál. A beépítettionokat sajátjainkra lecserélhetjük, illetve saját stock ikonok regisztrálására is lehetoség van.

Ikonhalmazok. Egy adott azonosítóhoz tartozó ikon méretbeli (menünek, gombnak, dialógusnak, . . . megfelelo méret),illetve a widgetek lehetséges állapotainak (normál, kiválasztott, aktív, . . . ) megfelelo variációk ikonhalmazokat hoznaklétre, melyekben az egyes elemek a beépített ikonokhoz hasonlóan cserélhetoek.

Buborékok

Némiképp méltatlanul hanyagolt widget a súgó-, vagy más néven leíróbuborék (tooltip), pedig egy magára valamit is adóapplikáció nem nélkülözheti ezt az eszközt, lévén ez a felhasználó informálásának egyik leginkább bevett módszere.

8.3. ábra. Buborék[7]

8.1.3. SzövegformázásA GTK, pontosabban szólva a bevezeto részben már említett Pango, rendelkezik egy saját leíró (markup) nyelvvel, aszövegek formázását teszi lehetové a felhasználó felületen. Ezen nyelv segítségével állíthatjuk be a szöveg megváltoztatnikívánt paramétereit, úgy mint betu típusa, mérete, stílusa, színe és így tovább. Ezen tulajdonságok leírását magával aszöveggel együtt adjuk meg.

<span foreground="blue"size="100">Blue text</span>is <i>cool</i>!

A példában eloször a Blue text szöveg színét, illetve méretét állítjuk át igényeknek megfeleloen, úgy hogy a kívántszöveg köré az egyes tulajdonságok (foreground, size), illetve azok értékeinek leírását helyezzük el. Ezzel a mód-szerrel ez egyes tulajdonságokat külön-külön adhatjuk meg, ugyanakkor a gyakran, és jellemzoen egymagukban használtbeállításokhoz (félkövér, dolt, aláhúzott) léteznek önálló leírók is (b, i, u), ahogy ez a cool szövegrésznél is látszik.

8.1.4. Widgetek összefüggéseiBizonyos típusú widgetek sajátja, hogy nem önmagukban léteznek, hanem vagy valamilyen csoportnak tagjai, vagy egykonkrét widgettel állnak valamilyen összefüggésben. Ez utóbbi igaz a címkék esetén is, amiknél megadható, hogy melyikmásik widget az, amire vonatkoznak, amit leírnak. Ez az összefüggés egyes widgettípusok esetén (pl: gombok) eseténautomatikus, míg más esetekben (pl: beviteli mezok, listák, . . . ) magunknak kell megadnunk. Ezen kapcsolat megadá-sának közvetlen elonye egyrészrol a tesztelés során mutatkozik meg, ahol a címke segít a leírt widget megtalálásában,másrészrol a felhasználó felület billentyuzetrol történo használatát könnyíti meg, ahogy arról a késobbiekben szó esik.

8.2. Alapmuveletek

8.2.1. LétrehozásAz ebben a fejezetben tárgyalt widgetek esetén – hasonlóan a korábbiakhoz – a létrehozás maga nem különösebbenbonyolult feladat. Némi rutint csak a létrehozást követo testreszabás igényel, amihez szükséges az alapveto muködési

54

Page 60: Helló Window!

sajátosságok tisztázása. A kezdeti beállításokat követoen jellemzoen ezen widgetek nemigen változnak, úgyhogy eztkövetoen már csak a widget konténerbe való behelyezése jelenthet kihívást.

GtkLabel

A legegyszerubb eset, ha szeretnénk valamilyen statikus szöveget, mindenféle formázás nélkül megjeleníteni a felhaszná-lói felületen. Erre a problémára a megoldás is rendkívül egyszeru. Mindhárom nyelvi változatban esetén csupán egyetlenparamétert kell megadnunk a címkét létrehozó függvénynek, ez pedig a címke szövege.

GtkWidget*gtk_label_new(constgchar*label);

GtkWidget*gtk_label_new_with_mnemonic(constgchar*label);

explicitLabel(constGlib::ustring& label,boolmnemonic=false);

Label(constGlib::ustring& label,floatxalign,floatyalign,boolmnemonic=false);

Label(constGlib::ustring& label,Alignxalign,Alignyalign=ALIGN_CENTER,boolmnemonic=false);

def__init__(self,label=None,**kwds):

Kódrészlet 8.1. Címke létrehozása

Ahogy látszik a különbözo nyelvi változatoknál a címkék már létrehozáskor ennél változatosabban paraméterezheto.A létrehozás követoen pedig további nyilván paraméterek is állíthatóak. Késobbiekben meglátjuk mik ezek a paraméterekés milyen haszonnal bírnak a hétköznapi használat során, most vegyük sorra a létrehozás paramétereit.

label A címke szövege. Általánosságban véve a címkék szövegeinek megalkotásánál célszeru valamilyen egységes irány-elvet követni. A GNOME által követett irányelvek szerint a GtkLabel típus felhasználási területein – legyen az abeviteli mezok címkéi, jelölonégyzetek szövegei, vagy más egyéb – a mondatok elso szava írandó nagybetuvel, míga többi kisbetus.

mnemonic A címkék leggyakoribb felhasználása a beviteli mezok illetve jelölonégyzetek címkézése. Ezen esetekbena címkék szövegében megjelölhetünk egy karakter úgy, hogy egy aláhúzást karakter írunk elé a szövegben (pl:"_Label text"), és azt a karaktert késobb a címke által hivatkozott widget elérésére használhatjuk. A gyakorlatbanez a billentyuzetrol történo navigációt – ezzel együtt a felhasználói felület hatékonyabb használatát – segíti eloazáltal, hogy a widget, az elobb említett példánál maradva, az Alt+L billentyuvel aktiválható lesz.

A címke által hivatkozott widget alapértelmezés szerint az a widget – pontosabban az a konténer – lesz amibea címkét helyeztük, vagy annak elso olyan szüloje, ami implementálja a GtkActivatable interfészt. Az olyanwidgetek, amik maguk is tartalmaznak címkét (pl: gombok, jelölonégyzetek, . . . ) ez a feltétel kézenfekvoen adott,mivel a widget implementálja a GtkActivatable interfészt. Amennyiben nem ez a helyzet, hanem például egybeviteli mezot címkézünk, akkor magunknak kell megadnunk a kapcsolódó widgetet a set_mnemonic_widgetfüggvény segítségével, aminek értelemszeruen az aktiválandó widget a paramétere. Az aktiváláskor a beállítottwidget az aktiválás hatására fókuszba kerül, ami egy beviteli mezonél például azzal az elonnyel jár, hogy a nem kellváltogatnunk az egér és a billentyuzet között a felület használatakor.

xalign, yalign A vízszintes, illetve függoleges igazítás 0 és 1 közötti lebegopontos értékekkel adhatóak meg, ahol a 0 abal oldalt, illetve a felso pozíciót, az 1 pedig a jobb oldalt, illetve az alsó pozíciót jelenti. Mind a vízszintes, minda függoleges igazítás alapértelmezett értéke 0,5. Ez az esetek jelentékeny részében nem felel meg az igényeknek,hiszen a címkéket többnyire balra, esetenként jobbra igazítjuk, vagyis az xalign tulajdonságot értéke 0, illetve1 kell legyen. Bár a GtkMisc osztály, illetve annak tulajdonságai még érvényben vannak, a GTK fejlesztoi nemajánlják használatukat újólag írt kódban, lévén a GtkWidget típus korábban már említett halign, valamint valigntulajdonságai helyettesítik oket.

xpad, ypad Az itt megadott értéknek megfeleloen a vízszintesen, illetve függolegesen ad térközt a widget köré. Hason-lóan azonban az elobbiekhez ennek a módszernek a használata sem javasolt új kódokban, helyette a GtkWidgetmargin tulajdonsága állítandó.

GtkImage

Ahogy arról szó esett a bevezetoben, képek létrehozására számos mód kínálkozik. Ezek közül most csak a népszerubbeketvesszük számba. Az elso és talán legfontosabb a fájlból való betöltés, amire a new_from_file függvényt használhatjuk.

55

Page 61: Helló Window!

Amennyiben a megadott fájl betöltése valamilyen oknál fogva sikertelen (pl: fájl nem létezik, jogosultsági problémák,. . . ), akkor egy olyan képet kapunk vissza, ami a betöltési hibára utal.

Amennyiben a fájl betöltése során felmerülo hibákat magunk szeretnénk kezelni egy alacsonyabb szintu megoldást kellválasztanunk, ami egyébiránt a GTK fájlból való betöltés végzo függvény hátterében is áll. Ezt a megoldás nem meglepomódon a GDK adja, hiszen a kifejezetten grafikai kódok itt kapnak helyet. A GdkPixbuf típus szintén rendelkezikfájlból való betöltésre alkalmas függvénnyel (new_from_file), ami a GtkImage hasonló függvénnyel szemben hibaesetén a nyelvi változatnak megfelelo hibajelzés történik. A C nyelvu változat esetén a hibát egy GError típusú változóbankaphatjuk vissza, míg a C++, illetve Python változat esetén kivél váltódik ki.

A harmadik eset amikor egy beépített ikont szeretnénk képként használni, amit megtehetünk a new_from_stockfüggvény használva, ami paraméterként a beépített ikon (stock icon) nevét veszi át paraméterként, éppúgy, ahogy teszi azta gomb létrehozáskor.

GtkTooltip

A súgóbuborék funkciója és megadása is hasonlít némiképp címkééhez, hiszen mindkét widget egy másik widget azono-sítására, szerepének tisztázására szolgál. Mindkét widget valamilyen szöveges leírást ad hozzá tartozó felületi elemrol,a címke rövidebb, míg a súgóbuborék rendszerint hosszabb formában. Ennek okán a súgóbuborék megadásának legegy-szerubb módja azonos a címke létrehozásánál leírtakkal, vagyis csupán a kívánt szöveget kell megadnunk paraméterként.Mivel a súgóbuborék csak konkrét widgethez tartozhat, a függvény a GtkWidget típus függvénye (set_tooltip_text).

8.2.2. MegjelenítésGtkLabel

Formázás. A címkék szövegének formázására a bevezetoben említett Pango Markup Language elnevezésu leírónyelvhasználható. A címkék létrehozása úgy történik, hogy a megadott szöveget alapértelmezetten nem tekintjük leíró nyel-ven megfogalmazottnak, azaz a use-markup tulajdonság értéke FALSE. Ez azonban a névkonvenciónak megfeleloena set_use_markup függvénnyel megváltoztatható. Ezzel azonban kello óvatossággal kell bánnunk. Amennyiben ause-markup tulajdonság értéke TRUE, a címke szövege meg kell feleljen leíró nyelv szabályainak.

Ez két esetben kritikus. Az egyik, ha a szöveg olyan elemeket tartalmaz, amik a leíró nyelvben is értelmezettek. Ebbenaz esetben az ilyen elemeket úgy kell megváltoztatnunk (escape), hogy leíró nyelvnek megfeleljen, ugyanakkor a jelentésene változzon. A Glib markup_escape_text függvénye pontosan a leírtakat implementálja. Ezen függvényt minden olyanesetben használandó, amikor nem statikus szövegrol van szó, hanem a szöveg például felhasználótól származik. Ilyen lehetpéldául egy korábban bekért név, ami tartalmazhat például kisebb, vagy nagyobb jelet. A másik kritikusnak mondhatóeset, ha a leíró nyelvu szöveget egy printf típusú formátumleíróval akarjuk létrehozni. Ehhez a szintén a Glib részekéntelérheto markup_printf_escaped függvény nyújt segítséget.

Tördelés. Hosszabb szövegu címkék használata esetén – ami jellemzoen akkor fordul elo, ha egy magyarázó szövegetakarunk például egy üzenetablakban megjeleníteni – célszeru tördelnünk a szöveget elkerülendo, hogy a szöveg helyigé-nye miatt a címke – és ezzel együtt az üzenetablak – vízszintes helyigénye aránytalanul megnojön, ami esetleg ahhozvezethet, hogy az ablak kilóg a munkaterületrol, így annak egyes funkció – legrosszabb esetben a bezáró gomb – elérhe-tetlenné váljanak.

Erre természetesen több módszer is kínálkozik. Magunk is megadhatunk a szövegben tördelést azáltal, hogy sortöréstteszünk a szövegbe. Ez egyszerubb estekben megteszi, de nem túl elegáns megoldás, mivel nem számol a megjelenítendocímke számára aktuálisan rendelkezésre álló hellyel, vagyis ha a címkét tartalmazó ablak méretét növeljük, a sortöréshelye nem változik, a szöveg nem használja ki a rendelkezésre álló helyet, csökkenteni pedig csak addig lehet az ablakméretét, amíg el nem érjük a leghosszabb sor minimális helyigényét. A probléma akkor igazán szembetuno, ha címkeszövege nem statikus, hanem valamilyen felhasználótól származó adat (pl: objektum neve, IP cím, . . . ), vagy valamilyenmódszerrel szur lista (pl: hibás elemek listája) szerepel benne.

Célravezeto a GTK, konkrétabban a Pango által nyújtott kész megoldás használata. Ez lehetové teszi a címke külön-bözo módokon történo automatikus tördelését. Ehhez eloször is engedélyeznünk kell ezt a funkciót (set_line_wrap),másrészrol választanunk kell tördelési módot. Ez utóbbi alapértelmezetten a szóhatárokon való tördelés (PANGO_WORD),ami helyett a set_line_wrap_mode függvény meghívásával beállíthatunk karakterenkénti (PANGO_WORD), illetve vegyes(PANGO_WORD_CHAR), ami azt jelenti, hogy egy szó kifér a rendelkezésre álló helyen a sor végén, akkor szóhatáron történikmeg a sortörés, ha nem, akkor az adott szóból annyi karaktert teszünk az adott sorba amennyi még oda kifér.

Részben megmutatás. Ha a rendelkezésre álló hely csekély és a címke valamely részletébol (eleje, vége, esetleg mind-ketto együtt) következtethetünk a címke teljes szövegére1, akkor elegendo lehet csak a ténylegesen információt hordozórészt megmutatnunk. A feleslegesnek ítélt részeket pedig úgymond kihagyhatjuk.

1bizonyos elnevezési konvenciók esetén a nevek elso része mindig azonos, vagyis ez nem hordoz érdemben információt, így helyszuke eseténérdemes csak a szövegek végét megmutatni

56

Page 62: Helló Window!

Ez a kihagyás a gyakorlatban úgy történik, hogy megadjuk, melyik az a része a címkének (eleje, közepe, vagyvége) amit elhagyhatónak ítélünk és hely szukében a tényleges szöveg helyett csak a kihagyás jelzo karakter (ellip-sis: ’. . . ’) írunk ki. A beállításra szolgáló függvény értelemszeruen a set_ellipsize, paramétere pedig az elha-nyagolás pozíciója, ami lehet a címke eleje (PANGO_ELLIPSIZE_START), közepe (PANGO_ELLIPSIZE_MIDDLE), vége(PANGO_ELLIPSIZE_END), illetve módunk van kikapcsolni ezt a funkciót (PANGO_ELLIPSIZE_NONE).

Szélesség. Az elozo két funkció használatakor két tulajdonság révén kontrollálhatjuk a címke szövegének szélességét.Az egyik (width-chars) a szöveg kívánt szélességét adja meg karakterekben, míg a másik (max-width-cars) révén amaximális szélességet határozhatjuk meg szintén karakterekben. Mindkét tulajdonság esetén megadható -1, mint széles-ség, ami automatikus szélességkalkulációt jelent, ami értheto okoknál fogva az alapértelmezett érték.

8.2.3. KezelésGtkLabel

Kijelölés. Alapértelmezetten a címkék nem kijelölhetoek és ez a muködés a legtöbb esetben meg is felel a kívánalmak-nak. Vannak azonban olyan esetek, ahol kifejezetten zavaró, ha a szövegeket nem lehet kijelölni és ezzel együtt másolnisem. Ilyen eset például, amikor hibaüzeneteket jelenítünk meg címkék segítségével. Az ehhez hasonló esetekben a fel-használó számára roppant bosszantó, hogy bár a hibaüzenet látható, mégsem lehet az egyszeruen átmásolni például egyhibabejelento urlapra. Ennek elkerülésére a címkét kijelölhetové tehetjük (set_selectable, ám célszeru ezt csak ak-kor megtenni, ha a címke fontos és manuálisan nehézkesen reprodukálható információt tartalmaz, mivel a kijelölhetoségegyben fókuszálhatóságot is jelent, ami billentyuzetrol való kezelést megnehezíti.

8.3. Haladó muveletek

8.3.1. GtkLabel

Hivatkozás. A címkék formázásának egy speciális esete, amikor hivatkozást szeretnénk a szövegben elhelyezni. AHTML esetén használatos módszert alkalmazhatjuk a Pango leíró nyelv esetén is, vagyis a hivatkozás szövege <a> nyitó-,illetve </a> záróelem között helyezkedik el, míg a hivatkozás a href attribútum értékeként adható meg. A hivatkozásoképp úgy viselkednek, mint ahogy azt a böngészok esetén megszoktuk, vagyis a hivatkozás szövege aláhúzott, színe pedigmegváltozik a hivatkozás elso aktiválása után.

A hivatkozás aktiválásának eseményét magunk is kezelhetjük, a activate-link szignál segítségével. A kezelo-függvényben a kívánt muveletsor hajtható végre, annak függvényében, hogy a címke melyik hivatkozása került akti-válásra, amit az értéket a kezelofüggvény paramétereként is megkapunk. A függvény visszatérési értéke – hasonlóana delete-event szignálhoz – azt fejezi, ki, hogy az eseményt kezeltük-e. Ezért az ott leírtak ennek az szignálnak akezelésénél is alkalmazhatóak.

8.3.2. GtkTooltip

Testre szabott súgóablak.

Formázás. A korábban leírt formázó nyelv a súgóablakok szövegében is alkalmazható. A GtkWidget osztályset_toolip_markup függvénye paraméterként ilyen leírónyelvu szöveget vesz át paraméterként és annak megfeleloenformázza a súgóablakban megjeleno szöveget.

Saját ablak. Amennyiben ennél is tovább szeretnénk menni – esetleg képeket helyeznénk el a súgóablakban –akkor saját súgóablakra van szükségünk. Ezt a saját ablakot a korábban már ismertetett módszerekkel hozhatjuk létreés abban gyakorlatilag bármit elhelyezhetünk. A megjelenítésrol, eltüntetésrol, illetve ezek idozítésérol továbbra is aGTK gondoskodik, nekünk csak a tartalmat kell biztosítanunk. Az elkészült ablak beállítható a GtkWidget osztályset_tooltip_window nevu függvényével, aminek a beállítandó ablak a paramétere, vagy a nyelvnek megfelelo NULLérték, ami az alapértelmezett ablak visszaállítását jelenti. Amennyiben a szokásos sárga súgóablak témát szeretnénkviszontlátni, az ablak nevét gtk-tooltip értékre kell állítanunk, amit könnyedén megtehetünk a GtkWidget osztályset_name függvényével.

57

Page 63: Helló Window!

8.4. Tesztelés

8.4.1. ObjektumA Dogtail Node típusa által reprezentált objektum az amin keresztül gyakorlatilag minden információ elérésére lehetosé-günk van, amire a tesztelés során szükségünk lehet. Számos esetben közvetlenül az objektum függvényének meghívása,vagy attribútum kiolvasása révén, más esetekben valamilyen interfészen keresztül. Elobbiekbol néhányat veszünk a to-vábbikban sorra.

Keresés. Az ebben a részben tárgyalt widgettípusok közül a GtkTooltip a legegyszerubb és egyszersmind a leggya-koribb esetben nem valódi típus, mint ahogy azt a fejlesztésrol szóló részben tárgyaltuk, ennek okán a tesztelés soránsem jelenik meg külön típusként, kiolvasására Node objektumból közvetlenül van lehetoség. A GtkLabel és a GtkImagetípusú objektumok az akadálymentesítés szempontjából betöltött szerepének neve (roleName) label, illetve icon. Azilyen típusú elemek keresésénél tehát ezeket az értékeket kell megadni a roleName paraméternek.

A másik keresési lehetoség, hogy az objektum nevére keresünk. A címke esetén az objektum

Súgószöveg. Az egyes widgethez tartozó súgóbuborék szövegét rendkívül egyszeruen megállapíthatjuk, ez atextttNode objektum description attribútumának értéke.

8.4.2. ÁllapotokGtkLabel

Többsoros szöveg. A címkék alapvetoen alkalmasak többsoros megjelenítésre, így egy állapot ezen widgetek kapcsánbizonyos lesz mégpedig a MULTI_LINE, amit a korábbiakban ismertetetteknek megfeleloen a getState függvénnyeltudunk lekérdezni. Létezik egy másik – ezzel ellentétes értelmu – státusz is, ami a címkék esetén természetesen sosemlesz része az állapothalmaznak, ez pedig a SIGNLE_LINE.

Fókuszálhatóság. A címkék kapcsán fontos lehet, hogy a szöveget ki lehessen jelölni, ami egyúttal magával vonja,hogy az adott widget fókuszálhatóvá is válik, ami alapértelmezés szerint nem igaz. Ezt a muködést tehát ellenorizni istudjuk a megfelelo állapoton (FOCUSABLE) meglétén keresztül.

8.4.3. InterfészekCsak olvasható szöveg

A csak olvasható szövegek kezelésére az ATK egy külön interfészt definiált (AtkText). Az olyan elemekhez, amik azakadálymentesítés és éppúgy a tesztelés szempontjából is csak olvasni szokás, az ATK megvalósító implementáció csakolvasási hozzáférést ad, annak ellenére is, hogy természetesen megvalósítható lenne, az írást biztosító implementáció is.Ilyen elemek például a címkék, a táblázatos megjelenítést biztosító widgetek egyes cellái, illetve minden olyan elem, amiírható hozzáférést is ad, mint például egy egysoros beviteli mezo.

Az interfészen keresztül nem csupán a szöveget magát, de annak egyes részeit, illetve az egyes részek formázásiparamétereit is elérhetjük. A queryText függvénnyel lekérdezheto interfész objektum számos szolgáltatást nyújt, bárezek jelentékeny részére – mint amilyen például a szövegrészek kezelése – a címkék során nem, vagy csak nagyon ritkánlesz szükségünk, így ezekkel majd egy késobbi részben foglalkozunk. A legfontosabb információ természetesen magaa szöveg, esetenként annak hossza, elobbi a getText függvényen, utóbbi a characterCount attribútum révén érhetoel. A getText függvény esetén fontos körülmény, hogy az két kötelezo paraméterrel is rendelkezik, a kíván szövegkezdeti- és végpozíciójának indexével, ahol a második paraméter esetén a −1 érték a szöveg végét jelenti. Mivel teljesszöveg a leggyakrabban érdeklodésre számot tartó érték, a Dogtail ehhez a text interfészt, illetve annak muködését elrejtoelérést is biztosít a Node osztály text tagja révén, aminek kiolvasása a queryText().getText(0, -1) szerinti hívásávalközel egyenértéku. A különbség az, hogy text attribútum kiolvasása kezeli azt a helyzetet, hogy az adott objektum nemimplementálja a text interfészt, ilyen esetben None értékkel tér vissza, míg ilyen esetekben a queryText függvényhívás –minden más interfész lekérdezéséhez hasonlóan – NotImplementedError kivételt dob.

A szöveg, amit a text interfészen keresztül kiolvashatunk csak a nyers szöveget tartalmazza aze egyszeru kezelésérdekében annak formázási paramétereit nem. Ha azt szeretnénk megtudni, hogy az egyes karakterek miként vannakformázva, akkor a text interfész getAttributeRun függvényét használhatjuk, aminek egyetlen paraméter a karaktersorszáma, visszatérési értéke pedig egy lista, ami elso eleme egy újabb lista, ami a karakter formázási paramétereinektartalmazza, másik két eleme pedig annak szövegrésznek a kezdo és végpozíciója, ami ugyanezen paraméterekkel lettformázva.

58

Page 64: Helló Window!

Kép

A képek kezelésére létezik egy külön interfész az ATK függvénykönyvtárban, amihez tartozó implementációt a Dogatilegy Node objektumára a queryImage függvénnyel kérdezhetünk le. Az interfész sajnos nem nyújt különösebben szélesköru szolgáltatásokat, mindössze a kép méretét, elhelyezkedését és leírását áll módunkban lekérdezni. Sajnálatosan aDogatil ehhez nem sok segítsége nyújt, ezért is kell az Image interfészt elkérnünk és azt magunknak kezelnünk. Ez azinterfész viszont már átvisz minket a Dogtail alatt meghúzó AT SPI2 világába, aminek részleteiben nem célunk elmerülni,így itt csak azokat a hívásokat ismertetjük, amikre a képek tesztelésénél szükségünk lehet.

Az elobb említett paraméterek közül a legegyszerubb a kép mérete, amit a getImageDescription paraméter nélkülifüggvény hívása révén kérdezhetünk le, ami az x, illetve y irányú pixelben vett méretek listáját adja vissza. A pozíciólekérdezése is teljesen hasonlóan muködik, méret helyett az x, y koordinátákat visszakapva a listában, viszont a pozíciómaga relatív és hogy mire relatív azt a getImagePosition függvénynek paraméterként kell átadnunk. Ez a paraméterlehet a pyatspi modul WINDOW_COORDS, vagy DESKTOP_COORDS értéke, aminek megfeleloen vagy az ablakhoz, vagymagához a munkaterülethez képest értelmezendoek a koordináták. Lehetoség vagy a két értékpár együttes lekérdezéséreis a getImageExtents függvénnyel, ami ugyanazt a paraméter veszi át, mint a getImagePosition és szintén egy listávaltér vissza, aminek elso két eleme a relatív koordinátákat, második két elem pedig a méreteket tartalmazza. Ezen értékekbolmesszemeno következtetéseket levonni nem lehet. Különösen akkor vagyunk bajban ha két azonos méretu ikon közöttszeretnénk különbséget tennünk a tesztelés során, ami nem ritka példa, hiszen szokás különbözo színu ikonokkal mondjukvalamilyen állapotot jelezni. Ebben az esetben mentsvárunk a kép leírása lehet, amit a Node imageDescription értéketartalmaz és amit alkalmazásunk fejlesztésekor az AtkObject set_image_description függvénnyel állíthatunk be.

8.4.4. ViszonyokAz egyes widgetek között bizonyos vonatkozásokban összefüggések állhatnak fenn. Ezeket az összefüggéseket – amiketszabad fordításban nevezhetünk viszonyoknak (relation) –, a Dogtail segítségével is le tudjuk kérdezni. Túlnyomó több-ségben nem is viszonyokról, hanem viszonypárokról beszélünk, amik között 1 : 1 és 1 : N típusú kapcsolatok egyarántvannak.

Általánosságban az mondható el, hogy minden egyes Accessible objektum többféle viszonyban is lehet, több másobjektummal. Elso körben tehát azt tudhatjuk meg, hogy mik azok a viszonyok, amikkel összekapcsolják az adott ob-jektumot más objektumokkal. Ennek lekérdezésére a getRelationSet függvény szolgál, ami egy konténert ad vissza,benne a viszonyok leírására szolgáló Atspi.Relation objektumokkal. Ezen objektumokból kiderítheto a viszony tí-pusa a getRelationType függvény hívásával, valamint, hogy a hány objektummal áll fenn az adott viszony, amit agetNTargets függvény visszatérési értéke ad meg. Az N darab objektum egyesével a getTarget függvény révén érhetoel paraméterként egy sorszámot átadva.

Az egyik viszony, a címkére és az általa felcímkézett widgetre vonatkozik, ahol természetesen több címke is vonatkoz-hat ugyanarra widgetre, viszont egy címke egyszerre csak egy widgetet vonatkozhat. Ez következik a GtkLabel osztályset_mnemonic_widget függvény használatából is, hiszen paraméterként csak egy widget adható meg. A viszony típusaebben az irányban pyatspi.RELATION_LABEL_FOR, ahol tehát a getNTargets függvény mindig egyet ad vissza, míg azellenkezo irányban a viszony típusa pyatspi.RELATION_LABELLED_BY, ahol több cél is lehetséges, de ez nem jellemzo.Erre a gyakran használt viszonypárra a Node osztály is ad megoldást, a relation interfész közvetlen használatánál szá-mottevoen egyszerubbet. Egy adott Node címkéinek listája a labeller, az adott Node által címkézett objektum pedig alabelee attribútum keresztül érheto el.

2a rövidítés az angol Assistive Technology Service Provider Interface kifejezést takarja

59

Page 65: Helló Window!

9. fejezet

Egysoros beviteli mezok

Most, hogy már túl vagyunk a konténerek mibenlétének megtárgyalásán, ideje azzal foglalkozni milyen widgeteket lehet akonténerekben elhelyezni. Az elso és talán legkézenfekvobb megoldás erre a kérdésre egy beviteli mezo, hiszen ablakainkjelentékeny részét arra használjuk, hogy a felhasználótól adatokat kérjünk be. Ez a rész a beviteli mezokkel általános ésaz egysoros beviteli mezok konkrét fejlesztési és tesztelési kérdéseivel foglalkozik.

9.1. Fogalmak

9.1.1. Beviteli mezok típusaiAz adatbevitelre szolgáló widgetek számos szempont szerint csoportosíthatóak. Ezek közül csak a legkézenfekvobbeketvesszük röviden sorra, azokat addig a mértékig, amíg ezen rész szempontjából érdekesek.

Bevitt adat típusa szerint

Gyakorlatilag minden adat beviheto szövegként, ugyanakkor nem mindig ez a leginkább célravezeto módszer. Felhasz-nálói szempontból egy üresen álló szöveges beviteli mezo meglehetosen kétségbeejto látvány. Ilyen esetben ugyanissemmi nem utal arra, hogy voltaképpen a beviteli mezot milyen típusú, formátumú, hosszúságú, kódolású szöveggel le-het feltölteni. Emiatt mindig célszeru a leheto legspecifikusabb widgetet alkalmazni. Emellett persze az is igaz, hogy aleggyakoribb beviteli eszközünk mégiscsak a szöveges beviteli mezo marad.

(a) Szövegbeviteli mezo[9] (b) Számbeviteli mezo[9]

9.1. ábra. Szöveg és szám bevitele

Szöveg. A szöveg bevitele tehát a legáltalánosabb szükséglet, amit egy UI kapcsán elképzelni lehet, mégis számos olyanfunkció létezik, ami egy szöveges bevitelt lehetové tevo widgetnek teljesítenie kell. Ilyen például kijelölés, a másolás,a beillesztés, beszúrás, a unicode karakterek kezelése, a használt betutípus-paraméterek megadásának lehetosége akárkarakterenként. Erre a GTK szöveges bevitelre alkalmas widgetei természetesen mind képesek, sot, de errol majd késobb.

Szám. Számok bevitele gyakorlatilag a szövegbevitel egyfajta specializációja, legalábbis a bevitel ellenorzésének tekin-tetében. Számok bevitele esetén nyilván csak számjegyeket, illetve a számok beviteléhez kapcsolódó egyéb karaktereket(tizedes vesszo, elojel, . . . ) engedünk bevinni. Emellett egy widget nyújthat még egyéb kényelmi szolgáltatásokat is amikmegkönnyítik a fejlesztok munkáját, min például a minimális, maximális elfogadható érték, tizedes jegyek számánakmeghatározása, lépésköz megadása az érték lépésenkénti megváltoztatásához, vagy akár számként való beállítás, illetvelekérdezés lehetosége.

Sorok száma szerint

A sorok száma szerint mindössze két típus megkülönböztetésének van létjogosultsága. Az egy, illetve a több sor kezelésérealkalmas widgetek mind a felhasználás körében, mind a kezelés módjában, mind pedig a tesztelésben gyökeresen eltérnek

60

Page 66: Helló Window!

egymástól.

Egysoros beviteli mezok. Ezen beviteli mezok esetén a tárolt, illetve megjelenített értéket jellemzoen egy egységkéntkezeljük. Egyszeru, néhány tíz karakternyi, adatot kérünk be ezen widgetek segítségével, így sem különösebb szövegszer-kesztési, sem pedig látványos megjelenítési funkciókat nem kell a widgetnek ellátnia. Ennek megfeleloen sem a használat,sem pedig a tesztelés nem jelent alapesetben különös kihívást.

Többsoros beviteli mezok. Lévén ezek a widgetek gyakorlatilag egy egyszerubb szövegszerkeszto alkalmazásként isfelfoghatóak, akár komplett fájltartalmak kezelésére is alkalmasak, akár olyan extra funkciók igénybevétele mellett, minta tartalom formátumának megfelelo szintaxis kiemelés (syntax highlight). Ennek megfeleloen a kezelés sem annyirakézenfekvo, mint az egysoros widget esetén. Tesztelési szempontból azonban – mindaddig amíg csak a tartalmat egybenakarjuk kiolvasni, vagy beírni – nem lesz igazán nehéz dolgunk.

9.1.2. InterfészAzon widgetek kezeléséhez, melyek valamilyen tartalom szerkesztésére szolgálnak – függetlenül attól, hogy ez a tarta-lom szám vagy akár szöveg, egy vagy több sort foglal el – a GTK egy interfészt (GtkEditable) definiál. Ez az inter-fész meghatároz számos olyan muveletet (insert_text, select_text, get_position, . . . ), illetve szignált (changed,delete-text, insert-text), amiket az interfész megvalósító widgetnek implementálnia kell. Ennek eredményekéntezen widgetek egy egységes felületen (API) keresztül kezelhetoek és csak a specifikumok tekintetében kell az interfésztimplementáló widgethez tartozó függvényt, szignált használnunk.

9.2. Alapmuveletek

9.2.1. LétrehozásA létrehozás formai elemei a korábbi részek ismeretében nem okozhatnak meglepetést, így a tartalmi elemekre koncent-rálunk. Mindkét widget esetében van mire.

GtkEntry

A GtkEntry osztály esetében létezik egy paraméter nélküli konstruktor függvény (new), ami voltaképpen semmit külö-nöset nem tesz. Létrehoz egy olyan GtkEntry objektumot, ami a tulajdonságinak alapértelmezett értékeit veszi fel, amipontosan megfelel a hétköznapi használat szükségleteinek. A másik konstruktor függvény egy GtkEntryBuffer objek-tumot vesz át paraméterként, aminek segítségével lehetové válik a buffer által tárolt adat megjelenítése több különbözoGtkEntry példányban.

A GtkEntryBuffer voltaképpen az egysoros beviteli mezo adattárolója, az ehhez szükséges kevés számú tulajdon-sággal (text, length, max-length) és szignállal (deleted-text, inserted-text), valamint az ezen tulajdonságokbeállítására, lekérdezésére – a szokásos nevezéktan szerint –, illetve a szignálok kiváltására szolgáló függvényekkel.

GtkSpinButton

A GtkSpinButton, bár a GtkEntry osztályból származik, annak specializációja, attól mégis jelentékeny mértékben eltér,mind a létrehozás, mind a késobbi kezelés tekintetében. Létrehozására egy három paraméteres függvény szolgál, aminekminden paramétere szorul némi magyarázatra.

adjustment A GtkAdjustment egy olyan valós számot reprezentál, ami nem csupán egy magában álló érték – hiszen erremegfelelne egy egyszeru gdouble is –, hanem bizonyos paraméterekkel van összerendelve. Ezek a paraméterek, akonstruktor függvénynek való átadás sorrendjében, a következok.

value A konkrét érték.

lower, upper Az érték által felveheto minimális, maximális érték.

step increment, page increment Az érték felhasználó által történo növelésekor, csökkentése során használandólépésköz. Konkrétan a le, fel, valamint a page down, page up billentyuk lenyomásakor a step increment,illetve a page increment értékével csökken, illetve no az érték. A widget maga is tartalmaz egy fel-le nyílpárost, ami az érték növelésére, csökkentésere szolgál és szintén a step increment értéket használja.

page size A GtkSpinButton esetében nem használt beállítás.

digits A widget által az érték megjelenítésekor használt tizedes jegyek száma.

61

Page 67: Helló Window!

climb rate Az érték felhasználó általi ismétlodo növelésekor, csökkentésekor bizonyos lépésszám után használandó gyor-sítási arány. Gyakorlatban a folyamatosan lenyomott le, fel, page down, page up billentyuk, illetve a widget fel lenyilai hatására lép életben ez a mechanizmus.

9.2.2. Tartalom kezeléseA tartalom kezelése alapvetoen kétféleképp történhet; szöveg szerint, illetve érték szerint. Természetesen a GtkEntryesetén csak a szöveg elérése lehetséges, míg a GtkSpinButton esetén mindkét mód elérheto, lévén a GtkSpinButtonvoltaképpen GtkEntry. A függvények nevei értelemszeruen GtkEntry esetén get_text, illetve set_text, a get_value,set_value, illetve get_value_as_int, set_value_as_int.

9.2.3. Csak olvasható módEgy alapvetoen adatok bekérésére szolgáló widget esetén a szerkeszthetoség tiltása ugyan ritka, de nem szokatlan muve-let. Ennek lehetoségét a korábban már említett – a GtkEntry és GtkSpinButton által is implementált – GtkEditableinterfész biztosítja. Értelemszeruen a set_editable függvény az, amit hívva a szerkeszthetoséget állíthatjuk.

9.2.4. Jelszavak kezeléseA jelszavak kezelésének szempontjából két függvényt, illetve az általuk módosított tulajdonságot kell megismernünk. Azelso tulajdonság visibility, ami alapértelmezetten TRUE értéket vesz fel –, meghatározza, hogy a beviteli mezoben, azannak értékül adott szöveget fogjuk látni, vagy ehelyett az azzal megegyezo számú egyforma karaktert. Utóbbi esetbenez a karakter, a másik megemlítendo tulajdonság (invisible-char) révén adható meg. Ennek alapértelmezett értékét aGTK határozza meg a rendszeren elérheto betukészleteknek megfeleloen.

9.2.5. SzignálokA szignálok tekintetében általában csupán a beviteli mezo tartalmának módosítása az, ami az érdeklodésünkre számot tart.Ez a változás ugyanakkor többféle is lehet. Egyrészrol szövegszeru változás egy szöveges beviteli mezo esetén, az értékváltozása szám bevitelére szolgáló widget esetén. Ez utóbbi esetben megkülönböztethetoek azok az helyzetek, amikor aszöveg áírása vezetett az érték változásához, illetve az az eset, amikor a widget saját funkcióit (fel/le nyíl, page up/downbillenyuk, . . . ) kihasználva növeljük, illetve csökkentjük az értéket.

Szöveg változása

A GtkEditable típus – ezzel együtt tehát az interfészt implementáló GtkEntry, illetve az abból származó GtkSpinButton– changed szignálja minden esetben kiváltódik, amikor a beviteli mezo tartalma megváltozik, pontosabban megváltozott.A szignált kezelo függvény már csak azt követoen hívódik meg, miután a szöveg már megváltozott, vagyis a szignálkezelofüggvényben lekérdezve már az új értéket kapjuk.

Mivel a szignál voltaképpen minden billentyu leütést követoen– legyen az beírás vagy törlés – meghívódik, célszerua kezelofüggvényben implementáltak kapcsán ezt figyelembe venni, például a lefutás idoigényét a lehetoség szerintiminimumon tartani. Még ha a kód lefutása gyors is, attól érdemes óvakodni, hogy minden egyes billentyu lenyomásáravégrehajtsuk azt a muveletet (validáció, más felületi elemek változtatása, . . . ), amit elegendo akkor megtennünk, ha abeviteli mezo elvesztette a fókuszt. Az erre alkalmas szignál is adott (focus-out), a bevitt adatokra vonatkozó validáció(pl: IP cím, reguláris kifejezés), vagy éppen a tartalom egyéb widgetekre gyakorolt hatása szabályainak célszeruen amúgysem magából a hatásból kell kiderülniük. Erre alkalmas eszköz inkább valamely súgó, ami egy késobbi részben kerülismertetésre, ugyanakkor a hiba jelzésére használhatóak a korábban ismertetett ikonok.

Érték változása

A GtkSpinButton két szignállal is szolgál a tárolt érték megváltozásának jelzésére. A value-changed minden olyanesetben kiváltódik, miután a tárolt érték megváltozik, bármi legyen is annak az oka, míg a change-value szignál csakabban az esetben ha a GtkSpinButton tartalma nem szöveg beírásának hatására, hanem a billentyuzetrol történo vezérlés(fel/le nyilak, page up/dowm, . . . ) következményeként változik meg.

9.3. Haladó muveletek

9.3.1. IkonokBeviteli mezonkben lehetoség van ikonok megjelenítésére a szövegmezo mindkét oldalán. Ugyanakkor nem csupánmegjelenítésrol van szó, hiszen az ikonok különbözo muveleteit is kezelni tudjuk, ami számos hasznos, kényelmes és

62

Page 68: Helló Window!

látványos funkció megvalósítására ad lehetoséget.

9.2. ábra. Ikonok használata szövegbeviteli mezoben

keresés. Egy-egy ikonnal jelezhetjük a beviteli mezo két oldalán, hogy a beírt értéket keresni szeretnénk valamelytartalomban – legyen az egy több soros beviteli mezo, egy fájl, vagy bármi más –, míg a másik ikon szolgálhat a keresésikifejezés törlésére.

jelszóbevitel. A jelszavak bevitelekor ikonnal jelezhetjük, hogy a megjeleno karakterek nem véletlenül nem az általunkbeírtakat mutatják, a másik ikon pedig figyelmeztetésül szolgálhat, hogy ha a caps lock be van kapcsolva1.

fájlmuveletek. Fájlnevek bevitelekor jelezhetjük a felhasználó felé, hogy e mezoben szereplo fájlév mire szolgál majdvoltaképp. Ezen túlmenoen az ikonra kattintva felhozhatjuk a fájlválasztásra szolgáló dialógust.

9.3.2. FolyamatindikátorA beviteli mezo háttereként használhatunk folyamatindikátort, amivel jelezhetjük egy – a beviteli mezo tartalmával össze-függo – folyamat állását. Amennyiben a folyamatot magát akarjuk érzékeltetni, viszont nem ismerjük annak végét, illetveaktuális állását, akkor választhatunk egy olyan módot ahol a folyamatindikátor a beviteli mezo két vége között pulzál, ahola progress_pulse függvény hívásával mozdíthatjuk tovább a folyamatindikátort a progress-pulse-step tulajdonságáltal meghatározott (0 és egy közötti érték) mértékben.

9.3. ábra. Ikonok használata szövegbeviteli mezoben

Amennyiben a folyamat állapota pontosan ismert, akkor az ábrához hasonló eredmény – ahol az indikátor a folyamatállapotának mértékében foglalja el a hátteret –, a progress-fraction tulajdonság állításával érheto el, ahol az érték 0 és1 között a folyamat készenléte.

9.3.3. Iránymutató szövegEgy beviteli mezok esetén – legyen az szöveg vagy szám bevitelére szánva – nincs kevésbé felhasználóbarát viselkedés,mint hogy a widget semmilyen utalást nem tartalmaz arra nézvést, mit is kellene voltaképp tartalmazni. Erre természetesenalkalmas megoldás az elozo részben megismert címke, illetve súgóbuborék, de a GtkEntry, valamint az abból származówidgetek is rendelkeznek egy említésre érdemes megoldással. Ez pedig nem más, mint hogy a beviteli mezobe egy olyanszöveget írhatunk, ami addig látszik, amíg felhasználó a mezobe írni nem kezd, vagy az írás végeztével üresen hagy.Ez a szöveg szolgálhat mintául a beírandó adat tartalmára, formájára és teszi ezt anélkül, hogy felhasználói interakciótigényelne, mint a súgóbuborék. Ezen szöveg a megadására az GtkEntry osztály set_paceholder_text függvényeszolgál.

9.3.4. BufferA GtkEntry típus esetén az adattároló réteg (model) elválasztásra került a megjelenítést, illetve vezérlést (view, control-ler) végzo rétegtol, ami a tulajdonképpeni GtkEntry. Egy GtkEntry létrehozható paraméterek nélkül, vagy adattárolástvégzo típus – a GtkEntryBuffer – egy példányának megadásával. Ez egyrészt lehetové teszi, hogy több, a megjele-nítést végzo widget osztozzon ugyanazon az adattárolón eltéro kijelölés, vagy éppen kurzorpozíció mellet. Másrészrolleszármazva a GtkEntryBuffer buffer típusból, magunk is implementálhatunk adattárolót melyek a legkülönbözobbigényeknek tehetnek eleget.

1Ezt funkciót készen kapjuk a GtkEntry esetén, be-, illetve kikapcsolása a caps-lock-warning tulajdonság állításával lehetséges.

63

Page 69: Helló Window!

9.3.5. FormázásA GtkSpinButton esetén magunk is meghatározhatjuk, hogy a tárolt adatot milyen formátumban fogadjuk el, illetvemilyen formában jelenítjük meg. Ehhez nem kell egyebet tennünk, mint az input, illetve az output szignálokra megfelelokezelo függvényeket kötni.

1 staticgboolean2 on_output(GtkSpinButton*spin)34 {5 GtkAdjustment*adjustment=gtk_spin_button_get_adjustment(spin);6 gdoublevalue=gtk_adjustment_get_value(adjustment);7 gintdigits=gtk_spin_button_get_digits(spin);8 gchar*buf=g_strdup_printf("%0.*f", digits,value);9

10 if (strcmp(buf,gtk_entry_get_text(GTK_ENTRY(spin))))11 gtk_entry_set_text(GTK_ENTRY(spin),buf);1213 g_free(buf);1415 returnTRUE;16 }

1staticgint2on_input(GtkSpinButton*spin,3gdouble *new_val)4{5gchar*err=NULL;678*new_val=g_strtod(gtk_entry_get_text(GTK_ENTRY(spin)), &err);9if(*err)10returnGTK_INPUT_ERROR;1112131415returnTRUE;16}

Kódrészlet 9.1. Az input és output szignálok felüldefiniálása az alapértelmezett muködést implementálva

2. sor Az input és az output szignál kezelésére szolgáló függvény annyiban tér el egymástól, amennyiben a feladat.

Az input szignált kezelo plusz paramétere (new_value) – az input kezelo függvényéhez képest –, a szövegkéntelérheto érték lebegopontos számként történo visszaadására szolgál.

15. sor A visszatérési érték TRUE értéke mindkét esetben azt jelenti, hogy a szignált sikeresen kezeltük, az átalakítástelvégeztük. A FALSE visszatérési érték esetén a hívó az alapértelmezett átalakító függvényt – aminek muködéseazonos a bemutatottal – hívja meg.

Az input szignál esetén lehetséges még egy visszatérési érték (GTK_INPUT_ERROR), amit arra használunk, hogy ahívó felé jelezzük, a GtkSpinButton szövege nem értelmezheto. Ezért is gint ezen függvény visszatérési értéké-nek típusa, míg a másiké gboolean, hisz ott csak azt közölhetjük a GtkSpinButton a beírást azt általunk kívántformátumban megtettük-e vagy sem.

11. sor Az input esetén alapértelmezetten a GtkSpinButton szövegét decimális számrendszerbeli tizedes törtté pró-báljuk alakítani. Ha ez sikerül TRUE értékkel térünk vissza, ha nem, akkor a visszatérési érték GTK_INPUT_ERRORjelezvén, hogy kezeltük az input szignált, de az átalakítás sikertelen volt, így az alapértelmezetten input szignáltkezelo függvényt szükségtelen meghívni.

Az output szignál kezelésekor épp ez elobbiek ellenkezoje történik. A GtkSpinButton aktuális számszeru értékétalakítjuk a megfelelo formátumú szöveggé, ez esetben a digits tulajdonságnak megfelelo számú tizedes jeggyeldecimális formában. Ezt írjuk vissza a beviteli mezobe. A visszaírás viszont csak akkor történik meg, ha az akorábbi értéktol különbözik.

Az input és output szignálok felhasználására kézenfekvo példa lehet, hogy ha nem decimális számrendszerbenszeretnénk a számokat megjeleníteni. Ez esetben a g_strtod függvény helyett – ami csak tízes számrendszerbeli szá-mokkal muködik – a g_strtoull függvény használható, ami a paraméterként kapott számrendszerrel képes dolgozni,illetve a g_strdup_printf függvény esetén a formátum leírót kell a számrendszernek megfeleloen módosítani (pl: %.*xtizenhatos számrendszer esetén).

Ettol tágabban is értelmezhetjük a be-, illetve kimenet módosítását. Lehetoségünk van akár arra is, hogy a GtkSpinButtonne számokat tároljon, hanem szövegeket, például a hónapok neveit.

64

Page 70: Helló Window!

1 staticgint2 on_input(GtkSpinButton*spin,3 gdouble *new_val)4 {5 staticgchar*month[12]= { "January", "February", "March",6 "April", "May", "June",7 "July", "August", "September",8 "October", "November", "December" };9

1011 constgchar*text=gtk_entry_get_text(GTK_ENTRY(spin));12 ginti;1314 for(i = 1; i<=sizeof(month) /sizeof(*month); i++)1516 if(!strcmp(month[i - 1],text))17 {18 *new_val= i;19 returnTRUE;20 }2122 *new_val=0.0;23 returnGTK_INPUT_ERROR;24 }

1staticgint2on_output(GtkSpinButton*spin)34{5staticgchar*month[12]= { "January", "February", "March",6"April", "May", "June",7"July", "August", "September",8"October", "November", "December" };9GtkAdjustment*adjustment=gtk_spin_button_get_adjustment(spin);10gintvalue=gtk_adjustment_get_value(adjustment);11constgchar*text=gtk_entry_get_text(GTK_ENTRY(spin));12131415if (value&&strcmp(month[i - 1],text))16{17gtk_entry_set_text(GTK_ENTRY(spin),month[i - 1]);1819}202122returnTRUE;23}

Kódrészlet 9.2. Az input és output szignálok felüldefiniálása hónapok megjelenítéséhez

Ebben az esetben nem történik más, mint hogy a formázást némiképp továbbgondoljuk. Az input szignál kezelése-kor – ahogy a korábbi példában is –, megpróbáljuk a beírt szöveget a formátumnak megfeleloen értelmezni. Ez korábbanannyit jelentett, hogy tizenhatos számrendszerbeli számként próbáltuk a szöveget értelmezni, most viszont – mivel azértékkészletünk kicsi – ellenorizzük, hogy a beírt szöveg az értékkészlet (hónapok nevei) valamelyik eleme-e. Ha igen,akkor a hónap sorszáma lesz az új értékünk, ha nem akkor GTK_INPUT_ERROR értékkel térünk vissza.

A formázás – vagyis az output szignál kezelése –, során mindössze az aktuális értéknek megfelelo szöveget kapjaértékül a GtkSpinButton, feltéve ha eddig nem ez volt az értéke.

Természetesen egy ilyen formázási megoldásnál célszeru a GtkSpinButton minimum és maximum értékét az álta-lunk kezelt értékek számosságának megfeleloen beállítani, ami a hónapok esetén egyet jelent, mint minimum értéket éstizenkettot, mint maximumot.

9.4. TesztelésLévén a beviteli mezok a leggyakrabban használt widgetek, tesztelésük is épp ily gyakran fordul elo. Ennek kapcsán meg-ismerkedünk néhány – a tesztelés során használt – alapfogalommal, amik a késobbiekben is folyamatosan visszatérnek.

9.4.1. KeresésAz ebben a részben tárgyalt két widgettípushoz – az szöveg-, illetve számbeviteli mezo – tartozó objektumok keresésekor aGenericPredicate roleName paraméterének text, illetve spin button adandó meg. A GtkEntry kereséséhez létezikegy specializált Predicate osztály is, a IsATextEntryNamed, aminek egyetlen paramétere a beviteli mezo neve.

9.4.2. InterfészekÍrható szövegek

Az adatbevitelre szolgáló widgetekbol értelemszeruen nem, vagy nem csak kiolvasni szeretnénk értéküket, hanem írni is,így ismerve a text interfész adta lehetoségeket, ehhez egy másik interfészre lesz szükségünk. Az ATK EditableText né-ven definiálja azt az interfészt ami lehetové teszi, hogy szövege írjunk, szúrjunk be, vagy illesszünk be a beviteli mezobe.A legegyszerubb esetben, amikor csak egy adott szöveggel szeretnénk felülírni a beviteli mezo aktuális tartalmát erre azinterfészre nem lesz szükségünk, a korábban a text interfésznél ismertetett text attribútumnak való értékadás pontosanúgy muködik, ahogy azt elvárjuk. Természetesen ennek megvan a megfeleloje a queryEditableText függvényhívás ré-vén beszerezheto editable text interfészben is, mégpedig a setTextContents függvény, aminek paraméterül a beírandószöveget kell átadnunk. Szöveg beszúrására is van lehetoség a insertText függvénnyel, aminek paraméterei a beszúráspozíciója, a beszúrandó szöveg, illetve a beszúrandó karakterek száma.

A vágólap muveletek szintén ezen az interfészen keresztül érhetoek el. A másolást (copyText), törlést (deleteText),illetve kivágást (cutText) végzo függvényi két paramétert vesznek át, a szövegrész kezdo-, illetve végpozícióját amin amuveletet végre akarjuk hajtani. A beillesztés (pasteText) függvénynek csak egy paraméterre, a beillesztés helyére vanszüksége.

Számok

Azon widgettípusokat, amiket számok bevitelére használunk nyilvánvalóan a tesztelés során is így szeretnénk kezelni,még akkor is ha voltaképpen szövegként is kiolvashatnánk, vagy beírhatnánk az adatot, aztán a konverziót megejthetnénk

65

Page 71: Helló Window!

magunk. Az ATK szerencsénkre definiál a számok kezelésére használatos interfészt (AtkValue), így ezzel különösebbgondunk nem is lesz. Az interfész azonban nem csupán annyit nyújt, hogy számként írhatjuk, olvashatjuk az objektumtartalmát, de lehetoséget ad a mezo által elfogadott intervallum alsó, illetve felso határának kiolvasására is. Az interfészlekérdezése a már megszokott elnevezési konvenció szerinti függvénnyel történik (queryValue), rajta keresztül az inter-vallum alsó határát a minimumValue, felso határát a maximumValue, míg aktuális értékét a currentValue attribútumonkeresztül érhetjük el. Elobbi kettot csak olvasásra, míg utóbbit írásra és olvasásra egyaránt. Gyakori esetrol lévén szó ezenfeladatok a Dogatil Node osztályán keresztül is elvégezhetoek, rendre a minValue, maxValue, illetve a value attribútu-mok segítségével. Ez utóbbi megoldás elonye, hogy megspórolhatjuk az interfész lekérdezését, illetve nem kell e kivé-telkezeléssel sem foglalkoznunk, mivel ha az interfész nem implementált, akkor lekérdezéskor a NotImplementedErrorkivétel helyett egyszeruen csak None értéket kapunk.

9.4.3. ÁllapotokMivel a beviteli mezoket tárgyaltunk ebben a részben, amik természetüknél fogva írhatóak, ugyanakkor lehetoség vanarra a szerkeszthetoség letiltására, kézenfekvoen adódik, hogy ezt az állapotot le is lehessen kérdezni. Ezt minden továbbinélkül meg is lehet tenni a már ismert getState függvénynek a pyatspi.EDITABLE értéket megadva paraméterként.Egy másik állapot is kézenfekvoen adódik a tárgyalt widgetek típusából, mégpedig, hogy csak egy sornyi adat fogadásáraképesek, vagyis a pyatspi.SINGLE_LINE állapot mindig része a widget állapothalmazának, míg a pyatspi.MULTI_LINEsosem.

9.4.4. TulajdonságokMivel sem a text, sem pedig az editable text interfész nem ad módot rá módot, a már említett tulajdonságok (attribute)közül olvasható ki a GtkEntry osztály set_paceholder_text függvénye révén beállított szöveget, placeholder-textkulcs alatt.

9.4.5. AkciókA gombokhoz hasonlóan a beviteli mezok is rendelkeznek egy rajtuk kiváltható akcióval, ez pedig a az alapértelmezettwidget aktiválása. Amennyiben a vonatkozó tulajdonság (activates-default) igaz, az Node objektumon az activateakció.

66

Page 72: Helló Window!

10. fejezet

Választáson alapuló adatbevitel

A szöveges adatbevitel leginkább kézenfekvo módjának tárgyalása már megtörtént. Ott a szöveget szabadon, illetve avalidációnak megfeleloen választhattuk meg, ezúttal viszont olyan adatok bevitelével foglalkozunk, ahol az értékkészletlényegesen szukebb, mint ami egy GtkEntry, vagy akár egy GtkSpinButton esetén szokásos. Az ebben a részbenbemutatandó widgetek akkor használatosak, ha a választható értékek száma kicsi, adott esetben csupán ketto, de nemtöbb, mint egy tucat.

10.1. Fogalmak

10.1.1. Beviteli mezok típusaiAhogy arról szó volt a szöveges bevitelrol szóló részben, speciális esetek speciális widgeteket kívánnak, lévén az ilyenhelyzetekben maguk a widgetek jóval inkább testreszabhatóak, mint egy általános helyzetnek is megfelelo szöveges bevi-teli mezo. A testreszabott widgetek megjelenése, beviteli módszereik könnyen alkalmazkodhatnak az igényekhez.

Bináris változók

A specializáció egyik véglete, amikor a bevinni kívánt adat mindössze két értéket vehet fel. Az ilyen bináris adatok szépszámban fordulnak elo, legyen szó akár egy opció ki-, vagy bekapcsolt értékérol, vagy más igaz hamis jellegu adatról.

10.1. ábra. Jelölonégyzet[9]

Az opciókat érdemes lehet csoportokba szervezni, ahol az egyes elemek valamilyen logika mentén együvé tartoz-nak, ugyanakkor az általuk felvett értékek függetlenek egymástól. Ilyen helyzetekben is alkalmazható ez a widgettípus,lehetoség szerint függoleges elrendezésben, nyolcnál nem nagyobb elemszámú csoportokban.

Egymást kizáró elemek

Kis elemszám. Olyan esetekben, amikor néhány lehetoség – ha mód van rá, nem több, mint nyolc – közül választ-hatunk oly módon, hogy az egyes esetek kölcsönösen kizárják egymást, célszeru a felhasználónak minden lehetoségetmegmutatni, megkönnyítendo a választást, viszont nem engednénk meg, hogy egyszerre egy elemnél többet ki lehessenválasztani.

Közepes elemszám. Amennyiben az elemek száma meghaladja az imént említetteket, a felhasználói felületen történoelhelyezés – úgy, hogy az elemek egyidoben láthatóak legyenek – már nehézkes. Ezen oknál fogva olyan megoldásalkalmazandó, ahol alapvetoen csak az aktuálisan kiválasztott elem látszik, ugyanakkor mód van az összes lehetoségáttekintésére is.

67

Page 73: Helló Window!

10.2. ábra. Rádiógombok[9]

10.3. ábra. ComboBox[9]

Választott érték

Nincs választás. Azon esetekben, ahol nem tudunk megfelelo alapértelmezett értéket adni indulásképp, lehetoség vanarra, hogy aktuálisan ne legyen egyetlen lehetséges elem sem kiválasztott állapotban. Ezt a helyzetet kezelnünk kell,vagyis az bevitt adatok validációjának erre a helyzetre is ki kell terjednie.

Inkonzisztens állapot. A bináris adatok bevitelére – egyúttal természetesen megjelenítésére is – szolgáló widgeteklehetséges állapotainak száma, eltéroen attól amit elsore gondolnánk, három. A két kézenfekvo állapoton túl létezik egyinkonzisztens állapot is, ami sem egyik sem másik állapotnak nem felel meg. Gyakorlatban ennek akkor van jelentosége,amikor eltéro értékeket egyszerre szeretnénk megmutatni.

10.4. ábra. Inkonzisztens állapot jelölonégyzetek esetén[6]

10.2. Alapmuveletek

10.2.1. LétrehozásA létrehozás nem rejt magában különösebb bonyodalmakat, hiszen maguk a widgetek is rendkívül egyszerunek mondha-tók, ugyanakkor egy-egy gondolat erejéig mégis érdemes megállni.

GtkCheckButton

Az ehhez a típushoz tartozó widgetek létrehozásánál érdemben csak egy paramétert kell megadnunk, mégpedig a jelölo-négyzet szövegét. A szöveggel szemben azonban támasztunk néhány követelményt, amiket célszeru a felhasználói felületkészítése közben figyelembe venni. Az elso kifejezetten a szövegre vonatkozik, ami szerint a szöveg elso szavát nagy-,míg a többit kisbetuvel kezdjük. A másik csak közvetetten vonatkozik a szövegre magára, annyi lenne az elvárás, hogyminden jelölonégyzet állítható legyen közvetlenül billentyuzetrol is, amit a címkéknél már említett módszerrel (mnemo-nic) érhetünk el.

68

Page 74: Helló Window!

GtkWidget*gtk_check_button_new(void);

GtkWidget*gtk_check_button_new_with_label(constgchar*label);

GtkWidget*gtk_check_button_new_with_mnemonic(constgchar*label);

CheckButton();

explicitCheckButton(constGlib::ustring&label

boolmnemonic=false);

classCheckButton(Gtk.ToggleButton):

def__init__(self,label=None,stock=None,use_stock=False,use_underline=False,**kwds):

Kódrészlet 10.1. CheckButton létrehozása

A mnemonic, valamint a use_underline paraméterek célja azonos, a címkéknél korábban említett módszer – misze-rint a közvetlen elérést biztosító billentyut aláhúzás jel (_) elozi meg – engedélyezésére szolgálnak. A use_underlinetulajdonság hamis értéke esetén az aláhúzás jelként látszódik, tehát a közvetlen eléréshez a use_underline tulajdonságértékének igaznak kell lennie.

A Python nyelvu változat a konstruáláskor lehetoséget ad a beépített ikonok (stock icon) megadására, akár a labelparaméter értékeként, amennyiben a use_stock paraméter értéke igaz. Amennyiben az utóbbi érték hamis a labelparaméter értéke szó szerint jelenik meg, akkor is ha az egy beépített ikon neve is egyben. A

GtkRadioButton

Mivel a rádiógomb típus közvetlen leszármazottja az imént tárgyalt GtkCheckButton típusnak, nem meglepo módon létre-hozása is nagyban hasonlít a jelölonégyzetek létrehozásával, legalábbis ami a címke szövegét illeti. Mivel ezt a widgetetcsoportban használjuk – lévén egymást kizáró lehetoségek közül választunk – a csoportot valamilyen módon meg kelltudnunk adni.

A gyakorlatban a rádiógomb-csoportok kódból történo létrehozása úgy történik, hogy az elso rádiógombot úgymondönmagában, csoport nélkül hozzuk létre, a további rádiógombok esetén pedig az elso elem létrejöttekor automatikusanelkészült csoportot használjuk fel. A csoportot a get_group függvény segítségével kérdezhetjük le és a visszakapottcsoportot adhatjuk át az újabb rádiógombok létrehozásakor paraméterként. Ha a rádiógomb már korábban elkészült, vagya címke megadásával hoztuk létre, akkor a csoport természetesen utólag is átállítható (set_group).

GtkComboBoxText

Amennyiben az elemek túl nagy száma, vagy a helyhiány miatt úgy döntünk, hogy az egymást kizáró elemek közül nemrádiógombok csoportjából egyet megjelölve akarunk választani, akkor az elemeket listába szervezhetjük, ahol csak azaktuálisan kiválasztott elem jelenik meg a felületen. A létrehozás során csupán egy döntést kell meghoznunk, hogy azelem közötti választást lehetové tesszük-e úgy is, hogy az elem szövegét a felhasználó egy egysoros beviteli mezobe(GtkEntry) gépelhesse be.

GtkWidget*gtk_combo_box_text_new(void);

GtkWidget*gtk_combo_box_text_new_with_entry(void);

explicitComboBoxText(boolhas_entry=false);classComboBoxText(Gtk.ComboBox):def__init__(self, **kwds):

@staticmethod@defnew_with_entry():

Kódrészlet 10.2. ComboBoxText létrehozása

Amint látszik az egyes nyelvi változatok valamelyest eltérnek egymástól, ám az eltérés nem számottevo. Voltaképpena nyelvi különbözoségeknek megfelelo konstruktorok megvalósításokról beszélünk. A C++ változat esetén paraméterkéntvesszük át annak értékét, hogy a ComboBoxText tartalmazzon-e beviteli mezot, vagy csak választani lehessen az elemekközött, míg a C változat erre két függvényt ad, ahogy a Python is1.

10.2.2. KezelésGtkCheckButton

Billentyuzetrol történo használat. A használhatóság szempontjából fontos kiemelni, hogy minden jelölonégyzetnekadjunk meg közvetlen elérést biztosító billentyut (mnemonic key), mivel az nagyban könnyíti és gyorsítja a billentyuzetroltörténo használatot. Ez természetesen nem csak a GtkCheckButton, de az abból származó GtkRadioButton esetén isigaz.

GtkComboBoxText

Elemek hozzáadása. Elemek hozzáadására alapvetoen három mód kínálkozik, amibol ketto tulajdonképpen a harmadikspecializált esete. Adhatunk új elemet a már meglévo elemek elé (prepend_text), mögé (append_text), vagy beszúr-hatjuk az a már meglévo elemek közé (insert_text). Elobbi két esetben csupán a szöveget kell megadnunk, hiszen itt

1a Python változat __init__ függvénye is tartalmazhatna alapértelmezett paramétert a beviteli mezore vonatkozóan, viszont a C változathoz a kétkülön metódus (a konstruktor és egy statikus metódus) áll közelebb

69

Page 75: Helló Window!

az új elem pozíciója adott, míg a harmadik esetben paraméterként az új elem pozícióját is meg kell adni. Amennyibenpozícióként nullánál kisebb értéket adunk meg, akkor az új elem a meglévo elemek mögé, amennyiben nullát, akkor ameglévo elemek elé, míg pozitív szám esetén az adott pozícióra szúródik be.

Elemek törlése. Elemek törlésére szintén két mód kínálkozik. Egyrészrol törölhetünk egyes elemeket a pozíciójukalapján (remove), vagy törölhetjük egyszerre akár az összes elemet is (remove_all).

Kiválasztott elem. A kiválasztott elem szövegének lekérdezésére a get_active_text függvény szolgál, aminek mu-ködése kézenfekvonek tunhet, mégsem az, mivel az implicit módon függ a widget bizonyos állapotaitól. Egyrészrol, ha awidget rendelkezik saját beviteli mezovel (10.2), akkor minden esetben az abban lévo értékkel tér vissza, ha nem, akkor akiválasztott elem szövegével, illetve ha nincs kiválasztva elem, akkor a nyelvi változatnak megfelelo értékkel. Ez Pythonesetén egy None érték, a C esetén egy NULL pointer, míg a C++ esetén egy üres Glib::ustring objektum.

10.2.3. SzignálokGtkRadioButton

A GtkRadioButton, pontosabban szólva annak ose a GtkToggleButton mindössze egy szignállal rendelkezik (toggled),ami a gomb állapotának – vagy ha úgy tetszik értékének – megváltozása után váltódik ki, ahogy az a nevébol és a korábbanismertetett hasonló célt szolgáló szignálok muködésébol is következik. A szignál különösebb figyelmet csak a rádiógom-bok esetén érdemel, mivel itt midig két widget szignálja váltódik ki közvetlenül egymás után. Ez annak következménye,hogy az azonos csoportba tartozó rádiógombok kölcsönösen kizárják egymást, így ha az egyikük aktívvá válik, akkor akorábban aktív állapotú gomb elveszti aktív státuszát. A szignálok kezelésekor tehát ügyelni kell arra, hogy ezen duplánmegkapott szignálok esetén a megfelelo muveleteket csak egyszer hajtsuk végre.

10.3. Haladó muveletekGtkCheckButton

Inkonzisztens állapot. A GtkToggleButton típusból származó osztályok (GtkCheckButton, GtkRadioButton) ese-tén az inkonzisztens állapot (10.1.1) használatakor arra kell tekintettel lennünk, hogy ezen állapot beállítása csak a widgetmegjelenítésére van hatással, az aktív állapot értékét nem változtatja meg. Ezen túlmenoen az inkonzisztens állapot meg-szüntetésérol is magunknak kell gondoskodnunk, mivel azt a felhasználói interakció (egérkattintás, gyorsbillentyu, . . . )nem módosítja, annak hatására csak az aktív állapot változik, pontosan ugyanúgy váltakozik, mintha az inkonzisztensállapot be sem lenne állítva.

10.4. Tesztelés

10.4.1. KeresésAz ebben a részben tárgyalt widgettípusokra vonatkozó specifikus Predicate osztály nincs, így a findChild függvényesetén a GenericPredicate osztályt használhatjuk, ahol a jelölonégyzet esetén check box, rádiógomb esetén radiobutton, a GtkComboBoxText osztály esetén pedig combo box adandó meg a roleName paraméter értékeként.

10.4.2. StátuszokA tárgyalt widgetek (a GtkToggleButton és annak leszármazottjai) esetén gyakorlatilag egy érdemleges kérdés van,mégpedig a widget aktuális állapota. Ez egyrészrol lekérdezheto az ablakok kapcsán már ismertetett getState függ-vényt használva, a függvények pyatspi.STATE_CHECKED állandót átadva paraméterként, illetve a Dogtail Node objektumcheck attribútumának kiolvasásával. Az inkonzisztens állapotra a Dogtail egyelore nem ad burkolófüggvényt, így marada getState függvény hívása pyatspi.STATE_INDETERMINATE paraméterrel.

10.4.3. AkciókA jelölonégyzet és a rádiógomb értékének módosítására a click akciót használhatjuk, amit vagy az action interfészenkeresztül érhetünk el, vagy egész egyszeruen meghívjuk a Node objektum click függvényét.

70

Page 76: Helló Window!

10.4.4. ViszonyokA rádiógombok mindegyike rendelkezik egy olyan viszonnyal (relation), ami a többi rádiógombhoz köti, amikkel egy cso-portban van, beleérte magát a rádiógombot is aminek a viszonyáról szó van. A címkéknél már említett getRelationSet,illetve getTarget függvényekkel az összetartozó rádiógombok megkaphatóak, a pyatspi.RELATION_MEMBER_OF vi-szonytípust felhasználva.

10.4.5. InterfészekA GtkComboBox típus és annak leszármazottai – közöttük az ebben a részben tárgyalt GtkComboBoxText – aktív eleméneklekérdezésére és beállítására több lehetoség is kínálkozik.

Ezek közük egy a selection interfész. Az interfész azon widgetek kezelésére alkalmas, amik önmagukban több elemmegjelenítésére és a köztük történo választásokra használatosak, hiszen itt merül fel a kérdés, hogy a widget melyik elemevan aktuálisan kiválasztva, illetve hogy hogyan lehetne valamelyik elemet kiválasztani. Ahogy arról korábban szó esettaz egyes felületi elemek által alkotott fa szerkezet2 visszaköszön a teszteléshez használt objektumoknál is, sot esetenkéntmég ennél többrol is szó van.

A fa hierarchia legalsó szintjén álló widgetek ugyanis nem feltétlenül állnak a tesztelés során használt Node objektu-mok hierarchiájának legalján. Különösen igaz az önmagukban több elembol való választást lehetové tevo widgetek3 ese-tén, ahol a választható elemek valamilyen formában a Node elemeiként jelennek meg. Konkrétan a ComboBox widgetnekmegfelelo Node mindig rendelkezik egy menu, míg az a ComboBox elemszámának megfelelo mennyiségu menu itemtípusú (roleName) gyerekkel. Utóbbiak közül választhatjuk ki az aktív elemet a selection interfésszel.

A querySelection függvény által visszaadott interfész selectChild függvénye választja ki a megadott sorszámúgyerekelemet. Az aktuálisan kiválasztott elemek az interfész getSelectedChild függvénye segítségével kérdezhetoekle, ami paraméterként azt várja, hogy hányadik kiválasztott elemre vagyunk kíváncsiak. Ez az érték kisebb, mint aznSelectedChildren attribútum érték, ami az aktuálisan kiválasztott elemek számát tartalmazza. A Dogtail a Node ob-jektum részeként is kínál számos függvényt kijelölt elemek kezelésére. Egy adott gyerekelem kiválasztása a hozzá tartozóNode (menu item) select függvényének meghívásával lehetséges. Az isSelected attribútum az adott gyerekelemkiválasztott mivoltáról árulkodik, míg egy szülo kiválasztott elemei4 a selectedChildren attribútum révén érhetoek el.

Érdemleges körülmény, hogy a ComboBox widgethez tartozó Node objektum name attribútumának értéke az aktuálisankiválasztott elem értékét veszi fel, így a név csak körülményesen használható fel a Node megkeresésekor. Kézenfekvoencsak a roleName használható, ami viszont egyedileg csak akkor azonosít, ha az adott részfa alatt csak ez az egy ilyen elemtalálható, amit a részfa szukítésével tudunk eszközölni.

2a konténerek és elemeik egymással 1 : N viszonyban állnak, összességében fa szerkezetet alkotva3a rádiógombok is lehetové tesznek választást, de csak több azonos widget révén4GtkComboBox esetén az elmondottak alapján a hozzá tartozó Node gyereke – ahol a roleName menu – használandó e tekintetben

71

Page 77: Helló Window!

rész III

Összetett widgetek

72

Page 78: Helló Window!

11. fejezet

Táblázatos megjelenítés alapjai

Elérkeztünk a GTK egyik legrugalmasabb, legszélesebb köru funkciókkal felruházható, ugyanakkor legkomplexebb esz-közéhez. A widget objektumok együtteseként egyszerre teszi lehetové az adatok megjelenítését, bevitelét, az elemekkiválasztást, keresését, szurését, rendezését. Alapveto funkciója azonos típusú objektumok tulajdonságainak táblázatosformában történo megjelenítése, illetve kezelése, ahol a sorok az egyes objektumokhoz reprezentálják, míg az oszlopokaz objektum egy adott tulajdonságához tartoznak.

11.1. Fogalmak

11.1.1. Modell-nézet-vezérloA widget muködése a modell-nézet-vezérlo elvet követi, vagyis elválik egymástól az adatok tárolására, illetve megjeleníté-sére, valamint vezérlésére szolgáló réteg. Ez egyfelol rugalmasságot tesz lehetové a muködés tekintetében, a performanciajavulását is eredményezheti kello körültekintés mellett, ugyanakkor sajnálatos módon bonyolítja az implementációt. Eddi-giekben is találkoztunk már ezt a mintát követo widgetekkel (GtkEntry, GtkComboBoxText), ugyanakkor ezen widgetekesetén – bár a háttérben ugyan ez a minta húzódik meg – a használat során ennek hatásait elkerülhetjük. Most azonban anézet és a modell elválasztása kötelezo, hatási nem megkerülhetoek.

11.1.2. ModellA modell-nézet-vezérlo mintában a modell jelenti adattároló réteget, a GTK terminológia annyiban eltér, hogy a modellegy absztrakt osztály, ami a nézet irányába történo adatszolgáltatáshoz szükséges függvényeket, illetve szignálokat írjale. A tényleges adattárolóknak ezen absztrakt osztályt kell megvalósítaniuk, vagyis saját belso adatreprezentációjukatelrejtve a külvilág – ez esetben a nézetet megvalósító widget – felé a modell által definiáltak megfeleloen kell muködniük.A GTK a modell két implementációját is biztosítja, egyet a lista, egyet a fa szerkezettel rendelkezo adatok számára,ezzel együtt természetesen ha a saját adattárolónkat tartjuk performancia vagy egyéb okoknál fogva alkalmasabbnak,azt minden további nélkül használhatjuk, ha implementáljuk a GtkTreeModel osztály által leírt alkalmazásprogramozásifelületet (API).

11.1.3. NézetA nézet (view) a megjelenítést végzo réteg a modell-nézet-vezérlo mintában. A modellben tárolt adatokat, valamint azadatok változását jelzo szignálokat felhasználva végzi adatok megjelenítését, számos, az adatok típusára, formátumáravonatkozó paraméternek megfeleloen. Maga a nézet alakítja ki a táblázatos megjelenési formát, annak oszlopaival éssoraival, az ezek metszéspontjaiban található cellákkal, kezelve a megjelenítéskor azt a helyzetet, hogy a modellben tároltadatok mennyisége többszöröse lehet annak, amit a nézet egy idoben megjeleníteni képes.

Egy modellhez több nézet is rendelheto, vagyis ugyanazon adatok több különbözo formában is megmutathatjuk afelhasználói felület más-más részein, vagy ugyanott az eltérések hangsúlyozása végett. A nézet különbsége jelenthetipéldául, hogy más-más modellbeli oszlopok megjelenítése történik, más sorrendben, vagy szurésnek, rendezésnek vetjükalá a elemeket, esetlen eltéro szövegformázási paramétereket alkalmazunk. Mindezt úgy tehetjük meg, hogy az adattárolónem kell lemásolnunk, vagyis az applikáció memóriaigénye nem no.

11.1.4. Elemek eléréseMaga a modell definiálja az adattároló elemeihez – sorok és ezen belül cellák – hozzáférését, illetve az adattároló eleme-inek bejárását lehetové tevo eszközöket, valamint módszereket. Ezen eszközök közül három is rendelkezésünkre áll azadattároló egyes elemire való hivatkozásra, amik mind alkalmazási területükben, mind muködésükben eltérnek egymástól.

73

Page 79: Helló Window!

Bejáró. Az elso és egyben a leggyakrabban használt ezek közül a bejáró (iterator), ami az adattároló egy eleméhez– legyen az lista vagy fa – ad hozzáférést. A bejárón keresztül közvetlenül írhatjuk vagy olvashatjuk a vonatkozó soregyes elemeit, celláit. A bejárók erosen kötodnek a hivatkozott adattárolóhoz, egy adott modellre vonatkozó bejáró másmodell esetén nem használható fel. A bejáró az adott elemre (itt sor) nézve csak addig bír érvényes hivatkozással, amíg azadattároló szerkezete változatlan. Egyszerubben fogalmazva egy bejáró csak addig érvényes, amíg az adattárolóhoz újabbelemet nem adunk (amivel egyébiránt újabb bejáró jön létre), vagy veszünk el belole. A tárolt adatokhoz való hozzáféréstermészetesen a szerkezetet nem érinti, így az sem aktuálisan hozzáférést biztosító, sem más bejárókat nem érvénytelenít.

Útvonal. A modell egy eleme megadható a modellben elfoglalt pozíciójával is, ezt teszi az útvonal (path) leírásáraszolgáló objektum. A modellben elfoglalt pozíció szó szerint az jelenti, hogy az adott elem hányadik az eleme sorában,a C nyelvi hagyományoknak megfeleloen nullától kezdve a számlálást. Ha nem listában, hanem fában gondolkodunk,akkor ez annyi elemet jelent amilyen mélyen (depth) az adott sor elhelyezkedik a fában. Az elem sorozata pedig a fagyökerétol indulva az adott részfában a sor indexe.

11.1. ábra. Útvonal fa szerkezeten belül[6]

Az ábrán a William Field értéket tartalmazó sor útvonalát az 1,0,2 sorozat írja le, hiszen a legfelso szinten lévomásodik elem, elso gyerekének, harmadik gyermeke. Az útvonal nem kötodik a modellhez, így egy útvonal bármelyikmodellel együtt értelmezheto. Ha az útvonalnak megfelelo sor létezik az adattárolóban, akkor visszakaphatjuk a mutatottsorhoz tartozó bejárót, amin keresztül már hozzáférhetünk az adatokhoz. Mivel nincs kötodés a modellel, maga az útvonalobjektum mindig érvényes, csak egy adott modell esetén nem garantált, hogy az útvonalon található sor.

Referencia. A referencia (row reference) olyan hivatkozás az adattároló egy sorára, ami mindaddig érvényes marad,amíg maga a sor létezik. A bejáróval ellentétben tehát hiába változik az adattároló szerkezete – hozzáadás, vagy törlésrévén – a referencia érvényes marad, eltekintve attól ha épp az adott sort töröltük. A referencia hasonlóan a bejáróhozkötodik az adattárolóhoz, csak egy adott modellen érvényes. Az érvényesség lekérdezheto, az objektum mind útvonallá,mind pedig bejáróvá átalakítható. Ez utóbbi révén a modell adataihoz való hozzáférés is biztosított. A kényelemnek, amitez a fajta muködés ad, a performancia oldalán kell az árát megfizetnünk, így ezen elemek nagy mennyiségu használatasebességbeli problémákhoz vezethet.

11.2. Alapmuveletek

11.2.1. LétrehozásModell

A GTK által implementált – listák kezelésére használható – adattároló (GtkListStore) legegyszerubben úgy képzelhetoel, mint egy táblázat. Ezen táblázat alapvetése, hogy egyforma típusú elemek különbözo példányainak soronkénti megje-lenítésére szolgál, jelentsenek az elemek bár fájlokat, elektronikus leveleket, vagy éppen tuzfalszabályokat. A sorokbanaz egyes elemek tulajdonságai szerepelnek, azaz minden oszlop típusa azonos, amik soronként eltéro értékeket vehetnekfel.

Ennek megfeleloen a létrehozás során meg kell adnunk az oszlopok számosságát, illetve az egyes oszlopok típusát.Ez lényegében nem jelent komoly feladatot, ugyanakkor viszont ez egyes nyelvi változatok között komoly eltérésektapasztalhatóak, ami leginkább a nyelvi sajátosságoknak köszönhetoek.

74

Page 80: Helló Window!

GtkListStore*gtk_list_store_new(gintn_columns,

...);

GtkListStore*gtk_list_store_newv(gintn_columns,

GType*types);

staticGlib::RefPtr<ListStore>create(constTreeModelColumnRecord&columns);

classListStore(Gtk.ListStore,TreeModel,TreeSortable):

def__init__(self, *column_types):

Kódrészlet 11.1. GtkListStore létrehozása

Ahogy látszik a (C nyelvi változat elvárja tolünk, hogy megadjuk hány oszlopa lesz az elkészítendo adattárolónknak(store). A változó hosszúságú paraméterlistájú függvénynél csak akkor van módunk hívottként rájönni, hogy mikor vanvége a típusok felsorolásának, ha van olyan érték, amit az átvett típus (GType) elemei nem vehetnek fel. Hasonló ahelyzet az átadott tömb kapcsán is. Így viszont nem marad más választásunk mint az oszlopok számát elore közölni. Amásik két nyelvi változat kézenfekvo módszereket ad egy konténer méretének, illetve az átadott paraméterek számánaklekérdezésére így annak külön megadására explicit módon nincs szükség.

Külön említést érdemel a függvények visszatérési értéke, azaz az adattároló nyelvi változatnak megfelelo reprezentá-ciója. Mindhárom esetben igaz, hogy a visszakapott érték egy referencia-számlált objektumok, lévén a GtkListStore aGObject típusból származik, viszont ez az egyes nyelveken különbözo módokon jelenik meg. A C változat egy mutatóvaltér vissza, ahogy az szokás a widgetek esetén is, a különbség mindössze annyi, hogy a specifikus típusra kapunk mutatót,ahogy ez a többi GObject típusból származó osztály esetén is igaz lesz. Itt a referencia növelése csak akkor történik meg,ha azt explicit módon kérjük, vagy azt adattárolóval hívott objektum – például a nézet – maga megteszi. A C++ változategy intelligens mutatóval (smart pointer) tér vissza, ami másolásakor implicit módon maga biztosítja a referenciaszámnövelését, így ezzel nekünk nem kell törodnünk. A Python nyelvi szinten rendelkezik referencia-számlálási megoldással,így itt nem szükséges az objektumok allokációjának és felszabadításának részleteivel törodnünk.

A referencia-számlálás már csak azért is fontos, mert egy modellre több nézetet is csatolhatunk, amiknek egyenkéntnövelik a referencia értékét, hogy biztosítsák az adattároló objektum fennmaradását addig, amíg a nézet ezt használja. Azadattároló nem widget, vagyis az alapfogalmak között említett ”lebego” referencia szerinti muködés itt nincs érvényben,következésképp az adattároló referenciaszámának értéke egy lesz létrehozáskor és már ez elso nézet modellhez valócsatlakoztatása azt eredményezi, hogy referenciaszám eggyel no. Így tehát minden csatolt nézet megszunése után azadattároló referenciaszámának értéke újra egy lesz, tehát az nem szunik meg automatikusan. Ha azt szeretnénk, hogy amodell a nézetek megszuntével maga is megszunjön, akkor a nézet(ek) csatolása után magunknak kell csökkentenünk areferencia értékét. Mivel a referencia-számlálás funkcióját a GObject típus adja, ennek unref függvényét kell hívnunk.

typedefenum{

COL_ID,COL_NAME,NUM_COLS

}ModelColumns;

store=gtk_list_store_new(NUM_COLS,G_TYPE_UINT,G_TYPE_STRING);

structModelColumns:publicGtk::TreeModel::ColumnRecord{ModelColumns(){add(col_id);add(col_name); }

Gtk::TreeModelColumn<unsignedint>col_id;Gtk::TreeModelColumn<Glib::ustring>col_name;

};

ModelColumnsColumns;store=Gtk::ListStore::create(Columns);

classModel(object):

COL_ID = 1COL_NAME= 2NUM_COLS= 3

store=Gtk.ListStore(int,str)

Kódrészlet 11.2. Példa GtkListStore létrehozására

A gyakorlatban minden nyelvi változat esetén kialakult a nyelv sajátosságaihoz idomuló módszer, amivel a késobbi-ekben a tároló könnyel kezelhetové válik, aminek alapjait értelemszeruen a létrehozáskor tesszük le.

A C nyelvi változat esetén egy sorszámozott típus (enum) tud rajtunk leginkább segíteni, ahol a típus egyes elemeineknevei utalnak funkciójukra. Az elotagként használhatunk a tárolóra utaló (pl: STORE_NAMES) szöveget, majd folytathatjuka COL rövidítéssel, ami az érték oszlopsorszám mivoltát jelenti, míg az utótag az oszlopban tárolt adatra vonatkozhat. Ezzelolyan nevet kapunk, amit a késobbiekben a tárolóba való írás, illetve az onnan való olvasás esetén öndokumentálóvá teszia kódot, ellentétben a puszta sorszámokkal, amik magic numbert alkotnak a kódban.

A C++ változat maga ad egy osztályt (TreeModel::ColumnRecord) ami az oszlopok leírására szolgál. A gyakorlat-ban származtatás révén érjük el, hogy tároló konstruktora az épp elkészítendo objektumra specifikus oszlopleírót kaphas-son. Ebben a származtatott osztályban nincs más dolgunk – ahogy az a példában is látszik –, mint publikus tagként – mivela késobbiekben ezen objektum adattagjait használjuk az írás, olvasás muveletnél az oszlopok azonosítására – felsorolniés ezeket a konstruktorban hozzáadni az objektum privát konténeréhez (add). Ezen konténer mérete lekérdezheto a sizefüggvénnyel, aminek révén a tároló is tudni fogja voltaképpen hány oszloppal is rendelkezünk.

A Python változat újfent a legegyszerubb, de legalábbis a legrövidebb, így bizonyos értelemben ezzel járunk a legjob-ban. Újdonságot a másik két változathoz képest nem tartalmaz, megjegyezni is csak annyit érdemes vele kapcsolatban,hogy az oszlopokhoz használt saját osztály helyett ha származtatunk a GtkListStore típusból, akkor azon belül defini-álhatjuk a névvel azonosított oszlopsorszámokat. A C változathoz hasonlóan itt is érdemes különös figyelmet fordítaniarra, hogy az oszlopok sorszámai és a típusok helyes sorrendben legyenek, különben futás közben váratlan és nehezenvisszanyomozható hibákba ütközhetünk.

75

Page 81: Helló Window!

Nézet

A nézet (view) létrehozása önmagában egy rendkívül egyszeru feladat, hiszen ez akár paraméter nélkül is lehetséges, vagyparaméterként átadható egy modell, ami természetesen késobb is beállíthatunk, vagy átállíthatunk (set_model).

GtkWidget*gtk_tree_view_new(void);

GtkWidget*gtk_tree_view_new_with_model(GtkTreeModel*model);

TreeView();

explicitTreeView(constGlib::RefPtr<TreeModel>&model);

classTreeView(Gtk.TreeView,Container):def__init__(self,model=None):

Kódrészlet 11.3. GtkTreeView létrehozása

A teljes modell-nézet-vezérlo szerkezet nézet részének csak egy része maga a GtkTreeView, ugyanakkor a feladatérdemi részét nem ez, hanem az oszlopok hozzáadása jelenti. Amint az a modell felépítésébol következik, az oszlopokazonos típusú értékeket tartalmaznak, tehát azonos eszközzel is kell oket megjelenítenünk. Ez az eszköz az oszlop – ahozzá tartozó osztály pedig a GtkTreeViewColumn –, ami gondoskodik az oszlop, illetve fejlécének megjelenítésérol, azoszlop átméretezhetoségérol, nem gondoskodik azonban a sorok és oszlopok metszéspontjaként adódó cellák megjelení-tésérol, ezt egy külön osztály végzi.

GtkTreeViewColumn*gtk_tree_view_column_new(void);

GtkTreeViewColumn*gtk_tree_view_column_new_with_area(GtkCellArea*area);

GtkTreeViewColumn*gtk_tree_view_column_new_with_attributes(

constgchar*title,GtkCellRenderer*cell,...)G_GNUC_NULL_TERMINATED;

TreeViewColumn();

explicitTreeViewColumn(constGlib::ustring&title);

TreeViewColumn(constGlib::ustring& title,CellRenderer&cell);

template<classT_ModelColumnType>TreeViewColumn(

constGlib::ustring& title,constTreeModelColumn<T_ModelColumnType>&column);

classTreeViewColumn(Gtk.TreeViewColumn):def__init__(self,

title=’’,cell_renderer=None,**attributes):

Kódrészlet 11.4. GtkTreeViewColumn létrehozása

Ahogy látszik GtkTreeViewColumn létrehozható akár paraméterek megadása nélkül is, bár ennek csak akkor nincssok értelme, hiszen a létrehozást követoen úgy is megadjuk a paramétereket, amikre a létrehozáskor is lehetoségünklett volna. Az oszlop kapcsán kézenfekvo paraméter az oszlop fejlécének szövege (title), a cella kirajzolását végzoobjektum (renderer). Ezt a rajzolást végzo objektum paraméterei követik, amik közül messze a legfontosabb, hogy amodell melyik oszlopában található az adat, aminek a képernyon való megjelenítése a feladat lesz.

Az egyes nyelvi változatokban léteznek függvények (insert_column, insert_column_with_attributes) az iméntipéldában látott paramétersorrenddel, amik egyrészt elkészítik a GtkTreeViewColumn, illetve GtkCellRenderer objek-tumokat, majd az oszlopot hozzá is adják nézethez, az elso paraméterként megadott pozícióra. Az oszlopok sorrendjetehát a hozzáadásakor már meghatározottá válik, hiszen az egymás után a nézethez hozzáadott (append_column) oszlo-pok – hasonlóan a konténerekbe helyezett widgetekhez – egymás mellett, balról jobbra helyezkednek majd el, a beszúrtoszlopoknál (insert_column) a helyet egy kötelezoen megadandó paraméter határozza meg.

renderer=gtk_cell_renderer_text_new();gtk_tree_view_insert_column_with_attribute(

GTK_TREE_VIEW(view),-1,"ID",renderer,"text", COL_ID,NULL);

renderer=gtk_cell_renderer_text_new();gtk_tree_view_insert_column_with_attributes(

GTK_TREE_VIEW(view),-1,"Name",renderer,"text", COL_NAME,NULL);

view.append_column(

"ID",

Columns.col_id);

view.append_column(

"Name",

Columns.col_name);

renderer=Gtk.CellRendererText()column=Gtk.TreeViewColumn(

"ID",renderer,text=Model.COL_ID)

tree.append_column(column)renderer=Gtk.CellRendererText()column=Gtk.TreeViewColumn(

"Name",renderer,text=Model.COL_NAME)

tree.append_column(column)

Kódrészlet 11.5. Példa GtkTreeView létrehozására

Az oszlopok hozzáadásának két fontos kérdését segít tisztázni a fenti példa. Az elso az oszlop pozíciója. Mint arrólszó esett az append_column függvény minden eseteben az eddigi oszlopok után fuzi az új oszlopot, ahogy ez a nevébolis következik, míg az insert_column a megadott pozícióra szúrja be azt, kivéve ha a pozíció −1, mert ez esetben azappend_column függvénnyel azonos módon muködik. Visszatérési értékük az új oszlop pozíciója ami alapján késobb anézet vissza tudja adni nekünk magát az oszlopot. Elkerülendo, hogy ez érték kompromittálódjon, célszeru az oszlopokatmindig a sor végére hozzáfuzni.

A másik kérdés, hogy miként szerez tudomást a GtkCellRenderer objektum, hogy honnan szerzendo be a megje-lenítendo adat, illetve mik lennének annak megjelenítési paraméterei. Az append_column, illetve az insert_columnfüggvényeknek átadott utolsó két paraméter nem az oszlopnak, hanem a GtkCellRenderer objektumnak szóló paramé-terek és azt mondják meg, hogy a létrejött GtkCellRendererText objektum az egyes sorok esetén az adattárló mely

76

Page 82: Helló Window!

oszlopából vegye a saját text paraméterének értékét. Nem meglepo módon a GtkCellRendererText objektum a textattribútumának értékét jeleníti meg a sor és az oszlop által meghatározott cellában. Látható, hogy számok megjelenítéseépp ugyanezt a metódust használjuk ami azért lehetséges, mert a modellbol egy általános adattárolóba (GValue) olvassukki a cella értékét, amit aztán karakterlánc formára konvertálva veszünk ki ebbol a tárolóból. Amennyiben az átalakítássikeres, a GtkCellRendererText is használható.

A modell egy cellájában tárolt érték alapján történo megjelenítés nem csak azt teszi lehetové, hogy a megjelenítendoszöveget adjuk meg a modell valamelyik oszlopában – ami egyrészrol értelemszeru, másrészrol a muködéshez elégedetlenis – de például annak elotét-, vagy háttérszínét (foreground, background) is.

Különbözo adattípusok más-más megjelenítést igényelnek, ami egyúttal más GtkCellRenderer osztály is jelent. Ké-pek megjelenítésére a GtkCellRendererPixbuf osztály illetve annak pixbuf tulajdonsága, gbooleann értékek jelölonégyzet-szeru megjelenítésére a GtkCellRendererToggle, illetve annak active tulajdonsága használható. Amennyiben folya-matindikátort jelenítenénk meg a cellában, annak értékét a value, szövegét pedig text tulajdonság kell tartalmazza. EzenGtkCellRenderer osztályok, illetve az említett paramétereik épp így adhatóak meg, mint ahogy a GtkCellRenderer ésannak text attribútuma a fenti példában.

11.2.2. KezelésModell

Hozzáadás. Az adattároló feltöltésének elso lépése az új sor hozzáadása. Ennek legegyszerubb módja az, ha az eddigielemek mögé egy új elemet szúrunk be. Ez minden további nélkül megteheto az adattároló (store)1 bejárót visszaadóappend függvényével. Természetesen éppúgy lehetoség van a meglévo elemek elé (prepend), vagy adott pontra történobeszúrására (insert), bár ezek gyakorlati jelentosége kisebb. Fák esetén ugyanezen függvények ugyanúgy léteznek, egyeltéréssel. Ez pedig az, hogy a elso paraméterként a szülo elemhez tartozó bejárót kell megadni, illetve ha legfelso szintuelemet szeretnénk, akkor a nyelvi változatnak megfelelo null értéket kell átadni.

voidgtk_list_store_append(GtkListStore*list_store,

GtkTreeIter *iter);

voidgtk_list_store_insert_before(GtkListStore*list_store,

GtkTreeIter *iter,GtkTreeIter *sibling);

iteratorappend();

iteratorinsert(

constiterator&sibling);

defappend(self,

row=None):

definsert_before(self,

sibling,row=None):

Kódrészlet 11.6. Elem hozzáadása listához

voidgtk_list_store_append(GtkListStore*list_store,

GtkTreeIter *iter,GtkTreeIter *parent);

voidgtk_list_store_insert_before(GtkListStore*list_store,

GtkTreeIter *iter,GtkTreeIter *parent,GtkTreeIter *sibling);

iteratorappend(

constTreeNodeChildren&parent);

iteratorinsert(

constiterator&sibling);

defappend(self,

parent,row=None):

definsert_before(self,

parent,sibling,row=None):

Kódrészlet 11.7. Elem hozzáadása fához

A különbség ez egyes nyelvek között talán nem látványos, de egy ponton mindenképpen számottevo. A C++, illetvePython nyelvu megvalósítás az új bejárót visszaadja, míg a C megoldásában egy kimeneti paraméterben kapjuk vissza. Ezelsore csak formai eltérésnek tunhet, de ennél azért mélyebb oka van. Az elobbi két nyelv esetén a bejárok objektumok,lévén az utóbbi nyelv esetében errol nem lehet szó, mindössze egy struktúra. Az paraméterként elvárt mutató, mivelkimeneti paraméterrol beszélünk, egy létezo bejáró struktúrára kell mutasson, amit az append, insert_before és egyébfüggvények töltenek ki a megfelelo értékekkel, amit ezt követoen vehetünk az adatok eléréséhez használatba.

Feltöltés. Az elem hozzáadására használt függvények deklarációjából látszik, hogy a Python nyelv esetén lehetoség vanegy lépésben hozzáadni egy új elemet az adattárolóhoz és egyszersmind megadni az új sor egyes oszlopokba eso értékeit,a row paraméter révén. A C illetve C++ változat ezt két lépésben végzi, eloször hozzáadja az új elemet az adattárolóhoz,majd csak ezt követoen ad értéket a celláknak. Ez nem teljesen fedi a valóságot, mivel a C nyelv esetén is van mód azegy lépéses megoldásra, ami nem is meglepo, hiszen a Python változatban sem lenne különben erre lehetoség. A C nyelvifüggvény neve insert_with_values, ami paraméterezését tekintve összefuzése az insert és a feltöltésre szolgáló setfüggvényekének. A C++ esetén a valóság megegyezik a látszattal, vagyis csak a két lépcsos megoldás létezik, illetve ahelyzet még ettol is egy kicsikét bonyolultabb.

1megjegyzendo, hogy az elem hozzáadását nem a modell definiálja, ez csak olvasáshoz ad interfészt

77

Page 83: Helló Window!

voidgtk_tree_store_set(GtkTreeStore*tree_store,

GtkTreeIter *iter,...);

voidgtk_tree_store_set_value(GtkTreeStore*tree_store,

GtkTreeIter *iter,gint column,GValue *value);

classTreeRow:publicTreeIter{

template<classColumnType>voidset_value(

intcolumn,constColumnType&data)const;

template<classColumnType>voidset_value(

constTreeModelColumn<ColumnType>&column,constColumnType&data)const;

template<classColumnType>inlineTreeValueProxy<ColumnType>operator[](constTreeModelColumn<ColumnType>&column);};

classTreeModel(Gtk.TreeModel):defset_row(self,

treeiter,row):

classTreeModelRow(object):defset_value(self,treeiter,column,value):

def__setitem__(self, key,value):

Kódrészlet 11.8. Sor értékeinek beállítása

Értékadás cellának. A értékadó függvények közös nevezoje – vagyis ami minden nyelvi változat esetén elérheto –az a módszer, ami egyszerre csak az adott sor adott oszlopában található értéket állítja be (set_value). Az eltérés abbanáll, hogy míg a GTK+ esetén az érték beállítását végzo függvény az adattárolóhoz kötodik, addig a C++ és Python eseténa sor reprezentálására szolgáló külön osztályok (TreeRow, TreeModelRow) keresztül valósul meg az érékadás. Ez perszeleginkább formai különbségeket eredményez a három nyelv esetén a gyakorlati muködés szempontjából nincs számottevoeltérés. A C változatban alkalmazott GValue osztály részleteit – mivel a gyakorlatban nem ez a módszer használatos –a késobbiekre halasztjuk. Elöljáróban csak annyit, hogy a GValue egy olyan adattároló megoldás, ami képes különbözotípusú adatokat tartalmazni, míg erre a hagyományos típusok (int, double, . . . ) erre nem képesek. Felhasználásukáltalában a generikus algoritmusokban történik, ahol csak futásidoben ismert az adott oszlop típusa. Mindhárom nyelvesetén az értéket beállító függvénynek paraméterként megadandó az oszlop sorszáma, illetve maga a beállítandó érték,pontosabban GTK+ esetén annak címe, míg gtkmm esetén annak referenciája. Ehhez képest az indexelo operátor haszná-lata csak annyiban más, hogy a sort reprezentáló objektum az adott oszlop számával indexelve a konkrét cellát érjük el,majd ennek adunk értéket. A gtkmm indexelo operátorra kétféleként is implementált, az egyik esetén numerikus adhatjukmeg az oszlop sorszámát, míg a másik esetben a modellt leíró osztály (ModelColumns) megfelelo adattagjának (col_id)segítségével, ahol az olvashatóság szempontjából mindenképpen ez utóbbi preferált.

GValueid =G_VALUE_INIT;g_value_init(&id,G_TYPE_INT);g_value_set_int(&id,42);

gtk_list_store_set_value(store,&tree_iter,COL_ID, &id);

row.set_value(Columns.col_id,42);

row.set_value(0,42);

row[Columns.col_id] =42;

row.set_value(Model.COL_ID,42)

row[Model.COL_ID] = 42

Kódrészlet 11.9. Példa sor egy értékének beállítására

Értékadás teljes sornak. Teljes sorok egyideju kitöltésére is van lehetoség, azonban csak a C, valamint a Pythonváltozat esetén, ami komoly hiányossága a C++ változatnak. Ezen hiányosságok okozta nehézségekkel az egyszerubbfelhasználás esetén nem találkozunk, így ezzel csak késobb foglalkozunk. A másik két nyelv jócskán eltéro módszerekkeléri el az egy lépéses beállítást és mindkét esetben lehetoség van részleges beállításra is.

gtk_list_store_set(store,&tree_iter,COL_ID, 42,COL_NAME, "JohnDoe",-1);

store.set_row(tree_iter,[42,’JohnDoe’])

store[tree_iter] = [42,’JohnDoe’]

Kódrészlet 11.10. Példa teljes sor beállítására

A GTK+ egy változó paraméterhosszúságú paraméterlistájú függvénnyel oldja meg a problémát, ahol a paraméterek-ben az oszlop sorszámok és a beállítandó értékek párjai követik egymást, amit egy −1 érték zár. Erre azért van szükség,hogy a set függvény paramétereinek kiolvasásakor tudja, mikor fejeztük be a az oszlopsorszám-érték párok felsorolást.Erre a negatív érték kiválóan alkalmas, hiszen az oszlopok sorszámozása nullától kezdodik, vagyis negatív értéket nemvehet fel. Egyébiránt a záróérték elmaradása kellemetlen meglepéseket eredményez, mivel a set függvény az oszlopsor-szám helyén kapott negatív értékig olvassa a vermet, így erre érdemes különös figyelmet fordítani.

A Python változat ettol jóval kevésbé körülményes megoldást alkalmaz. A set_row függvénynek paraméterként, azindexelo operátor révén visszakapott TreeModelRow objektumnak pedig értékül adhatjuk a sor eleminek listáját, ahol a

78

Page 84: Helló Window!

sorrendnek követnie kell az adattároló létrehozásakor megadottakat, míg a C változatban szabadon választhatjuk meg asorrendet, hiszen az oszlopok sorszámaikkal azonosítottak.

Lekérdezés. A feltöltés kapcsán már tettünk említést a lekérdezésrol, hiszen az objektum-orientált megoldások sorokatreprezentáló osztályai (TreeRow, TreeModelRow) nemcsak íráshoz, hanem olvasáshoz is ad interfészt. Lényegében tehátezen esetek nem szorulnak különösebb magyarázatra, viszont a C nyelv sajátosságai, illetve az egyes nyelvi változatokkuszaságai miatt mégis érdemes kis kitérot tenni.

voidgtk_tree_model_get(GtkTreeModel*tree_model,

GtkTreeIter *iter,...);

voidgtk_tree_model_get_value(GtkTreeModel*tree_model,

GtkTreeIter *iter,gint column,GValue *value);

classTreeRow:publicTreeIter{

template<classColumnType>ColumnTypeget_value(constTreeModelColumn<ColumnType>&column)const;

template<classColumnType>inlineTreeValueProxy<ColumnType>operator[](constTreeModelColumn<ColumnType>&column)const;

};

classTreeModel(Gtk.TreeModel):

defget(self,treeiter,*columns):

classTreeModelRow(object):def__getitem__(self,key):

Kódrészlet 11.11. Sor értékeinek lekérdezése

Cella lekérdezése. Minden nyelvi változat ad lehetoséget az egyes cellák értékének lekérdezésére. A C változat egy-részrol az imént megismert GValue típus révén (get_value, másrészt egy változó hosszúságú paraméterlistájú függvényrévén, amit a teljes sorok lekérdezésénél részletezünk. A másik két változat a már említett indexelo operátort használja azelemek lekérdezésére, aminek csak formai változata gtkmm get_value függvénye.

Teljes sor lekérdezése. A GTK+ – a set függvényhez hasonlóan – oszlopsorszám és értékpárokat sorozatát várjaparaméterként, amit szintén egy −1 értéknek kell zárni. A különbség a két függvény között abból adódik, hogy itt azértékeket ki szeretnénk nyerni az adattárolóból, tehát a tároló adott oszlopának típusával azonos típusú változó címét kellátadnunk az oszlopsorszámot követoen, amibe az érték tárolásra kerül. Karakterláncok esetén (G_TYPE_STRING) – ami aleggyakoribb felhasználás – egy gchar * típusú változó címe adandó át a set függvénynek, amiben egy újólag allokáltmemóriaterület címét kapjuk vissza, amit a felhasználás végeztével fel kell szabadítanunk (g_free). Mutató típusú oszlop(G_TYPE_POINTER) esetén sem a tárolóba történo behelyezés, sem az onnan történo lekérdezéskor nem történik másolás– már csak azért sem mert a konkrét típusról, illetve annak másolásának módjáról a GTK+ mit sem tud –, a mutatottmemóriaterület menedzsmentjérol magunknak kell gondoskodnunk. A Python újfent egy nagyságrenddel egyszerubbmegoldást kínál, hiszen a get függvénynek átadhatjuk a lekérdezendo oszlopok listáját, ami a sor megfelelo oszlopaibanlévo értékek listájával tér vissza. Egyébiránt Python indexelo operátoránál megszokottak itt is érvényesülnek, vagyis akárintervallumokat is megadhatunk index gyanánt.

Vezérlo

A nézet vezérlése szempontjából számottevo jelentoséggel bír az elemek kiválasztása. Maga a nézet az általa megjelenítettsorok kiválasztásával kapcsolatos teendoket egy külön osztályra bízza (GtkTreeSelection), amit a nézet létrejöttévelegy idoben, automatikusan készít el, így ezzel nekünk nem kell törodnünk. Egy adott nézethez tartozó objektum – anevezéktannak megfeleloen – a get_selection függvénnyel kérdezheto le.

Módok. Az objektum viszonylag szuk, de annál hasznosabb funkcionalitáshalmazzal rendelkezik. Ezek közül mind-járt az elso az elemek kiválasztásának módja (mode), ami meghatározza, hogy egyszerre hány elem lehet kiválasztva. ASELECTION_NONE érték azt jelenti, hogy egy elem sem, a SELECTION_SINGLE, hogy legfeljebb egy elem, a SELECTION_BROWSE,hogy egy és csak egy, a SELECTION_MULTIPLE pedig, hogy akárhány elem lehet kiválasztva egy idoben.

Kiválasztott elemek kezelése. Elobbiek komoly hatással vannak a GtkTreeSelection osztály másik funkciójára, akiválasztott elemek kezelésére. Ez egyfelol jelenti a kiválasztott elemek hozzáadását (select_iter, select_path,select_all) és elvételét (unselect_iter, unselect_path, unselect_all), illetve az aktuálisan kiválasztott elemeklekérdezésére. A kiválasztás esetén ha a mód szerint legfeljebb egy elem lehet kiválasztva, ha volt korábban kiválasztottelem az elveszti kiválasztott mivoltát és az új elem nyeri el ezt míg, ha mód szerint több elem kiválasztása is lehetsé-ges, akkor a korábbiak mellet az új elem is kiválasztásra kerül. Ha egy elem kiválasztása sem engedélyezett, akkor afüggvényhívás hatástalan. A kiválasztás megszüntetésénél a módtól függetlenül megszunik az elem kiválasztott állapota.

79

Page 85: Helló Window!

kiválasztott elemek lekérdezése. Mindössze két függvény van, amivel a kijelölt elemek lekérdezhetoek. Az egyik(get_selected) csak abban az esetben muködik, ha legfeljebb egy elemet lehet kiválasztani és eredményeként egy bejá-rót kapunk vissza, míg a másik get_selected_rows minden esetben helyes eredményre vezet, de felesleges bonyolítástjelent, különösen a C változat esetén, ahol visszatérési értékét – a kijelölt elemek útvonalait tartalmazó listát – természe-tesen fel is kell szabadítani.

11.2.3. SzignálokVezérlo

A kiválasztást vezérlo osztály mindössze egy, de annál nélkülözhetetlenebb szignált (changed) nyújt, ami akkor váltódikki, amikor a kiválasztott elem mennyiségében változás áll be. A változást a rendszer csak egyszer jelzi, vagyis ha számoselem ki volt választva és meghívjuk az unselect_all függvényt, akkor mindössze egyszer váltódik ki a changed szignál,nem pedig annyiszor, ahány elem korábban kiválasztva volt. Természetesen ha egyesével (unselect_iter) csökkentjüka kiválasztott elemek számát, akkor ennek megfeleloen a szignál kiváltódására is többször kerül sor. Ne feledjük, ha akijelölt elemeket akarjuk törölni, akkor az egyes elemek törlése érvénytelenítheti a többi elemet. Törölni tehát mindig alentebb lévo elemeket kell eloször, mivel ez a fenti elemek útvonalát nem változtatja.

11.3. Tesztelés

11.3.1. KeresésElsoként az fontos tisztázni, hogy a GtkTreeView által megjelenített adatokra – függetlenül attól, hogy a hozzátartozómodell GtkListStore vagy GtkTreeStore – az akadálymentesítési réteg, mint táblázatra tekint, ami késobb számos mu-ködési sajátosságra lesz majd hatással. Elsoként arra, hogy miként érjük el magát a GtkTreeView objektumnak megfeleloNode objektumot, az GtkTreeModel által elérheto adatokat, illetve az oszlopok fejléceit. A nézetnek egy önálló Nodefelel meg, aminek roleName értéke table, vagy tree table, elobbi ha listáról, utóbbi ha fáról van szó. Ezen objektum-nak két típusú gyereke van. Az egyik az egyes oszlopok fejléceit reprezentáló Node, amiknél a roleName érték tablecolumn header. Ezen gyerekek mindig a nézetnek megfelelo Node elso n elemét jelentik, ahol az n a nézet aktuálisanlátható oszlopainak száma. Az ezeket követo gyerekek a cellákat reprezentálják, roleName értékük table cell, szá-mosságuk megfelel a látható oszlop és a sorok számának szorzatával. Az oszlopfejléceket és a cellákat reprezentáló Nodeobjektumok nevei felveszik azt az értéket, amit szövegesen tartalmaznak, így megtalálásuk ennek megfeleloen történhet.Ezen objektumok rejtenek tehát minden információt, illetve rajtuk keresztül érhetünk minden olyan funkciót amire a nézetvezérléséhez szükség lesz.

11.3.2. InterfészekNézet. A nézet Node objektuma implementálja az table interfészt, ami egyrészrol a táblázat általános adataihoz engedhozzáférést, mint amilyen a sorok száma (nRows), az oszlopok száma (nColumns), az oszlop fejléce (columnHeader).Másrészrol hozzáférhetünk az egyes cellákhoz x, illetve y pozíció alapján (getAccessibleAt).

Cellák. Az egyes cellák – már amik a GtkCellRendererText segítségével rajzolódnak ki – implementálják a text in-terfészt ennek megfeleloen az általuk megjelenített értékek a korábbiakban megismerteknek megfeleloen olvashatóak ki.A GtkCellRendererPixbuf osztályt használó cellák az image interfészen keresztül érhetoek el, így biztosítva informá-ciókat a megjelenített képekrol.

11.3.3. ÁllapotokA GtkCellRendererToggle cellák értékei a GtkCheckButton használatakor bemutatott pyatspi.STATE_TOGGLE stá-tusz alapján kérdezhetoek le.

11.3.4. AkciókA GtkCellRendererToggle cellák értékei a GtkCheckButton kapcsán megismert toggle akció révén módosítható.

80

Page 86: Helló Window!

A. Függelék

Licencelési feltételek

Ez a mu a Creative Commons Nevezd meg!-Így add tovább! licencének hatálya alatt áll.

A következoket teheted a muvel:.

• szabadon másolhatod

• terjesztheted

• bemutathatod és eloadhatod a muvet

• származékos muveket (feldolgozásokat) hozhatsz létre

Az alábbi feltételekkel:.

Nevezd meg! A szerzo vagy a jogosult által meghatározott módon fel kell tüntetned a muhöz kapcsolódó információkat(pl. a szerzo nevét vagy álnevét, a Mu címét).

Így add tovább! Ha megváltoztatod, átalakítod, feldolgozod ezt a muvet, az így létrejött alkotást csak a jelenlegivelmegegyezo licenc alatt terjesztheted.

Az alábbi figyelembevételével:.

Elengedés A szerzoi jogok tulajdonosának engedélyével bármelyik fenti feltételtol eltérhetsz.

Más jogok A következo jogokat a licenc semmiben nem befolyásolja:

• A fentiek nem befolyásolják a szabad felhasználáshoz fuzodo, illetve az egyéb jogokat.

• A szerzo személyhez fuzodo jogai

• Más személyeknek a muvet vagy a mu használatát érinto jogai, mint például a személyiségi jogok vagy azadatvédelmi jogok.

Jelzés Bármilyen felhasználás vagy terjesztés esetén egyértelmuen jelezned kell mások felé ezen mu licencfeltételeit.

Ez a Legal Code (jogi változat, vagyis a teljes licenc) szövegének közértheto nyelven megfogalmazott kivonata, teljesváltozata a Creative Commons oldalán érheto el.

81

Page 87: Helló Window!

Tárgymutató

öröklodés, 7, 23, 25átlátszatlan mutató, 7

ablakmodalitás, 29pozíció, 31típus, 30

popup, 29, 30, 33toplevel, 29, 30, 33

tranziencia, 30, 31ablakkezelo, 19

bezárás, 19minimalizálás, 19

Accerciser, 6ATK, 6AtkObject, 42, 59

függvényekset_image_description, 59set_name, 42

Atspi.Accessible, 59Atspi.AccessibleAtspi.Accessible

függvényekgetState, 43

Autoconf, 14Automake, 14automata tesztelés, 5Autotools, 14

billentyuenter, 33tab, 33

bináris csomag, 13

C, 5, 13C++, 4, 13Cairo, 4callback, 9

deb, 13dinamikus modulbetöltés, 4Dogtail, 6, 13

procedural, 19focus.application, 20GtkDemoTest, 20

tree, 19root.application, 20

utilsrun, 20

dogtail.ConfigtulajdonságoksearchBackoffDuration, 41searchCutoffCount, 41

dogtail.Nodefüggvényeksatisfies, 42

tulajdonságokname, 42roleName, 42

dogtail.Nodedogtail.NodefüggvényekfindChild, 42

dogtail.Predicate, 42függvényeksatisfiedByNode, 42

dogtail.Rootdogtail.Rootfüggvényekapplication, 42

dogtail.treefüggvényekapplications, 41application, 41

egységbezárás, 7

fókuszbillentyuzet, 33

fordítás, 18, 27fordítási ideju típusellenorzés, 8futtatás, 18, 27

Gail, 6GDK backend

Broadway, 3Wayland, 3X11, 3

GIMP, 2Git, 2GLib

függvényekprint, 22

makrókG_GNUC_PRINTF, 36NULL, 23OBJECT, 23

típusokgboolean, 23

GObject, 4GObject, 7–9

függvényeksignal_connect_data, 23unref, 75

makróksignal_connect, 23signal_connect_swapped, 24

82

Page 88: Helló Window!

tagfüggvényekconnect, 27connect_object, 27

GObject Introspection, 5gomb

jóváhagyó, 40menekülo, 40

grafikus szerverWayland, 3X11, 3

GTK, 13GTK, 3

fejlécfájlok, 17GDK, 3, 4GLib, 3

GIO, 4GModule, 4GObject, 4GThread, 4

kódolási konvenciók, 16formázás, 16nevezéktan, 16

Gtkfüggvényekfalse, 37init, 17main, 18main_quit, 24true, 37

konstansokRESPONSE_DELETE_EVENT, 39RESPONSE_NONE, 39WIN_POS_ALWAYS, 31WIN_POS_CENTER, 31WIN_POS_CENTER_ON_PARENT, 31WIN_POS_MOUSE, 31WIN_POS_NONE, 31

tagfüggvényekmain_quit, 25, 27

GtkBin, 29, 32, 45GtkBox, 29, 45

függvényekpack_end, 29, 47pack_start, 29, 47

gyerek tulajdonságokexpand, 32, 46–49fill, 32, 46–49pack-type, 46, 49padding, 47, 50, 51

tulajdonságokhomogeneous, 47–49spacing, 47, 50

GtkButtonfüggvényeknew_with_label, 23

szignálokclicked, 23–25, 27destroy, 27

GtkButtonBox, 29GtkComboBoxText

függvények

append_all, 69get_active_text, 70insert_all, 69new, 69new_with_entry, 69prepend_all, 69remove, 70remove_all, 70

GtkContainer, 33, 45függvényekadd, 47add, 48remove, 48remove, 48set_focus_chain, 33

GtkDialog, 29, 30, 34belso elemekaction_area, 29content_area, 29

függvényekrun, 31, 36, 39set_alternative_button_order, 40set_alternative_button_order_from_array, 40

szignálokresponse, 39

GtkEditable, 61függvényekset_editable, 62

GtkEntry, 33függvényekget_text, 62new, 61new_with_buffer, 61set_invisible_char, 62set_placeholder_text, 63set_text, 62set_visible, 62

tulajdonságokactivates-default, 33caps-lock-warning, 63invisible-char, 62placeholder-text, 63progress-fraction, 63progress-pulse, 63progress-pulse-step, 63text, 62visibility, 62

GtkEntryBuffer, 61, 63GtkGrid, 45

függvényekattach, 48

tulajdonságokcolumn-homogeneous, 46, 48, 50column-spacing, 46, 50row-homogeneous, 46, 48, 50row-spacing, 46, 50

GtkImage, 53GtkLabel, 53

függvényeknew, 55new_with_mnemonic, 55

83

Page 89: Helló Window!

GtkMessageDialog, 35GtkMisc

tulajdonságokxalign, 55xpad, 55yalign, 55ypad, 55

gtkmm, 4, 7GtkRadioButton, 70GtkSeparator, 29GtkSpinButton, 64

szignálokinput, 64output, 64

tulajdonságokvalue, 62

GtkStockId, 34GtkTextView, 33GtkToggleButton

szignáloktoggled, 70

tulajdonságokactive, 70inconsistent, 70

GtkTooltip, 54GtkWidget, 8

függvényekdestroy, 23–25, 47get_accessible, 42grab_focus, 33hide, 25, 31, 38show, 18, 31, 36, 39show_all, 36

szignálokdelete-event, 23, 25–27, 38, 39destroy, 24, 39unmap, 39

tulajdonságokcan-default, 33can-focus, 33expand, 48halign, 48, 49has-default, 33hexpand, 48valign, 48, 49vexpand, 48

GtkWindow, 29, 30függvényekfullscreen, 40get_position, 31iconify, 40maximize, 40move, 31new, 18, 23present, 36resize, 32set_default_size, 31set_deletable, 40set_geometry_hints, 32set_keep_above, 40set_position, 31

set_resizable, 32set_skip_taskbar_hint, 40set_transient_for, 31stick, 40

tulajdonságokdestroy-with-parent, 30gravity, 31modal, 29, 30, 36size-request, 31transient-for, 30type, 29

GUI, 3GUI eszközkészlet, 2

interfész, 10

konténer, 45size allocation, 49size request, 31

”lebego” referencia, 10, 18, 47, 75libsigc++, 4libsigc++

függvényekbind, 25mem_fun, 25ptr_fun, 25

típusokslot, 25

Libtool, 14licenc

LGPL, 2Linux, 2, 3

Mac OS X, 2, 3Main, 17

függvényekquit, 18run, 18

main loop, 9, 21, 36, 39Motif, 2

nyelv változatokgtkmm, 13PyGObject, 13

nyelvi változatokGTK+, 13

operációs rendszerLinux, 2, 3Mac OS X, 2, 3, 9Windows, 2, 3, 9

Pango, 4POSIX, 4PyGObject, 5, 7Python, 5, 13Python

oskill, 20

signalSIGTERM, 20

84

Page 90: Helló Window!

timesleep, 20

unittest, 19main, 20setUp, 20tearDown, 20TestCase, 20

referencia-számlálás, 10, 48, 75rpm, 13

SignalProxyBasetagfüggvényekconnect, 25

szülo-gyerek kapcsolat, 10szálkezelés, 4szignál, 21, 23

alapértelmezett eseménykezelo függvény, 23, 26blokkolása, 23, 26, 27eseménykezelo függvények sorrendje, 24eseménykezelo felkötése, 23, 25, 27

típuskényszerítés, 23, 24teszt szkript

futtatás, 20

widget, 8szignálok, 8delete-event, 18, 19

tulajdonságok, 8Windows, 2, 3wrapper, 4

85

Page 91: Helló Window!

Táblázatok jegyzéke

1.1 A GTK+, gtkmm, PyGObject összehasonlítása . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

86

Page 92: Helló Window!

Ábrák jegyzéke

2.1 Öröklodés hasonló funkciójú widgetek között . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82.2 Akadálymentesített szoftverek elérése[1] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

4.1 Minimális mintapéldák képernyoképi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

6.1 Billentyuzet fókusz jelzése a widgeten[6] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336.2 Gombok szokások sorrendje egy dialógusban[6] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336.3 Tipikus ablakszerkezetek[6] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346.4 Egy tipikus gombsor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346.5 Tipikus üzenetablakok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

7.1 Méretarányos és homogén elhelyezésének a konténeren belül[8] . . . . . . . . . . . . . . . . . . . . . . 497.2 Tér az elemek között és körül a konténerben[8] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

8.1 Címke[9] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 538.2 Kép[9] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 548.3 Buborék[7] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54

9.1 Szöveg és szám bevitele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 609.2 Ikonok használata szövegbeviteli mezoben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 639.3 Ikonok használata szövegbeviteli mezoben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63

10.1 Jelölonégyzet[9] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6710.2 Rádiógombok[9] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6810.3 ComboBox[9] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6810.4 Inkonzisztens állapot jelölonégyzetek esetén[6] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68

11.1 Útvonal fa szerkezeten belül[6] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74

87

Page 93: Helló Window!

Kódrészletek jegyzéke

4.1 Minimálisan szükséges kód GTK+, gtkmm, illetve PyGobject használata mellett . . . . . . . . . . . . . . 174.2 Minimálisan szükséges teszt tree, illetve procedural API használata mellett . . . . . . . . . . . . . . . . 195.1 Szignálok kezelése C, illetve C++ nyelven . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225.2 Szignálok kezelése Python nyelven . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266.1 Window létrehozása . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306.2 Dialog létrehozása . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316.3 Minimál példa GtkWindowhoz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326.4 Gombok hozzáadása GtkDialoghoz[6] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346.5 Tipikus gombsor hozzáadása GtkDialoghoz[6] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356.6 MessageDialog létrehozása . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 366.7 Szignálkezelo függvény perzisztens ablakhoz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376.8 Szignálkezelo függvény bekötése perzisztens ablakhoz . . . . . . . . . . . . . . . . . . . . . . . . . . . 376.9 Egyszerusített szignálkezelo függvény bekötése . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376.10 Egyszerusített szignálkezelo függvény bekötése . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386.11 Minimál példa GtkDialoghoz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396.12 Minimál példa GtkDialoghoz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396.13 Egyszerusített response-kezelo függvény bekötése . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396.14 Eseménykezelo felüldefiniálása saját widgetosztályban . . . . . . . . . . . . . . . . . . . . . . . . . . . 406.15 Alternatív gombsorrend beállítása . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 416.16 Alapveto elemek keresése teszt tree, illetve procedural API használata mellett . . . . . . . . . . . . . . . 416.17 Node keresése GenericPredicate, illetve findChild függvény segítségével . . . . . . . . . . . . . . . 426.18 Applikáció keresése application függvénnyel, illetve IsAnApplicationNamed objektummal . . . . . . 426.19 Ablak keresése specializált Predicate objektummal . . . . . . . . . . . . . . . . . . . . . . . . . . . . 436.20 Node státuszvizsgálatához szükséges függvény sémája . . . . . . . . . . . . . . . . . . . . . . . . . . . 438.1 Címke létrehozása . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 559.1 Az input és output szignálok felüldefiniálása az alapértelmezett muködést implementálva . . . . . . . . 649.2 Az input és output szignálok felüldefiniálása hónapok megjelenítéséhez . . . . . . . . . . . . . . . . . 6510.1 CheckButton létrehozása . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6910.2 ComboBoxText létrehozása . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6911.1 GtkListStore létrehozása . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7511.2 Példa GtkListStore létrehozására . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7511.3 GtkTreeView létrehozása . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7611.4 GtkTreeViewColumn létrehozása . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7611.5 Példa GtkTreeView létrehozására . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7611.6 Elem hozzáadása listához . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7711.7 Elem hozzáadása fához . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7711.8 Sor értékeinek beállítása . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7811.9 Példa sor egy értékének beállítására . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7811.10Példa teljes sor beállítására . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7811.11Sor értékeinek lekérdezése . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79

88

Page 94: Helló Window!

Irodalomjegyzék

[1] GNOME Accessibility Developers Guide. http://developer.gnome.org/accessibility-devel-guide/stable/.

[2] GNU Coding Standards. http://www.gnu.org/prep/standards/standards.html.

[3] Linux kernel coding style. http://www.kernel.org/doc/Documentation/CodingStyle.

[4] The Python Standard Library. http://docs.python.org/py3k/library.

[5] FSF.hu Alapítvány. Szabad szoftver pályázat 2011. http://fsf.hu/2011/09/27/szabad-szoftver-palyazat-2011.

[6] Calum Benson, Adam Elman, Seth Nickell, and Colin z Robertson. GNOME Human Interface Guidelines. http://library.gnome.org/devel/hig-book/.

[7] Murray Cumming et al. Programming with gtkmm 3. The GNOME Project. http://developer.gnome.org/gtkmm-tutorial/unstable/.

[8] Tony Gale, Ian Main, and the GTK team. GTK+ 2.0 Tutorial. The GNOME Project. http://library.gnome.org/devel/gtk-tutorial/stable/.

[9] The GNOME Project. GTK+ 3 Reference Manual. http://developer.gnome.org/gtk3/stable/.

[10] Havoc Pennington. GTK+ / Gnome Application Development. New Riders Publishing, 1999. http://developer.gnome.org/doc/GGAD/ggad.html.

[11] Read the Docs project. The Python GTK+ 3 Tutorial. http://python-gtk-3-tutorial.readthedocs.org.

89