Ezt az útmutatót egy Windows-ból jött programozó szemével mutatom be, aki gyakorlott a System.Windows.Forms
programozásában, ugyanakkor nem csak a hasonszőrű embereknek szól,
hiszen hasznos lehet a C/C++ fejlesztőknek is akik a Gtk+-t direktben
programozzák vagy akár Gtk-ban teljesen kezdőknek is.
Írta: Zac Bowling
Forrás: http://www.mono-project.com/GtkSharpBeginnersGuide
Ezt az útmutatót egy Windows-ból jött programozó szemével mutatom be, aki gyakorlott a System.Windows.Forms
programozásában, ugyanakkor nem csak a hasonszőrű embereknek szól,
hiszen hasznos lehet a C/C++ fejlesztőknek is akik a Gtk+-t direktben
programozzák vagy akár Gtk-ban teljesen kezdőknek is.
A válasz a kérdésre talán sokkal jobban megválaszolásra került a
Gtk# oldalán. Legegyszerűbben: egy burkolat a Gimp Toolkit-en vagy
GTK+-on, a keresztplatformos GUI keretrendszeren.
Fut bármely X szerveren, direkt Framebuffer-en vagy éppen a natív
Windows felhasználói felületen. Kis túlzással fut mindenen, de Linuxon
néz ki a legjobban, rendben van Windowson és mondjuk úgy, hogy
működőképes MacOSX-en. A GTK+ több éve elérhető, így elég stabil és jól
letesztelt, valamint majd minden asztali Linux-on megtalálható.
Az egyik legnagyobb félreértés, hogy a Gtk#-hoz szükség van a
Mono-ra. Ez távol áll az igazságtól. Amire a GTK#-nak szüksége van az
egy .NET kompatibilis futtató környezet. Az általam ismert futtató
környezetek az MS.NET, a Mono és a DotGNU. A GTK# tökéletesen tesztelve lett MS.NET-el
és Mono-val egyaránt. Ezidáig nem teszteltem DotGNU-val, de amennyiben
a futtató környezet 100 %-ig .NET kompatibilis, úgy kell, hogy működjön
DotGNU-val is. Ez tehát azt jelenti, hogy amennyiben az alkalmazásodat
GTK#-ban fejleszted és eltökélt szándékod, hogy az fusson Windowson is
akkor választhatsz, hogy az alkalmazást csak GTK#-al fordítod és a
Microsoft futtatókörnyezetével futtatod vagy a Windows-os Mono
futtatókörnyezetre támaszkodsz ugyanilyen módon.
Nézz utána a GUI eszközkészleteknek, hogy
eldöntsd. Jóllehet a Gtk# a kedvencem és szolgáltatásokban nagyon
gazdag, néha a GTK# nem pont az az eszköz melyet keresel mikor az
alkamazásodat tervezed és egyébként sem akarom erőltetni.
Az első dolog a GTK# letöltése és telepítése.
Linux, MacOSX, FreeBSD és mások: Legelőször nézz utána, hogy a
disztribúciódhoz létezik-e hivatalos GTK# csomag vagy Mono csomag.
Amennyiben nem, akkor látogass el a Letöltések oldalra, hogy letölthesd
a számodra legideálisabb csomagot. Legrosszabb esetben kénytelen leszel
saját magadnak lefordítani.
Windows: amennyiben Windows-on dolgozol, töltsd le az integrált
telepítőt a letöltési oldalról. Szedd le a Gtk# integrációs csomagot is
Visual Studióhoz, amely lehetővé teszi a GTK# használatát Mono nélkül.
Én a GTK# 2.0-t (az írás idejében v1.9.3) amely a GTK+ 2.4-en
alapul. Választhatod a GTK# 1.0-t (GTK+ 2.2 alapú) is, de a GTK# 2.0
használata erősen javasolt.
A GTK# különböző csomagokból épül fel.
Vannak más könyvtárak is melyeket gyakran a Gtk# keretrendszer
részeként tudatosítunk - pl. Gecko#, Gtksourceview#, Gsf#, Guile# és
Gst# - de nincsenek a Gtk# kiadásokhoz csomagolva, de a legtöbb esetben
ugyanazon csata résztvevői. Ezek nagyon hasznosak és speciális dolgokat
végeznek, de jelenleg nem nagyon érintjük őket.
Néha, mikor a fejlesztő nekikezd a programozásnak, összezavarodik,
hogy ugyan melyiket használja, a Gtk#-ot vagy a Glade#-ot. Ehhez
azonban meg kell érteni, hogy mi is az a Glade# és mi is az a Gtk#. A
Gtk# az ablakozó és widget rendszer magja (a widget olyan mint a
control az SWF-ben). A Glade# a Gtk#-ból öröklődik, tehát
tulajdonképpen egy részhalmaza a Gtk#-nak, de autómatikusan tervezi meg
a felületet egy XML alapú erőforrásfile-ból. Az XML file a WYSIWYG gui szerkesztő glade eszközzel generálható.
A legtöbb esetben a Glade# tökéletes eszköz. Rengeteg időt lehet
vele megtakarítani azáltal, hogy maga generálja a kódot, és a felület a
jövőben is könnyen módosítható lesz. A probléma attol függ, hogy mit
szeretnénk megvalósítnai, mivel a Glade# könnyen idegesítővé válhat
azáltal, hogy néha lehetetlen vele magoldani azt amit igazából
szeretnénk, különösen akkor, ha az ablak valami különleges dolgot
csinál (elemek elrejtése, részek dinamikus betöltése, widget-ek
szülőjének módosítása stb.) Igazából csak a gyakorlat segíthet abban,
hogy eldöntsük, hogy adott esetben melyiket érdemes használni.
Helyezkedjünk el kényelmesen. Vegyönk magunkhoz egy kis üdítőt
(esetleg kávét - a fordító) és tegyünk be valami kellemes zenét.
Rendben, készen is állunk!
Először is el kell készítenünk egy könyvtára a mi kis projektünknek.
(Windows felhasználóknak: kerüld a szóközök használatát a
könyvtárnevekben a későbbi fejfájás elkerülése érdekében!) Ezután
nyissunk egy shell-t. (Windows-on nyisd meg a Start menüt, majd
“Programs→Mono 1.x.x→Mono Command Prompt”. Ezáltal az elérési utak
megfelelőek lesznek, szóval nem kell semmi extrát sem csinálni.) Ezután
menjünk az újonnan létrehozott mappába. Még sokszor fogjuk ezt a
shell-t használni, úgyhogy maradjon aktív a háttérben. Most menjünk
tovább, indítsuk el kedvenc editorunkat (MonoDevelop, vi, emacs,
notepad stb.) majd állítsunk be egy üres projektet (amennyiben ez
kivitelezhető az adott editorral) és készítsünk egy üres file-t.
Mentsük el a file-t “helloworld.cs” néven.
Feltételezem, hogy már rendelkezel némi C# ismerettel, így a
következők nem jelentenek újdonságot. Készítenünk kell egy hivatkozást
a Gtk#-ra, valamint csinálunk egy új osztályt és egy belépési pontot az
alkalmazásunknak. Ennek valahogy így kell kinéznie:
Ennek ismerősnek kell lennie. Ezek után használjuk a parancssori
fordítót, miután elmentettük a kódot. Menjünk vissza a korábban nyitott
shell-be és futtassuk a következő parancsot:
mcs -pkg:gtk-sharp-2.0 helloword.cs
A rendszer figyelmeztethet, hogy a “-pkg:” nem szokványos paraméter,
ha csc fordítót Window-on használjuk. Ez nem létezik a csc-ben mert a
Mono a Linux világából jön. Ez azt csinálja, hogy utánnanéz egy csomag
konfigurációs file-nak ez alatt a név alatt. A csomag konfigurációs
könyvtárban van egy file “gtk-sharp-2.0.pc” néven, amely - sok egyéb
mellett - információt tartalmaz arról, hogy hol találhatóak a csomaghoz
tartozó library-k. Ezáltal nem szükséges kézzel beírnünk a
következőket: “-r:gtk-sharp-2.0.dll -r:atk-sharp-2.0.dll
-r:pango-sharp-2.0.dll ….”.
Most térjünk vissza a kódunkhoz és töröljük ki a “Console.WriteLine”
utasítást. Az első dolog, hogy készíteni fogunk egy új ablakot. Ezt úgy
csináljuk, hogy hozzáadunk egy új ablak utasítást valamint egy
alkalmazás blokkot (hogy elinduljon a fő szál). Valahogy így:
Ezután lefordítjuk az elkészült kódot ugyanúgy ahogyan legutóbb is
tettük, majd futtatjuk a `mono HelloWorld.exe` paranccsal, melynek
hatására valami ilyesmit kell kapnunk:
Ugye milyen egyszerű? Az első dolog ami bizonyára feltűnt, ha eddig System.Windows. Forms-t használtál, hogy nem adtunk meg igazítást a Label-nek. Például nem mondtunk, hogy myLabel.Left = 100 vagy myLabel.Width = 200 vagy bármi hasonlót, egyszerűen csak hozzáadtuk a Label-t a Form-hoz: myWin.Add(…). Ez azért van mert a Gtk.Window egy Widget, amely a Bin osztályból öröklődik. A másik érdekes dolog lehet, hogy mit jelent az Application.Init() és Application.Run(). Ez sok szempontból ugyanaz mint az Application.Run() a System.Windows.Forms-ban. Normál esetben, ha az alkalmazás az összes kódot lefuttatta a fő szálban, akkor kilépne. A példában a “ShowAll()”-tól semmi sem gátolná a program további futását, tehát az Application.Run() nélkül befejeződne. Az Application.Init() mondja meg a futtatókörnyezetnek, hogy figyeljen bármely Gtk.Windows indítására, majd a Run()
metódus meghívását követően elinful a fő ciklus ezeken az ablakokon. Ez
a fő ciklus tartja “életben” az alkalmazást mindaddig, amig az összes
ablak be nem csukódik. További információk a Monodoc-ban találhatóak az
Application objektumról.
Most talán azt kérdezed magadtól, “Hogyan lehetne több Widget-et
adni egy ablakhoz, ha az csak egy Widgetet tud tartalmazni?”. Nos
ahogyan korábban mondtuk a Window egy widget, egy egyszerű widget
tárolóval. Vannak azonban más widgetek, melyek képesek több widget-et
tartalmazni egy időben. Néhány ezek közül a Gtk.Bin tároló widget-ből
vagy a container widgetből közvetlenül öröklődik. A Bin is a
container-ből öröklődik mint minden más tároló is, de a Bin csak egy
widget-et tud tárolni.
Ahhoz hogy több widget is megjelenjen az ablakunkon, ahhoz azok
közül a widgetek közül kell valamelyiket használnunk, amelyik képes
több widget megjelenítésére. Rengeteg olyan vezérlőelem van amely képes
erre, jelen esetben azonban elegendő, ha megismerkedünk a HBox-al a
VBox-al és esetleg a Table-el.
Minden osztály mely a Widget-ből van származtatva, birtokol egy nagy
csokor eseményt melyekhez saját metódusokat csatolhatunk. Ezek a
következők:
Ahhoz, hogy egy eljárást csatoljunk a kiválasztott eseményhez,
egyszerűen csak hozzá kell csatolnuk az eseményt ahhoz a metódushoz
melyet akkor akarunk futtatni amikor a kívánt esemény bekövetkezik. Ez
az eljárás az esemény típusától függően némiképp eltérő lehet, de
általánosan így néz ki:
public static void HandlerMetod(object obj, EventArgs args)
Néhány esetben a változások az eljárásban magukkal vonják a
változásokat az EventArgs paraméterben is. Rendszerint ezek az
EventArgs osztályból származtatott változások. Például, hogy
lekezeljünk egy eseményt - melyet egy gomb egérrel történt lenyomásával
idéztünk elő - a következő eljárást kell alkalmaznunk:
A ButtonPressEventArga az EventArgs-ból származtatott osztály, amely
- sok más osztályhoz hasonlóan - egy új Gtk.Event tulajdonságot ad az
EventArgs class-hoz.
A Gdk.Event az az osztály melynek Type tulajdonsága pontosabban
meghatározza, hogy melyik esemény következett be. Más tulajdonságok is
hozzáadhatóak a Gdk.Event-ből való származtatással, mint a
Gdk.Eventbutton esetében is, amely - többek közt - definiálja a Button
tulajdonságot így megadva, hogy az egér melyik gombja lett lenyomva. A
típusok a következők:
Ahhoz, hogy egy Gdk.Event eseményt használjunk, valami ilyesmit kell tennünk:
Ezáltal, ha az egér egy gombját megnyomják, sokkal több részletet
megtudhatunk az eseményről. Például: egy egyszerű vagy egy
duplakattintás volt-e, az egér jobb vagy bal gombjával.
A Glade# egy libglade összerendelés a grafikus felhasználói felületű
alkalmazások egyszerű tervezéséhez. Jelenleg két eszköz létezik
glade-formátumú file-ok előállításához: a Glade és természetesen a
Stetic.
XML-ben íródott file-ok, melyek a megtervezett
grafikus felületet reprezentálják; elmentve annak minden attribútumát
és tulajdonságát, ezáltal elérhetővé téve bármely programozási
nyelvből.
Nézzük a következő példát, amely tartalmazza szükséges dolgokat a teszteléshez. file: gui.glade
Ez egy kicsit túl sok információnak tűnhet, de a libglade
library-nek mindezekre szüksége van a felhasználói felület
megrajzolásához.
Tekintsük úgy, hogy a glade GUI file-ja
valamilyen úton-módon már el lett készítve, akár Glade-el akár
Stetic-kel. Egy video a Stetic-ről megtekinthető itt. Tekintettel erre
a példára a GUI-t a gui.glade file-ba kell menteni,
és kell, hogy tartalmazzon egy ablak definíciót melynek neve window1,
egy nyomógombot button1 néven valamint egy cimkét label1 néven.
Készítenünk kell egy új hivatkozást a Gtk#-ra és Glade#-ra, majd ezután
egy új osztályt és belépési pontot az alkalmazásunknak. Ennek valahogy
így kell kinéznie:
Ezután a glade.cs forrásfile-t le kell fordítanunk, hivatkozva a
glade névtérre mely a glade-sharp.dll-ben foglal helyet. A fordításhoz
használt parancs a következő:
A parancs hatására létrejön a glade.exe program. Amennyiben a
glade.exe program könyvtára nem tartalmazza a gui.glade file-t, akkor
az alkalmazás hibával áll le. Ha próbáljuk futtatni az alkalmazást
akkor a program kivétel generálásával kilép arra hivatkozva, hogy
bizonyos függvények - melyek a gui.glade-ben lettek definiálva -
hiányoznak. A következő részben ez a hiba javításra kerül.
Ahhoz, hogy bármely glade file-ban definiált objektumot elérjünk a
programkódból, tudnunk kell annak pontos nevét és típusát, majd a
megfelelő C# kódot kell hozzáadni az alábbi formában:
Alkalmazva ezt a mi példánkban:
Események hozzáadásához ugyanazon lépéseket kell követni minden más eseményhez, melyek akár a Glade-ből is hozzáadhatóak.
Következésképpen láthatjuk a teljes kódot a gui.glade-ben definiált
események kezeléséhez. A fenti kód javítja azt a hibát mely az előző
részben bemutatott példában volt.