Professional Documents
Culture Documents
Delphi 2
Delphi 2
procedure TElso.Hozzaad;
begin
x := x + xp;
y := y + yp;
end;
procedure TElso.Init;
begin
x := 0;
y := 0;
end;
1
TMasodik – az új, származtatott osztály (gyerek).
…
Egy ősből több származtatott osztályt is létrehozhatunk. Minden
var ElsoObj: TElso;
begin származtatott osztálynak csak egy őse lehet.
ElsoObj := TElso.Create;
ElsoObj.Inicialas;
…
ElsoObj.Free; Sokalakúság (polymorphism)
end;
Ugyanarra a metódusra a különböző objektumok
különbözőképpen reagáljanak. A származtatás során az ősosztályok
metódusai képesek legyenek az új, átdefiniált metódusok használatára
1.1. Az OOP alapelvei újraírás nélkül is.
Egységbezárás (encapsulation) Ezt virtuális (vagy dinamikus) metódusokkal érhetjük el.
Az adatokat és a hozzájuk tartozó eljárásokat egyetlen
egységben (osztályban) kezeljük. Az osztály adatmezői tárolják az
type TElso = class
adatokat, a metódusok kommunikálnak a külvilággal (az osztály …
adatmezőit csak a metódusokon keresztül változtathatjuk meg). procedure Akarmi; virtual;
end;
TMasodik = class(TElso)
Öröklés (inheritance) …
procedure Akarmi; override;
Az osztály továbbfejlesztése. Ennek során a származtatott end;
• Public
type TMasodik = class(TElso) Ide kerülnek olyan attribútumok, melyek nem igényelnek
…
end; speciális szabályozást. Az ilyen attribútumok értékeinek
megváltoztatása nem okozhat problémát az objektum működésében.
2 3
• Private Adatrejtés előnyei:
Az itt felsorolt attribútumokhoz a külvilág nem férhet hozzá. Ide A mezőkhöz a hozzáférés szabályozható.
írjuk általában azokat a változókat, melyek értékeit szabályozni akarjuk.
Továbbá ide kerülnek a segégváltozók is.
Adatrejtés hátrányai:
Az itt szereplő metódusokat a külvilág nem hívhatja meg, csak
Nem szabályozható, hogy csak az olvasást, vagy csak az írást
az adott osztály metódusaiból érhetők el.
engedélyezzük, illetve tiltjuk. Csak egy időben lehet mindkét elérést
engedélyezni / tiltani.
• Protected
Olyan attribútumok, melyekhez a külvilág nem férhet hozzá, de Adatok nem elrejtésének a hátránya:
a leszármazott osztályok metódusai hozzáférhetnek.
type TSzemely = class
Az itt feltüntetett metódusokat a külvilág nem hívhatja meg, de a public
eletkor: integer;
leszármaztatott osztályok metódusaiból meghívhatók. …
end;
…
type TSzemely = class
private begin
nev: string; …
eletkor: integer; Peti.eletkor := -400; { HIBÁS érték }
public …
procedure Valami; end;
end;
…
Adatrejtés hátrányai:
var
Peti: TSzemely; type TSzemely = class
begin private
… eletkor: integer;
Peti.eletkor := 18; { ez NEM mukodik !!! } …
… end;
end;
…
4 5
begin
…
Peti.eletkor := -400; { ez nem működik } 1.3. Virtuális mező (property)
writeln(Peti.eletkor); { de EZ SEM megy }
… A virtuális mezők (property) segítségével jobban megoldhatók
end;
az előző problémák az adatrejtésnél:
6 7
A read után meg kell adnunk, hogy ha a virtuális mezőt A konstruktor (constructor) speciális metódus, amely feladata
(Eletkor) olvassák, mit tegyen a program. Itt két lehetőségünk van: az objektum létrehozása az osztályból és annak alaphelyzetbe állítása
(mezők kezdőértékeinek beállítása).
• a read után írhatunk egy ugyanolyan típusú fizikai mezőt,
var
Peti: TSzemely;
A property kulcsszónál nem kötelező megadnunk mindkét begin
Peti := TSzemely.Create;
módot (read, write). Ha csak az egyiket adjuk meg, akkor a virtuális …
mező csak olvasható ill. csak írható lesz. end;
8 9
type A destruktor neve általában Destroy. A destruktor meghívhatjuk
TSzemely = class ezzel a névvel, vagy meghívhatjuk a Free metódus segítségével, amely
private
fEletkor: integer; leelenőrzi, hogy az objektum létezik, majd meghívja a Destroy nevű
public destruktort.
constructor Create;
constructor Init(kor:integer);
…
end; type
TSzemely = class
… private
fEletkor: integer;
constructor TSzemely.Create; public
begin destructor Destroy; override;
fEletkor := 0; …
… end;
end;
…
constructor TSzemely.Init;
begin constructor TSzemely.Destroy;
fEletkor := kor; begin
… …
end; end;
… …
var var
Peti, Jani: TSzemely; Peti: TSzemely;
begin begin
Peti := TSzemely.Create; Peti := TSzemely.Create;
Jani := TSzemely.Init(20); …
… Peti.Free; { vagy: Peti.Destroy; }
end; end;
A destruktor (destructor) szintén egy speciális feladatú Fontos, hogy a programunkban ha egy objektumot már nem
metódus, amely feladata az objektum megszüntetése és a memória használunk, akkor meghívjuk a Free metódust vagy közvetlenül a
felszabadítása. Itt még lehetőségünk van a lefoglalt erőforrások (file-ok, destruktort mielőtt kilépnénk a programból. Ha ezt nem tesszük meg,
memória, hálózati kapcsolatok, …) felszabadítására. beragadhat a memóriába a lefoglalt terület és az erőforrások (file,
hálózat, …) foglalva maradhatnak.
10 11
function Akarmi:integer;
end;
12 13
type TAOszt = class
public
type TAOszt = class
function Elso:integer;
public
function Masodik:integer;
function Elso:integer;
end;
function Masodik:integer;
end;
TBOszt = class(TAOszt)
public
TBOszt = class(TAOszt)
function Elso:integer;
public
end;
function Elso:integer;
…
function Masodik:integer;
end;
function TAOszt.Elso:integer;
…
begin
Result := 1;
function TAOszt.Elso:integer;
end;
begin
Result := 1;
function TAOszt.Madosik:integer;
end;
begin
Result := Elso + 1;
function TAOszt.Madosik:integer;
end;
begin
Result := Elso + 1;
function TBOszt.Elso:integer;
end;
begin
Result := 10;
function TBOszt.Elso:integer;
end;
begin
Result := 10;
…
end;
var
function TBOszt.Madosik:integer;
pa:TAOszt;
begin
pb:TBOszt;
Result := Elso + 1;
begin
end;
pa := TAOszt.Create;
pb := TBOszt.Create;
…
writeln(pa.Masodik); { Mennyit ír ki? 2-t }
writeln(pb.Masodik); { Mennyit ír ki? 2-t }
var
…
pa:TAOszt;
end;
pb:TBOszt;
begin
pa := TAOszt.Create;
Mi lehetne erre a megoldás? Például leírhatjuk újra a Masodik pb := TBOszt.Create;
writeln(pa.Masodik); { Mennyit ír ki? 2-t }
függvényt is a származtatott osztályba: writeln(pb.Masodik); { Mennyit ír ki? 11-t }
14 15
…
end; TBOszt = class(TAOszt)
public
function Elso:integer; override;
end;
Jó megoldás ez? Ez működik, de EZ NEM JÓ MEGOLDÁS, …
mivel:
function TAOszt.Elso:integer;
• Az ős osztály (TAOszt) függvényének programkódját nem begin
Result := 1;
biztos, hogy a TBOszt programozója ismeri. end;
var
pa:TAOszt;
A jó megoldás: virtuális metódus használata. Ez egy olyan pb:TBOszt;
begin
metódus, mint bármelyik másik metódus, a virtuális jelzőt a pa := TAOszt.Create;
programozónak kell rátennie egy virtual; szó hozzáírásával az pb := TBOszt.Create;
writeln(pa.Masodik); { Mennyit ír ki? 2-t }
eljáráshoz az ősosztályban. Ez azt jelzi a fordítónak, hogy a metódust a writeln(pb.Masodik); { Mennyit ír ki? 11-t }
származtatott osztályban valószínűleg felül fogják definiálni. A …
end;
származtatott osztályban (és abból származtatott osztályokban is) a
felüldefiniált eljáráshoz override; kulcsszót kell kitenni.
Mit is jelent valójában ez a kulcsszó? Az ilyen, virtuális
metódusokat a fordító másképp kezeli. A virtuális metódus hívásának
type
TAOszt = class pillanatában az objektum ismeretében megkeresi a legfejlettebb verziót
public az adott metódusból, és ez hívódik meg. Tehát az, hogy melyik
function Elso:integer; virtual;
function Masodik:integer;
end;
16 17
változatú „Elso” függvény kerül meghívásra, menet közben dől el. Ezt • ha a leszármaztatott osztályban van VIRTUAL, akkor új
nevezik sokalakúságnak (polymorphism). sort adunk a táblázat végéhez.
Honnan tudja a program eldönteni, hogy melyik változatot kell Milyen lesz így a virtuális metódusok táblája?
meghívnia, melyik a legfrissebb változat? Ebben segít a virtuális
• ugyanazon metódus a táblázatban (ősök és
metódus tábla (VMT). Minden osztályhoz készül egy ilyen tábla,
leszármazottak VMT-iban) mindig ugyanazon sorban
melyben szerepel a virtuális metódus neve és annak elérhetősége. A
fog szerepelni,
VMT-ban csak a virtuális metódusok szerepelnek. Pl.:
• a származtatott VMT-ja legalább annyi elemű lesz, mint
az ősé, de ennél hosszabb is lehet (ha van benne új
type TElso = class
procedure A; virtuális metódus),
procedure B;
procedure C; virtual; • ahogy haladunk lefelé az öröklődési gráfban, úgy a
procedure D; virtual; VMT-k egyre hosszabbak és hosszabak lesznek.
end;
A VMT előnye: gyors, mivel nem kell keresni a táblázatban, ha
TMasodik = class (TElso)
procedure A; az ős VMT-ben egy metódus az N. helyen van, akkor minden
procedure C; override;
leszármazottjánál is az N. sorban lesz.
Procedure E; virtual;
end;
A VMT hátránya: sok memóriát igényel (bejegyzésenként
4 byte).
Ezekhez az osztályokhoz tartozó VMT-k:
Minden osztályhoz 1 VMT tartozik (nem példányonként 1 drb),
TElso.VMT TMasodik.VMT tehát ugyanazon osztályból létrehozott objektumnak (példánynak)
metódus C = TElso.C metódus C = TMasodik.C
metódus D = TElso.D metódus D = TElso.D ugyanaz a VMT-ja.
metódus E = TMasodik.E
Az objektumhoz a VMT hozzárendelését a konstruktor végzi,
ezt a kódrészletet a Delphi generálja bele a konstruktor lefordított
A leszármazott osztály VMT-je úgy jön létre, hogy: kódjába.
• lemásuljuk az ős VMT-jét, A konstruktor nem lehet virtuális, mivel a konstruktor hívásakor
• ha a leszármaztatott osztályban van OVERRIDE, akkor még nem működik a mechanizmus (késői kötés).
azt a sort kicseréljük,
18 19
A konstruktorban a VMT hozzárendelése az objektumhoz a A működése a programban ugyanaz, mint ha virtuális metódust
konstruktor elején történik (a Delphiben), így a konstruktor belsejében használtunk volna. A különbség a memóriafoglalásban és a
már lehet virtuális metódust hívni. gyorsaságban van.
A destruktor (Destroy metódus) mindig virtuális, ezért tudja a A dinamikus metódusok a VMT-tól egy kicsit eltérő, Dinamikus
Free metódus meghívni mindig a megfelelő destruktort. Metódus Táblát (Dynamic Method Table) használnak a legújabb
verziójú metódus megtalálásához. Ennek felépítését is egy példán
A virtuális metódus paraméterezése és visszatérési értéke
szemléltetjük:
nem változhat, mert akkor az ős metódusa nem lenne képes meghívni
ezt a felüldefiniált változatot!
type TElso = class
procedure A;
procedure B;
1.7. Dinamikus metódus (dynamic) procedure C; dynamic;
procedure D; dynamic;
end;
A dinamikus metódusok hasonlóan a virtuális metódusokhoz a
polimorfizmus futás közbeni támogatására szolgálnak. TMasodik = class (TElso)
procedure A;
A Delphiben a dinamikus metódusokat hasonlóan tudjuk procedure C; override;
Procedure E; dynamic;
kialakítani, mint a virtuális metódusokat, csak itt a virtual szó helyett a end;
dynamic szót használjuk. Pl.
TBOszt = class(TAOszt) Mindegyik táblában szerepel az is, hogy melyik osztályból lett
public
function Elso:integer; override; származtatva az új osztály. Ha nincs ilyen osztály, akkor ennek a
end; mutatónak az értéke nil (ős = nil).
…
A származtatott osztály DMT-ja a következő képpen alakul ki:
20 21
• kezdetben nem másoljuk le az ős DMT-jét, hanem csak metódus ritkán kerül meghívásra, és ezért nem számít a
override esetén vagy dynamic esetén egy új sort sebességcsökkenés.
szúrunk be,
Virtuális legyen egy metódus minden más esetben, tehát ha
A DMT tábla tehát csak változtatásokat tartalmaz. Így sűrűn felüldefiniáljuk, vagy a metódust sűrűn hívjuk (pl. egy ciklusban)
lehetséges, hogy valamelyik metódusra utaló bejegyzés nem is szerepel és fontos a sebesség.
csak az ősben, vagy annak az ősében (amennyiben a leszármazott
osztályokban nem lett fölüldefiniálva az override segítségével).
A fentiekből adódik, hogy futás közben a DMT-k között lehet, 1.8. Absztrakt metódus (abstract)
hogy keresni kell. Ha nincs az adott dinamikus metódusra utalás az Nézzük meg, hogyan nézhetne ki egy osztály, amelyet azért
objektumhoz tartozó DMT táblában, akkor meg kell nézni az ős DMT- hozunk létre, hogy később ebből származtassunk új osztályokat, pl. kör,
jében, ha ott sincs, akkor annak az ősének a DMT-jében, stb. négyzet, téglalap grafikus objektumok osztályait.
A DMT előnye: kevesebb memóriát igényel, mivel nincs az ős
egész DMT-je lemásolva, csak a változtatások vannak bejegyezve. type TGrafObj = class
private
A DMT hátránya: a keresés miatt lassabb. x,y: integer;
public
A dinamukus és virtuális metódusokat lehet keverni egy procedure Kirajzol; virtual;
osztályon belül. Tehát egy osztályban lehetnek dinamukus és virtuális procedure Letorol; virtual;
procedure Mozgat(ujx, ujy:
metódusok is. Azt, hogy valamelyik metódus milyen lesz, a metódus integer);
legelső bevezetésekor kell megadni a „virtual” vagy „dynamic” kulcsszó end;
22 23
stb., de általános esetben ezt nem tudjuk. Ezért itt ez a következő A származtatott osztályoknál az absztrakt metódusokat az
módon nézhetne ki, mivel ez a leszármazottakban úgyis felül lesz override kulcsszó segítségével írhatjuk felül. Nem kötelező rögtön
definiálva (kör, négyzet, téglalap, stb. kirajzolására): mindegyiket felülírni, de akkor megint nem lehet példányosítani
(objektumot létrehozni belőle).
24 25
procedure JobbraMozdit(graf: TObject);
begin
Tehát: a gyermek-osztályok felülről kompatibilis típusok az ős- if graf is TKor then …
end;
osztályukkal, és azok őseivel is: biztos, hogy megvannak nekik is
ugyanazon nevű mezők és metódusok, mint az ősének (mivel örökölte
őket). Ebből következik, hogy minden osztály kompatibilis a TObject- A then ágra akkor kerül a vezérlés, ha a „graf” objektum
el. kompatibilis a TKor osztállyal. Vagyis ha a „graf” egy TKor vagy annak
valamelyik leszármazott osztályából létrehozott objektum.
Viszont a JobbraMozdit eljárás az alábbi paraméterezéssel nem
működik, mivel a TObject-nek nincs „Mozgat” metódusa. De az alábbi módon az eljárás még mindig nem működik,
ugyanis a graf-ról az eljárás még mindig úgy tudja, hogy TObject és
ennek nincs Mozgat metódusa.
procedure JobbraMozdit(graf: TObject);
begin
graf.Mozgat(graf.x + 1, graf.y);
end; procedure JobbraMozdit(graf: TObject);
begin
if graf is TKor then
graf.Mozgat(graf.x + 1, graf.y);
Tehát a típus megválasztásánál mindig azon osztály nevét kell end;
megadni, amelyik már tartalmazza az összes olyan metódust és mezőt,
amelyekre az eljárás törzsében hivatkozunk.
Az is operátor csak egy ellenőrző függvény, nem alakítja át a
TObject-et TKor-é!
Előfordulhat azonban, hogy nem sikerül találni a fenti 1.11. Típuskonverzió (as operátor)
követelménynek megfelelő típust. Ilyenkor olyan típust kell megadni,
A fenti probléma megoldására alkalmazható az as operátor.
amelyikkel minden szóba jöhető példány kompatibilis (ez legrosszabb
Ennek használata: objektumnév AS osztálynév. Pl:
esetben a TObject), és az eljáráson belül if-ek (elágazások)
segítségével kell eldönteni, hogy a paraméterként megadott példány
valójában milyen típusba tartozik. A típus vizsgálatához az is operátor procedure JobbraMozdit(graf: TObject);
begin
használható a következő képpen: objektumnév IS osztálynév. Pl.: if graf is TKor then
(graf as TKor).Mozgat(graf.x + 1, graf.y);
end;
26 27
metódusa, mellyel a megadott osztályból készített objektumot adott x,y
koordinátákra helyezzük. A TObject osztálynak (ős osztálynak) viszont
vagy egy másik megoldás:
nincs ilyen Mozgat metódusa. Milyen típust adjunk meg ilyenkor az
procedure JobbraMozdit(graf: TObject); alábbi eljárás paramétereként?
begin
if graf is TKor then
TKor(graf).Mozgat(graf.x + 1, graf.y);
procedure ArrebRak(x: ………… );
end;
begin
x.Mozgat(12,17);
end;
Ilyenkor a kifejezés idejére az objektumot a fordítóprogram úgy
tekinti, mintha az adott osztályból készült példány lenne.
Eddigi ismereteink alapján az egyetlen szóba jöhető osztály a
Ha az as operátort helytelenül alkalmazzuk (pl. az objektum
TObject, viszont ekkor az eljárás belsejét meg kell változtatnunk a
nem kompatibilis a megadott osztállyal), akkor futás közben
következőre:
hibaüzenetet kapunk! Ezért az as operátort az is operátorral való
vizsgálat után alkalmazzuk!
if (x is TKor) then (x as TKor).Mozgat(12,17);
if (x is TLabda) then
(x as TLabda).Mozgat(12,17);
Képzeljük el, hogy a következő helyzet áll elő: Ez azonban nem a legjobb megoldás, ugyanis később ha újabb
osztályok kerülnek elő, melyeknek van Mozgat metódusuk, azokra
újabb if-ekkel kell kiegészíteni az eljárást.
TObject
Az ilyen esetekben a megoldás az interface használata lehet.
Segítségével azt lehet leírni, hogy különböző ágakon levő osztályokban
type
IMozgathato = interface
Van egy TKor osztályunk, melyet a TObject-ből származtattunk procedure Mozgat(x,y: integer);
end;
és van egy TLabda osztályunk, melyet szintén a TObject-ből
származtattunk. Mindkét származtatott osztálynak van Mozgat
28 29
Az interface olyan osztálynak látszó valami, melyben: Az ArrebRak eljárásunk pedig így módosul:
30 31
darab := darab + 1; Más nyelveken, mint pl. a Java, C# az ilyen jellegű mezőket az
end;
osztály részévé lehet tenni, természetesen megjelölve, hogy azok csak
egy példányban kellenek. Erre általában a „static” módosító szó
De hol deklaráljuk a „darab” mezőt? A TAkarmi osztályon belül használható.
nem deklarálhatjuk, mivel ekkor minden példányosításnál egy új mező
A következő kérdés, hogy hogyan lehet az ilyen változók
jön létre, melynek értéke kezdetben meghatározatlan.
kezdőértékét megadni. Ezt a unit inicializációs részében tehetjük meg:
A megoldás, hogy nem az osztályban, hanem a Unit-ban
deklaráljuk:
unit Sajat;
interface
unit Sajat;
type TAkarmi = class
interface …
constructor Create;
type TAkarmi = class …
… end;
constructor Create;
… implementation
end;
var darab: integer;
implementation
constructor TAkarmi.Create;
var darab: integer; begin
darab := darab + 1;
constructor TAkarmi.Create; end;
begin
darab := darab + 1; …
end;
initialization
…
darab := 0;
end.
end.
32 33
Ha a mező a unit interface részében lenne deklarálva, akkor Ez azonban nem a legjobb megoldás, mivel a Darabszam
egyszerűen elérhető a unit-on kívülről is. függvény használatához létre kell hoznunk egy objektumot a TAkarmi
osztályból:
Ha az implementation részben van deklarálva, mint a mi
esetünkben is, akkor nem érhető el kívülről, tehát kell hozzá
készítenünk egy függvény, amely kiolvassa az értékét: var x: TAkarmi;
begin
x := TAkarmi.Create;
Write(’Eddig ’,x.Darabszam,’ objektum van.’);
unit Sajat; …
end;
interface
darab := 0; …
end.
34 35
Ezt a függvényt most már példányosítás nélkül is tudjuk nyertes.Gratulal(self);
end;
használani:
procedure TJatekos.Gratulal;
begin
begin writeln(’Gratulalt nekem: ’,gratulalo.nev);
… end;
Write(’Eddig ’,TAkarmi.Darabszam,’ drb van.’);
…
end;
2. OOP a gyakorlatban
A ilyen osztály szintű metódusok belsejében csak osztály szintű Ebben a részben néhány gyakorlati példán keresztül
mezőket használhatunk, példány szintűt (melyek az osztály belsejében szemléltejük az objektum orientált programozás alapjait.
vannak definiálva) nem használhatunk! A gyakorlatban az osztályokat (azok deklarációját és
metódusainak implementációját) mindig külön Unit-ba tegyük, így
programunk áttekinthető lesz és be lesz biztosítva az adatrejtés is az
1.15. Hivatkozás a példányra a self azonosítóval alkalmazáson belül.
Az objektumra a saját metódusának belsejében a self (más Új unitot az alkalmazásba a File – New – Unit - Delphi for
nyelvekben „this”) azonosítóval hivatkozhatunk. Pl: Win32 menüpont segítségével tehetünk.
type
TJatekos = class 2.1. Pattogó labdák
…
nev: string;
Ebben az alkalmazásban készítünk egy TLabda osztályt, melyet
penz: integer;
procedure kihasználva írunk egy olyan programot, melyben labdákat jelenítünk
Vesztett(nyertes: TJatekos);
procedure meg. Mindegyik labda valamilyen irányba mozog, ha eléri a kép szélét,
Gratulal(gratulalo: TJatekos); akkor visszapattan. Ha valamelyik labda ütközik egy másik labdával,
…
end; akkor elpattannak egymástól. 002
procedure TJatekos.Vesztett;
begin
penz := penz – 1000;
nyertes.penz := nyertes.penz + 1000;
36 37
public
x,y,dx,dy:integer;
constructor Create(kx, ky,
kdx, kdy, ksz:integer;
khova:TImage);
procedure Kirajzol;
procedure Letorol;
procedure Mozgat;
end;
implementation
constructor TLabda.Create;
begin
x:=kx;
y:=ky;
dx:=kdx;
dy:=kdy;
sz:=ksz;
hova:=khova;
end;
procedure TLabda.Kirajzol;
begin
hova.Canvas.Pen.Color := sz;
hova.Canvas.Pen.Width := 2;
hova.Canvas.Brush.Color := sz;
hova.Canvas.Ellipse(x-20,y-20,x+20,y+20);
end;
Nézzük először is mit tartalmaz a TLabda osztályunk, melyet a procedure TLabda.Letorol;
Unit2 modulban írtunk meg: begin
hova.Canvas.Pen.Color := clWhite;
hova.Canvas.Pen.Width := 2;
hova.Canvas.Brush.Color := clWhite;
unit Unit2; hova.Canvas.Ellipse(x-20,y-20,x+20,y+20);
end;
interface
procedure TLabda.Mozgat;
uses ExtCtrls, Graphics; begin
Letorol;
type TLabda = class x:=x+dx;
private y:=y+dy;
hova:TImage; if (x+dx+20>hova.Width) or (x+dx-20<0) then
sz:integer; dx:=-dx;
38 39
if (y+dy+20>hova.Height) or (y+dy-20<0) then Az ábrán a szürke kör jelöli a labda régi helyét, a fekete a labda
dy:=-dy;
Kirajzol; az új helyét egy elmozdulás után. Láthatjuk, hogy a labda új x, y
end; koordinátáit úgy számíthatjuk ki, hogy az x-hez hozzáadjuk a dx-et, y-
40 41
Az alkalmazásunkban lesz egy 10 elemű TLabda típusú tömb,
{$R *.dfm}
mely valójában a 10 pattogó labda objektumunk lesz.
var l:array [1..10] of TLabda;
Ezt a 10 objektumot a Form – OnCreate eseményében fogjuk
létrehozni. function Utkozik(a,b:TLabda):boolean;
begin
A labdák mozgatását a Timer – OnTimer eseményében if Sqrt(Sqr(a.x-b.x)+Sqr(a.y-b.y))<=40 then
result := true
végezzük el. else
result := false;
Végül a programból való kilépéskor a Form megszüntetésekor a end;
Form – OnDestroy eseményben felszabadítjuk az objektumoknak
procedure TForm1.FormCreate(Sender: TObject);
lefoglalt memóriát. var
i,j,kdx,kdy:integer;
Alkalmazásunk forráskódja így néz ki: ok:boolean;
begin
randomize;
for i:=1 to 10 do
unit Unit1;
begin
repeat
interface
kdx:=random(3)-1;
kdy:=random(3)-1;
uses
until (kdx<>0) or (kdy<>0);
Windows, Messages, SysUtils, Variants, Classes,
l[i]:=TLabda.Create(random(Image1.Width-60)+30,
Graphics, Controls, Forms, Dialogs, ExtCtrls, Unit2;
random(Image1.Height-60)+30,
kdx, kdy,
type
RGB( random(256),
TForm1 = class(TForm) random(256),
Image1: TImage;
random(256)),
Timer1: TTimer;
Image1);
procedure Timer1Timer(Sender: TObject);
repeat
procedure FormCreate(Sender: TObject);
ok:=true;
procedure FormDestroy(Sender: TObject);
for j:=1 to i-1 do
private
if utkozik(l[i],l[j]) then ok:=false;
{ Private declarations }
if not ok then
public
begin
{ Public declarations }
l[i].x := random(Image1.Width-60)+30;
end;
l[i].y := random(Image1.Height-60)+30;
end;
var
until ok;
Form1: TForm1;
l[i].Kirajzol;
end;
implementation
end;
42 43
labdával. Ha ütközés van, akkor az új labdának más véletlen
procedure TForm1.Timer1Timer(Sender: TObject);
var koordinátákat generálunk ki és újból leellenőrizzük, nem ütközik-e
i,j,k:integer; valamelyik labdával. Ezt mindaddig ismételgetjük, amíg nem sikerül
begin
for i:=1 to 10 do olyan koordinátákat kigenerálnunk, melynél már a labda nem ütközik
begin egyik előtte létrehozott labdával sem. Végül a Kirajzol metódus
l[i].Mozgat;
for j:=i+1 to 10 do segítségével kirajzoljuk a labdát, és jöhet a következő labda
if Utkozik(l[i],l[j]) then begin létrehozása…
k:=l[i].dx;
l[i].dx:=l[j].dx; A Timer – OnTimer eseményében mindegyik labdának
l[j].dx:=k;
k:=l[i].dy; meghívjuk a Mozgat metódusát, majd leelenőrizzük hogy a labda az új
l[i].dy:=l[j].dy; helyen nem ütközik-e valamelyik utána következő labdával. Ha ütközés
l[j].dy:=k;
end; áll fenn, akkor egyszerűen kicseréljük a két labda dx, dy adatait, így úgy
end; néz ki, mintha a két labda elpattanna egymástól (amelyik irányba
end;
mozgott az egyik labda, abba fog mozogni a másik és amelyikbe
procedure TForm1.FormDestroy(Sender: TObject); mozgott a másik, abba az irányba fog mozogni az előző labda).
var
i:integer;
begin
for i:=1 to 10 do l[i].Free;
end;
end.
A Form – OnCreate eseményében sorban létrehozzuk a A szürke nyílak jelölik az ütközés előtti régi irányokat (melyek
labdákat véletlenszerű adatokkal. Minden labda létrehozása után dx, dy segítségével vannak meghatározva), a piros nyilak jelölik az új
ellenőrizzük, hogy nem ütközik-e valamelyik már előtte létrehozott
44 45
irányokat (melyek valójában a két labda dx, dy mezőinek felcserélésével
jöttek létre).
46 47
a kört, téglalapot és háromszöget megvalósító osztályokat. Ennek az constructor Create(kx, ky, kdx, kdy,
ka, kb: integer; kkep: Timage);
általános grafikus osztálynak a programunkban a TGrafObj nevet procedure kirajzol; override;
adtuk. Ebből az osztályból hozzuk létre az öröklést felhasználva a TKor, procedure letorol; override;
end;
TTeglalap és THaromszog osztályokat.
THaromszog = class (TGrafObj)
Az osztályokat tartalmazó Unit2 modulunk így néz ki: private
a,m:integer;
public
unit Unit2; constructor Create(kx, ky, kdx, kdy,
ka, km: integer; kkep: TImage);
interface procedure kirajzol; override;
procedure letorol; override;
uses ExtCtrls,Graphics; end;
type implementation
TGrafObj = class
private constructor TGrafObj.Create;
x,y: integer; begin
dx,dy: integer; x := kx;
kep: TImage; y := ky;
public dx := kdx;
constructor Create(kx, ky, dy := kdy;
kdx, kdy: integer; kkep: TImage); kep := kkep;
procedure kirajzol; virtual; abstract; kirajzol;
procedure letorol; virtual; abstract; end;
procedure mozdul;
end; procedure TGrafObj.mozdul;
begin
TKor = class (TGrafObj) letorol;
private if (x+dx>kep.Width) or (x+dx<0) then dx := -dx;
s:integer; x := x + dx;
public if (y+dy>kep.Height) or (y+dy<0) then dy := -dy;
constructor Create(kx, ky, kdx, kdy, y := y + dy;
ks: integer; kkep: Timage); kirajzol;
procedure kirajzol; override; end;
procedure letorol; override;
end; constructor TKor.Create;
begin
TTeglalap = class (TGrafObj) s := ks;
private inherited Create(kx,ky,kdx,kdy,kkep);
a,b:integer; end;
public
procedure TKor.kirajzol;
48 49
begin kep.Canvas.pen.Width := 2;
kep.Canvas.pen.Width := 2; kep.Canvas.Pen.Color := clBlack;
kep.Canvas.Pen.Color := clRed; kep.Canvas.MoveTo(x - a div 2, y + m div 2);
kep.Canvas.Ellipse(x-s,y-s,x+s,y+s); kep.Canvas.LineTo(x + a div 2, y + m div 2);
end; kep.Canvas.LineTo(x, y - m div 2);
kep.Canvas.LineTo(x - a div 2, y + m div 2);
procedure TKor.letorol; kep.Canvas.FloodFill(x,y,clBlack,fsBorder);
begin kep.Canvas.Pen.Color := clBlue;
kep.Canvas.pen.Width := 2; kep.Canvas.MoveTo(x - a div 2, y + m div 2);
kep.Canvas.Pen.Color := clWhite; kep.Canvas.LineTo(x + a div 2, y + m div 2);
kep.Canvas.Ellipse(x-s,y-s,x+s,y+s); kep.Canvas.LineTo(x, y - m div 2);
end; kep.Canvas.LineTo(x - a div 2, y + m div 2);
end;
constructor TTeglalap.Create;
begin procedure THaromszog.letorol;
a := ka; begin
b := kb; kep.Canvas.pen.Width := 2;
inherited Create(kx,ky,kdx,kdy,kkep); kep.Canvas.Pen.Color := clWhite;
end; kep.Canvas.MoveTo(x - a div 2, y + m div 2);
kep.Canvas.LineTo(x + a div 2, y + m div 2);
procedure TTeglalap.kirajzol; kep.Canvas.LineTo(x, y - m div 2);
begin kep.Canvas.LineTo(x - a div 2, y + m div 2);
kep.Canvas.pen.Width := 2; end;
kep.Canvas.Pen.Color := clGreen;
kep.Canvas.Rectangle(x - a div 2, y - b div 2,
x + a div 2, y + b div 2); end.
end;
procedure TTeglalap.letorol;
begin Ami érdekes a TGrafObj osztálynál és még nem találkoztunk
kep.Canvas.pen.Width := 2; vele az előző feladatban, az a Letorol és Kirajzol metódusok, melyek itt
kep.Canvas.Pen.Color := clWhite;
kep.Canvas.Rectangle(x - a div 2, y - b div 2, virtuális, abstrakt metódusok. Tehát itt csak felsoroljuk őket, de azt,
x + a div 2, y + b div 2); hogy konkrétan mit csináljanak ezek a metódusok, nem adjuk meg (nem
end;
implementáljuk őket). Ez érthető, hiszen itt ez csak egy általános
constructor THaromszog.Create; grafikus osztály, melynél még nem tudjuk mit fog kirajzolni és letörölni.
begin
a := ka; Ezt a TGrafObj osztályt csupán azért készítettük, hogy ebből könnyel
m := km;
létrehozhassuk a másik három osztályt.
inherited Create(kx,ky,kdx,kdy,kkep);
end;
A TKor osztálynak lesz egy plussz mezője, ami a kör sugarát
procedure THaromszog.kirajzol; jelöli (s). A Create metódusban beállítjuk ennek a mezőnek a kezdeti
begin
50 51
értékét, majd az inherited Create(kx,ky,kdx,kdy,kkep); { Private declarations }
public
parancs segítségével meghívjuk az ős (TGrafObj) osztály Create { Public declarations }
metódusát, amely elvégzi a többi beállítást és kirajzolja az alakzatot. end;
52 53
mx, my, random(50)+10,
random(50)+10, Image1);
3. Vizuális komponensek létrehozása
end; és megszüntetése a program
end;
futása közben
procedure TForm1.FormDestroy(Sender: TObject);
var Mivel a vizuális komponensek is objektumok, amelyek bizonyos
i:integer;
osztályokból lettek létrehozva, ezért ezeknél is felhasználhatjuk ez előző
begin
for i:=1 to 15 do k[i].Free; fejezetekben tanultakat.
end;
Amikor például tervezési nézetben egy nyomógombot teszünk a
end.
formra, melynek neve Button1 lesz, akkor valójában a TButton
osztályból hozunk létre egy példányt (objektumot), amely a Button1.
Megfigyelhetjük, hogy a programban ugyanazt a tömböt Vizuális komponenseket a program futása alatt is bármikor létre
használjuk a TKor, TTeglalap, THaromszok objektumokra is. Ez azért tudunk hozni. Például egy nyomógomb a program futása alatt a
lehetséges, mivel ezt a tömböt TGrafObj típusúnak deklaráltuk, és a következő néhány sorral hozható létre:
TGrafObj kompatibilis az összes leszármaztatott osztállyal.
54 55
programkódot. Természetesen Self helyett írhattunk volna Form1-et is, X, Y: Integer);
…
ha ez a neve a formunknak. private
{ Private declarations }
Amikor a nyomógomb tulajdonosa felszabadul a memóriából procedure MyButtonClick(Sender:TObject);
(tehát a mi esetünkben a form), akkor automatikusan a nyomógomb is public
{ Public declarations }
felszabadul. Ebben az esetben tehát nem kell törődnünk a end;
nyomógombunk felszabadításával, amennyiben azt szeretnénk, hogy az
…
létezzen addig, amíg a formunk létezik. Természetesen, ha előbb meg
szeretnénk szüntetni a nyomógombot, akkor azt felszabadíthatjuk a implementation
MyButton.Free metódussal. {$R *.dfm}
A gomb létrehozása után meg kell adnunk a gomb szülőjét procedure TForm1.MyButtonClick(Sender:TObject);
(MyButton.Parent). Amit megadunk a gomb szülőjének, azon a begin
… { ide jön az a programrész,
komponensen fog elhelyezkedni a nyomógomb. A mi esetünkben itt … amely a gomb megnyomásakor torténjen }
szintén Self-et adtunk meg, ami ebben az esetben az alkalmazásunk end;
formját jelenti. Itt is megadhattuk volna helyette pl. a Form1-et, vagy procedure TForm1.FormMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState;
akár Panel1-et, ha van panel komponensünk a formon.
X, Y: Integer);
var MyButton: TButton
Végül megadjuk a gomb elhelyezkedését a Top és Left
begin
tulajdonságok segítségével, és a gombon megjelenítendő szövegget a MyButton := TButton.Create(Self)
MyButton.Parent := Self;
Caption tulajdonság segítségével.
MyButton.Left := X;
MyButton.Top := Y;
A futásidőben létrehozott nyomógombhoz is adhatunk meg MyButton.Caption := 'Katt ide…';
eseményeket. Ha például azt szeretnénk, hogy a felhasználónak a OnClick := MyButtonClick;
…
gombunkra kattintáskor történjen valami, akkor előbb meg kell írnunk az end;
OnClick eseményhez tartozó eljárást, majd ezt az eljárást hozzárendelni
end.
a mi nyomógombunk OnClick eseményéhez. Ehhez a következő
sorokkal kell kibővítenünk alkalmazásunk forráskódját:
56 57
Hasonlóan a nyomógombhoz létrehozhatunk futásidőben más var
Form1: TForm1;
vizuális komponenseket is, például Label-t, Edit-et, Image-t, stb.
implementation
{$R *.dfm}
3.1. Nyomógombok létrehozása, megszüntetése
procedure TForm1.ButtonMouseUp(Sender: TObject;
egérkattintáskor Button: TMouseButton; Shift: TShiftState;
A következő példaprogram segítségével megmutatjuk a X, Y: Integer);
begin
gyakorlatban is a nyomógomb létrehozását és megszüntetését. (Sender as TButton).Free;
end;
A feladatban a program indításakor csak egy üres form-unk
lesz. Ha egérrel erre a formra kattintunk, akkor a kattintás helyére procedure TForm1.FormMouseDown(Sender: TObject;
Button: TMouseButton; Shift: TShiftState;
létrehozunk egy nyomógombot. Ha a létrehozott nyomógombok X, Y: Integer);
valamelyikére kattintunk, akkor az adott nyomógombot megszüntetjük begin
With TButton.Create(Self) do
(felszabadítjuk a memóriából). 037 begin
Parent := Self;
Alkalmazásunk forráskódja a következőket fogja tartalmazni: Left := X - Width div 2;
Top := Y - Height div 2;
Caption := 'Click';
OnMouseUp := ButtonMouseUp;
unit Unit1; end;
end;
interface
end.
uses
Windows, Messages, SysUtils, Variants, Classes,
Graphics, Controls, Forms, Dialogs, StdCtrls;
A forráskódban a vastag betűkkel írtakat kellett nekünk
type
TForm1 = class(TForm) beírnunk, a többit a Delphi írta be helyettünk, ahogy azt már
procedure FormMouseDown(Sender: TObject; Button: megszokhattuk. Láthatjuk, hogy létre kellett hoznunk egy egész eljárást
TMouseButton; Shift: TShiftState; X, Y: Integer);
private (TForm1.ButtonMouseUp), melyet a program futása közben mindegyik
{ Private declarations } nyomógomb OnMouseUp eseményéhez rendeltünk hozzá. A
procedure ButtonMouseUp(Sender: TObject; Button:
TMouseButton; Shift: TShiftState; X, Y: Integer); programban a nyomógomra kattintáskor azért ezt az eseményt
public használtunk az OnClick esemény helyett, mert ez után az esemény után
{ Public declarations }
end; már nincs kritikus kód, tehát ebben megtörténhet a gomb felszabadítása
a Free metódusának a segítségével.
58 59
Ha az OnMouseUp helyett az OnClick eseményt használtuk hagyományos Button összes tulajdonságával és metódusával, tartalmaz
volna, és ebben szabadítottuk volna fel a gombot, akkor a nyomógomb még néhány tulajdonságot (dx, dy) és egy Mozgat metódust, melynek
felszabadításakor hibajelentést kaptunk volna. Ennek oka az, hogy a meghívásával a nyomógomb mozogni fog a formon a dx-szel és dy-nal
gomb lenyomásakor bekövetkezik az OnMouseDown, OnClick és végül megadott irányba. 038
az OnMouseUp esemény. Ha azonban az OnClick-ben felszabadítjuk a
Ezt az új osztályt (TMyButton) a TButton osztályból
nyomógombot, akkor az OnMouseUp esemény már nem tud
származtattuk. A form létrehozásakor az új osztályból létrehoztunk 10
bekövetkezni utána, ami hibához vezet.
darab objektumot. Ha valamelyik ilyen nyomógombra rákattintunk, azt
Mivel az egyes létrehozott gombokat nem szükséges külön felszabadítjuk a memóriából.
megjegyeznünk, ezért a nyomógombokra nem használtunk külön
Alkalmazásunk tervezési nézetben a formon csak egy Timer
változókat (tehát a létrehozott objektumokra nincs hivatkozásunk, mint
komponenst fog tartalmazni, amely a gombok mozgatását végzi el. A
amilyen az előző fejezetben volt a MyButton). Helyette a with parancs
programunk forráskódja:
segítségével oldottuk meg a kezdeti tulajdonságok beállítását rögtön a
gomb létrehozása után.
unit Unit1;
interface
3.2. Vizuális komponensből származtatott osztály uses
Windows, Messages, SysUtils, Variants, Classes,
Amint már mondtuk, a vizuális komponensek (nyomógomb, kép, Graphics, Controls, Forms, Dialogs, ExtCtrls,
beviteli doboz, stb.) is osztályakból létrehozott objektumok. Ezért StdCtrls;
60 61
public a[i].Top:=Random(Form1.ClientHeight-30);
procedure Mozdul; repeat
end; a[i].dx:=random(3)-1;
a[i].dy:=random(3)-1;
var until (a[i].dx<>0) or (a[i].dy<>0);
Form1: TForm1; a[i].OnMouseUp:=ButtonMouseUp;
end;
implementation end;
62 63
eket használni? Ehhez nézzük meg a DLL-ek felhasználhatóságát és
azok főbb előnyeit:
4.1. DLL készítése osztójának meghatározására (lko), egyet pedig a legkisebb közös
többszörös meghatározására (lkt). Továbbá a DLL tartalmazzon még
Ha dinamikus csatolású könyvtárat szeretnénk létrehozni,
egy paraméter nélküli függvényt, amelynek meghívásával megkapjuk a
válasszuk ki a Delphi fejlesztőkörnyezetében a File – New – Other…
Fibonacci számsor (1, 1, 2, 3, 5, 8, 13, 21, …) következő elemét (fibo)
menüpontot. A megnyíló ablakban válaszzuk ki a Delphi Projects
és egy paraméter néküli eljárást, melynek segítségével beállítjuk a
kategória alatt a DLL Wizard-ot, majd klikkeljünk az OK gombra.
Fibonacci számok generálását az elejére (fiboinit). 005
64 65
programokból, melyek a DLL-t használni fogják) is meg kívánunk hívni, end;
felsoroljuk az exports záradék után. Itt adhatunk a függvényeknek és
{Fibonacci szamok}
eljárásoknak más nevet is (ahogy ezt tettük a fibo-nál és fiboinit-nél), var
a1, a2, ssz: int64;
vagy hagyhatjuk azt a nevet, melyen a DLL-ben a függvényt megírtuk.
Ha új nevet adunk, akkor az alkalmazásokból, melyekben a DLL- {a Fibonacci szamsor kovetkezo eleme}
function fibo: int64; stdcall;
függvényt vagy eljárást használni akarjuk, az új néven érhetjük majd el. begin
if ssz<=2 then result := 1
Ha más fejlesztői rendszerből is el szeretnénk érni a DLL else begin
exportált eljárásait és függvényeit, akkor a Delphi-ben alapértelmezett result := a1 + a2;
a1 := a2;
register paraméterátadási mód helyett a stdcall szabványos Win32 a2 := result;
paraméterátadási módot kell használnunk. end;
inc(ssz);
Nézzük most meg, hogyan is néz ki a könyvtár forráskódja: end;
66 67
Fibonacci számgenerátort majd a beep függvénnyel megszólaltatjuk a Ahhoz, hogy a kész dinamikus csatolású könyvtár függvényeit
hangszórót – így majd halljuk mikor fut le az alkalmazásunkban ez az és eljárásait fel tudjuk használni alkalmazásainkban, a lefordított DLL
inicializációs rész. állományt MS Windows XP rendszer esetében az alábbi alkönyvtárak
valamelyikében kell elhelyeznünk:
Amik nem közvetlenül a dinamikus csatolású könyvtárakhoz
tartoznak, de eddig még nem biztos, hogy találkoztunk velük, az a min • abba a könyvtárba, ahonnan az alkalmazás betöltődik,
és max függvények, melyek a Math unitban találhatók. Ezek két szám
• az aktuális könyvtárban,
közül visszaadják a kisebb, illetve nagyobb számot.
• Windows\System32 könyvtárban,
Ami még a forráskódban új lehet, az az int64 típus. Ez a Delphi-
ben használható legnagyobb egész szám típus. Azért használtuk ezt az • Windows\System könyvtárban,
integer típus helyett, mivel a Fibonacci számok generálásakor nagyon • Windows könyvtárban
hamar elérhetünk olyan értékeket, melyeket már az integer típusban
• a path környezeti változóban felsorolt könyvtárak
nem tudunk tárolni.
valamelyikében
A megírt forráskódot a Project – Compile … (Ctrl+F9)
A mi esetünkben most a legegyszerűbb a lefordított DLL
menüpont segítésgével tudjuk lefordítani és így létrehozni a DLL
állományt oda átmásolni, ahova az alkalmazásunkat menteni fogjuk és
kiterjesztésű fájlt.
ahol létrejön majd az alkalmazásunk EXE fájlja (tehát ahonnan
Ha már megírtuk volna azt az alkalmazást, amely a dinamikus betöltődik az alkalmazásunk). Gondolom természetes, hogy ha a kész
csatolású könyvtárat használja, akkor azt futtatva tesztelhetjük a DLL alkamazásunkat majd terjeszteni szeretnénk, akkor az EXE állománnyal
futását is a közvetlenül a Delphi-ből. Ehhez elég a DLL mappájába együtt a DLL állományt is terjesztenünk kell, mivel az nélkül a
(ahova elmentettük) átmásolnunk a lefordított EXE fájlt és a Run – programunk nem fog működni.
Parameters… menüpontot kiválasztva a Host application alatt
De nézzük meg, hogyan is tudjuk az alkalmazásunkban
megadni ezt az alkalmazást. Ezután a DLL-t tesztelhetjük a beállított
felhasználni a megírt dinamikus csatolású könyvtárat.
alkalmazásunk segítségével a Run főmenüpontot kiválasztva (pl. Run –
Run menüponttal). Készítsük el az alábbi alkalmazást, melyben felhasználjuk az
előző feladatban megírt DLL állományt. Ne felejtsük el a lefordított DLL
fájl (dll_pelda.dll) átmásolni abba a mappába, ahová az
68 69
Az alkalmazásunkhoz tartozó forráskód fontosabb része:
implementation
{$R *.dfm}
70 71
StartFibonacci; A függvény paramétereként csak a könyvtár nevét kell megadni
end;
elérési útvonal nélkül. Ha a függvény 0 (nulla) értékkel tér vissza, akkor
procedure TForm1.Button4Click(Sender: TObject); sikertelen volt a betöltés, egyébbként a betöltött modul azonosítóját
begin
Memo1.Text := Memo1.Text kapjuk vissza.
+ IntToStr(Fibonacci) + ', ';
end; Ha a DLL-t sikerült betöltenünk a memóriába, akkor
hivatkozhatunk a DLL-ben tárolt függvényekre és eljárásokra. A
end.
hivatkozáshoz azonban meg kell tudnunk a DLL-függvény vagy eljárás
belépési pontjának címét. A cím lekérdezéséhez a GetProcAddress()
függvényt használhatjuk:
4.3. Statikus és dinamikus importálás
72 73
Nézzük meg egy példa segítségével, hogyan használhatjuk a az eljarasok hivasahoz }
dinamikus importálást az alkalmazásunkban. A programban a 3.1. type
fejezetben létrehozott DLL-t fogjuk felhasználni, ezért ezt a fájlt TLkoLkt = function (a,b:integer):integer; stdcall;
TFibo = function:int64; stdcall;
(dll_pelda.dll) ne felejtsük el átmásolni abba a könyvtárba, ahová az TFiboStart = procedure; stdcall;
alkalmazást el fogjuk menteni (ahonnan futtani fogjuk).
var
Az alkalmazásunk most csak egy üres Memo és egy Button lko, lkt: TLkoLkt;
fibo: TFibo;
komponenseket tartalmazzon. A nyomógomb megnyomásakor a DLL- fibostart: TFiboStart;
függvény segítségével kiszámoljuk, majd kiírjuk a Memo komponensbe i: integer;
HLib: THandle;
8 és 12 legnagyobb közös osztóját, legkisebb közös többszörösét és az
első 10 Fibonacci számot. 007 begin
Memo1.Clear;
{ konyvtar betoltese }
HLib := LoadLibrary('dll_pelda.dll');
if hlib = 0 then
ShowMessage('A dll_pelda.dll betoltese
sikertelen volt!')
else
try
{ belepesi pontok lekerdezese }
addr(lko) := GetProcAddress(HLib,'lko');
addr(lkt) := GetProcAddress(HLib,'lkt');
addr(fibostart) := GetProcAddress(HLib,
'StartFibonacci');
addr(fibo) := GetProcAddress(HLib,'Fibonacci');
{ fuggvenyek hivasa }
if addr(lko)<>nil then
Memo1.Lines.Add('LKO(8,12) = '
+ IntToStr(lko(8,12)));
if addr(lkt)<>nil then
Memo1.Lines.Add('lkt(8,12) = '
Az alkalmazásunk nyomógombjának OnClick eseményéhez
+ IntToStr(lkt(8,12)));
tartozó forráskód: if addr(fibostart)<>nil then
fibostart;
if addr(fibo)<>nil then
begin
… Memo1.Lines.Add('Elso 10 Fibonacci szam: ');
for i:=1 to 10 do Memo1.Text := Memo1.Text
procedure TForm1.Button1Click(Sender: TObject); + IntToStr(fibo) + ', ';
end;
{ megfelelo mutatotipusok es fuggvenyek finally
74 75
{ konyvtar felszabaditasa } 4.5. Form tárolása DLL-ben
FreeLibrary(HLib);
end; A Form DLL-be való helyezésekor ugyanúgy kell eljárnunk, mint
end;
… ha normál alkalmazásba szeretnénk új formot tenni. Tehát miután
létrehoztuk a dinamikus csatolású könyvtárat (File – New – Other… –
Delphi Projects – DLL Wizard), hozzáteszünk ehhez a könyvtárhoz
Amivel az eddigi programozásunk során nem találkoztunk, az a
egy formot a File – New – Form - Delphi for Win32 menüpont
függvény és eljárás típusok definiálása. Az így definiált függvényeknek
segítségével.
és eljárásoknak a programban meg kell adnunk a belépési címüket
(ahol a memóriában a függvény és alprogram található). A függvény A dinamikus könyvtár azon függvényében vagy eljárásában,
címét az addr operátor segítségével addhatjuk meg, illetve kérdezhetjük melyben szeretnénk használni ezt a formot, létrehozzuk, megnyitjuk
76 77
komponenseket és állítsuk be az eseményeket. A Form és rajta levő
komponensek esményeihez tartozó programkód: library dll_form;
uses
SysUtils, Classes, Forms, Controls,
… Unit1 in 'Unit1.pas' {Form1};
procedure TForm1.ScrollBar1Change(Sender: TObject);
{$R *.res}
begin
Label2.Caption := IntToStr(ScrollBar1.Position); function AdatBekeres:Integer; stdcall;
end; var
Form: TForm1;
procedure TForm1.Button1Click(Sender: TObject);
begin
begin { form letrehozasa }
ModalResult := mrOk; Form := TForm1.Create(Application);
end;
{ form megjelenitese - adatbekeres }
if Form.ShowModal = mrOk then
procedure TForm1.Button2Click(Sender: TObject); result := Form.ScrollBar1.Position
begin else
ModalResult := mrCancel; result := -1;
end;
{ form felszabaditasa }
Form.Free;
procedure TForm1.FormShow(Sender: TObject);
end;
begin
Left := (Screen.Width - Width) div 2; Exports
Top := (Screen.Height - Height) div 2;
AdatBekeres;
end;
begin
… end.
78 79
end.
{$R *.dfm}
Declare Function AdatBekeres
function AdatBekeres:integer; stdcall; Lib "dll_form.dll" () As Integer
external 'dll_form.dll';
Sub Makro1()
procedure TForm1.Button1Click(Sender: TObject); ActiveCell.FormulaR1C1 = AdatBekeres
begin End Sub
Label1.Caption := 'Beolvasott szám: '
+ IntToStr(AdatBekeres);
end;
80 81
Látjuk, hogy hasonlóan a Delphi programozási nyelvéhez, itt is A feliratokat és a képet erőforrás fájlokban fogjuk tárolni,
előbb deklarálnunk kell a függvényt. A deklarációnál meg kell adnunk, melyeket hozzácsatolunk a DLL-ekhez. A feliratokhoz sztringtáblát
melyik DLL állomány tartalmazza a függvényünket (dll_form.dll). definiálunk.
STRINGTABLE DISCARDABLE
4.8. Erőforrások tárolása DLL-ben BEGIN
1 "Calculation"
Az alkalmazásunk hatékony működése érdekében néha a 2 "Exit"
programkód helyett erőforrásokat (kép, hang, szöveg, …) tárolunk a 3 "Datainput"
4 "Result"
dinamikus csatolású könyvtárakban. END
brcc32.exe eng.rc
brcc32.exe hun.rc
82 83
Ezzel elkészítettük a két DLL állományt (magyar és angol),
melyeket a következő feladatban fogunk felhasználni.
Ezek után a bináris erőforrásfájlokat (.res) a $R fordítási
direktívával beépítjük a dinamikus csatolású könyvtárakba:
{$R hun.res}
begin
end.
library dll_en;
uses
SysUtils,
Classes;
{$R *.res}
84 85
end;
implementation end;
86 87
Egy alkalmazás állhat egy programszálból (mint pl. az eddigi
alkalmazásaink) vagy több programszálból is. A programszálak
párhuzamosan végzik tevékenységüket. Például a MS Word alkalmazás
működése közben a helyesírás ellenőrző egy külön programszálon fut,
ezért tud folyamatosan (párhuzamosan) dolgozni, miközben a
felhasználó szerkeszti a dokumentumot.
implementation
{ … }
procedure TVonalSzal.Execute;
begin
{ Place thread code here }
end;
88 89
end. igaz (true) értéket adunk meg, akkor a programszál létrehozása után a
szál felfüggesztett állapotban marad, tehát nem kezdődik el
automatikusan az Execute metódus futása. Ebben az esetben a
Nekünk ezek után csak ezt kell kiegészítenünk, illetve
programszál futását a Resume metódus meghívásával indíthatjuk el. A
módosítanunk. Fontos, hogy a programszál fő részét, tehát azt a
futó szálat a Suspend metódus meghívásával szüneteltethetjük
programot, amit a programszálnak el kell végeznie az Execute metódus
(függeszthetjük fel). Az Execute metódust soha ne hívjuk meg
implementációjában kell megadnunk.
közvetlenül! Azt, hogy egy programszál éppen fut-e vagy szüneteltetve
A létrehozott szálból elérhetjük, módosíthatjuk a vizuális van, a Suspended tulajdonság segítségével állapíthatujuk meg.
komponenseket (VCL) is. Ha csak olvasni akarjuk valamelyik
A programszál teljes leállítását a Terminate metódus
komponens tulajdonságát, azt megtehetjük az Execute metódusban is.
segítségével végezhetjük el, amely valójában a Terminated
Ha viszont meg szeretnénk változtatni vizuális komponensek bármelyik
tulajdonságot állítja igaz (true) értékre. Az Execute metódusban ezért
tulajdonságát, azt nem végezhetjük el közvetlenül az Execute
fontos, hogy időközönként ellenőrizzük a Terminated tulajdonság
metódusban, mivel a képernyő aktualizálásáért kizárólag az alkalmazás
értékét, és ha ez igaz, akkor fejezzük be a program futását.
fő programszálja a felelős. A programszálunknak létezik azonban egy
Synchronize metódusa, melynek meghívásávál jelezhetjük a fő A programszálat, miután befejeződött a futása, a memóriából
programszálnak, hogy mit szeretnénk elvégezni a vizuális felszabadíthatjuk a már megszokott Free metódus segítségével.
komponenseken (a Synchronize metódus paraméterében azt az eljárást Egy másik megoldás a programszál memóriából való
kell megadnunk, amelyben a vizuális komponensekkel való műveletek felszabadítására, hogy még az elején (pl. létrehozás után) beállítjuk a
szerepelnek). A fő programszál a Synchronize meghívása után a FreeOnTerminate tulajdonság értékét true-ra. Ilyenkor ha a
megadott műveleteket a komponenseken automatikusan elvégzi. programszál futása befejeződik, automatikusan felszabadul a
Amint már említettük, TThread osztály legfontosabb metódusa memóriából (nem kell meghívnunk a Free metódust).
az Execute, amely azt a programot tartalmazza, melyet a szálnak el kell
végezni. Ez a metódus felelős azért, hogy TThread Terminated
tulajdonságát folyamatosan ellenőrizze, és ha ennek a tulajdonságnak 5.2. Szakaszok párhuzamos kirajzolása
az értéke igaz (true), akkor az Execute metódus befejeződjön, és ezzel
Készítsünk alkalmazást, amely véletlen helyű és színű
a programszálunk is befejeződjön.
szakaszokat fog kirajzolni egy külön programszál segítségével egy
A programszálunk (Execute metódus) a szál létrehozása után image komponensre. Az így megírt programunk a kirajzolás alatt is fog
automatikusan elindul, ha a konstruktor paraméterében false (hamis) reagálni az eseményekre, pl. a kirajzolás közben is át tudjuk majd
értéket adtunk meg. Ha a létrehozáskor a konstruktor paramétereként
90 91
helyezni az alkalmazás ablakát, mivel a rajzolás egy külön, a private
x1,y1,x2,y2: integer;
főprogramszállal párhuzamos szálon fut. 015 Szin: TColor;
protected
procedure Execute; override;
procedure KepFrissitese;
end;
implementation
uses Unit1;
procedure TVonalSzal.Execute;
begin
repeat
x1 := Random(Form1.Image1.Width);
y1 := Random(Form1.Image1.Height);
x2 := Random(Form1.Image1.Width);
y2 := Random(Form1.Image1.Height);
Szin := Random($FFFFFF);
Synchronize(KepFrissitese);
until Terminated = true;
end;
procedure TVonalSzal.KepFrissitese;
Hozzunk létre egy új alkalmazást, melyre helyezzünk el egy begin
Form1.Image1.Canvas.Pen.Color := Szin;
Image komponenst és két nyomógombot. Az egyik nyomógombbal Form1.Image1.Canvas.MoveTo(x1,y1);
Form1.Image1.Canvas.LineTo(x2,y2);
fogjuk a mi programszálunkat elindítani, a másikkal felfüggeszteni.
end;
Nézzük meg először a programszálunkat tartalmazó unit
end.
forráskódját:
Bevezettünk néhány privát változót (x1, y1, x2, y2, szin) melyek
unit Unit2;
a kirajzolandó vonal helyét és színét határozzák meg.
interface
Azt, hogy a programszálunk pontosan mit végezzen el, az
uses Execute metódusban kell megadnunk. Láthatjuk, hogy itt kigenerálunk
Classes, Graphics;
véletlen számokat, majd a KepFrissitese eljárás segítségével kirajzoljuk
type a vonalat a képernyőre. Ez addig fog körbe-körbe futni egy repeat..until
TVonalSzal = class(TThread)
92 93
ciklus segítségével, amíg nem állítjuk le a programszálat a Terminate procedure TForm1.Button2Click(Sender: TObject);
begin
metódussal (ez a metódus állítja be a Terminated tulajdonságot, melyet Button1.Enabled := True;
a ciklusban vizsgálunk). Button2.Enabled := False;
Szal.Suspend;
A komponensre való kirajzolást a fő programszálnak kell end;
elvégeznie, ezért a KepFrissitese eljárást a Synchronize metódus procedure TForm1.FormDestroy(Sender: TObject);
segítségével végezzük el. begin
if Szal.Suspended then Szal.Resume;
Az alkalmazásunk komponenseinek eseményeihez tartozó Szal.Terminate;
Szal.Free;
forráskód: end;
end.
unit Unit1;
94 95
metódus segítségével felszabadítjuk a programszálnak lefoglalt manuálisan a másik programszálunkat is (TSzor2Szal). Ennek a
memóriát. modulnak a programkódja így néz ki:
unit Unit2;
5.3. Szálak szinkronizálása – várakozás egy
interface
másik programszálra
uses
A programszálak használatakor sokszor szükségünk lehet a Classes;
szálak szinkronizálására – például egy szálban kiszámított eredményre type
szüksége lehet egy másik programszálnak. Ilyenkor a legegyszerűbb THozzaadSzal = class(TThread)
private
szinkronizálási módszer a várakozás a másik programszál futásának { Private declarations }
befejezésére. Erre készítünk egy példaprogramot. protected
procedure Execute; override;
Az alkalmazasunk tartalmazzon egy Label komponenst és két end;
implementation
procedure THozzaadSzal.Execute;
begin
szam := szam + 1;
end;
procedure TSzor2Szal.Execute;
Miután létrehoztuk az alkalmazást, hozzunk létre egy var
HozzaadSzal: THozzaadSzal;
programszálat új unitban (THozzaadSzal), majd ebbe a unitba beírjuk begin
96 97
szam := StrToInt(Form1.Label1.Caption); implementation
HozzaadSzal := THozzaadSzal.Create(false);
HozzaadSzal.WaitFor; {$R *.dfm}
HozzaadSzal.Free;
szam := 2 * szam; procedure TForm1.Button2Click(Sender: TObject);
Synchronize(LabelFrissites); begin
end; Close;
end;
procedure TSzor2Szal.LabelFrissites;
begin procedure TForm1.Button1Click(Sender: TObject);
Form1.Label1.Caption := IntToStr(szam); begin
end; Szor2Szal := TSzor2Szal.Create(false);
Szor2Szal.FreeOnTerminate := true;
end. end;
end.
unit Unit1;
interface
uses
Windows, Messages, … , Unit2;
var
Form1: TForm1;
Szor2Szal: TSzor2Szal;
98 99
tpNormal Normál prioritású szál.
100 101
Ezek kezdeti értékeinek beállításához készítünk egy konstruktort. Az új repeat
{ bonyolultabb szamitas helyett }
konstruktor paramétereként megadjuk az Image komponest és a for i:=0 to 1000000 do
prioritását a programszálnak. FXKoord := FXKoord + 1;
FXKoord := FXKoord - 1000000;
A programszál forráskódja: { ha kimenne a kepernyorol,
a masik oldalra atrakjuk }
if FXKoord > Form1.ClientWidth-FGolyo.Width then
FXKoord := 2;
unit Unit2; { golyo kirajzolasa }
Synchronize(Kirajzol);
interface until Terminated;
end;
uses
Classes, ExtCtrls; procedure TGolyoSzal.Kirajzol;
begin
type { golyo athelyezese }
TGolyoSzal = class(TThread) FGolyo.Left := FXKoord;
private { tovabbi esemenyek feldolgozasa }
FGolyo: TImage; Application.ProcessMessages;
FXKoord: integer; end;
protected
procedure Execute; override; end.
procedure Kirajzol;
public
constructor Create(AGolyo: TImage;
APrioritas: TThreadPriority); A Create konstruktorban létrehozzunk az ős konstruktorának
end;
meghívásával a programszálat, melyet rögtön futtatunk is (false
implementation paraméter). Ezután beállítjuk az FGolyo és FXKoord mezők kezdeti
értékeit és a programszál prioritását.
Uses Unit1, Forms;
102 103
A Kirajzol métódus arréb teszi a golyót, majd feldolgozza az procedure TForm1.Button1Click(Sender: TObject);
begin
alkalmazás további eseményeit. Erre az Application.ProcessMessages g1.Resume;
parancsra csak azért van szükség, mert ha átállítanánk a prioritást g2.Resume;
Button1.Enabled := false;
magasra, akkor a golyók mozgatása olyan nagy prioritást kapna, hogy Button2.Enabled := true;
az alkalmazás a többi eseményre nem tudna időben reagálni. end;
implementation end.
{$R *.dfm}
104 105
Ehhez először is a Delphi-ben hozzunk létre a már megszokott
módon egy új alkalmazást (File – New – VCL Form Application - Delphi
5.5. Többszálú MDI alkalmazás for Win32). Erre helyezzünk el egy főmenü (MainMenu) komponenst,
Az eddig létrehozott alkalmazásaink mindig csak egy, kettő, melynek állítsuk be a szükséget tulajdonságait. Továbbá ne felejtsük el
maximum három programszálat használtak. A következő feladatban beállítani a Form FormStyle tulajdonságát fsMDIForm értékre.
olyan MDI alkalmazást készítünk, melynek mindegyik gyermek ablaka Ezek után hozzuk létre a gyermek ablakot. Ehhez hozzunk létre
külön szálat fog használni, így az alkalmazásunk több szálat is egy új Form-ot (File – New – Form - Delphi for Win32), melyre
használhat majd. helyezzünk el egy Image komponenst. Az Image komponens Align
Készítsünk MDI alkalmazást, amelyben minden MDI-gyermek tulajdonságát állítsuk be alClient-re. A Form FormStyle tulajdonságát
ablakra egy új szál segítségével párhuzamosan fogunk kirajzolni ennél a form-nál fsMDIChild értékre állítsuk.
véletlen helyzetű és színű szakaszokat. 019 Végül hozzunk létre a már megszokott módon egy Thread
Object-et egy új unitban (File – New – Others – Delphi Projects – Delphi
Files – Thread Object). Az új programszálunk neve legyen TSzal.
unit Unit3;
interface
uses
Classes, Graphics, ExtCtrls;
type
TSzal = class(TThread)
private
106 107
{ Private declarations } A programszál x1, y1, x2, y2 mezőiben jegyezzük meg a
x1,y1,x2,y2: integer;
szin: TColor; kirajzolandó vonal koordinátáit, a szin mezőben a vonal színét és az
img: TImage; img mezőben azt az image komponenst, melyre a szakaszokat a szál
protected
procedure Execute; override; rajzolni fogja. Ez utóbbit a konstruktor segítségével adjuk meg a
procedure Kirajzol; programszálnak.
public
constructor Create(iimg: TImage); Nézzük most meg, hogy néz ki az MDI-gyermek ablakhoz
end;
tartozó programkód (Unit2):
implementation
108 109
Szal.Free;
end; uses
Windows, Messages, SysUtils, Variants, Classes,
procedure TForm2.FormCreate(Sender: TObject); Graphics, Controls, Forms, Dialogs, Menus, Unit2;
begin
DoubleBuffered := true; type
Szal := TSzal.Create(Image1); TForm1 = class(TForm)
end; MainMenu1: TMainMenu;
Menu1: TMenuItem;
procedure TForm2.FormResize(Sender: TObject); jablak1: TMenuItem;
begin N1: TMenuItem;
Image1.Picture.Bitmap.Width := Image1.Width; Kilps1: TMenuItem;
Image1.Picture.Bitmap.Height := Image1.Height; procedure FormCreate(Sender: TObject);
end; procedure jablak1Click(Sender: TObject);
procedure Kilps1Click(Sender: TObject);
end. private
{ Private declarations }
public
{ Public declarations }
A form osztályának private deklarációjába ne felejtsük el beírni end;
a programszálunkat (Szal:TSzal;).
var
Ezt a programszál-objektumot a form létrehozásakor a Form – Form1: TForm1;
n: integer;
OnCreate eseményében hozzuk létre.
implementation
A Form – OnClose eseményében beállítjuk, hogy a form
{$R *.dfm}
bezárása után felszabaduljon a számára lefoglalt memória, majd
leállítjuk és megszüntetjük a formunkhoz tartozó programszálat is. procedure TForm1.Kilps1Click(Sender: TObject);
begin
A Form - Resize eseményében csupán a képünk bitmapjának a Close;
end;
méretét állítjuk be, hogy a rajzolás a form átméretezésekor a teljes
képre történjen. procedure TForm1.jablak1Click(Sender: TObject);
var
Végül nézzük meg az MDI Form-hoz tartozó programkódot is Form2: TForm2;
begin
(Unit1): Form2 := TForm2.Create(self);
Form2.Caption := 'Ablak' + IntToStr(n);
inc(n);
unit Unit1; end;
110 111
n := 1; Az alkalmazás (OLE kliens) kihasználhatja valamelyik, a
end;
számítógépen elérhető OLE szervert olyan tevékenységek elvégzésére,
end. melyre saját maga nem képes. Ha van például MS Word szerverünk,
amely felkínálja „DOC formátumú dokumentumok megjelenítését”, akkor
a mi alkalmazásunkból ezt a szervert meghívhatjuk és így a saját
Ebben a modulban használtunk egy globális n változót, amely
alkalmazásunkban Word dokumentumot jeleníthetünk meg az nélkül,
csupán arra szolgál, hogy a létrehozott gyermek ablak feliratába be
hogy a programunkban ezt saját magunk implementáltuk volna.
tudjuk írni, hányadik ablak jött éppen létre.
Az OLE tehát egy olyan mechanizmus, amely lehetőséget ad az
Az MDI-gyermek ablakot az alkalmazásunk megfelelő
alakamazásunkban elhelyezni olyan objektumot, amely egy másik
menüpontját kiválasztva hozzuk létre a Create konstruktor segítségével.
alkalmazásban van definiálva.
Az új ablak tulajdonosának az MDI Form-ot (self) állítjuk be.
Jelenleg az OLE 2-es verziója használt a gyakorlatban. Ez
lehetőséget ad tehát más alkalmazásban definiált objektum
megjelenítésére a saját programunkban. Ha ezt az objektumot a
6. OLE technológia
programunkban szerkeszteni szeretnénk (dupla kattintással aktiválva),
Az OLE (Object Linking and Embedding = objektum csatolása akkor vagy egy új ablakban, vagy a programunk belsejében nyílik meg
és beszúrása) egy érdekes technológia, melyet a Microsoft fejlesztett ki. az objektumhoz tartozó alkalmazás (pl. MS Word).
Ennek segítségével megjeleníthetünk, sőt szerkeszthetünk bitmap-okat Az OLE-val való munkánk során megkülönböztetünk:
a programunkban az nélkül, hogy ezt külön be kéne programoznunk.
• Beszúrást (Enbedding) – az objektum fizikailag is az
Valójában ezt a technológiát használjuk ki akkor is, amikor
alkalmazásba kerül. Ha például van a merevlemezünkön
például a MS Word-be írt dokumentumba egy Paintban rajzolt képet
egy „teszt.doc”, melyet az alkalmazásba beszúrunk, akkor
vagy egy MS Excel-ben elkészített táblázatot rakunk be. Ha az ilyen
ez a fájl fizikailag át lesz másolva a helyéről az
objektumra duplán rákattintunk, akkor az megnyílik szerkeszthető
alkalmazásba.
formában a Paint, ill. MS Excel programban.
• Csatolást (Linking) – az alkalmazásba nem lesz beszúrva
Az OLE technológia a munkájához ú.n. OLE szervereket
fizikailag az objektum, csak egy hivatkozás rá. Az állomány
használ. Ne gondoljunk itt most külön szerver számítógépekre! Az OLE
tehát meg fog jelenni az alkalmazásunkban, de fizikailag
szerverek a saját gépünkben futó alkalmazások (egyszerűen
dolgozni a merevlemezen levő állományal fogunk.
megfogalmazva), melyek más alkalmazások (nevezzük ezeket OLE
klienseknek) részére felkínálják a szolgáltatásaikat és működésüket.
112 113
6.1. A Delphi és az OLE Egy egyszerű példa segítségével megmutatjuk, hogyan lehet az
alkalmazásunkban megjeleníteni Word, Excel, Paint, stb.
Az OLE-vel való munkánk alapkomponense a System palettán
dokumentumokat. 020
található OleContainer. Ez a komponens jelképezi az OLE szervereket
a Delphi-ben létrehozott alkalmazásban. A formra helyezzünk el egy Panel komponenst, melynek Align
tulajdonságát állítsuk be alBottom-ra. Erre a Panel-ra tegyünk rá két
Az OleContainer lehetőséget ad bármilyen OLE objektum
Button komponenst, egyet az objektum beszúrására, egyet pedig a
kiválasztására és elhelyezésére vagy csatolására (linkelésére) az
programból való kilépésre. Végül a Panel fölé tegyünk be egy
alkalmazásunkban. Az objektumok száma, melyek közül választhatunk,
OleContainer komponenst. Méretezzük át úgy, hogy kitöltse a
a számítógépen telepített programoktól függ. Ezért az alábbi
fennmaradó részt, majd állítsuk be mind a négy Anchors trulajdonságát
mintafeladatok küzöl nem biztos, hogy mindegyik fog működni a
true-ra. Ezzel az alkalmazásunk tervezésével megvagyunk.
számítógépünkön. Ha ugyanis a számítógépen nincs például MS Word,
akkor nem tudunk MS Word dokumentumot megjeleníteni az
alkalmazásunkban.
114 115
felhasználó kiválaszthatja, hogy milyen objektumot és milyen formában
… (beszúrva, csatolva) szeretne az alkalmazásunk OLE kontainerében
megjelentetni.
procedure TForm1.Button1Click(Sender: TObject);
begin
A csatolás természetesen csak akkor lehetséges, ha nem új
OleContainer1.InsertObjectDialog;
end; objektumot hozunk létre, hanem egy létező fájlból jelenítjük meg az
objektumot.
procedure TForm1.FormCreate(Sender: TObject);
begin
{ az OLE objektum merete az ablak Továbbá ebben a dialógusablakban megadhatjuk azt is, hogy
atmeretezeskor valtozzon } az objektumot eredeti formában (tartalmát) akarjuk megjeleníteni, vagy
OleContainer1.Anchors :=
[akLeft, akTop, akRight, akBottom]; csak az ikonját szerenénk megjeleníteni az OLE kontainerben.
{ az OLE objektum ne 3D, hanem egyszeru
feher 2D hatteren legyen }
OleContainer1.Ctl3D := false;
end;
end.
Az alkalmazásunk leglényegesebb része az “Objektum egy Word dokumentumot az alkalmazásunkba a fenti dialógusablak
beszúrása” gomb megnyomásakor történik. Ennek a gombnak az segítségével. Láthatjuk, hogy ez a dokumentum megjelent az
116 117
Menü megjelenítése
Aktiválás új ablakban
Az OLE azonban ennél többre is képes. Ha duplán rákattintunk (szerkesztéskor) mindig a Microsoft Word, Paint, … az alkalmazásunk
ablakában jelenik meg. Ezen is tudok változtatni. Elég, ha az
a dokumentumunkra, akkor azt szerkeszthetjük is – az alkalmazásunk
OleContainer AllowInPlace tulajdonságát beállítjuk false-ra és a
MS Word-é válik.
dokumentumunkat máris új ablakban szerkeszthetjük majd. Egészítsük
ki tehát az előző alkalmazásunk Form – OnCreate eseményéhez
tarrozó eljárást ennek a tulajdonságnak a beállításával:
118 119
OleContainer1.AllowInPlace := false;
end;
120 121
ablakban végezzük el, akkor külön ablakban megnyílik az objektumhoz • osEmpty – az OLE kontainer üres, nincs benne semmilyen
tartozó alkalmazás, melyben az objektumon történő változtatások objektum.
azonnal megjelennek az alkalmazásunk ablakában (OleContainer-ben)
• osLoaded – az OLE kontainerben van megjelenítve
is.
objektum, de nem aktív – a hozzá tartozó OLE szerver nem
A SizeMode tulajdonság segítségével megadhatjuk, hogyan fut.
jelenjen meg az objektumunk az OLE kontainerben. Lehetséges
• osRunning – az OLE kontainerben van megjelenített
értékek:
objektum és ennek OLE szervere fut.
• smClip – alapértelmezett érték – az objektum eredeti
• osOpen – az OLE objektum a saját alkalmazásának
méretben jelenik meg, az objektum azon része, amely nem
ablakában (új ablakban) van megjelenítve.
fér bele a komponensbe nem látható.
• osInPlaceActive – az OLE objektum helyben van aktiválva,
• smCenter – az objektum eredeti méretben jelenik meg, de
de még nincs összekapcsolva az összes menü és
a komponensben középre lesz igazítva, tehát az előzőhöz
eszköztár. Amint a teljes aktiválás befejeződik, az értéke
képest az objektumnak nem a bal felső része, hanem a
osUIActive-ra változik.
közepe lesz látható.
• osUIActive – az OLE objektum helyben lett aktiválva,
• smScale – megváltoztatja az objektum méretét az oldalak
jelenleg is aktív és az össze menü és eszköztár össze lett
arányát betartva úgy, hogy a komponensbe beleférjen.
kapcsolva.
• smStretch – hasonlóan az előzőhöz megváltoztatja az
objektum méretét úgy, hogy a komponensbe beleférje, de
az oldalak arányát nem tartja be (széthúzza az objektumot 6.4. Kulcsszavak lekérdezése és végrehajtása
az egész komponensre).
Ha meg szeretnénk tudni, milyen kulcsszavakat támogat az
• smAutoSize – az objektumot eredeti méretében jeleníti OLE kontainerben levő objektum, használhatjuk az ObjectVerbs
meg és a komponens méretét állítja át úgy, hogy az egész tulajdonságot. Ezeket a kulcsszavakat mint szöveget kapjuk meg, így
objektum beleférjen. tartalmazhatnak & jelet is, melyek a gyors billentyűelérést jelölik.
Az OleContainer State tulajdonságának kiolvasásával Az alkalmazásunk tartalmazzon egy ListBox komponenst,
megtudhatjuk, hogy az OLE objektum éppen milyen állapotban van. melyben megjelenítjük az összes objektum által támogatott kulcsszót.
Lehetséges értékek: Továbbá alkalmazásunkon legyen egy Panel komopnens is, melyre
elhelyezünk három nyomógombot: egyet az objektum beolvasására,
122 123
egyet a kulcsszavak megjelenítésére és egyet a listából kiválasztott procedure TForm1.Button1Click(Sender: TObject);
begin
kulcsszó végrehajtására. És természetesen végül alkalmazásunk OleContainer1.InsertObjectDialog;
tartalmazni fog egy OleContainer komponenst is. 021 end;
end.
124 125
Az első nyomógombnál a CreateObjectFromFile segítségével
beolvassuk a Word dokumentumot a kontainerbe. Ha a teszt.doc
állományban valóban Word dokumentum van, akkor ez hiba nélkül
végrehajtódik. A paraméterben az ExpandFileName függvényt azért
kellett használnunk, mivel ennek a metódusnak az állomány nevét a
teljes útvonallal együtt kell átadnunk. A metódus második paramétere
azt adja meg, hogy az objektum csak ikon formájában jelenjen meg
(true) vagy látható legyen a tartalma (false).
Az egyes nyomógombokhoz tartozó események: Ha a teszt.doc állományban nem Word dokumentum van,
hanem OLE objektum, akkor azt csak a LoadFromFile metódus
segítségével tudjuk beolvasni.
…
Az OLE kontainerben megjelenített objektumot a SaveToFile
procedure TForm1.Button1Click(Sender: TObject);
segítségével menthetjük el úgy, mint Word típusú OLE objektum.
begin
OleContainer1.CreateObjectFromFile(
ExpandFileName('teszt.doc'), false); A SaveAsDokument metódussal az objektumot elmenthetjük
end; mint Word dokumentum.
procedure TForm1.Button2Click(Sender: TObject); Lehet hogy egy kicsit furcsának tűnik, miért van ilyen nagy
begin
OleContainer1.LoadFromFile('teszt.doc'); különbség a hagyományos állomány (Word dokumentum, bitmap, …)
end; között és az OLE objektumot tartalmazó állomány formátumja között,
procedure TForm1.Button3Click(Sender: TObject); amikor az első példánkban minden gond nélkül be tudtunk olvasni
begin dokumentumot az OLE kontainerbe. Ez azért volt, mivel ott a beolvasást
OleContainer1.SaveToFile('teszt.doc');
end; az InsertObjectDialog metódus segítségével hajtottuk végre, amely
automatikusan elvégezte a dokumentum átalakítását OLE objektummá.
procedure TForm1.Button4Click(Sender: TObject);
begin
OleContainer1.SaveAsDocument('teszt.doc');
end;
6.6. Menük összekapcsolása
…
Az első példánkban láthattuk, hogy ha elhelyezünk egy
MainMenu komponenst a form-on, akkor az objektum szerkesztésekor
126 127
ebben megjelennek az OLE szerverhez tartozó menüpontok. De mi van
akkor, ha az alkalmazásunkban is szeretnénk saját menüt kialakítani?
Erre nézzünk most egy mintafeladatot. 024
128 129
• Az alkalmazásunk főmenüjének azon menüpontjai,
melyeknek GroupIndex értéke 0, 2, 4, … mindig láthatóak
lesznek és nem lesznek felülírva az OLE szerver menüjével
(ez a helyzet állt elő az előző példában is, ahol a
GroupIndex értéke mindegyik menüpontnál 0 volt).
130 131
egy főmenüpontja lesz, a File. A többi menüpontot a beolvasott A menüpontok OnClick eseményeihez tartozó eljárások:
objektumhoz tarozó OLE szerver fogja berakni.
procedure TForm1.Dokumentummegnyitsa1Click(Sender:
TObject);
begin
if OpenDialog1.Execute then
OleContainer1.CreateObjectFromFile(
OpenDialog1.FileName,false);
end;
procedure TForm1.OLEobjektummegnyitsa1Click(Sender:
TObject);
begin
if OpenDialog1.Execute then
OleContainer1.LoadFromFile(OpenDialog1.FileName);
end;
procedure TForm1.Mentsmintdokumentum1Click(Sender:
TObject);
begin
if (OleContainer1.State <> osEmpty) and
Az alkalmazásra helyezzünk el még egy OpenDialog és egy
(SaveDialog1.Execute) then
SaveDialog komponenst a fájlok beolvasásához és mentéséhez OleContainer1.SaveAsDocument(
SaveDialog1.FileName);
(pontosabban a beolvasandó és elmentendő állomány nevének és
end;
helyének meghatározásához).
procedure TForm1.MentsmintOLEobjektum1Click(Sender:
A tervezéskor állítsuk be az OleContainer Align tulajdonságát TObject);
begin
alClient-ra, hogy az OLE kontainer az egész formon jelenjen meg. if (OleContainer1.State <> osEmpty) and
Továbbá ne felejtsük el megadni az OleContainer SizeMode (SaveDialog1.Execute) then
OleContainer1.SaveToFile(
tulajdonságát smScale-ra. Ezzel elérjük, hogy az egész dokumentum SaveDialog1.FileName);
látható lesz a kontainerben (arányosan lekicsinyítve vagy felnagyítva a end;
kontainer méretéhez). procedure TForm1.Bezrs1Click(Sender: TObject);
132 133
begin
OleContainer1.DestroyObject;
7. OLE Automation
end;
Az OLE Automation olyan technológia, amely lehetőséget ad
procedure TForm1.Aktivls1Click(Sender: TObject);
egy alkalmazás képességeit kihasználni egy másik alkalmazásból. Az
begin
if OleContainer1.State <> osEmpty then OLE Automation a Microsoft által bevezetett Component Object Model-t
OleContainer1.DoVerb(ovShow);
end; (COM) használja ki.
procedure TForm1.Vgolapramsols1Click(Sender:
TObject);
begin
7.1. MS Word irányítása Delphi-ből
if OleContainer1.State <> osEmpty then
OleContainer1.Copy; A most következő mintapélda segítségével megismerkedhetünk
end; az OLE Automatizálással a gyakorlatban.
end.
134 135
végbemennek, azonban a szokásosnál több memóriát igényelnek (16
byte), továbbá a variáns típusok alkalmazása lassítja a program
működését. Ezért próbáljuk ezek használatát kizárólag a COM
objektum-modellek programozására szűkíteni!
136 137
procedure TForm1.Button1Click(Sender: TObject); begin
begin w.Quit(false); { false = kilepeskor a
if VarIsEmpty(w) then 'Menti a valtozasokat?'
begin kerdesre a valasz nem }
w := CreateOleObject('Word.Application'); w := Unassigned;
w.Visible := true; end;
w.Caption := 'Delphi-ből megnyitott Word'; end;
end;
end; end.
138 139
end;
140 141
Ebben a példában az Excel „DisplayAlert” tulajdonságának
false-ra állításával értük el, hogy az Excel bezárásakor ne kérdezze unit Unit1;
meg, hogy szeretnénk-e a munkafüzetet menteni.
interface
Hasonlóan a Word-hoz, az Excel objektumainak
uses
tulajdonságairól és metódusairól sok információt megtalálhatunk az Windows, Messages, … , Spin, ComObj;
Excel súgójának Visual Basic részében. Konkrét műveletek
…
elvégzéséhez tartozó objektumok és tulajdonságok meghatározásában
implementation
itt is segítségünkre lehet az Excel makrója.
{$R *.dfm}
var
Szorzótábla beírása MS Excel-be Delphi-ből Excel: Variant;
Az alábbi egyszerű példa szintén az Excel irányítását fogja procedure TForm1.Button1Click(Sender: TObject);
bemutatni. Az Delphi-ben írt alkalmazásunk segítségével megadott var
i,j: integer;
méretű szorzótáblát fogunk az Excel-be beírni (kigenerálni). 027 begin
Excel := CreateOleObject('Excel.Application');
Excel.Interactive := False;
Excel.Visible := true;
Excel.WorkBooks.Add;
for i:=1 to SpinEdit1.Value do
begin
Excel.ActiveSheet.Cells[i+1,1].Font.Bold := True;
Excel.ActiveSheet.Cells[i+1,1] := i;
end;
for i:=1 to SpinEdit2.Value do
begin
Excel.ActiveSheet.Cells[1,i+1].Font.Bold := True;
Excel.ActiveSheet.Cells[1,i+1] := i;
end;
for i:=1 to SpinEdit1.Value do
for j:=1 to SpinEdit2.Value do
Excel.ActiveSheet.Cells[i+1,j+1] := i*j;
Alkalmazásunk két Label komponenst, két SpinEdit komponenst Excel.Interactive := True;
end;
és egy nyomógombot fog tartalmazni. A nyomógomb OnClick
eseményéhez tartozó programkód: end.
142 143
A Excel objektum „interactive” tulajdonságát rögtön az Excel Ahhoz, hogy megmutathassuk, hogyan alakíthatunk ki
alkalmazás létrehozása (megnyitása) után beállítottuk false-ra, majd kapcsolatot két alkalmazás között, mindenekelőtt fontos megértenünk
csak az összes adat beírása után állítottuk vissza true-ra. Ez egy az alábbi három fogalmat:
nagyon hasznos tulajdonság az OLE automatizálás használatánál,
• szolgáltatás (service) – ez valójában az alkalmazásunk
ugyanis ennek segítségével elértük, hogy az adatok beírása alatt a
neve (bár ez nem feltétel). Ha például egy DATUM.EXE
felhasználó az Excellel ne tudjon dolgozni, így nem tudja az adatok
alkalmazásunk lesz a DDE szerver, akkor a szolgáltatás
beírását és egyébb Excel műveletek elvégzését sem megszakítani.
neve a „DATUM”. A Delphi-ben ehhez a projektet
Fontos, hogy ha ezt a tulajdonságot használjuk, akkor az Excellel való
DATUM.DPR néven kell elmentenünk.
műveletek befejezése után ne felejtsük el visszaállítani true értékre!
• téma (topic) – a kommunikáció pontosabb definiálása. Egy
szerver több témában is szolgáltathat információkat. Ha
csatlakozni akar a kliens a szerverhez, a kliensnek mindig
8. DDE Technológia meg kell adnia a témát, amelyben a szerverrel „társalogni”
A DDE (Dynamic Data Exchange) a Microsoft által kifejlesztett akar. A Delphi-ben a téma neveként a szerver konverzációs
protokol. A DDE ma már egy kicsit elavult technológia, az újabb komponensének a nevét kell megadnunk (DdeServerConv).
alkalmazásokban már helyette főleg az OLE automatizálást vagy más • elem (item) – az átadott adatok legpontosabb
COM-on alapuló technológiát használnak. Ennek ellenére elsősorban az identifikációja. Egy témához több elem is tartozhat. A
egyszerűsége végett érdemes a DDE-vel megismerkednünk. Delphi-ben az elem neveként a DDE szerver
A DDE szerver az az alkalmazás, amely információt nyújt más elemkomponensének a nevét kell megadnunk
alkalmazásoknak. (DdeServerItem).
A DDE kliens ezeket az információkat használja, szükség Míg a kliens mindig a konkrét szerverrel kommunikál (ismeri a
esetén kéri az adatokat a szervertől. szervert), a szerver a konkrét klienseket nem ismeri. A szerver csak
üzeneteket küld a klienseknek, az adatok a megosztott memórián
Egy DDE szerver több kliens részére is szolgáltathat
keresztül vannak továbbítva a kliensek felé.
információkat, és hasonlóan egy DDE kliens-alkalmazás több szervertől
is kaphat információkat.
A DDE kliens felelős a kapcsolat kialakításáért, majd kérvényt 8.1. DDE a Delphiben
küld a szervernek az adatokért, esetleg valamilyen parancs
végrehajtását kéri a szervertől.
144 145
A Delphiben a DDE-el való munkához négy komponens létezik. o CloseLink – befejezi az aktuális konverzációt.
Szerver létrehozásakor ezek közül a két szerver komponenst, kliens
o ExecuteMacro – parancsot küld a szervernek.
létrehozásakor a kliens komponenseket használjuk. Mind a négy
Az alábbi ábra szemlélteti egy egyszerű DDE konverzációhoz
komponens a System kategória alatt található:
szüksége tulajdonságok és metódusok beállítását a szerveren és a
• DdeServerConv – a szerver témája. A kliensek ehhez a
kliensen:
témához a komponens nevének megadásával
csatlakozhatnak.
• DdeClientConv – a DDE kliens témája. Ez a komponens szerepel. Itt fontos a kis- és nagybetű is!
146 147
Az első DDE szerverünk egy DdeServerConv, DdeServerItem end;
és egy Edit komponenst fog tartalmazni. A szerver feladata az lesz, end.
hogy amit beírunk az Edit komponensbe, azt a szöveget szolgáltassa a
klienseknek. 030
A Form – Create eseményében beállítjuk a kezdeti
beállításokat: kitöröljük a szövegdoboz tartalmát és összekapcsoljuk a
DdeServerItem1 komponenst a DdeServerConv1 komponenssel.
Fontos, hogy a projektet DDESzerver.dpr néven mentsük el, használni, mivel nincs még elkészítve a kliens-alkalmazásunk. Fordítsuk
ugyanis ebben az esetben lesz a futtatható állomány neve le a szerver-alkalmazást, majd lássunk neki a kliens-alkalmazás
148 149
Label1.Caption := DdeClientItem1.Text;
end;
end.
150 151
szervernek. Természetesen minden esetben a szerver dönti el, hogy a Az ExecuteMacro metódus true értéket ad vissza, ha a
kliens által küldött kérelmet teljesíti-e vagy nem. Az alábbi ábra parancsot a szerver elfogadta, false értéket ha valamilyen hiba történt.
szemlélteti hogyan küldhet a kliens a szervernek valamilyen parancsot Az, hogy a parancsot a szerver elfogadta, nem jelenti azt, hogy végre is
(kérelmet). hajtja!
Szerver létrehozása
adja meg, hogy várnia kelljen-e a kliensnek a következő DDE művelet implementation
hívásával arra, hogy a szerver befejezze ennek a makrónak a
{$R *.dfm}
végrehajtását. Ha a második paraméter true lenne, akkor a kliens
következő DDE hívását a szerver nem fogadná mindaddig, amíg ezt az procedure TForm1.FormCreate(Sender: TObject);
begin
előzőt nem hajtotta végre. Edit1.Text := '';
DdeServerItem1.ServerConv := DdeServerConv1;
end;
152 153
procedure TForm1.Edit1Change(Sender: TObject);
begin …
DdeServerItem1.Text := Edit1.Text;
end; implementation
{$R *.dfm}
procedure TForm1.DdeServerConv1ExecuteMacro(Sender:
TObject; Msg: TStrings); procedure TForm1.FormCreate(Sender: TObject);
begin begin
if Msg[0]='torold' then Label1.Caption := '';
begin DdeClientItem1.DdeConv := DdeClientConv1;
ShowMessage('A kliens kéri a szövegdoboz end;
törlését.');
Edit1.Text := ''; procedure TForm1.Button1Click(Sender: TObject);
end; begin
end; if DdeClientConv1.SetLink('DDESzerver',
'DdeServerConv1') then
end. begin
ShowMessage('A kapcsolat létrejött.');
DdeClientItem1.DdeItem := 'DdeServerItem1';
end
Kliens létrehozása else
ShowMessage('A kapcsolatot nem sikerült
Kliensünk is hasonló lesz az első DDE klienshez, csak létrehozni.');
end;
tartalmazni fog még egy nyomógombot, melynek megnyomásával a
kliens kéri a szervert a szövegdoboz kitörlésére. 033 procedure TForm1.DdeClientItem1Change(Sender:
TObject);
begin
Label1.Caption := DdeClientItem1.Text;
end;
end.
154 155
Miután elkészültek a szerver és kliens alkalmazásaink, azok A szerver a Memo-ba írt szöveget fogja szolgáltatni a kliensnek.
elindításával, majd a kliens kapcsolódásával a szerverhez Tehát amikor megváltozik a Memo komponensben a szöveg, akkor azt
kipróbálhatjuk a „torold” parancs küldését is a szervernek. berakjuk a DdeServerItem1 Text tulajdonságába is.
Végezetül megmutatunk egy érdekességet. Készítünk egy DDE de a kapcsolatot is a szerver elemére. Mivel ezzel a paranccsal több
szerver-alkalmazást, de a DDE kliens most a MS Word lesz. 034 különböző adatot is másolunk (szöveg és kapcsolat), a vágólapot a
Clipboard.Open, illetve a Clipboard.Close parancsokkal meg kell
Az alkalmazásunk egy Memo komponenst, DdeServerConv,
nyitnunk a másolás előtt, illetve be kell zárnunk a másolás után.
DdeServerItem komponenseket és egy Button komponenst fog
tartalmazni. Mivel a vágólapot is használjuk ebben a programban, ne
felejtsük el a modulunk uses részét kiegészíteni a Clipbrd unittal!
unit Unit1;
interface
uses
Windows, Messages, SysUtils, …, StdCtrls, Clipbrd;
implementation
{$R *.dfm}
156 157
end;
end.
158 159
A következő képernyőn adjuk meg a projekt mentésének a
helyét és a projekt nevét (Project Name). A projekt neve ne
tartalmazzon ékezetes betűket és szóközt sem. Ugyanez a név kerül a
Filename mezőbe is. A név megadása után kattintsunk a Next gombra.
160 161
Ezek után a program bal szélén megjelent a szerkezet, melyet
megadtunk (ezeket itt már átnevezhetjük úgy, hogy tartalmazzanak
ékezetes betűket is – rákattintunk az egér jobb gombjával, majd
„Edit / Rename (F2)”). Itt bármelyik részre kattintva megadhatjuk a
hozzá tartozó tartalmat is a program jobb részén található A „SajatHelpem” részre kattintva a bal oldali sávban
szövegszerkesztő segítségével. Itt már használjunk ékezetes betűket is! megadhatjuk az egész súgóra vonatkozó beállításokat. Itt érdemes
foglalkoznunk a General résszel (füllel) és a Website egyes részeivel.
162 163
További hasznos beállítási lehetőségeket találunk még a
Window List – Main alatt, ahol megadhatjuk a háttér színét, a súgó
ablakának kezdeti méretét és elhelyezkedését, továbbá itt a HtmlHelp A programozás szempontjából nagyon fontos számunkra, hogy
Options alatt egyszerű jelölőnégyzetek segítségével azt is, hogy a a súgó mindegyik oldalának legyen egy egyedi azonosítója, mellyel
súgónk ablakának melyik részei legyenek láthatók. majd tudunk hivatkozni rá a Delphiből. Ez az azonosító a „Help Context
Number”. Ezek megadásához kattitntunk a bal oldali sávban (Document
Map) valamelyik lapra (pl. Bevezető), majd az eszköztárból válasszuk ki
164 165
állományt kell majd a Delphi-ben megírt alkalmazásunkkal együtt
terjesztetünk.
Fordítás után rögtön megjelenik a súgónk és letrejön egy .chm A programunkból a súgót kétféle képpen nyithatjuk meg:
kiterjesztésű állományunk (_tmphhp almappában), amely valójában a
• úgy, hogy a súgó kezdőoldala jelenjen meg,
súgó összes részét tartalmazza. Ezt az egyetlen CHM kiterjesztésű
166 167
• úgy, hogy rögtön az adott számú (Help Context Number)
oldal jelenjen meg. Miután megvizsgáltuk, hogy az F1 gomb volt-e lenyomva,
Ha az alkalmazás Súgó – Súgó menüpontjára kattintva azt hasonlóan az előző példához a HtmlHelp paranccsal megnyitjuk a
szeretnénk, hogy jelenjen meg a súgó a kezdőoldallal, akkor a menühoz súgót. Itt azonban már harmadik paraméternek HH_HELP_CONTEXT
a következő programkódot kell beírnunk: konstanst adunk meg, negyedik paraméternek pedig az oldalunk
azonosítóját (Help Context Number).
HtmlHelp(handle,'sajathelpem.chm',0,0);
168 169
Majd válasszuk ki a File – New menüpontot. Megjelenik egy új
Miután mindezt megtettük és végigmemtünk az összes lépésen,
ablak, melyben pár egyszerű kérdés segítségével beállíthatjuk a
a programban megjelennek egy szkript formájában a beállítások. Ezeket
telepítőnk paramétereit.
még szükség szerint itt is módosíthatjuk.
170 171
Gyakorlatok:
1. Készítsünk egy TCsepp osztályt, amely egy esőcseppet (kört) rajzol
ki a megadott Image komponensre. A TCsepp osztálynak írjuk meg
a konstruktorát, mely csak egy paramétert tartalmazzon – annak
az Image (ExtCtrls unitban található) komponensnek a nevét, ahová
az esőcseppet ki akarjuk rajzolni. A konstruktor generáljon ki egy
véletlenszerű koordinátát ezen a képen (Image-en) és egy véletlen
sugarat (0-tól 29-ig). Majd rajzolja ki az esőcseppet erre a
koordinátára. Az osztály tartalmazzon még egy kirajzol és egy
letöröl eljárást, amely kirajzolja a kört az objektumhoz tartozó
koordinátára az objektumhoz tartozó sugárral. A kirajzolást bsClear
(graphics unitban található) ecsetstílussal és RGB(sugár*8, sugár*8,
100+sugár*5) (windows unitban található) kék színárnyalatú
körvonallal végezzük. Az osztálynak legyen még egy növekszik
metódusa, amely letörli az esőcseppet, növeli a sugarát, majd
A telepítő állomány létrehozásához (lefordításához) válasszuk ki
megnézi hogy a sugár nem érte-e el a 30 pixelt. Ha elérte, akkor
az eszköztárból a ikont vagy a menüből a Build – Compile beállítja 0-ra és új koordinátákat generál ki az objektumnak. Végül
menüpontot. kirajzolja az esőcseppet.
Ekkor létrejön egy EXE kiterjesztésű állomány, amely Ezt az osztályt felhasználva készítsünk egy programot, amely
segítségével a felhasználók telepíthetik az alkalmazásunkat a saját megjelenít 30 esőcseppet és 25 század-másodpercenként növeli
gépükre. Terjesztéskor elég ezt az egyetlen állományt forgalomba azok nagyságát (amíg nem érik el a 30-at, utána egy másik helyen
hoznunk. 0 sugárral jelennek meg). 004
172 173
2. Készítsünk egy TDeszka osztályt, majd ennek segítségével azt az 3. Készítsünk TCsillag osztályt, amely kirajzol egy véletlen méretű (pl.
alkalmazást, melyben véletlen hosszúságú deszkák úszkálnak a 1-től 5-ig) csillagot. Az osztálynak legyen egy olyan metódusa,
vízen. Az első sor balra, a második jobbra, a harmadik megint balra, melynek meghívásával a csillag eggyel nagyobb méretű és
a negyedik megint jobbra, az ötödik sor pedig ismét balra ússzon. világosabb kék színű lesz. Ha eléri az 5-ös méretet, akkor tűnjön el
Ügyeljünk arra, hogy két egymás melletti deszka között mindig és egy új, véletlen helyen jelenjen meg 1-es mérettel és sötétkék
legyen egy kis hely. Az egyik szélén kiúszó deszkák a másik szélén színnel.
ússzanak be (ne hirtelen jelenjenek meg). 003
A TCsillag osztály segítségével készítsünk egy képernyővédőt,
amely teljes képernyőn kirajzol 100 csillagot, majd ezek méretét és
fényességét a megírt metódus segítségével növeli (ha valamelyik
csillag eléri a maximum méretét, akkor egy másik helyen jelenjen
meg kis méretben). Az alkalmazás bármelyik billentyű
megnyomásával fejeződjön be. 013
174 175
képernyő tetejéről "jöjjön be" más x koordinátával, más mérettel és
más színnel (véletlenszerű). 035
176 177
6. Készítsünk alkalmazást prímszámok generálására, amely egy
nyomógombot, egy ListBox-ot és egy Gauge komponest tartalmaz.
A nyomógomb megnyomása után a program a ListBox-ba generálja
ki az első 20000 prímszámot. A Gauge komponens folyamatosan
jelezze, hogy a 20000 prímszámnak eddig hány százaléka található
a ListBox-ban. A nyomógomb megnyomása után a prímszámok
generálását (majd beírását a ListBox-ba és a Gauge komponens
frissítését) egy külön programszálban végezzük el! Próbáljuk úgy
megírni az algoritmust, hogy az a számok generálását minél
hamarabb elvégezze. 018
178 179
Irodalomjegyzék:
[1] Václav Kadlec: Delphi Hotová řešení, ISBN: 80-251-0017-0,
Computer Press, Brno, 2003
180