Ebben a fejezetben tutorialok találhatóak első programjaink megírásához.
A kódok egy része működhet minden környezetben, de javasolt a különböző
implementációk sajátosságaihoz igazodva egyéni tutorialok készítése.
Általános (ANSI standard) Smalltalk Tutorial
Egy általános Smalltalk nyelvi tutorial, kommentezett futtatható kódokkal.
Használatához valamilyen futtatókörnyezetbe másolandó a kód.
Tesztelése és fejlesztése a Pharo 1.3 környezettel történt.
Tutorial letöltése
Készült: 2012
Készítő: Ormándi Mátyás
Az ANSI standard dokumentációjának feldolgozás
Pharo
A Pharo futtatókörnyezet egy 2009-es fejlesztésű, kényelmes grafikus felületet nyújtó framework.
Elérhetősége: Hivatalos oldal
Innen letölthető, és kicsomagolás után már használható is.
A példaprogram futtatásához új workspace nyitása után oda bemásolandó a futtatható kód, majd kijelölés után az egyes részek Crtl+D vagy jobb klikk + Do it utasítással történnek meg az üzenetküldések.
Dolphin Smalltalk Tutorial
A következő tutorial a Dolphin Smalltalk X6 (kereskedelmi program)
használatához készült. Egy része a helpjének fordítása, tartalmaz tapasztalati
tippeket, illetve példán keresztül mutatja meg a programkészítést.
A 8.2. pontban már találkozhattunk a Dolphin implementációval, bemutatásra
kerültek a környezet sajátosságai, a beépített browser ablakok.
Gyorstalpaló:
- A fejlesztői környezet indítása után használjuk a File > New Workspace
menüpontot. Ekkor megjelenik egy szövegszerkesztő (Workspace
window, a kód is szöveges formában
menthető).
- A kód bármely részlete futtatható ha kijelöljük, kommentezésre
használhatjuk az ""-t,
és használjuk a .-t, mint
utasítás-elválasztót (nem
utasítás-lezáró).
futtatás:
ctrl+E , futtatás és megjelenítés: ctrl+D (a Workspace menüből érhetőek el
(8.2.2.pont))
-
Írjuk be, pl.:
"első programom"
'hello world'.
3+4
- Jelöljük ki külön-külön a második illetve a harmadik sort, majd üssünk
Ctrl+D-t. A sor végén megjelenik a kimenet. Ne felejtsünk el ezért egy DEL-t
ütni, hogy ne rontsuk el a kódunkat.
Hasznos dolgok:
-
Nagy számok: (nincs korlátozás a számok méretére és típusára, nem is kell
tudnunk a tárolás módját, a Smalltalk automatikusan kezeli)
1000 factorial
Hasznos lehet futásidő mérésérére, pl. előző esetben is, ha a beépített Time
osztályt mint időzítőt használjuk:
Time millisecondsToRun: [1000 factorial].
Time microsecondsToRun: [1000 factorial].
Ekkor a szögletes zárójelben
[ ] lévő kód azután lesz végre hajtva, hogy #value üzenetet
kapott. Fenti esetben pedig #microsecondsToRun üzenet
küldi a #value-t a blokknak, hogy végrehajtódjon.
-
Felsorolások: (ilyen esetben a Ctrl+I = Inspect It menüpont inkább
használható (8.2.2.pont), ahol egy ablakban felsorolja a végrehajtási
eredményként kapott objektumokat)
"A rendszer összes osztályának kilistázása növekvő sorrendben"
Class allClasses asSortedCollection.
A Smalltalkban a legtöbb üzenet, ami
#asXXX-szel kezdődik arra használható,
hogy az objektum más típusúra konvertálja át magát. Itt az
OrderedCollection (ami az #alClasses
által válaszol) kapja az utasítást, hogy
SortedCollection-né konvertálódjon (rendezett tömb lesz).
-
"Az objektumok száma rendszerben"
Object allSubinstances size.
Az eredmény meglepően nagy (100ezres
nagyságrendű). Kérdezhetnénk, miért van ilyen sok objektum, amikor még
nem is
programoztunk. Nos, ahogy erről korábban is szó volt, a Smalltalkban minden
objektum, a fejlesztői környezet is objektumok sokasága, amik egymással
kommunikálnak. Tehát egy üres Workspace ablak esetén is rejtőzhet rengeteg
objektum a háttérben.
-
"Tömb készítés:"
#(1 2 3 4 5 6 7 8 9).
(1 to: 100) asArray.
Az első esetben ("literal array") az Array objektum
közvetlenül a Smalltalk compiler által épül, ami feltölti egészekkel 1-9-ig.
Nyilvánvalóan nagyobb tömb esetében az ilyen jellegű inicializáció nehézkes.
A második példa mutatja, hogyan készítsünk egy számsorozatot
(egy köztes intervallum objektumot), és utasítsuk arra az
#asArray üzenettel, hogy konvertálja magát tömbbé.
-
"Beépített két dimenziós pont osztály"
(50 @ 150) + (25 @ 35)
(640 @ 480) * 3
Ha a @ üzenetet küldjük egy egésznek, két dimenziós
pontobjektum jön létre. Érdekesség, hogy ez a
szekvenciális kifejezés mint üzenetküldés egy objektumként kezelhető (de
fontos megjegyezni, hogy a @ nem része a nyelvnek, hanem egy egyszerű üzenet).
-
Random használat:
"Random használat, lottó számok"
(Random new next: 6) collect: [:n | (n * 49) rounded + 1].
Ez a példa lottó számokat generál, az eredmény tömbben lesz,
pl.: #(12 40 25 28 11 18)
Az első alkifejezés a zárójelben egy új 6 számból (0-1 érték) álló tömböt
generál. A #collect üzenet ennek a tömbnek lesz küldve, ami által a blokk
egyesével lefut rajtuk, így a végeredmény 1-50 közötti (kerekítés miatt) egész
számokból álló tömb lesz.
"Random lottó számok, rendezéssel"
r := Random new. s := Set new.
[s size = 6] whileFalse: [s add: (r next * 49) rounded+1].
s asSortedCollection asArray.
-
Szálkezelés példa: indítunk egy szálat (fork), végtelen
ciklusként fut (blokk ismétlése repeat-tel, beépített Time idő használata,
beépített View osztályra a megjelenítés).
"Indítás"
digitalClockProcess := [[
Processor sleep: 1000.
(View desktop canvas)
font: (Font name: 'Arial' pointSize: 36) beBold;
text: Time now printString at: 10@10;
free
] repeat fork.
"Leállítás, az első sor leállítja a processzt, de még egy újrarajzolás
szükséges, mert ott marad a screenen."
digitalClockProcess terminate.
UserLibrary default invalidate: nil lpRect: nil bErase: true.
-
A Dolphin Smalltalkban könnyen használhatunk
multimédiás eszközöket is, pl.:
(Sound fromFile: '\Windows\Media\TaDa.wav') woofAndWait;
-
Beépített programocskák ("játékok"): Természetesen
ezekből is saját alkalmazásokat származtathatunk, amiket bővíthetünk,
módosíthatunk.
"Rajzoló pad megnyitása, Ctrl+E"
scribble := Scribble show.
"Hagyjuk az előző ablakot nyitva, és készítsünk egy vele párhuzamosan rajzoló ablakot."
EtchASketch showOn: scribble model.
-
Hasznos lehet:
#panic
"Az összes nézet bezárása, környezet újraindítása (azaz a System Folder
bezáródik, és egy kis idő múlva egy új indul helyette)."
SmalltalkSystemShell default panic
Fontos: ha a #panic-ot használjuk, elveszítjük az összes megnyitott
Dolphin ablakot, és nem lesz lehetőség később menteni a tartalmukat. A Panic
eszköz ikonként is elérhető a System Folderből. Néha hasznos lehet a munka
során.
Csomag (package) készítése:
A Smalltalk implementációk
fejezetben a Package Browser kapcsán esett szó a
PAC részletes
leírásáról.
Ami fontos számunkra, ha elkezdünk package-t
használva programot írni:
- A PAC egy szövegfájl, ami csomag
komponenseit tartalmazza. A csomag komponensei alatt osztályokat értünk. Egy
osztály csak egy csomag
tagja (vagy egyikbe se tartozik).
-
Számunkra ez azt jelenti, hogy egy Project elemeit
egy csomagba tehetjük. A csomagok konzisztensek a rendszerrel, így a
változtatások mind a rendszerben, mind a fájlokban jelentkeznek (mentéskor).
- Ebben az esetben, ha project készítésére használunk egy csomagot, a
pac kiterjesztésű file külön menthető, átvihető
és beilleszthető máshol, másik környezetbe (jelen esetben betehetünk mindent
egy pac fájlba, de a csomagolásnak a 8.2.5.
pontban leírtak szerint más módja is van, pl.: pax,
amikor külön fájlokban is tárolhatjuk az osztályainkat).
- Ilyenkor a project keretein belül készült "programunk" futtatására elég a
csomagban lévő megfelelő osztályból példányosítani.
Miért jó package-t használni a program szerkesztés szempontjából?:
- Egyszerű program esetében megírhatjuk a kódunkat egyetlen szöveges fájlban
(például a Workspace-ablak szövegszerkesztőjével, és elmenthetjük
.st kiterjesztéssel).
- Ha már több osztályunk van, és még az egyes üzenetek is elég sok kódot
tartalmaznak, akkor ez a megfelelő módja a látszólagos modularizációnak.
-
A kód ekkor is csak egyetlen fájlban lesz, de a rendszerben már
elkülöníthető, a Package-Browserben megnézhetjük adott csomag osztályait,
illetve a Class-Browserben is megjelennek az osztályaink a többi rendszer
osztály között.
-
A kód írására ekkor már maga a Class Browser javasolt!!!, itt egy grafikus
fa-diagrammból választhatjuk ki a megfelelő osztály megfelelő üzenetét, és ott
szerkeszthetjük közvetlenül a kódot.
Package Browser |
Class Browser |
|
|
Érdemes párhuzamot vonni, hogy a modernebb objektum-orientált programozási
nyelvekhez készített környezet alapján könnyen eligazodjunk:
package-készítés |
modularizáció, projektkészítés |
package browser |
project class-browsere, a project osztályainak
azonosítására |
class browserrel üzenetek szerkesztése |
mintha a class-browserrel metódusokat szerkesztenénk |
Az utolsó pontnál kiemelhetjük azt a párhuzamot, hogy ha
grafikus programot készítünk, és ablak jellegű (pl. ShellView) osztályból
származtatunk, akkor az "eseménykezelőket" megírhatjuk úgy, mint az ősosztály
üzeneteinek felüldefiniálása.
Package használata, osztályok hozzáadása a package-hez:
- Használjuk a Tools/Package Browser menüpontot a
Package-Browser indításához.
- File/New
menüpont új package létrehozásához, illetve File/Install
Package,
ha egy meglévőt akarunk a renszerhez hozzáadni.
(megjegyzés: A Dolphin Smalltalk Community Edition-ben (szabad verzió) a
rendszerhez a package-eket minden indításkor hozzá kell adnunk. !!!)
- Osztályok hozzáadása a package-hez:
- A Class Browser Class-ből indítható egy
ablak a Class/New menü segítségével.
- Adunk az osztálynak nevet (Fontos: Nagybetűvel kezdődnek a Smalltalk
osztály nevei)
- Megadjuk az ősosztályt (lehet az object is)
- Ha be van jelölve a Packaged opció, kiválasztjuk az előbb létrehozott vagy
megnyitott package-hez.
10.1.3. Példa Program készítése:
A programunkhoz készítünk egy saját package-t, installáljuk a rendszerbe. Az
osztályok hozzáadása már mind a grafikus felületen keresztül történik. A
programunk futtatása a package megjelölt osztályának a példányosításával
történik. A kód írását a Class-Browser-ben végezzük, itt készítjük el az
osztályainkat, a származtatás jól látszik a fa-diagrammban, itt hozzuk létre az
üzenetek kódjait is.
Eszközök: ablakhasználat, egér használat (drag and drop), alakzatok
rajzolása (osztály hierarchia).
- Ablak: Származtassuk a programunk osztályát a
ShellView-ból, így lesz rajzolható Canvas-ünk, illetve használhatóak az ablak
egér- és billentyűzet-eseménykezelői.
Ha kiválasztunk a Class-Browserben egy osztályt, használjuk a
Method/New menüpontot üzenetek definiálásra.
(Megjegyzés: az irodalomban szinte mindenhol a "message" szót használják az
üzenetekre, érdekes módon környezetekben gyakran "method"-nak hívják őket)
ShellView subclass: #Bead "Fő osztály származtatásának, a New Class ablakkal generált kódrészlete (csomagunk osztálya)"
instanceVariableNames: '...alakzatok width height ...'
classVariableNames: ''
poolDictionaries: ''
classInstanceVariableNames: ''
"... eseménykezelő üzenetek ..."
UjAlakzat: alakzat "feltöltésre szolgáló üzenet"
Futtatás: Ekkor a futtatás a példányosítás után az ős (ShellView) show üzenetével érhető el.
bead := Bead show. "futtatási kód, bármilyen WorkSpace ablakba beírhatjuk, szokásos módon Ctr+D hatására lefut"
-
"Eseménykezelők": A környezet üzeneteket küld az
ősosztálynak (ShellView), ha valamilyen változás történik az ablakon. Ezeket a
származtatott osztályunkban felüldefiniálhatjuk, az üzenetek paramétereit
felhasználhatjuk.
Üzenetek:
onCreated: anEvent |
"például kezdeti ablak pozíció és
méret adható meg" |
initialize |
"kezdeti értékek megadása" |
onKeyPressed: aKeyEvent |
"pl. ESC kezelése" |
onPaintRequired: aPaintEvent |
"ablak újrarajzolása" |
onLeftButtonPressed: aMouseEvent |
"Drag" |
onMouseMoved: aMouseEvent |
"and" |
onLeftButtonReleased: aMouseEvent |
"Drop" |
A 3 egérre vonatkozó üzenettel megvalósítjuk a "drag and
drop"-ot, ekkor a megfelelő Alakzat setpos üzenet
hívásával pozíció-módosításokat hajtunk végre.
-
Alakzatok, származtatás: (A következő kódokat
érdemes a Class-Browser-ben szerkeszteni, ahol már üzenetekre lebontva
láthatjuk(Methods). )
Alakzat: Pozíció-,
szöveg-, mérettulajdonságai vannak. Rendelkezik egy kirajzolás üzenettel. A
fő osztályunk
egy "Alakzat"-okból álló tömböt ismer.
Object subclass: #Alakzat
instanceVariableNames: 'position size color tegla...' "a tegla változó a befoglaló téglalapra utal, kirajzoláskor ez alakzat specifikus"
" ... "
"üzenetei:"
draw: canvas "általános kirajzoló parancs, a speciálisabb osztályokban fejtjük ki, milyen alakzatnál ez mit takar"
moveto: newpos
position := newpos. "ezek a kódrészletek közösen használhatóak mindenféle alakzatra"
resize: newsize
size := newsize.
"..."
Kör, Tégla: Az Alakzat osztályból származnak, megtartanak
közös tulajdonságot, felüldefiniálják a specifikusakat
Alakzat subclass: #Kor
" ..."
draw: canvas
canvas ellipse: tegla. "ellipszis kirajzolás"
super draw: canvas.
Alakzat subclass: #Tegla
"..."
draw: canvas
canvas rectangle: tegla. "téglalap kirajzolás"
super draw: canvas.
-
Alakzatok példányosítása:
Miután futtattuk a programunkat (pl. egy WorkSpace ablakból),
példányosítsunk tetszőleges számú, különböző alakzatot ugyanilyen módon (WorSpace
ablak). Ennek a módja, hogy ezeket a példányokat az
UjAlakzat nevű üzenet paramétereként küldjük a fő
osztályunk futtatott
példányának.
UjAlakzat: alakzat
alakzatok add: (alakzat)
A fő osztályunk ezeket egy alakzatok nevű tömbben tárolja. Időben változó
méretű tömbre lesz szükségünk, erre az OrderedCollection
osztályt érdemes használni.
alakzatok := OrderedCollection new.
Kirajzoláskor a do iterátor használatával minden
tömbelemnek, mint objektumnak elküldjük a draw üzenetet.
| canvas |
canvas := self canvas.
(1 to: (alakzatok size)) do: [:i | (alakzatok at: i) draw: canvas ].
Forrás
bead.pac
Végső kinézet:
Squeak Smalltalk Tutorial:
Tilitoli példaprogram
Forrás
Smalltalk_tilitoli.zip
A feladat
A feladat egy tilitoli program készítése.
A program egy ablakból áll, azon van 4x4 kocka, ezek egyike a lyuk. A lyuk
tologatásával mozognak a kockák is (bármely, a lyukkal szomszédos kockát ki
lehet cserélni a lyukkal: ehhez a kockára kell kattintani). A kockák mellett
az ablakban van egy gomb, ami kiírja, minden kocka jó helyen van-e, egy másik,
ami megkeveri a kockákat, és egy harmadik, amin az látható, hogy a legutóbbi
keverés óta hány lépés (csere) történt.
A terv
A program öt osztályt tartalmaz:
- TT: a fő osztály. Ő az ablak, ő tartalmazza és inicializálja a többieket.
- HButton: egy kocka. Lehet lyuk is. Ismeri a saját aktuális helyzetét és célhelyzetét, valamint a rajta megjelenítendő képet is.
- TTStateTextMorph: kiírja, hogy minden kocka jó helyen van-e (ha igen, piros lesz, ha nem, sárga).
- TTRandomizeButton: ha rákattintanak, megkeveri a kockákat.
- TTCounter: azt jeleníti meg, hogy a legutóbbi keverés óta hány lépés (csere) történt.
A megvalósítás érdekesebb részei
Az osztályok kinézete
Az osztályok deklarációja eléggé hasonlít egymásra, így csak a HButtont írjuk ide példaként.
A HButton a BasicButton leszármazottja, és azt a megadott adattagokkal terjeszti ki.
A (posx,posy) az aktuális pozíció, a (goalx,goaly) a kocka helye.
Az m a tartalmazó mátrix, az imagem a tartalmazott kép, a tt a tartalmazó TT osztályú objektum.
BasicButton subclass: #HButton
instanceVariableNames: 'posx posy goalx goaly m imagem tt'
classVariableNames: ''
poolDictionaries: ''
category: 'TiliToli'
A logikai réteg néhány függvénye a HButtonban
A kocka inicializálásakor a ttInitx:y:m:prefix:postfix:postfix:tilitoli:
metódust kell meghívni (amit a TT meg is tesz). A képfájlnevek mind
prefix(i)postfix alakúak, ahol az (i) 0 és 15 közt végigmegy. A self
visszaadása nem fontos, nem használjuk fel máshol.
"HButton:"
ttInitx: xa y: ya m: ma prefix: prefix postfix: postfix tilitoli: tta
"a kocka kezdeti beállítása"
"(x,y): kezdeti (és cél) koortináta"
"m: a tartalmazó mátrix"
"prefix, postfix: a képfájlnevek prefixe és postfixe"
| index picFileName |
tt := tta.
m := ma.
posx := xa.
goalx := xa.
posy := ya.
goaly := ya.
index := (posy-1)*4+(posx-1).
self label: index asString.
self width: 50; height: 50; useSquareCorners.
picFileName := Text fromString: prefix.
picFileName append: (index asString).
picFileName append: postfix.
imagem := (Form fromFileNamed: (picFileName asString)) asMorph.
self addMorph: imagem.
^self
Az osztály legbonyolultabb függvénye is csak egy elágazást tartalmaz.
Felhívjuk a figyelmet arra, hogy az and: és or: üzenetek előtt a kifejezések
kerek zárójelben vannak, míg utánuk szögletesben ((valami) and: [valami]).
"HButton:"
trySwapx: otherX y: otherY
"Megpróbálja megcserélni az adott kockát az (otherX,otherY) koordinátájú kockával.
Ha ez nem legális, akkor nem történik semmi. "
| other myX myY |
(((m at: otherY at: otherX ifInvalid: false) ~= false) "létezik-e a (tryX,tryY) mezo?"
and:
[((m at: otherY at: otherX) zero) or: [self zero]]) "a (tryX,tryY) mezon zero van, vagy én zero vagyok?"
ifTrue: [
myX:=posx.
myY:=posy.
other := m at: otherY at: otherX.
"megcseréljük a koordinátáikat"
other posx: myX posy: myY.
self posx: otherX posy: otherY.
"megcseréljük a gombokat a mátrixban"
m at: myY at: myX put: other.
m at: otherY at: otherX put: self.
"beállítjuk a TT-ben a "
(self zero) ifTrue: [
tt holeX: otherX holeY: otherY.
] ifFalse: [
tt holeX: myX holeY: myY.
].
"pozíciók újraszámolása"
self reComputePosition.
other reComputePosition.
tt swapHappened.
^true.
].
^false.
A recomputePosition újraszámolja a kocka pozícióját a képernyőn, és arrébb
megy, ha kell. A left üzenettel arra kérjük, hogy mozduljon el úgy, hogy az ő
bal széle adott távolságra legyen a tartalmazójának a bal szélétől. A top
hasonló elven működik.
"HButton:"
reComputePosition
"a (posx,posy) alapján újraszámolja a pozícióját a képernyőn, és arrébb megy, ha kell"
self left: ((tt left) + ((posx-1)*50)+10).
self top: ((tt top) + (tt labelHeight) + ((posy-1)*50)+10).
^self
Az egérkattintások lekezelése a HButtonban
Az egérkattintásokat a kockákban le kellett kezelnünk.
Ehhez felül kellett definiálnunk a handlesMouseDown metódust, mivel ez a metódus adja vissza azt, hogy az adott osztály objektumai le szeretnék-e maguk kezelni a kattintásokat (alapból hamisat ad vissza). Mivel mi le szeretnénk kezelni, ezért egy igaz értéket kell visszaadnia a metódusnak:
"HButton:"
handlesMouseDown: e
"igen, le szeretnénk kezelni a MouseDown eseményt"
^true
Az esemény valódi lekezelése pedig a mouseDown-ban történik.
(Amikor rendszer érzékeli, hogy rákattintottak az objektumra, megkérdezi tőle, hogy szeretné-e lekezelni, és ha igen, akkor meghívódik ez a metódus.)
"HButton:"
mouseDown: evt
"ha lenyomták az egeret, akkor megpróbálunk cserélni a szomszédokkal"
^self trySwap.
A TT osztály inicializációja
A TT legbonyolultabb metódusa az initPrefix:postfix:, ami inicializálja.
Ez létrehoz egy 4x4-es m mátrixot, és ebbe cellánként belerakja a megfelelően beállított kockákat.
Ezután a három gombot is beállítja.
"TT:"
initPrefix: pre postfix: post
"inicializálás"
"prefix, postfix: a képfájlnevek prefixe és postfixe"
| b |
self width: 350; height: 220.
m := Matrix new: 4.
holeX:=1.
holeY:=1.
1 to: 4 do:
[:y | "sor"
1 to: 4 do:
[ :x | "oszlop"
b := HButton new.
b ttInitx: x y: y m: m prefix: pre postfix: post tilitoli: self.
self addMorph: b.
b reComputePosition.
m at: y at: x put: b.
].
].
state := TTStateTextMorph newM: m.
self addMorph: state.
state left: 250; top: (self labelHeight + 10).
state refresh.
counter := TTCounter new.
self addMorph: counter.
counter setZero.
counter left: 250; top: (self labelHeight + 10) +20.
randomizeButton := TTRandomizeButton newTT: self.
self addMorph: randomizeButton.
randomizeButton left: 250; top: (self labelHeight + 10) +40.
self openInWorld.
^self.
A TT osztályok létrehozása a newPrefix:postfix: osztályszintű üzenettel történhet. Itt meg kell adnunk a használandó image-fájlok prefixét és postfixét.
Ez a függvény meghívja az előző, TT objektumokat inicializáló függvényt.
"TT:"
newPrefix: pre postfix: post
"egy új TT objektum létrehozása"
| o |
o := TT labelled: 'Tilitoli (Hoch Csaba)'.
o initPrefix: pre postfix: post.
^ o.
Collapse/expand kezelése
A collapse és expand azt jelenti, hogy az ablak összemegy, illetve eredeti
méretére visszaméreteződik. Ez akkor történik, ha rákattintunk a jobb felső
sarkában lévő körre. Egy ablak alapértelmezett viselkedése, hogy összemegy,
illetve visszaméreteződik, de a tagjaiban semmi nem változik (azaz az ablak
összecsukódik, de pl. a kockák továbbra is láthatóak). Az elvárt viselkedés
viszont az egy programmal szemben, hogy a collapse eseménykor az összes gomb,
ami az ablakon van, tűnjön el, az expand eseménykor pedig jelenjenek meg.
Ehhez felül kellett definiálni a TT-ben az ősosztály collapse,
collapseOrExpand és expand metódusait (ezek hívódnak meg, amikor a felhasználó
a körre kattint). Mindegyik esetben végrehajtjuk az ősosztály tevékenységét
(azaz maga az ablak összehúzódik vagy visszaméreteződik), majd ezután
végrehajtjuk a gombok elrejtését, illetve megjelenítését is.
Példaként a collapseOrExpand függvényt írjuk le itt, a többi is hasonló:
"TT:"
collapseOrExpand
super collapseOrExpand.
self collapseOrExpandSubMorphs.
"TT:"
collapseOrExpandSubMorphs
isCollapsed ifFalse: [
state show.
counter show.
randomizeButton show.
m do: [ :b | b show. ]
] ifTrue: [
state hide.
counter hide.
randomizeButton hide.
m do: [ :b | b hide. ]
]
Megjegyzés
A program elkészítéséhez a Squeak 3.9 rendszert (interpreter és fejlesztőkörnyezet) használtuk.
GNU Smalltalk Tutorial
A tutorial készítéséhez fel lett használva az alábbi forrás:
http://bioskop.fr/gtk_tutorial/ch-GettingStarted.html
A tutorial Linux (Kubuntu 12.04) alatt készült, Windows alatt a forrásfájlokat nyitó megjegyzésre nincsen szükség.
Az egyszerű hello world program a GNU Smalltalk parancssoros környezeténél már bemutatásra került, így itt első lépésként jelenítsünk meg egy üres ablakot. Ez annyira nem tud semmit, hogy a bezáró gomb ugyan működik rajta, de magát a programot parancssorból kell majd kilőni. (Ennek a használt GTK az oka, lsd. később.) Az Eval objektum végrehajtja a blokkban lévő utasításokat.
#!/usr/bin/env gst
Eval [
PackageLoader fileInPackage: 'GTK'.
window := GTK.GtkWindow new: GTK.Gtk gtkWindowToplevel.
window show.
GTK.Gtk main
]
Mentsük el a forráskódot egy st kiterjesztésű fájlba. Futtatása a gst fájlnév paranccsal történik. Az eredmény:
Készítsük el az első saját osztályunkat (HelloWorld)!
A Smalltalkban minden osztály az Object leszármazottja, ezt a megszokottal ellentétben ki is kell írnunk. Új osztályt a subclass üzenettel hozhatunk létre. Az osztálynak két változója lesz, egy ablak (window), illetve egy gomb (button), amelyet majd elhelyezünk az ablakban. Mivel GTK-val dolgozunk, használat előtt be kell töltenünk a szükséges csomagot, ez található az osztály előtti Eval-ban.
Szintén a GTK következménye, hogy szükségünk van egy destroy nevű metódusra, amely az ablak bezárásakor hívódik majd meg, és amely a GTK tudtára adja, hogy nincs tovább szükségünk a létrehozott ablakra. Ezen kívül feltétlenül szükségünk lesz még egy metódusra, ami létrehozza és megjeleníti az ablakot. Ezt show-nak szokás elnevezni.
A show-ban létrehozunk egy ablakot, mint ahogy az előző példában is tettük, valamint egy gombot. Ezen kívül három példát is láthatunk események és eseménykezelők összekapcsolására. A connectSignal segítségével összekötjük az ablak bezásárakor keletkező destroy eseményt az általunk írt destroy metódussal, illetve a gomb click eseményét a szintén általunk írt hello metódussal, amely kiír egy üzenetet a standard kimenetre, valamint a destroy metódussal. Így a gomb kiír a standard kimenetre, valamint bezárja a programot. Végül hozzáadjuk a gombot az ablakhoz, és megjelenítjük őket.
Az osztály definíciója után már csak még egy Eval blokkra van szükségünk az osztály példányosításához, illetve az ablak megjelenítéséhez és a GTK elindításához.
#!/usr/bin/env gst
Eval [
PackageLoader fileInPackage: 'GTK'.
]
Object subclass: HelloWorld [
| button window |
hello [
'Hello World' printNl
]
destroy [
'destroy signal occured' printNl.
GTK.Gtk mainQuit
]
show [
window := GTK.GtkWindow new: GTK.Gtk gtkWindowToplevel.
window connectSignal: 'destroy' to: self selector: #destroy userData: nil.
window setBorderWidth: 10.
button := GTK.GtkButton newWithLabel: 'Hello World!'.
button connectSignal: 'clicked' to: self selector: #hello userData: nil.
button connectSignal: 'clicked' to: self selector: #destroy userData: window.
window add: button.
button show.
window show
]
]
Eval [
hello := HelloWorld new.
hello show.
GTK.Gtk main
]
A program futásának eredménye: