Tartalmi kivonat
Molnár Roland Adatbázis-kezelő rendszerek fejlesztése Delphi nyelven Bevezetés a Delphi-s adatbázis-kezelés rejtelmeibe Tartalomjegyzék Bevezetés.3 A munkához kapcsolódó gyakorlati feladatok .3 I.Az adatbázisokról4 Alapfogalmak, elvek .4 Adatbázis .4 Adatbázis-kezelő rendszer .4 Adatmodellek alapelemei .4 Az egyed.4 A rekordtípus.4 A tulajdonság.4 A kulcs.4 A kapcsolat .5 Adatmodell .5 A normálformák .5 Első normálforma (1NF) .5 Második normálforma (2NF).5 Harmadik normálforma (3NF).6 Magasabb normálformák.7 A lookup .7 Törzsadattábla.7 SQL.7 Adatbázis-tervezés .7 II.A Delphi adatbázis-kezelő rendszere 8 Alapok, BDE .8 Alias létrehozása, szerkesztése .9 Tábla létrehozása, szerkesztése.9 SQL lekérdezés készítése .10 Komponensek.10 III.A Delphi adatbázis-kezelő komponensei10 TDataSet, mindennek az alapja.10 Data Access komponensek.10 TDataSource.10 TTable .10 TQuery .11 TUpdateSQL .12 TBatchMove.12 Data Controls komponensek .12 TDBGrid
.12 TDBNavigator.13 TDBText .13 TDBEdit .13 TDBMemo .13 TDBImage.13 TDBListBox.13 TDBComboBox .13 1 TDBCheckBox .13 TDBRadioGroup .13 TDBLookupListBox.13 TDBLookupComboBox .14 TDBRichEdit.14 TDBCtrlGrid .14 TDBChart.14 Nyelvi problémák.14 TDataModule .14 IV.Egy adatbázis-kezelő alkalmazás elkészítése 15 Tervezés .15 A DataModule.15 Az ablakok .16 Ablaktípusok.17 A főképernyő .18 Táblamegjelenítő ablakok.18 Adatlapok.20 Műveletvégző ablakok.21 Kimutatások.22 Folyamatkijelzők (ProgressBar-ok) .23 Nyomtatás, riport .23 Project.24 V.Egyéb problémák 25 Hibakezelés .25 Hálózat használata Paradox rendszerrel .26 Biztonsági problémák.27 Biztonsági mentés.27 Adatsérülések kezelése .27 Telepítő készítése .28 Készítsünk CD-t! .30 VI.Lezárás 30 Irodalomjegyzék .31 2 Bevezetés A Borland Delphi napjaink egyik legfejlettebb alkalmazás-fejlesztő rendszere. Elsősorban adatbázisrendszerek készítésére szánják, de gyakorlatilag
bármilyen szoftvert el lehet készíteni vele Mivel mindig is érdekelt a programozás és a modern technológia, autodidakta módon foglalkozni kezdtem vele (a főiskolai tanórákon az 1992-es dátumú Turbo Pascal 7.0 volt a „legújabb” fejlesztőkörnyezet, amellyel találkoztam) Később már komolyabb alkalmazásokat is fejlesztettem Delphi-vel, így a számítástechnikának ez a területe lett az, amivel a legtöbbet foglalkoztam. Rengeteg tapasztalatot gyűjtöttem, amelyet mindig is szerettem volna megosztani olyanokkal, akiknek szükségük van rá. Ezért választottam ezt a témát dolgozati témának Én személy szerint a Delphi-vel való ismerkedésem idején szívesen forgattam volna egy ilyen munkát, elkerülve ezzel rengeteg bosszúságot és kísérletezést. Ezt a témát választva remélem, hogy ez a munka másokhoz is eljut, és segítő kezet nyújt mindazoknak, akik közelebbről is meg szeretnének ismerkedni a Delphi-vel. Ez a dolgozat azoknak szól,
akik most ismerkednek a Borland Delphi fejlesztőeszközzel, és szeretnék megtenni az első lépéseket az adatbázis-kezelő rendszerek fejlesztése felé. Ez a dolgozat egy rövid, de lényegre törő kalauz, mely végigvezet a kezdetektől egészen a telepítő készítéséig minden nagyobb állomáson, amelyen egy adatbázis-kezelő szoftver fejlesztése során át kell haladni. Célja nem az, hogy megmutassa, hogyan kell fejleszteni, hanem az, hogy megmutassa, hogyan lehet elkezdeni. „Minden kezdet nehéz” – hangzik a kopott mondás. Ezen a nehéz kezdeten igyekszik átsegíteni olvasóját Végigolvasva, követve a tanácsokat már bátran hozzákezdhet a Kedves Olvasó a fejlesztéshez. Ne várjon azonban teljes körű leírást vagy magyarázatot, hiszen mindenről részletes információ található a Delphi súgójában (Help), és én nem azt kívántam magyarítani. Sokkal inkább egy átfogó segítséget szeretnék nyújtani, mindenhol utalva a Delphi Help-re.
Ahhoz, hogy e munka tanulmányozása eredményes legyen, a következő dolgok szükségesek: - Idő, hiszen ez nem megy egyik pillanatról a másikra. - Kísérletező kedv, mert programozni tanulni csak úgy lehet, ha kísérletezgetünk. - Angoltudás, mivel a Delphi és a hozzá tartozó súgó angol nyelvű, így a helyes használat érdekében legalább minimális angoltudással és egy jó szótárral kell rendelkezni. - Számítógép és egy telepített Delphi 3, 4 vagy 5 A dolgozat írásakor Delphi 5-öt használtam, így előfordulhat, hogy a többi verzióban egy-két dolog máshogy és máshol van, de a lényeg mindegyiknél megegyezik. A dolgozat felépítését tekintve az elmélettől halad a gyakorlat felé. Először adatbázis-kezelési alapfogalmakat ismertet, majd a Borland adatbázis-kezelésének elvi működését magyarázza, azután pedig rátér az adatbázis-kezelő rendszerek fejlesztésének lépéseire. Végül különböző gyakorlati problémák
megoldására nyújt hasznos tanácsokat. A dolgozatban sokat fogok hivatkozni a Delphi súgójára, a következőképpen: (Delphi Help: Témakör). Az itt megadott témakört beírva a Tárgymutató ablak kereső sorába, megtaláljuk az oda vonatkozó bővebb leírást. Az irodalomjegyzékben szereplő irodalmakra a következőképpen utalok: [jegyzékszám/oldalszám] (pl: [2/213] = Szelezsán János Adatbázisok c. könyvének 213 oldala) A munkához kapcsolódó gyakorlati feladatok Jelen munka készítése előtt már volt szerencsém adatbázis-kezelő alkalmazásokat fejleszteni, így kellő tapasztalatot szereztem a Delphi-s Paradox rendszerek működtetéséről, programozásáról. A dolgozatomban ezen munkákról készült képernyőképek (és egy-két helyen ezekből kimásolt kódrészletek) találhatóak. Ezek a munkák azonban nem kapcsolódnak konkrétan a dolgozat tárgyához, hiszen általánosságban beszélek a feladatokról. Nem láttam tehát sok értelmét
annak, hogy ezekkel bővebben foglalkozzak. A pontosság kedvéért azért szeretném közölni, milyen munkák ezek. Az egyik feladat egy helyi rádió reklámadminisztrációjának számítógépesítése volt. Az adatbázis rendkívül egyszerű, mindössze három fő- és négy altáblából áll. A három főtábla közül az egyik a reklámok adatait tárolja (felvétel időpontja, hirdetőpartner, hirdetésszervező, futás kezdete, vége, darabszám, fizetés dátuma, módja, a reklám szövege, típusa, stb.) A második táblában a hirdetőpartnerek, a harmadikban a hirdetésszervezők adatai vannak (név, cím, telefonszám, stb.) A négy altábla közül egy a reklámtípusokat, egy a fizetésmódokat, egy a felhasználókat, egy pedig a felhasználói típusokat tárolja. A táblák között egyszerű 1:N relációk vannak. 3 A másik feladat ennél lényegesen bonyolultabb: egy étterem rendelés-, számla-, és raktárnyilvántartását kellett megoldani. A rendszer
két részből áll: az éttermi program a rendelésekkel és a számlákkal foglalkozik, a raktár program pedig a raktárkészlettel, és az éttermi program által regisztrált rendelések alapján (a saját összetevő-adatbázist használva) a raktári tételek mennyiségének automatikus aktualizálásával. A rendszer két adatbázist (étterem és raktár), és összesen 29 Paradox táblát tartalmaz. Az éttermi program az általa tárolt hatalmas adatmennyiség (rendelések és számlák) ellenére megfelelő sebességgel dolgozza fel az adatokat, köszönhetően annak, hogy a rendelések és számlák adatait két-két táblában is tárolja. Egyikben az aktuálisakat, a másikban a régebbieket, melyekkel a program csak zárás esetén foglalkozik. Érdekes feladat volt még a számlanyomtatók kezelése, melyet úgy oldottam meg, hogy meghajtóprogram (driver) nélkül működjön. Mindkét szoftver lehetővé teszi a hálózati használatot, mely igazán az étterem
programnál volt nagy kihívás, hiszen nagyobb forgalom idején egyszerre több számítógépen veszik föl a rendeléseket és adják ki a számlákat, így rendkívül sűrű és nagy az adatmozgás. Az említett munkák készítése alatt szerzett tapasztalataim próbálom átadni ezen dolgozat keretein belül. I. Az adatbázisokról Alapfogalmak, elvek Ebben a részben az adatbázisokkal kapcsolatos általános fogalmakkal ismerkedünk meg. Adatbázis Amikor az ember adatbázis-kezelő szoftverrel dolgozik, hajlamos azt hinni, hogy minden, amit azzal végez, az adatbázis és adatbázis-kezelés. Ez azért van, mert nagyon sokan nincsenek tisztában az adatbázis fogalmával. Néhány adatfájl még önmagában nem adatbázis, csupán adattáblák halmaza Ahhoz, hogy adatbázissá váljon, a táblák közötti kapcsolatokat is definiálni kell. Gondoljunk például arra, hogy van két táblánk, az egyikben nevek, a másikban címek. Mindkettőnek van egy kód nevű mezője
is. Ez a leírás önmagában még nem egy adatbázist határoz meg, csupán két, egymástól független adattáblát írtunk le. A dolog akkor válik adatbázissá, ha kikötjük a következőt: a neveket és a címeket tartalmazó táblában lévő kód összekapcsolja a két táblát úgy, hogy egy, a neveket tároló táblában lévő névhez az azonos kódú, a címeket tároló táblában lévő cím tartozik. Ezzel definiáltuk az adatkapcsolatot Az adatbázison tehát voltaképpen adatoknak kapcsolataikkal együtt való ábrázolását, tárolását értjük. [2/13] Adatbázis-kezelő rendszer Az adatbázis önmagában semmit nem ér. A felhasználó számára akkor válik értékké, ha megadunk hozzá egy olyan szoftvert, amellyel az adatbázist kezelni tudja. Az ilyen szoftvert adatbázis-kezelő rendszernek nevezzük. Az adatbázis-kezelő rendszereknek két fő funkciója van: az adatdefiníció (az adatbázis szerkezetének definiálása, a szerkezet feltöltése
konkrét adatokkal, illetve ezek módosítása, törlése), valamint a lekérdezés (adatok visszakeresése, kimutatások készítése). [2/21] Adatmodellek alapelemei Az egyed „Egyednek hívunk minden olyan dolgot (objektumot), ami minden más dologtól (objektumtól) megkülönböztethető, és amiről adatokat tárolunk.” [2/33] A rekordtípus „Az adatbázis-kezelő rendszerekben () az egyedtípust rekordtípusnak hívjuk. Ez a legkisebb címezhető egység (ennél kisebb részeire nem lehet hivatkozni az adatbázisnak).” [2/34] A tulajdonság „Az egyedeket tulajdonságokkal (attribútumokkal) írjuk le. A tulajdonság az egyed egy jellemzője, ami megadja, meghatározza az egyed egy részletét.” [2/35] A tulajdonság és az attribútum azonos jelentésű fogalmak, és a továbbiakban felváltva használjuk őket. A kulcs 4 „Amennyiben egy tulajdonság vagy tulajdonságok egy csoportja egyértelműen meghatározza, hogy egy egyed melyik értékéről,
előfordulásáról van szó (vagyis az egyedhalmaz melyik eleméről), akkor ezeket a tulajdonságokat együtt kulcsnak nevezzük” [2/36] A kapcsolat „Kapcsolatnak nevezzük az egyedek közötti viszonyt.” [2/38] A kapcsolat fajtái: Egy-egy típusú kapcsolat (1:1 kapcsolat): olyan kapcsolat, ahol „az egyik egyedhalmaz mindegyik eleméhez a másik egyedhalmaznak pontosan egy eleme kapcsolódik (tehát a két egyedhalmaz egymásba kölcsönösen egyértelműen képezhető le).” [2/40] Egy-több típusú kapcsolat (1:N kapcsolat): „Azt mondjuk, hogy az A egyed és a B egyed között egytöbb (1:N) kapcsolat van, ha az A egyedhalmaz mindegyik eleméhez a B egyedhalmaz több eleme is tartozhat.” [2/40] Több-több típusú kapcsolat (N:M kapcsolat): „Több-több (jelben N:M) típusú kapcsolatnak nevezzük az A egyed és a B egyed közötti kapcsolatot, ha az A egyedhalmaz minden eleméhez a B egyedhalmaz több eleme tartozhat, és fordítva egy B-beli elemhez is több
A-beli elemet rendelhetünk hozzá.”[2/41] Adatmodell Ezek után már definiálhatjuk, mi is az adatmodell: „Az adatmodell egyedek, tulajdonságok és kapcsolatok halmaza, amely absztrakt módon tükrözi a valós objektumoknak, azok jellemzőinek (tulajdonságainak) és viszonyainak (kapcsolatainak) elvont kategóriáit.” [2/48] A normálformák Az adatbázisok belső szerkezetét ún. normálformák jellemzik Ha egy adatbázis eleget tesz bizonyos feltételeknek, akkor azt mondjuk, hogy egy adott normálformában van. Első normálforma (1NF) „Egy R relációról azt mondjuk, hogy első normálformában van, ha minden sorában pontosan egy attribútumérték áll.” [2/87] Ez első hallásra lehet, hogy bonyolultnak hangzik, annál is inkább, hogy az adatbázis-kezelők csak olyan adatbázist fogadnak el, amely 1NF-ben van. Egy példával lehet a legjobban érzékeltetni, mit is jelent ez valójában: NÉV Nagy Géza Kiss János Bíró Ödön SZAKKÉPZETTSÉG
gépészmérnök, közgazdász, műszerész lakatos fodrász, lakatos A fenti táblázat nincs 1NF-ben, mert a SZAKKÉPZETTSÉG mezőben néhol több érték is szerepelhet. Ezt úgy is lehetne ábrázolni, hogy minden egyes szakképzettséget külön sorba teszünk: NÉV Nagy Géza Kiss János Bíró Ödön SZAKKÉPZETTSÉG gépészmérnök közgazdász műszerész lakatos fodrász lakatos Amennyiben ezt első normálformára szeretnénk hozni, a legegyszerűbb, ha a több attribútumértéket tartalmazó sort annyi sorra bontjuk, amennyi az attribútumértékek száma: NÉV Nagy Géza Nagy Géza Nagy Géza Kiss János Bíró Ödön Bíró Ödön SZAKKÉPZETTSÉG gépészmérnök közgazdász műszerész lakatos fodrász lakatos Így a táblázat már 1NF-ben van. Második normálforma (2NF) 5 Egy reláció akkor van második normálformában, ha első normálformában van, és minden nem-kulcs tulajdonsága teljesen függ a kulcstól. [2/93] Nézzük pl. a következő, az
előzőnél kissé bonyolultabb, 1NF-ben lévő táblát, ahol számlák adatait tároljuk. A SZSZ a számlaszám, a TKOD a tételkód, a TNÉV a tételnév, az EÁR az egységár rövidítése ELADÁS SZSZ 234 234 235 235 DÁTUM 2000.0211 2000.0211 2000.0220 2000.0220 VEVŐ XYZ Kft XYZ Kft UVT Kft UVT Kft VEVŐCÍM Bp. Nagy út 10 Bp. Nagy út 10 Ózd, Híd út 3. Ózd, Híd út 3. TKOD 142 264 245 142 TNÉV Kifli Tej Kakaó Kifli DB 5 3 6 4 EÁR 20 40 10 20 ÁR 100 120 60 80 A kulcsot a számlaszám (SZSZ) és a tételkód (TKOD) alkotják, hiszen ketten együtt egyértelműen meghatároznak egy rekordot (ezeket dőlt betűvel jelöljük). A táblázat nincs 2NF-ben, mert vannak benne olyan másodlagos attribútumok, amelyek nem az egész kulcstól, csak az azt alkotó egyes attribútumoktól függnek. A DÁTUM függ az SZSZ-től, mert egy számlához egyazon dátum tartozik. Ez igaz a VEVŐ-re, és a VEVŐCÍM-re is. Hasonlóképpen belátható, hogy a TKOD is rendelkezik
azzal a tulajdonsággal, hogy több másodlagos attribútumot egyedül is meghatároz (TNÉV, EÁR). A 2NF-re hozás egyszerű művelet: a relációt több, 2NF-ben lévő táblázatra bontjuk. A mi esetünkben ez két táblát (ELADÁS1 és TÉTELEK) fog eredményezni, és azoknak a kulcsai az ELADÁS táblánk kulcsának két attribútuma lesz: ELADÁS1 SZSZ 234 234 235 235 DÁTUM 2000.0211 2000.0211 2000.0220 2000.0220 VEVŐ XYZ Kft XYZ Kft UVT Kft UVT Kft VEVŐCÍM Bp. Nagy út 10 Bp. Nagy út 10 Ózd, Híd út 3. Ózd, Híd út 3. TKOD 142 264 245 142 DB 5 3 6 4 ÁR 100 120 60 80 TÉTELEK TKOD 142 264 245 TNÉV Kifli Tej Kakaó EÁR 20 40 10 A két tábla közötti kapcsolatot a TKOD valósítja meg. Ezzel a relációt második normálformára hoztuk Harmadik normálforma (3NF) Egy reláció harmadik normálformában van, ha második normálformában van és egyetlen másodlagos attribútuma sem függ tranzitívan a kulcstól. [2/100] Egy C tulajdonság akkor függ
tranzitívan az A kulcstól, ha meghatározza a kulcstól szintén függő B tulajdonság is. [3/224] Vegyük alapul az előbbi ELADÁS1, 2NF-ben lévő táblát. A SZSZ meghatározza a VEVŐ attribútumot, az pedig meghatározza a VEVŐCÍM-et. Ez azt jelenti, hogy a VEVŐCÍM tranzitívan függ a SZSZ kulcstól, mivel azt meghatározza a nem kulcs VEVŐ tulajdonság is. A tábla tehát nincs 3NF-ben Ahhoz, hogy 3NF-ben legyen, ugyanazt a műveletet kell elvégezni, mit az imént: föl kell bontanunk a táblát. Itt azonban fel kell ismerni egy másodlagos kulcsot is: VEVŐ Ez lesz a két új tábla közötti kapcsolat. ELADÁS2 SZSZ 234 234 235 235 VEVŐK VEVŐ XYZ Kft UVT Kft DÁTUM 2000.0211 2000.0211 2000.0220 2000.0220 VEVŐ XYZ Kft XYZ Kft UVT Kft UVT Kft TKOD 142 264 245 142 DB 5 3 6 4 ÁR 100 120 60 80 VEVŐCÍM Bp. Nagy út 10 Ózd, Híd út 3. 6 Így kiküszöböltük a tranzitív függősséget, a reláció 3NF-ben van. Természetesen a modellhez tartozik még
a TÉTELEK tábla is, de azt az előző példához képest változatlanul hagyjuk, hiszen az megfelel a 3NF feltételeinek is. Magasabb normálformák A harmadik normálforma a minimum, amit egy adatbázis tervezésnél be kell tartani. Ne gondoljuk tehát, hogy ez a normalizálás csúcsa. Léteznek magasabb szintű normálformák is (BCNF, 4 és 5 NF), ezek tárgyalása azonban kimerítené ezen dolgozat kereteit, ezért nem foglalkozunk velük. A lookup Ezzel a szóval sokat fogunk még találkozni a dolgozat folyamán. Angolul keresést, utánanézést jelent Az adatbázis-kezelőkben a táblák közötti kapcsolat megvalósítására használják. Segítségével az iménti példáinkban szereplő ELADÁS1 és TÉTELEK táblát összekapcsolhatjuk úgy, hogy a megjelenített végeredmény egy ELADÁS-hoz hasonló táblázat lesz, ahol a TNÉV és az EÁR ún. lookup mezők, amelyek valójában nincsenek a táblázatban, csupán az adatbázis-kezelő helyezi oda őket, az
általunk definiált kapcsolat alapján. Ez a kapcsolat a következőképpen néz ki: ELADÁS1TKOD <==> TÉTELEK.TKÓD Így a program a TKÓD alapján a TÉTELEK táblából gyakorlatilag „behúzza” az adatokat az ELADÁS1 táblába. Ezzel azonban korántsem merülnek ki a lookup által nyújtott lehetőségek. Táblázatunkban ugyanis nem muszáj megjeleníteni a TKÓD mezőt, így a felhasználó azt látja, hogy ha felvesz egy új számlát, a VEVŐ mezőnek ad értéket, holott valójában a TKÓD-ot manipulálja. Ezt azonban a rendszer ügyesen elrejti előle. Hasonlóképpen állíthatunk lookup kapcsolatot az ELADÁS2 és a VEVŐK táblázatok között is, a VEVŐ mező alapján. Így az ELADÁS2, a VEVŐK és a TÉTELEK (3NF-ben lévő) táblák használatával megjeleníthetjük úgy az adatbázist, hogy az ELADÁS (csupán 1NF-ben lévő) táblát lássuk. Így nem veszítjük el a normalizálás által biztosított előnyöket, a felhasználó viszont egy helyen
lát mindent, így neki nem kell tudnia, mi is az a normalizálás. Törzsadattábla A normalizálás folyamán keletkeznek olyan táblák, amelyek a többihez képest viszonylag állandónak mondhatók (példánkban ilyen a TÉTELEK, ill. a VEVŐK tábla) Ezeket a táblák mindig valamilyen főbb táblával vannak kapcsolatban (itt: ELADÁS1, ill. ELADÁS2) Az ilyen táblákat törzsadattábláknak nevezzük A törzsadattáblák (értelemszerűen) törzsadatokat tartalmaznak. A törzsadatok manipulációját általában a program karbantartó egységében vagy automatikusan szokták megoldani. SQL Az SQL (Structured Query Language = Strukturált lekérdező nyelv) egy szabványosított (ISO 9075) adatbázis-kezelő nyelv. Alapvetően két része van: adatdefiníciós nyelv (DDL, Data Definition Language) és adatmanipuláló nyelv (DML, Data Manipulation Language). A DDL segítségével táblákat hozhatunk létre, törölhetünk, stb., míg a DML-lel beszúrhatunk,
szerkeszthetünk, törölhetünk, illetve lekérdezhetünk adatokat egy vagy több táblából. A Delphi természetesen támogatja az SQL-t. Komponensein keresztül könnyen építhetünk be alkalmazásunkba SQL-utasításokat. Mivel ebben a munkában a Paradox rendszerekről van elsősorban szó, az SQL-t bővebben nem tárgyaljuk. Adatbázis-tervezés Az előző részben megtudhattuk, hogyan kell az adatbázist egyszerűsíteni, normalizálni. A szabály tehát az, hogy úgy kell megtervezni az adatbázist, hogy az legalább a harmadik normálforma követelményeinek eleget tegyen. A gyakorlatban azonban ez nem mindig működik Az általam készített éttermi alkalmazásban található például olyan eset, amikor a rendelés fejlécét és a törzsét tartalmazó táblákban egyaránt szerepel az asztal kódja, holott elég lenne csupán a fejlécben tárolni, hiszen egy rendelés tételeihez csupán egy asztalkód tartozhat. Azért volt szükség ennek ellenére erre a
megoldásra, mert a számlázásnál asztalok szerint kell szűrni (mindig csak egy asztalhoz tartozó rendeléseket kell megjeleníteni), és jelentősen felgyorsította a működést az eredeti tervben nem szereplő asztalkód mező a rendelések törzsét tároló táblában. A másik eset, amikor hasonló problémával találkoztam, a számlák rész- és végösszegének tárolása volt Kénytelen voltam ugyanis eltárolni a bruttó árát is a számla tételeinek, illetve a számla bruttó végösszegét, pedig ezeket egyértelműen meg lehet határozni, mivel az adatbázis tartalmazza a tételek nettó árait és ÁFA-kulcsait, de kiszámításuk jelentősen lassította volna a működést. Mondhatnánk azt is, hogy a nettó árat sem kellene tárolni, hiszen azt is egyértelműen meghatározhatjuk, de gondolni kellett arra, mi történik, ha valaminek megváltozik az ára. Ekkor ugyanis az eredeti modell szerint (amelyben még nem tároltam a számlák összegeit) a régebben 7
kiadott számlák összege is megváltozott, így hamis adatokat kaptunk. A végleges megoldással már nem jelentkezhet ez a probléma, hiszen a számla a kiadásának időpontjában aktuális árakat tartalmazza. Egy szó, mint száz, nem lehet agyon optimalizálni az adatbázist. Megfogalmazhatjuk tehát az adatbázistervezés egyik legfontosabb titkát (amelyben Halassy Béla könyve is megerősített): „Sohasem szabad az adatszerkezetet egyszerűsíteni a programbonyolultság rovására.” [3/207] Azt természetesen mondani sem kell, hogy az adatbázis-tervezés két fontos szabálya: mindennek benne kell lennie, amire szükség van, és semminek nem szabad benne lennie, amire nincs szükség. Hogy ismét Halassy Béla szavait idézzem: „Az a jó adatféle, ami nincs.” [3/155] Mert ami nincs, azzal nem kell foglalkozni, nem foglal helyet, nem vesz igénybe processzoridőt, és nem kell fáradozni vele. Tehát fölösleges adatokat semmiképpen ne tároljunk. Ami viszont
kell, az kell Ezek a kijelentések lehet, hogy triviálisnak hangzanak, de fontosnak tartom leszögezni őket, mert sokszor nem tartjuk be ezeket, és későn ébredünk rá, hogy hibáztunk. Az adatbázis-tervezés bonyolult dolog. Rengeteget lehet hibázni, de a leggyakoribb hiba, ha nem ismerjük eléggé a működési mechanizmusokat, azaz nem tudjuk, mire lesz szükség. Ezért nagyon fontos, hogy előtte tájékozódjunk, és a feladatot nagyon pontosan határozzuk meg. Ha megkérnek, hogy készítsünk egy bizonyos alkalmazást, ne elégedjünk meg ennyivel, kérdezzük meg, hogyan kell működnie. Ezzel rengeteg vesződséget megspórolhatunk. Elmondható tehát az is, hogy az adatbázis-tervezés (de igaz ez a programtervezésre is) másik legfontosabb szabálya a minél pontosabb feladat-meghatározás. II. A Delphi adatbázis-kezelő rendszere Eddig az adatbázisokról általánosságban beszéltünk. Most áttérünk igazi témánkra, a Delphi-s rendszerek
fejlesztésére. Ehhez ismernünk kell a Delphi adatbázis-kezelő rendszerének működési alapjait Alapok, BDE A BDE a Borland Database Engine rövidítése, amely egy Windows alapú 32 bites adatbázis-motor. Feladata, hogy kapcsolatot teremtsen a fizikai adatbázis és az azt kezelő alkalmazások között, és ezzel megkönnyítse a Borland fejlesztőrendszereit használó programozók munkáját. Leveszi vállunkról a táblaformátumok kezelését, és alapvető adatbázis-kezelő rutinokat tartalmaz. A BDE a következő adatbázisokat támogatja: dBASE, Paradox, Text, FoxPro, Access, InterBase, Oracle, Sybase, Microsoft SQL, ODBC (Microsoft Open Database Connectivity). A programozó ezek közül szabadon választhat, anélkül, hogy módosítaná programját. Minden formátumnak saját, független meghajtója (drivere) van Ezeken kívül azonban vannak megosztott szolgáltatások is, amelyeket minden driver használhat. Ilyen pl a Buffer Manager (buffer kezelő), a Sort
Engine (rendező motor), stb. A Delphi adatbázis-kezelése teljes mértékben a BDE motorra épül, a Delphi minden DB komponense ezt használja az adatok közvetlen vagy közvetett eléréséhez. A komponensek a BDE API (Application Program Interface) függvényeken keresztül működtetik a Database Engine-t. A BDE függvényei objektum-orientáltan épülnek fel, így azok elérése egyszerű és strukturált. A Database Engine főbb részei a következők: - BDE mag (BDE core): a rendszer magját képező .DLL fájlok tartoznak ide - BDE API függvények: alapvető adatbázis-műveleteket tartalmazó függvénytár, mely tartalmaz adatbázis kezelésre, konfigurálásra, hibakezelésre, lekérdezésre, tranzakciókra használható függvényeket. - Adatbázis meghajtók (Database Drivers): ide tartozik az öt standard meghajtó (Paradox, dBASE, FoxPro, Access, és Text). - Opcionális meghajtók (Optional Drivers): egyéb meghajtókat tartalmaz (InterBase, DB2, Informix, Oracle,
Sybase, Microsoft SQL Server). - Query motorok (Query Engines): az SQL (Structured Query Language – Strukturált lekérdező nyelv) motorok. - BDE Administrator: a BDE beállításait végző program (BDEADMIN.EXE) - Database Desktop: adatbázis fájlok kezelését végző segédprogram, mellyel egyszerűen hozhatunk létre, tölthetünk ki, vagy módosíthatunk különféle formátumú táblákat. Ezen kívül SQL lekérdezéseket is készíthetünk segítségével. A BDE fontos szolgáltatása még az ún. alias-ok kezelése Az alias-ok segítségével elnevezhetjük adatbázisunkat, hogy később ezen a néven hivatkozzunk rá, annak fizikai helyétől függetlenül. Standard adatbázisoknál az alias gyakorlatilag egy egyszerű mutató, ami egy adott könyvtárra mutat. SQL adatbázisoknál viszont már egyéb információkat is tartalmaz (felhasználónév, jelszó, stb.) A BDE-nek rengeteg beállítási lehetősége van, és általában az alapértelmezett beállítások nem
felelnek meg az igényeinknek. Telepítéskor ezért mindig célszerű végignézni ezeket a beállításokat Ezt a BDE Administrator (DBEADMIN.EXE) programmal tehetjük meg A beállítások közül megemlítünk néhány 8 fontosabbat. Ilyen pl a Langdriver, amivel a nyelvet állíthatjuk be Ilyet minden egyes driver beállításai között találunk, mivel a driverekhez egyenként adhatjuk meg, milyen nyelvvel dolgozzanak. Ez a változó alapértelmezetten ascii ANSI értékre van állítva. Ha magyar, ékezetes szövegeket is tárolunk az adatbázisunkban, célszerű ezt HUN 852 DC értékre állítani. Mivel mi elsősorban a paradox rendszerekkel foglalkozunk, ezért a Paradox driver beállításait nézzük meg. Itt a Langdriver változónak a Paradox HUN 852 DC értéket kell adni. Ha hálózaton keresztül használunk paradox táblákat, két dologra kell odafigyelnünk. Az egyik a Net Dir. Ez a paradox hálózati kontroll fájl (PDOXUSRSNET) helyét határozza meg Ez a
fájl hangolja össze a táblákat hálózaton keresztül elérő programokat. Fontos, hogy ha több számítógépről használunk hálózaton keresztül egy adatbázist, ezt a változót minden számítógépen be kell állítani úgy, hogy az ugyanoda mutasson. Célszerű az adatbázist tároló számítógép egyik könyvtárát megadni. Csak akkor tudjuk egyszerre több számítógépről használni az adatbázisunkat, ha ezt a változót helyesen állítjuk be. A változónak mindig valamelyik meghajtó, vagy megosztott könyvtár gyökerére kell mutatnia. Másik fontos beállítás hálózati használat esetén a Local Share. Ezt a System/INIT beállítások között találjuk. Ha egyszerre több programból szeretnénk elérni egy adatbázist, ezt igazra (TRUE) kell állítani Ha ezt nem tesszük meg, az adatbázist a hálózaton keresztül elérő programok nem tudják frissíteni az adatbázis pillanatnyi változásait. Alias létrehozása, szerkesztése Ha létrehozunk egy
adatbázist, első dolgunk, hogy létrehozunk számára egy alias-t. Ezt többféleképpen is megtehetjük: használhatjuk erre a BDE Administrator-t, a Database Desktop-ot, és az SQL Explorer-t is. Én az elsőt ajánlom, mert kezelése egyszerűbb: az Object/New menüpontot kiválasztva meg kell adni az adatbázis típusát (mi Standard adatbázisokkal foglalkozunk), majd be kell írni az alias nevét, és ki kell tölteni a jobb oldalon a Path mezőt. Ezzel kész is az alias Ha az SQL Explorer-t használjuk, az eljárás ugyanez A Database Desktop esetében már másként kell eljárni: Tools/Alias Manager menüpont, az ablakon New gomb, megadjuk a nevet és utat, majd a Keep New gombra kattintva elmentjük. Ha ezek után ezzel az alias-szal szeretnénk dolgozni, egy valamit még érdemes megtenni: beállítani a Database Desktop munkakönyvtárának ezt az alias-t (File/Working Directory). Ez azért célszerű, mert a Database Desktop mindig a munkakönyvtárát nyitja meg, ha a
megnyitó vagy a mentő ablakot használjuk. Tábla létrehozása, szerkesztése Ezeket a műveleteket a Database Desktop-pal tudjuk elvégezni. Kattintsunk a File/New/Table menüpontra. Válasszuk ki a tábla típusát Mivel ebben a munkában Paradox rendszerekkel foglalkozunk, válasszuk a Paradox 7-et. A táblázat kitöltése nem bonyolult, viszont jobb oldalon találunk néhány opciót (a lenyíló listából választhatjuk ki őket). A Validity Checks segítségével a kijelölt mező értékére vonatkozó beállításokat tehetjük meg. Ezek értelemszerűen kitöltendők. Figyeljünk arra, hogy melyik mezőkre használjuk, illetve nem használjuk a Required Field opciót. Ha ez be van kapcsolva, a táblázatba nem lehet elmenteni rekordokat úgy, hogy ez a mező nincs kitöltve. Ez akkor hasznos, ha a mezőre mindenképp szükség van Ha ugyanis elfelejtjük a programban kitölteni, azonnal hibaüzenetet kapunk. Az esetleges lefagyások miatti üresen való elmentést is
megakadályozhatjuk ennek segítségével. A Secondary Indexes segítségével manipulálhatjuk a másodlagos indexeket. Jó tudni, hogy akkor működik rendesen egy index, ha tulajdonságaiban be van jelölve a Maintained jelölőnégyzet. Ezt viszont csak akkor tudjuk bejelölni, ha van legalább egy elsődleges indexünk (a mező-táblázatban a Key oszlopban csillag látható). A Maintained opció egyébként arra vonatkozik, hogy a BDE automatikusan frissítse-e a másodlagos indexeket. Ha ez nincs bejelölve, manuálisan kell megoldanunk az újraindexelést Ha valamilyen adatbázis-rendszert készítünk, előfordulhat, hogy ügyelnünk kell a biztonságra. Ezért ajánlatos az adatbázis tábláit jelszóval védeni. Egy adatbázison belül célszerű ugyanazt a jelszót használni Ezt a jelszót nem szükséges a felhasználóknak tudni, mivel beépíthetjük a programunkba. Így csak azok a programok tudnak hozzáférni az adatbázishoz, amelyek ismerik ezt a jelszót. Az
egyéb biztonsági intézkedéseket (felhasználók kezelése, azok eltérő jogai, stb.) már a programunkon belül intézhetjük A Paradox táblák lehetőséget nyújtanak arra is, hogy több jelszót adjunk meg, és azokhoz különböző jogokat rendeljünk (Auxilary Password). Ezt akkor érdemes csak használni, ha több program is hozzáférhet egy adatbázishoz, de mindegyikük más-más jogosultságokkal. Ezt a problémát így a legegyszerűbb lekezelni Mint azt az imént említettem, fontos az is, hogy milyen nyelvű a tábla. Ezt táblánként megadhatjuk (Table Language). A Database Desktop először mindig az alapértelmezett nyelvet állítja be a táblának (ezt a BDE Administrator-ban definiálhatjuk, lásd föntebb), de meg is változtathatjuk kedvünk szerint. Ha már mindent beállítottunk, a Save As gombbal menthetjük a táblát. 9 SQL lekérdezés készítése SQL lekérdezést a legegyszerűbben a Database Desktop-pal készíthetünk (File/New/SQL file).
Ezt szerkeszthetjük, menthetjük, futtathatjuk. Ha egy Delphi programba szeretnénk SQL-t építeni, a lekérdezést mindig célszerű itt megírni és kipróbálni (az SQL-ről bővebben: Program filesCommon filesBorland SharedBDElocalsql.hlp) Komponensek A Delphi-ben az adatbázis-kezelést komponenseken keresztül valósíthatjuk meg. A komponenseket mozaikként egymáshoz illesztve alakítjuk ki a programunk szerkezetét. A komponensek biztosítják a programozó és a BDE közötti kapcsolatot. Alapvető műveleteket tartalmaznak, melyek felhasználásával könnyen valósíthatjuk meg elképzeléseinket. Komponensek nélkül szinte elképzelhetetlen a munka, használatukkal nem kell törődnünk a kezelt adatbázis típusával, formátumával, lényegesen emberközelibb programozást biztosítanak. III. A Delphi adatbázis-kezelő komponensei A Delphi adatbázis-kezelő komponensei közül azokat a standard komponenseket vesszük sorra, amelyek a native táblák
kezeléséhez szükségesek. Ezeket a standard komponenseket két részre osztva találjuk meg a Delphi eszköztárán: Data Access (adathozzáférés), és Data Controls (adatkontroll, adatvezérlés). A komponensek leírásában nem a teljességre törekszem, hanem a lényegesebb dolgok kiemelésére. Bővebbeket ezekről a Delphi Help-ben olvashatunk. TDataSet, mindennek az alapja A TDataSet osztály minden olyan komponens alap osztálya, amely sorokban és oszlopokban megjelenő adatot reprezentál. Ez az osztály tartalmazza az alapvető táblamanipulációs metódusokat, tulajdonságokat, stb Munkánk során sokszor fogunk ezzel az osztállyal találkozni. Data Access komponensek Ide azok a komponensek tartoznak, amelyek segítségével megvalósítjuk az adathozzáférést, az adatbázis fájljaival való kapcsolatot, ezek a komponensek kapcsolják össze az adatokat az adatmegjelenítő, manipuláló objektumokkal. TDataSource Ez a komponens teremt kapcsolatot az ún.
DataSet komponensek és a vizuális adatmegjelenítő (manipuláló) komponensek között. A DataSet komponensek azok a komponensek, amelyek valamilyen adathalmazt biztosítanak (TTable, TQurey). A komponens Dataset tulajdonságát kell a megfelelő komponensre (Table-re vagy Query-re) állítani. TTable A TTable segítségével beépíthetünk egy táblát a programba. A komponens a BDE-t használva hozzáférést biztosít egy adatbázis-táblához. Ennek segítségével kezelhetőek az adatok (navigáció, hozzáfűzés, módosítás, törlés, stb.) Ha felhelyezünk egy ilyen komponenset a form-ra, az első dolog, amit tennünk kell, a táblázat fájlnevének megadása. Ehhez először a DatabaseName-et kell megadnunk, amennyiben létrehoztunk az adatbázisunk részére alias-t. Ezt a TableName tulajdonság beállítása követi A lenyíló listában megtalálhatjuk az adatbázisunk fájljait, vagy ha nem használunk alias-t, a program könyvtárában lévő fájlokat. Az
IndexName beállításával megadhatjuk az index nevét, de használhatjuk erre a célra az IndexDefs vagy az IndexFieldNames tulajdonságokat is (bővebbekért ld. a súgót) Az indexeket természetesen futásidőben is változtathatjuk, kivéve ha a CachedUpdates be van kapcsolva. A CachedUpdates módot akkor használjuk, ha gyorsabb adathozzáférést szeretnénk megvalósítani, illetve minimalizálni szeretnénk a hálózati forgalmat. Ha ez be van kapcsolva, a BDE a memóriában tárolja az adatokat, és azokat nem menti lemezre, egészen addig, amíg meg nem hívjuk az ApplyUpdates metódust. Tehát ha ezt nem tesszük meg, a módosítások elvesznek. A CachedUpdates mód akkor is hasznos lehet, ha olyan alkalmazást készítünk, amiben fontos, hogy akár nagy mennyiségű adatmódosítás is visszavonható legyen, ez a 10 legegyszerűbb módja: ha vissza szeretnénk vonni a változásokat, csupán a CancelUpdates metódust kell meghívnunk. A tulajdonságok között
találunk egy Active nevűt. Ezzel már akár szerkesztő módban is bekapcsolhatjuk a táblát, de használhatjuk futásidőben az Open ill. a Close helyett is Célszerű azonban szerkesztő módban nem bekapcsolni, mert bizonyos esetekben megnehezítheti a munkánkat, hiszen ilyenkor lefoglalja az adatbázist, így más program (pl. Database Desktop) nem biztos, hogy hozzá tud férni A TTable eseményei között megtaláljuk a navigáció és az adathozzáférés összes eseményét, ezen kívül pedig hibakezelést is végezhetünk velük. Érdemes őket végignézni Segítségükkel például egyszerűen megoldhatjuk a törlésre való rákérdezést, illetve a törlés megakadályozását bizonyos esetekben, stb. A TTable metódusai (az egyszerűbbek) már csekély angoltudással rendelkezők számára is egyszerűen megjegyezhetők: törölni a Delete-tel, hozzáfűzni az Append-del kell, stb. (bővebbeket ld Delphi Help: TTable) Fontos lehet, hogy a Table mezőit magunk is
megadhatjuk. Ha akarjuk, nem kerül bele minden mező a fizikai fájlból, de mi is adhatunk hozzá mezőket. Ezt a komponensre való dupla kattintással tehetjük meg a legegyszerűbben. A megjelenő ablak műveleteit a jobb gombbal való kattintással érhetjük el Az Add all fields minden mezőt behelyez, ami a fájlban található. Új mező létrehozása esetén (New field) meg kell adnunk, hogy az adatmező (Data field), számolt mező (Calculated field), vagy Lookup (kereső) mező legyen. Számított mezőnél a tábla OnCalcFields eseményébe írhatjuk be a végzendő számításokat (figyeljünk arra, hogy be legyen kapcsolva az AutoCalcFields, mely biztosítja, hogy a számolt mezőket automatikusan számoljuk). A Lookup mező arra szolgál, hogy más táblából (DataSet) keressünk ki egy rekord egy mezőjét (Result field), amelynek valamely mezője (Lookup Keys) megegyezik a jelen tábla egy mezőjének aktuális értékével (Key field). Ennek megértéséhez itt egy
egyszerű példa: Van két táblánk: Nevek (szemelykod, nev) Lakas (szkod, lakhely) A Table1-ben szeretnénk látni a neveket és a hozzá tartozó lakhelyet is. A Table1 forrása legyen a Nevek. A Table2 a Lakas táblát biztosítja A hozzájuk tartozó DataSet-ek értelemszerűen DataSet1, DataSet2. Adjuk hozzá a Table1 mezőihez mindet (Add all fields). Majd hozzunk létre egy új mezőt Válasszuk a Lookup field rádiógombot, majd töltsük ki a Lookup definition-t: Key Fields=szemelykod, DataSet=DataSet2, Lookup Keys=szkod, Result Field=lakhely. Az eredmény magáért beszél. Ha a tábla mezőit hozzáadtuk a mezőlistához, azokat kiválasztva szerkeszthetjük őket az Object Inspector segítségével. A rengeteg lehetőség közül egy hasznos dolgot emelnék ki, ez pedig a DisplayName, melyben azt a szöveget adhatjuk meg, amivel meg szeretnénk jeleníteni a rekordot. Ez azért hasznos, mert mezőneveknek általában rövidítéseket használunk, de ezeket a
felhasználónak nem kell tudnia. Ha ezt a tulajdonságot beállítjuk, ezen túl az adatokat megjelenítő komponensek ezt a nevet fogják kiírni. A táblában található adatokat a Filter tulajdonság segítségével szűrhetjük is (amennyiben a Filtered tulajdonság true). Ennek megvan a megfelelő szintaktikája, amelyet a Help-ben a TBDEDataSetFilter témakör alatt találjuk meg. Szűrés esetén a táblázatban csak a szűrőfeltételnek eleget tevő rekordok jelennek meg TQuery Ez a komponens egy olyan DataSet-et épít be a programba, ami SQL kifejezésen alapul. Arra használjuk, hogy SQL lekérdezéseket hajtsunk végre a segítségével. Azért hasznos ez a komponens, mert ezzel egyszerre több táblát is elérhetünk, illetve összekapcsolhatunk, valamint számításokat, rendezéseket, leválogatásokat is nagyon könnyen végezhetünk vele (az SQL nyelvből adódóan). Itt is találunk a Table-höz hasonló tulajdonságokat és eseményeket. Ebből is látszik, hogy
csak abban különbözik a TTable-től, hogy az adatforrása egy SQL lekérdezés. Természetesen a Query által visszaadott adatokat módosítani nem tudjuk, de magával a Query-vel végezhetők adatmódosító műveletek (Update, Delete, stb.), mivel ezeket az SQL nyelv lehetővé teszi Ekkor a komponens nem ad vissza táblát Az SQL lekérdezést a komponens SQL nevű tulajdonságában adhatjuk meg. Ha lekérdezést írunk, és még nem érezzük magunkat elég gyakorlottnak, célszerű a lekérdezést először a Database Desktop-pal elkészíteni, lefuttatni, és ha működik, utána átvinni a programunkba, így ugyanis egyszerűbb a hibaellenőrzés. Ha pedig valakinek nem tetszik a Delphi string-szerkesztő ablaka, a Code Editor gomb megnyomásával használhatja a normál kódszerkesztő ablakot is. Ennek azért van előnye, mert itt az SQL parancsokat a Delphi felismeri, és kiemeli A TTable-höz hasonlóan ennél a komponensnél is működik a filter. Metódusai közül az
ExecSQL nevűt említeném meg, mellyel lefuttathatjuk a lekérdezésünket. Természetesen a TTable-höz hasonlóan itt is működik az Open és a Close is. 11 TUpdateSQL Ha már a Query-nél tartunk, érdemes megemlíteni a TUpdateSQL komponenset, amely kifejezetten adatmódosításra szolgál. Itt külön törlő (DeleteSQL), módosító (ModifySQL) és hozzáfűző (InsertSQL) SQL-t adhatunk meg. Ezeket is az ExecSQL metódussal tudjuk futtatni, csak itt meg kell adnunk, hogy milyen módban indítjuk el (törlő, módosító, beszúró). TBatchMove Előfordulhat, hogy szükségünk van arra, hogy egy táblából bizonyos adatokat egy másik táblába másoljunk. Erre kitűnő eszköz ez a komponens Fontos, hogy forrásnak (Source) és célnak (Destination) TTable típust kell megadni, tehát az adott táblának léteznie kell a programban. Minden ilyen esetben ajánlatos ezt a komponenset használni, mivel ezzel lehet a leggyorsabban adatokat mozgatni. Még ha SQL-ben
egyszerűbbnek is tűnik egy ilyen művelet (mondjuk egy Insert vagy Update), a gyakorlat azt mutatja, egy query lefuttatása nagyságrendekkel több időt vesz igénybe – ami kevés adatnál még nem érzékelhető, de minél több adat van, annál lassabb lehet egy ilyen lekérdezés. Ezzel a komponenssel ráadásul nem csak másolni, hanem törölni is lehet (a céltáblából törli azokat az adatokat, amelyek megvannak a forrásban is). Data Controls komponensek Ide az adatokat megjelenítő, kontroláló vizuális komponensek tartoznak. TDBGrid Egyszerű táblamegjelenítő komponens. Egy TTable (vagy TQuery)objektum tartalmát jeleníti meg, standard formában (a táblázat oszlopai a mezők, sorai a rekordok). A tábla tartalmát szerkeszteni is tudjuk a segítségével (amennyiben a ReadOnly tulajdonsága false). A szerkesztés a standard táblázatkezelő programokhoz hasonlóan történik (érdemes vele kísérletezni). Szerkesztésre való használatát azonban csak
módjával javaslom, mert amilyen kevés dolga van vele a programozónak, annál több bosszúságot okozhat ez a komponens. Ennek több oka is van Egyrészt a felhasználók számára bizonyos esetekben bonyolultnak tűnhet ez az adatszerkesztési mód, másrészt a szerkesztést teljes egészében a komponens oldja meg, azaz nekünk nehezebb dolgunk van, ha egyénileg szeretnénk megvalósítani ezeket. Használata ezért csak egyszerűbb táblázatok szerkesztésére ajánlott. Kitűnő viszont arra, hogy összesített adatokat, lekérdezéseket (SQL), valamint listákat jelenítsünk meg vele. Ha szerkeszteni szeretnénk a tartalmát, célszerű erre a célra külön ablakot (adatlapot) gyártani, ami megkönnyíti a szerkesztést (lásd: V. fejezet) A komponens Options tulajdonságát kinyitva rengeteg beállítási lehetőséget találunk. Ezek közül néhányat emelnék csak ki. A dgEditing tulajdonságot false-ra állítva kikapcsolhatjuk a szerkesztő módot, így az adatokat
nem lehet módosítani. A dgRowSelect bekapcsolásával egyrészt automatikusan kikapcsoljuk a szerkesztést, másrészt engedélyezzük, hogy a táblázatnak mindig egy teljes sora legyen kijelölve. Ezzel azonban csínján kell bánni, mert ha nem fér ki egyszerre a táblázat összes oszlopa, és tovább lapozunk a gördítősávon, hogy láthatóvá tegyük a többi oszlopot is, majd valamelyik sorra kattintunk, a táblázat automatikusan visszaáll úgy, hogy az első oszlop lesz látható. Ez a jelenség csak a dgRowSelect bekapcsolt állapotánál jelentkezik, és sokszor kellemetlenségeket okoz. Figyeljünk arra is, hogy amennyiben a dbRowSelect be van kapcsolva, a dbEditing-et nem tudjuk true-ra állítani. A dgIndicator a táblázat bal oldalán lévő indikátor-oszlopot kapcsolja be, illetve ki. Ez az indikátor sokszor hasznos, mert látjuk az aktuális rekordot, és annak állapotát (szerkesztett, beszúrt, stb.) Sokszor lehet arra szükség, hogy a táblázatból csak
meghatározott mezőket jelenítsünk meg, illetve hogy csak meghatározottakat tudjunk szerkeszteni. Erre szolgál a DBGrid oszlopszerkesztője (Column Editor), amit a Columns tulajdonság gombjára kattintva érhetünk el (ezen kívül megnyithatjuk ezt az ablakot a komponensre való dupla kattintással, illetve a helyi menüjéből is). Alapértelmezésben ez az ablak üres, tehát a táblázat minden mezőt megjelenít, amit a hozzá tartozó Table tartalmaz. A helyi menüből (jobb gomb), illetve az ablak eszköztárából is elérhető Add All Fields paranccsal azonban hozzáadhatjuk az összes mezőt, hogy aztán saját kedvünk szerint szerkeszthessük azokat. Az ablak egy sorára kattintva az Object Inspector-ban megjelennek annak tulajdonságai, amelyek segítségével minden lényegi jellemzőt beállíthatunk. Nagy táblázatok esetén például ajánlatos különböző színű oszlopokat használni. Ha pedig valamelyik mezőre nincs szükségünk, egyszerűen
kitörölhetjük ebből a listából. Van a komponensnek egy apró, ám néha elég zavaró hibája: a görgetősávot nem hagyományos módon kezeli. A csúszka mérete ugyanis mindig állandó, és nem követi a görgetést, mivel ha az első elem az aktív, a csúszka legfelül van, ha az utolsó elemen állunk, akkor legalul, minden egyéb esetben pedig a sáv kellős közepén. Sőt, ha föl-le görgetünk, és az aljára illetve a tetejére lapozunk a táblázatban, a csúszka csak akkor mozdul el a sáv aljára ill. tetejére, ha az első ill utolsó elem az aktív, és észreveszi, hogy nem tud tovább 12 görgetni (EOF vagy BOF). Ez a hiba jelentősen megnehezítheti a táblázatban való navigálást, mivel nem látjuk, hogy hol járunk benne. TDBNavigator Ez egy egyszerű komponens, mely csupán az alapvető tábla-navigációs parancsok kiadására szolgál: lapozhatjuk vele adatainkat, szerkeszthetjük, elmenthetjük, törölhetjük (stb.) őket A VisibleButtons
tulajdonság segítségével megadhatjuk, mely gombok legyenek láthatóak. A következőkben olyan komponensekről lesz szó, amelyekhez hasonlók megtalálhatók a Standard komponensek között is. Ezért ezek használatát nem részletezem, csupán a standard változattól való eltérésekre és az adatbázis-kezelő vonatkozásaira térek ki. TDBText A standard TLabel-höz hasonló statikus adatmegjelenítő komponens, mely egy adatforrás egy mezőjének aktuális értékét jeleníti meg. Szerkesztésre nem használható A DataSet és a DataField tulajdonság segítségével adható meg a megjeleníteni kívánt mező forrása és neve. TDBEdit Ennek is megvan a standard párja, a TEdit, melytől csupán annyiban tér el, hogy szövegét egy tábla egy mezőjének aktuális értékéből veszi, illetve szerkesztésénél ezt a mezőértéket módosítja. Hasonló a TDBText-hez TDBMemo Memo típusú mezők megjelenítésére szolgáló komponens. Használata ennek is
triviális TDBImage Szintén egyszerű komponens, amely a képeket tartalmazó mezők megjelenítésére szolgál. TDBListBox Ez egy olyan speciális ListBox, melynek elemei (Items) egy adott tábla adott mezőjének lehetséges értékeit tartalmazhatja. Az aktuális értéket – amennyiben az megtalálható a listában – automatikusan kijelöli Az adott mező értékének szerkesztése úgy történik, hogy a listából (kattintással) kiválasztunk egy elemet, és az kerül be új értékként a mezőbe. A lehetséges elemeket a TStrings típusú Items tulajdonságában adhatjuk meg Akkor használjuk, ha egy mezőnek csak adott értékei lehetnek, így a felhasználó semmiképpen nem tud más értéket beállítani (pl. neme: férfi/nő) TDBComboBox Olyan a TComboBox nevű standard komponensből készült lenyíló lista, mely az imént említett TDBListBox-hoz hasonlóan működik, csak itt a lista a lenyitó gomb megnyomása után jelenik meg. TDBCheckBox Logikai (boolean)
típusú adatmezők megjelenítésére és szerkesztésére szolgáló jelölőnégyzet. Használata ugyancsak triviális. TDBRadioGroup A TDBListBox-al megegyező funkciókat lát el, rádiógombok használatával. TDBLookupListBox Olyan ListBox, amely lookup (kereső, lásd: II. fejezet) mező működtetésére szolgál Egy tábla egy mezőjének értékét jeleníti, illetve szerkeszti. A lista elemeit a ListSource (TDataSet) forrás ListField mezőjének 13 rekordjai alkotják. A KeyField tulajdonságba a ListSource azon mezőjét állítjuk be, amely alapján keresni akarunk a ListSource-ban. Természetesen a DataSource és a DataField a manipulálandó adat forrását és mezőjét adja meg. TDBLookupComboBox A TDBLookupListBox-hoz hasonló, csak ennél lenyíló listából választhatunk értéket. TDBRichEdit RichText szerkesztő, amely tartalmát memo típusú mezőben tárolja. Olyan helyzetekben célszerű használni, amikor szükség van a szövegek formázott
tárolására (például ha olyan szövegeket tárolunk, melyeket esetleg nyomtatni is fogunk, és szükségünk van alapvető formázási lehetőségekre), ugyanis ez a komponens (a szabvány RichText formátumot használva) formázással együtt kezeli a mező tartalmát (így hagyományos TDBMemo-val megjelenítve nem a kész szöveget, hanem annak forráskódját kapjuk). TDBCtrlGrid Olyan táblázat, amely egy adatforrás rekordjait jeleníti meg, szabad formájú elrendezésben. Ez azt jelenti, hogy különböző adatmanipuláló objektumokat helyezünk el a táblázat legfelső sorába, amiket a program futásakor a komponens megsokszoroz, és minden egyes sorban ugyanazzal a kiosztással megjeleníti a rekordok adatait. A táblázatra azonban csak bizonyos adatmanipuláló komponenseket helyezhetünk föl (például TDBEdit, TDBListBox, stb.) a DataSource tulajdonságot a DBCtrlGrid-nek kell beállítani, a rajta lévő objektumok DataSource nevű tulajdonsága nem
állítható. TDBChart Adatbázison alapuló grafikon készítésére szánt komponens. Mivel használata, működése olyan szerteágazó, és sokrétű, hogy talán egy külön munkát is megérne, nem foglalkozunk vele. Érdemes megtekinteni demóját, a Delphi könyvtárában a DemosTeechart eedemo.dpr fájlt megnyitva és futtatva Nyelvi problémák A Delphi adatbázis-kezelő komponenseit használva hamar rábukkanunk az angol nyelvű szövegekre, melyeket a komponensek működés közben jelenítenek meg. Például ha egy DBGrid-et vagy DBNavigator-t használunk, és bekapcsolva hagyjuk a ConfirmDelete tulajdonságot, majd törölni próbálunk egy adatot, azonnal elénk ugrik a következő kérdés: „Delete Record?”. Ha tovább játszadozunk a komponensekkel, az esetleges hibajelzéseket is angolul fogjuk megkapni. Ez egy magyaroknak szánt, magyar nyelvű program esetében nem mutat jól, és – amennyiben a felhasználó nem tud angolul – különösen zavaró lehet.
„Mit tehetünk?” – hangzik a kérdés. A válasz egyszerű, de nem kézenfekvő: át kell írni az angol szövegeket magyarra. Hogy hol? A Delphi könyvtárában a SourceVcl könyvtárban található egy dbconstspas nevű fájl. Ebben csak a Db-komponensekkel kapcsolatos szövegek vannak definiálva, mint konstansok Ezeket egyszerűen át kell írni, és a nyelvi problémáink nagy része megoldódik, méghozzá örökre. (Célszerű utána ezt az átszerkesztett fájlt biztonságos helyre is elmenteni.) Amennyiben még mindig van angol szöveg a programban, a Delphi 5-öt használóknak van még egy lehetősége: a ProjectLanguages menüpont, ahol némi ügyeskedés után lefordíthatjuk a programban található összes string-et (bővebbeket ld. Delphi Help: languages: adding to a project). TDataModule Ha már próbálkoztunk néhány bonyolultabb ablak összeállításával, láthatjuk, hogy a nem vizuális adatkomponensek (mint például a TTable és társai) sok helyet
foglalhatnak el a form-on. Ezt kiküszöbölendő született a TDataModule komponens, mely egy olyan ablak, amelyen csak nem vizuális komponenseket lehet elhelyezni. Előnye, hogy áttekinthetőbbé tudjuk tenni a programunkat, mivel ezen az ablakon bárhová tehetjük a komponenseket, és az ablak is bármekkora lehet, hiszen nem jelenik meg futás közben. Másik előnye, hogy ha egy ablakra kiteszünk egy komponenset, annak lenyitjuk a DataSource tulajdonságát, a Delphi az aktuális formon lévőkön kívül felkínálja azokat az objektumokat is, amelyek a DataModule-on vannak. Delphi 5-öt használók még adat-diagrammot is készíthetnek a DataModule segítségével. 14 IV. Egy adatbázis-kezelő alkalmazás elkészítése A továbbiakban arról lesz szó, milyen módszerekkel és feltételekkel készíthetünk adatbázis-kezelő alkalmazásokat Delphi-ben. Tervezés A programok készítésének első és legfontosabb fázisa ez. Gondos tervezés nélkül nem szabad
(és igazán nem is lehet) elkezdeni egy fejlesztést. A program először mindig a fejlesztő agyában születik meg, és csak aztán kezdődik a kódolás. Félreértés azonban ne essék! A program gondos megtervezése nem azt jelenti, hogy sorról sorra megírjuk előre a kódot, hanem hogy átgondoljuk működési elvét, rendszerét. A gyakorlati megvalósításon elég kódolás közben gondolkodni, mert ha jó a tervezés, nincs probléma vele. Csak akkor kezdjünk el programot tervezni, ha az adatbázis már gondosan megterveztük és elkészítettük. Annak ugyanis függetlennek kell lennie a programtól. Fontos szempont programtervezésnél, hogy rendszere átlátható és könnyen bővíthető legyen. Mivel a Delphi objektumorientált fejlesztőrendszer, nekünk is objektumorientáltan kell gondolkodnunk. A programunknak jól kell illeszkednie az általa használt adatbázis felépítéséhez. Minden feladatot az elvárt módon kell megoldania. Fel kell készülni arra is,
hogy az „éles” használat során hatalmas adatmennyiségek halmozódhatnak föl, így mindenre meg kell keresnünk a legoptimálisabb megoldást. Gyakori hiba, ha a fejlesztő nem gondol arra, hogy jóval több adat kerül majd az adatbázisba, mint amennyivel tesztelte. Az nem elég, ha a tesztek során a program szép eredményeket produkál. Mindig el kell gondolkozni azon, nem lehet-e még jobban, még egyszerűbben, még gyorsabban megoldani a feladatokat. Az ablakokat is érdemes előre megtervezni, így áttekinthetőbb lesz a programterv, és később is gyorsabban halad a munka. És persze a megrendelőnek meg lehet mutatni ezeket a terveket, ami nem egy utolsó szempont. Ez a fejezet nem programot tervezni tanít, inkább segítő kezet nyújt a tervezéshez és a kódoláshoz. A DataModule Ha túl vagyunk a tervezésen és az adatbázis létrehozásán, az új Project megnyitása után első dolgunk egy új Data Module megnyitása (File/New/Data Module). Ebbe kerülnek
bele aztán a táblák (TTable), és az SQL-ek (TQuery). Mindegyiknek be kell állítani a DatabaseName tulajdonságát, illetve a forrását (TableName és SQL). Ha ezeket meg is szeretnénk jeleníteni, csatlakoztatnunk kell hozzájuk egy-egy DataSource-t (ki kell helyezni őket, majd a DataSet tulajdonságukat a megfelelő értékre állítani). Ezek után be kell állítanunk a lookup kapcsolatokat. El kell döntenünk, hogy egy-egy táblát vagy Query-t mikor szeretnénk használni. Ha a program futása során állandóan (vagy elég gyakran) szükségünk van rá, célszerű ezt a program futásának kezdetén megnyitni (nem ajánlatos már szerkesztő módban megtenni ezt, az Active tulajdonság true-ra állításával, mert ez a későbbiekben problémákat okozhat). Ehhez érdemes a táblákat tartalmazó DataModule OnCreate eseményébe helyezni a megnyitási blokkot. A megnyitást mindig ajánlatos Try-blokkba tenni (ld Delphi Help: Tryexcept statements; VI. fejezet) Így ha
nem sikerül megnyitni valamelyik táblát, az esetleges hibákat az Except részben le tudjuk kezelni. A legegyszerűbb ilyen megnyitási blokk így néz ki: Try Table1.Open; Table2.Open; Query1.Open; //stb Except ShowMessage(’Hiba! A táblák megnyitása meghiúsult.’); Halt; End; Használhatunk persze olyan táblákat és lekérdezéseket is, amelyeket csak néha, vagy egy adott modul megnyitásakor kell megnyitnunk. Ekkor értelemszerűen járjunk el, és a megfelelő helyre helyezzük az Openeket, hogy elkerüljük a fölösleges erőforrás-pazarlást Ha a táblákhoz jelszó is tartozik, a jelszavakat a Session.AddPassword metódus segítségével tárolhatjuk el. Így a programunk tudni fogja a jelszót (vagy jelszavakat), és nem fog külön rákérdezni Az AddPassword metódust mindig a megnyitás előtt kell meghívnunk. Ha még csak most kapizsgáljuk a Delphi adatbázis-kezelését, még nincs rá szükségünk, de később felmerül a kérdés: „Mi lesz, ha
törlődik az alias, vagy olyan gépen indítjuk el a programunkat, ahol van BDE, elérhető az adatbázis is, de még nincs hozzá alias?” Ebben az esetben vagy kézzel hozzuk létre, vagy (és ez a szebb megoldás, hiszen ehhez a felhasználónak nem kell ismernie BDE Administrator-t) a programunkkal 15 végeztetjük el ezt a munkát. A megoldás alapesetben egyszerű, le kell kérdezni, hogy létezik-e az adott alias, és ha nem, létre kell hozni. Ez addig jó, amíg a programunk indítási könyvtárához képest fix helyen található az adatbázis könyvtára (mondjuk a program könyvtárában van egy Database könyvtár). Használhatjuk azonban az adatbázist hálózaton keresztül is. Ekkor nem tudhatjuk, hol lapul az adatbázisunk Ekkor bizony a vétlen felhasználóval kell megkerestetnünk azt. Ilyen problémák megoldására a következő módszert ajánlom. Ha a programot és az adatbázist egyazon számítógépre telepítjük, az adatbázis mindig legyen a program
könyvtárában, például egy Database nevű alkönyvtárban. Így ha nem létezik az alias, egyszerűen létrehozzuk, és beállítjuk neki könyvtárként ezt a könyvtárat. Arra is fel kell azonban készülni, hogy az adatbázist egy másik számítógépen szeretnénk tárolni Ilyenkor a programnak célszerű megnéznie, létezik-e ez a bizonyos Database könyvtár. Ha nincs, akkor bizonyára valahol máshol kell lennie. Ekkor a legjobb, ha közöljük a felhasználóval, hogy nem tudjuk, hol az adatbázisa, és legyen szíves, mutassa meg nekünk. Ezt egy egyszerű fájlmegnyitó ablakkal is megtehetjük Ha ez megvan, már létrehozhatjuk az alias-t. A figyelmes programozó az ekkor megadott könyvtár nevét eltárolhatja egy fájlban, így ha legközelebb „eltűnik” az alias, csak onnan kell kiolvasni annak helyét, nem kell a felhasználót nyaggatni ezzel. Egyébként aggodalomra semmi ok, az alias-ok nem szoktak „csak úgy eltűnni”. Ez a probléma általában a
program első indításakor jelentkezik. Persze a program telepítőjével (ld: VI fejezet) is létrehozhatjuk az alias-t, de tapasztalatom azt mutatja, hogy az ékezetes könyvtárnevekkel (ha az adatbázis elérési útjában van ékezet) vigyázni kell. A probléma programon belüli megoldására itt egy egyszerű példa (a DataModule OnCreate eseményében): var ActDir:string; {az aktuális könyvtárat fogjuk ebben tárolni} DBDir:string; {az adatbázis könyvtárát tároljuk ebben} {} if not Session.IsAlias(En Aliasom) then begin GetDir(0,ActDir); {Lekérjük az aktuális könyvtár nevét} if DirectoryExists(ActDir+Database) then DBDir:=ActDir+Database else begin Showmessage( Hibaüzenet ); Form1.OpenDialog1Directory:=ActDir; {Az OpenDialog valamely létező form-on kell, hogy legyen!} if not Form1.OpenDialog1Execute then Halt {ha a felhasználó mégsem-mel lépett ki, megszakítjuk a program futását} DbDir:=Form1.OpenDialog1Directory; end; try {Létrehozzuk az alias-t:}
Session.AddStandardAlias(EN Aliasom, DbDir, PARADOX); {Mentjük a BDE beállításait:} Session.SaveConfigFile; finally Showmessage(Az adatbázis inicializálása befejeződött.); end; ChDir(ActDir); {visszaállítjuk az aktuális könyvtárat} end; {} //Itt jöhet a táblák megnyitása. Ha ezzel is kész vagyunk, a táblák egyes eseményeihez hozzá kell rendelnünk a saját eljárásainkat. Meg kell adnunk, mi történjen, ha a felhasználó ki akar törölni egy adatot, vagy újat akar beszúrni, stb., azaz ki kell dolgozni a táblák eseményeit. Főként érinti ez a számított mezőket, hiszen azok számítási algoritmusát a táblák OnCalcFields eseményében kell kifejteni. Hogy ezen felül mire van még szükség, azt a program milyensége és a programozó ízlése határozza meg. Az ablakok Az alkalmazások fejlesztése során nagy hangsúlyt kell fektetnünk az ablakok kialakítására, hiszen a programot a felhasználónak készítjük, és a felhasználók
számára fontos a jó megjelenés és a könnyű kezelhetőség. Elsődleges szempont mindig a funkció Mindennek a helyén kell lennie, és megfelelően, logikusan kell működnie. Emellett azonban figyelnünk kell arra is, hogy fölösleges, zavaró dolgok ne kerüljenek a 16 képernyőre. Csak azt jelenítsük meg, ami információ-tartalommal bír A redundanciát lehetőleg kerüljük Ablakunkat továbbá minél átláthatóbbá kell tennünk. A felhasználók azt szeretik, ha egy általuk is követhető, egyszerű logika alapján épülnek fel a kezelőfelületek. Ez sokszor okoz fejtörést a fejlesztőnek, mivel ő a belső működése felől szemléli a programját. Akkor jó egy program, ha a felhasználónak nem szükséges tisztában lennie a belső működéssel. Ha az ablak funkcionálisan megfelelő felépítésű, még nem biztos, hogy könnyen kezelhető. Ehhez először is mindent jól láthatóvá és használható méretűvé kell tennünk. Ez alatt elsősorban
a betűtípust és méretet, valamint a gombok és egyéb objektumok nagyságát értem. A Windows által használt alapértelmezett betűtípusnál (MS Sans Serif 8) a 10-es méretű Arial olvashatóbb, ezért célszerű a form-unk létrehozása után rögtön erre beállítani a form Font tulajdonságát. Ez a beállított érték lesz érvényes a form-ra kerülő objektumokra is. Ha van a képernyőn hely, a gombokat érdemes az alapértelmezetthez képest egy kissé megnagyítani. Programunkat átláthatóbbá tehetjük továbbá azzal, ha a szabványos gombok (TButton) helyett TBitBtn-t használunk, amelyre kis képeket lehet helyezni (Glyph), ezzel megkönnyítve a felismerésüket. Elegendő képecskét találunk a Delphi képtárában, amelyet telepítéstől és verziótól függően a Delphi könyvtárán belül az ImagesButtons, vagy a Program filesCommon FilesBorland SharedImagesButtons könyvárban keressük. Találkozhatunk olyan esettel is, amikor a programot a
megrendelő érintőképernyős monitoron szeretné használni. Ekkor már tervezéskor figyelnünk kell az ablakokon található kezelők méretére, mert egy már meglévő ablakot átszabni úgy, hogy minden megfelelő méretű legyen, sokszor nagy vesződségekkel jár. Ha nem fér el minden szükséges funkció egy ablakban, akkor célszerű azokat egymás után megjelenő párbeszédablakokba helyezni. Ekkor figyeljünk arra, hogy logikusan, lépésről lépésre haladjunk Itt említem meg, hogy azok az objektumok, amelyek rendelkezhetnek gördítősávval, a Windows gördítősáv-beállításait használják. Ez gyakran kicsinek bizonyul Ebben az esetben célszerű a felhasználót felszólítani, hogy a megfelelő helyen tegye meg a szükséges beállításokat a Windows-ban. Ha erre felkészülünk, a programunkat célszerű nagy gördítősáv-mérettel is kipróbálnunk, hogy ne később érjen meglepetés. Létezik még egy erős buktató az ablakok tervezésében és
kivitelezésében. Ez pedig a felbontás kérdése Nem mindegy ugyanis, hogy milyen felbontásban tervezzük meg az ablakot, és hogy azt milyen felbontásban fogják használni. Gyakori hiba, hogy valaki egy 1024x768 méretű képernyőn tervezi meg az ablakjait, de azt egy 800x600-as képernyőn fogják futtatni, és azon bizonyos dolgok kilógnak a képből. De az ellenkezője is előfordulhat, amikor egy kisebb felbontásra tervezett programot nagyobb felbontásban futtatunk, és hatalmas üres helyek jelennek meg az ablakok jobb szélén és alján. A problémára több megoldás is van Először is célszerű skálázható ablakokat készítenünk. Ekkor azt kell meghatározni, hogy melyik komponens melyik irányba legyen mozdítható. Erre rendelkezésünkre áll a komponensek Anchors tulajdonsága Ennek négy logikai típusú eleme van (Left, Top, Right, Bottom), amelyekkel meghatározhatjuk, hogy a komponens az ablak melyik széléhez legyen rögzített távolságra
(célszerű kísérletezni vele). Jól skálázhatóvá tehetjük alkalmazásunkat, ha megfelelően használjuk a komponensek Align tulajdonságát (ez nem minden komponensnek van, és nem összekeverendő az Alignment-tel). Ha pl alClient-re állítjuk, a komponens kitölti a teljes területét az ablaknak vagy annak a panelnek, amelyre helyeztük. Érdemes egyébként TPanel-eket használni, objektumok egy csoportjának megjelenítésére, mert a segítségükkel csoportosíthatjuk a komponenseket, és a TPanel-nek is van Align tulajdonsága. Az Align tulajdonságot alLeft-re, alTop-ra, alBottom-ra vagy alRight-ra állítva értelemszerűen a megfelelő oldalra zárva jelenik meg a komponens. Csúnya, de sokszor elkerülhetetlen megoldás, ha az ablak (form) automatikus görgetését (AutoScroll) használjuk ki. Bizonyos speciális szoftvereknél azonban nem érdemes ilyenekkel vesződni, hanem egyszerűen meg kell határozni egy felbontást, amelyben a program működik. Ha pedig a
program elindításakor nem megfelelő a felbontás, figyelmeztetni kell erről a felhasználót (a kép méretét a Screen.Height, ill a ScreenWidth tulajdonságok segítségével kérdezhetjük le a legegyszerűbben). Ablaktípusok Alkalmazásunk tervezésénél figyelembe kell vennünk, hogy hogyan fogják használni. Két elrendezési típus közül választhatunk. Az egyik a hagyományos (SDI) ablakok, amikor egy újonnan megnyíló ablak a többi fölé rajzolódik, és esetleg váltogathatunk a megnyíló ablakok között. Ha ezt a megoldást használjuk, oda kell figyelnünk arra, hogy a párbeszédablakok mögötti ablak ne legyen elérhető (az Enabled tulajdonsága legyen false, vagy nem a Show, hanem a ShowModal metódussal jelenítjük meg a párbeszédablakot). Használhatjuk azonban az ún. MDI ablakokat is Ezek előnye, hogy a gyermekablakok a szülőablakon belül nyílnak meg (ezt a megoldást használják pl. a szövegszerkesztők is) Ehhez állítsuk a főablak
FormStyle tulajdonságát fsMDIForm-ra, a gyermekablakokét pedig fsMDIChild-ra. MDI ablakokat használva a Windows rengeteg lehetőséget nyújt, így egyszerűen készíthetünk ilyen alkalmazásokat (ld.: Delphi Help: MDI applications). Szintén érdemes megfontolni, hogy az általunk készített program az egyedüli alkalmazás-e, amit használni fognak a számítógépen. Ha igen, akkor hasznos lehet, ha a programot teljes képernyőn futtatjuk, eltakarva ezzel a Windows tálcáját is (ehhez a WindowsState-et wsMaximized-re, a BorderStyle-t pedig bsNonera kell állítani). Így a felhasználó nem tud véletlenül rossz helyre kattintani, elindítva ezzel valamilyen nem 17 kívánatos alkalmazást. Ha viszont más szoftvereket is használnak egy időben az általunk készítettel, mindenképpen adjunk arra lehetőséget, hogy a programot minimalizálni lehessen, és futásakor el lehessen érni a Start menüt és a tálcát. A főképernyő A szoftverek egyik legfontosabb
eleme (vizuális szempontból), a programmodulok közötti összekötő kapocs, a főképernyő. Nem szabad tehát elhanyagolnunk ezt sem, a modulokhoz hasonlóan, körültekintően kell bánnunk vele. A főképernyő milyensége elsősorban attól függ, hogy alkalmazásunk hagyományos SDI, vagy MDI ablakokat használ. Hagyományos ablakok esetén a hagyományos főképernyő-struktúra használata ajánlott. Ez azt jelenti, hogy a program különböző funkcióit a főképernyőn elhelyezett nyomógombokhoz rendeljük (ha túl sok ilyen funkció van, használhatjuk azt a megoldást, amikor egy gombot lenyomva egy legördülő menü [Popup Menu] jelenik meg). A gombok elhelyezkedése esztétikailag és logikailag is fontos, de lényegében a fejlesztő ízlése határozza meg milyenségét. Ha MDI ablakokat használunk, a program funkcióit az ablak tetején, menüsorban, illetve eszköztársorban érdemes elhelyezni. Mindkettőhöz teljes támogatást nyújt a Windows Megadhatunk pl
egy menüpontot (WindowMenu), amelybe a Windows automatikusan beírja a nyitva lévő gyermekablakok listáját, és segítségével könnyedén át is válthatunk egy másikra. Igényes fejlesztők szépíthetik az alkalmazásukat úgy is, hogy a főképernyő hátterébe valamilyen képet helyeznek el (ilyenkor a TImage nyújtja a segítő kezet). Internetről letölthető komponensekkel a gombok teljes átalakítását („textúrázását”) is meg lehet oldani. Minden csak ötletek kérdése A felhasználók pedig szívesebben dolgoznak szép programokkal. A többi ablak szépítését azonban nem kell túlzásba vinni, mert ez sokszor a használhatóság kárára válhat. Egy hagyományos főképernyő, textúrázott gombokkal és háttérrel és egy egyszerűbb elrendezésű, lenyíló menüvel Táblamegjelenítő ablakok Megszokott dolog, hogy ha adatbázisról beszélünk, rögtön összekapcsolt táblázatok sokaságára gondolunk. Adatbázis-kezelő programok sokasága
jeleníti meg táblázatban adatait Ez azonban nem jelenti azt, hogy ez minden esetben jó. Csak akkor szükséges ilyen ablakokat alkalmazni, ha szükség van a rögzített adatok 18 sokaságának áttekintésére, összehasonlítására. (Tehát egy jegykiadó-nyomtató programban fölösleges az eddig fölvett jegyek listájának megjelenítése, hiszen visszamenőleg csak bizonyos kimutatásokat, lekérdezéseket végzünk, de a konkrét adathalmazra nincs szükségünk.) Ezek után, ha úgy gondoljuk, hogy mégis szükségünk van táblázatra, azt inkább csak adatmegjelenítésre, és nem szerkesztésre ajánlatos használni. A táblázattal történő adatmegjelenítésnek van egy nagy előnye: ha ügyesen csináljuk, a felhasználó egyszerűen és gyorsan navigálhat, kereshet, rendezhet, szűrhet benne. Egy általános táblázat-ablaknak a következő részei lehetnek: (1) maga a táblázat, (2) a navigációs panel, (3) műveletvégző panel, (4) szűrő panel, (5)
információs sáv (státuszsor). Ezek közül természetesen nem kötelező mindnek szerepelni, és egyéb dolgot is megjeleníthetünk, igény szerint. A táblázatot (1) természetesen DBGrid-del valósítjuk meg (erről a komponensről már szót ejtettünk a IV. fejezetben) Az első, amit meg kell tennünk vele, hogy testre szabjuk a megjelenítését az Options és a Columns tulajdonságok segítségével. Beállítjuk a megjelenítendő mezőket, esetleg azok színét, méretét, stb ezek után gondolnunk kell az eseményeire. Gyorsbillentyűket definiálhatunk az OnKeyPress és az OnKeyDown esemény segítségével, és itt tudjuk megoldani a rendezés kérdését is. A leggyakoribb megoldás ugyanis egy táblázatban a rendezés beállítására, hogy amelyik oszlop fejlécére kattint a felhasználó, a szerint az oszlop szerint rendezzük az adatokat. Ehhez azonban tudni kell, hogy rendezni csak akkor tudunk, ha azt az adatokat biztosító Table lehetővé teszi, azaz a
definíciójában szerepel az adott mező szerinti index (Query használata esetén a rendezési sorrendet csak a lekérdezés módosításával és újrafuttatással változtathatjuk meg). Amennyiben erre lehetőségünk van, a megoldás egyszerű: a DBGrid OnTitleClick eseményében figyeljük, hogy melyik oszlopra történt a kattintás, és az annak megfelelő értékre állítjuk a tábla indexét. Érdemes még ilyenkor a fejléc betűtípusát megváltoztatni (pl. félkövérre, vagy aláhúzottra), hogy könnyen látható legyen az aktuális rendezés. Arra figyeljünk, hogy a lookup mezők szerint nem lehet rendezni Így csak a kapcsolatot megvalósító kulcs szerint rendezhetünk (ami általában szám), és egyáltalán nem garantált, hogy ez meg fog felelni az ízlésünknek. Megoldás lehet az, hogy SQL lekérdezéseket használunk, illetve ezeket vegyítjük a táblákkal (Találkoztam már olyan megoldással, hogy a két legfontosabb lookup-mezőhöz tartozott egy-egy
Query, és ha azon mezők fejlécére kattintott a felhasználó, a tábla helyett a Query-t kapcsolta be a program a táblázat adatforrásául, a Query-t pedig minden adatmódosításnál újrafuttatta. Ez kissé lassúnak tűnhet, de mindenképpen célravezető.) A navigációs panel (2) a másik dolog, amire szinte mindig szükség van. Szerepe akkor nagy, ha már nagyon sok adatunk van a táblázatban, és gyorsan el szeretnénk jutni egy adott rekordhoz. Ami biztosan kell, az a táblázat első és utolsó rekordjára mozgató gomb (segítségünkre van a TDBNavigator). A másik egy, a kulcsmezőkhöz rendelt ugró-sáv. Ezt egy egyszerű TEdit-tel és egy gombbal tudjuk megvalósítani Ha a gombra kattint a felhasználó, az adatbázisban megkeressük a begépelt szöveghez legközelebb eső adatot (Table.FindNearest) Fontos, hogy ezt csak kulcsmezőkkel tudjuk elvégezni Nehezebb a dolgunk, ha egy lookup-mező értékeit is bevesszük. Ekkor abban a táblában kell keresni,
amelyikből a lookup szedi a rekordokat, és a talált rekord kulcsértékét kell megkeresni a táblázatunkban. Külön panelen illik elhelyezni azokat a gombokat, amelyekkel műveleteket végezhetünk (3). A három alapvető műveletnek mindenképpen ott kell lennie (beszúrás, módosítás, törlés), de ide kerülhetnek a nyomtatás, szűrés és egyéb lekérdezések gombjai. Ha lehet, használjunk BitButton-okat (TBitBtn), és helyezzünk ki rájuk különböző kis képeket. Bizonyos esetekben használhatunk szerkesztő objektumokat is (DBEdit, DBComboBox, stb.), mentő és elvető gombokkal kísérve A szűrő panelnek (4) nem muszáj mindig látszania, inkább egy gombbal ki-be kapcsolgathatjuk. A szűrőt úgy kell elkészítenünk, hogy a lehető legtöbb adatra tudjunk vele szűrni. Itt is fontosnak tartom megemlíteni, hogy lookup típusú mezőkre nem lehet szűrni, ezt máshogy kell megoldanunk, például úgy, hogy a lookup mező forrástáblájából minden adatot
kilistázunk egy ComboBox-ba, és egy adott elem kiválasztásánál rákeresünk arra a kapcsolt táblában, majd a talált mező kulcsértéke szerint szűrjük a táblázatunkat. A ComboBox-ot normál szűrőknél is használhatjuk, de azt minden adatmódosítás esetén újra fel kell tölteni. A ComboBox-ba elhelyezhetünk egy (mind) elemet is, melyet kiválasztva az adott mezőre való szűrést kikapcsoljuk. A mind-et azért kell zárójelbe tenni, mert a zárójelet rendezésnél a Windows előre helyezi, így biztosak lehetünk abban, hogy ez a felirat az első elemek között lesz (a rendezéshez be kell kapcsolni a ComboBox-on az AutoSort-ot). Szűréshez ízlés szerint természetesen használhatunk Edit-et is, de vigyázzunk: ha az Edit OnChange eseményére (ez az esemény akkor hívódik meg, amikor változtatjuk az Edit tartalmát) változtatjuk a szűrőfeltételt, erősen lelassíthatjuk alkalmazásunkat azzal, hogy minden billentyűleütésre hosszas műveleteket
végzünk. Használjunk inkább frissítő-gombot A mai alkalmazásokban teljesen általánosnak mondható a státuszsorok (5) használata. Mi se ódzkodjunk ilyeneket használni, ha másra nem, hát arra, hogy az egérmutató alatt lévő objektumról adunk benne rövid leírást. Ezt úgy tehetjük meg, hogy az objektumok Hint („buborék-súgó”) tulajdonságába egy | jel után egy hosszabb leírást írunk, az Application.OnHint eljárásba pedig megadjuk, hogy StatusBar.SimpleText:=ApplicationHint (Delphi 5-öt használók ezt az TApplicationEvents komponens segítségével tehetik meg, a régebbi verziókban ezt kézzel kell hozzárendelni az eseményhez úgy, hogy készítünk egy saját eljárást, és egyenlővé tesszük az Application.OnHint értékével Ld: Delphi Help: Hint, OnHint, 19 GetLongHint example.) Természetesen a státuszsorba kiírhatjuk az aktuális rekord állapotát, vagy bármi mást, amit a felhasználóval közölni akarunk (a TStatusBar
használatáról ld. a Delphi Help: TStatusBar) A gyorsabb használhatóság érdekében nagy hangsúlyt kell fektetnünk a billentyűkombinációk alkalmazására. Ide nem csak a gombokon lévő aláhúzott karaktereket vehetjük be, hanem használhatjuk a funkcióbillentyűket is. Ebben az esetben az OnKeyDown eseményt használhatjuk Egy táblamegjelenítő ablak, szűrőkkel Adatlapok Egy rekord szerkesztésére legjobb eszköz az adatlap. Itt minden szükséges információ egy helyen van, de mindig csak az aktuális rekordról kapunk információkat. Adatlapot a legegyszerűbben úgy készíthetünk, ha megnyitjuk a megfelelő tábla FieldsEditor ablakát, kijelöljük a szükséges mezőket, majd egérrel ráhúzzuk az új ablakra. A hatás: megjelennek a megfelelő típusú, méretű, megfelelően beállított szerkesztőkomponensek, a megfelelő címkékkel ellátva. Ha ezek mellé kiteszünk egy DBNavigator-t, kész is az adatlapunk Az igényesebbek persze saját gombokat
is használhatnak. Néha célszerű azt a cselt bevetni, hogy az adatlap megjelenésekor nem szerkeszthető (hiába próbál a felhasználó valamit is módosítani), csak a szerkesztő gomb megnyomása után. Ehhez az adatlaphoz tartozó DataSet AutoEdit tulajdonságát kell false-ra állítanunk Ennél nagyobb gondosságra is szükség van azonban, ha egy, a program törzsét képező táblázat adatlapját készítjük. Nem mindegy ugyanis a mezők sorrendje, elhelyezkedése Ha például az adatlapnak létezik nyomtatott megfelelője is (amit ezen adatlap alapján töltenek ki, vagy fordítva), a számítógépen lévő adatlap jó, ha a papíron lévőnek a pontos mása. De ha nem is áll fenn ilyen helyzet, ismét gondoljunk arra, hogy ezt a programot a felhasználóknak írjuk, így az ő kényelmük a legfontosabb. Oda kell figyelni arra, hogy a komponensek sorrendje (Tab Order) megfelelő legyen, azaz a TAB billentyűt nyomogatva milyen sorrendben lépkedhetünk az adatlapon
(ennek beállítása: a form vagy label kijelölése után Edit/Tab Order menüpont). Érdemes továbbá a mezőkhöz gyorsbillentyűket rendelni, hogy minél könnyebb legyen egy adott mezőhöz ugrani. Ehhez a címkék szerepét betöltő label-ek feliratában (Caption) az & jellel meg kell jelölni a megfelelő betűket (Az & jel utáni karaktert a Windows automatikusan aláhúzza), majd a FocusControl nevű tulajdonságukban be kell állítani a hozzárendelt mezőt. Ha ezt megtettük, a felhasználó az aláhúzott betű ALT billentyűvel együtt történő lenyomásával a FocusControl-ban megadott mezőbe tud ugrani, és azt szerkeszteni. A lookup mezőkre általában DBLookupComboBox-okat szokás használni az adatlapokon. Ennek az a hátránya, hogy csak a listában szereplő elemek közül választhatunk. Ez persze egyértelműen következik a lookup mező működéséből – mondhatnánk. De a felhasználó nem tudja, mi az a lookup Ez nem is róható föl ellene
Inkább a fejlesztőnek kell megoldania, hogy ha nem szerepel egy elem a listában (törzsadattáblában), amire épp szükség lenne, akkor valamilyen módon fel lehessen venni úgy, hogy ne kelljen az adatlapot elhagyni. A legegyszerűbb, ha a lookupcombobox mellé helyezünk egy nyomógombot, amellyel elérhető az adott törzsadattábla ablaka, ahol módosíthatjuk a tartalmát. Másik megoldás, ha egy új elemet létrehozó gombot helyezünk ki, amely egy üres adatlapot jelenít meg. Ezt kitöltve a program fölveszi az elemet a törzsadattáblába, majd azt beírja a lookup mezőbe. Az adatlapok készítésekor gondolnunk kell arra, hogy minél automatikusabban működjenek. Egyrészt létezhetnek alapértékei az egyes mezőknek (pl. dátum esetén az aktuális dátum), másrészt előfordulhat, hogy egy mező értékéből lehet következtetni egy másik mező értékére is. Jó példa erre, hogy ha egy megrendelő adatlapon a partner kiválasztása után, ha annak van
bankszámlaszáma, a fizetésmódnál a banki átutalást, ha pedig nincs, akkor a készpénzfizetést jelöli ki a program. Az ehhez hasonló módszerek hosszútávon jól beváltják a hozzájuk fűzött reményeket. Egy igen hasznos és egyben látványos megoldás a „színváltós” mezőobjektumok használata. Ez azt jelenti, hogy a mezők színe az állapotuktól függ: más színe van, ha szerkeszthető, ha csak olvasható; de ami a legfontosabb: más színe van, ha aktív (benne van a kurzor). Ez egy nagyon látványos, de legalább annyira hasznos lehetőség, amit a Delphi komponensek írói még nem fedeztek fel, de már jó pár alkalmazásban lehet 20 találkozni vele. Ha valaki ilyet szeretne használni a programjában, két dolgot tehet: vagy ír egy saját komponenst, ami tudja ezt a lehetőséget, vagy pedig a programon belül kezeli le ezt a problémát. Az első megoldás kicsit bonyolultnak tűnik, de csak egyszer kell megtenni. A másodikat egyszerűbb, de
minden egyes programba külön bele kell építeni. Ha komponenst írunk, először tanulmányozzuk át a Delphi Help megfelelő fejezeteit (Creating a new component), csak aztán fogjunk bele. Ha ez már meg van, arra kell figyelni, hogy a színváltást nem az onEnter, illetve onExit eljárásokba, hanem a procedure CMEnter(var Message: TCMGotFocus); message CM ENTER; illetve a procedure CMExit(var Message: TCMLostFocus); message CM EXIT; deklarációjú eljárásokba kell foglalni. Abban az esetben viszont, ha programon belül oldjuk meg, nem kell mást tenni, mint hogy a megfelelő objektumok onEnter és onExit eseményében megadni a megfelelő színeket. Egy eseménykezelő persze egyszerre több objektumhoz is tartozhat, ekkor viszont célszerű a következőképpen megadni a színeket: (Sender as TControl).Color:=SajatSzin; Sorolhatnám még azon lehetőségeket, amelyekkel szebbé és használhatóbbá tehető egy alkalmazás. Minden csak a fejlesztő fantáziáján és
ötletességén múlik. Egy adatlap, „színváltós” beviteli mezőkkel Figyeljük meg, hogy a kapcsolódó táblákba (hirdető neve, szervező neve) közvetlenül vehetünk fel új rekordot az „Új hirdető”, ill. az „Új szervező” feliratú gombokkal További érdekesség, hogy a reklám szövegét formázni is lehet (RTF). Műveletvégző ablakok Ebbe a kategóriába sorolom azokat a párbeszédablakokat, amelyek nem egyszerű adatbázismanipulációt, hanem valamilyen összetettebb műveletet végeznek. Ezeket azért kell elkülönítenünk, mert felépítésükben, működésükben teljesen másak, mint mondjuk az adatlapok. Ezeket az ablakokat általában arra készítjük fel, hogy valamilyen (általában meghatározott) felhasználói műveletsorozatra valamilyen meghatározott adatbázis-műveletsorozatot hajtson végre. Itt ne egy új név felvételére gondoljunk egy telefonkönyv-program keretein belül, hanem például egy éttermi rendszer
fizetés-moduljára, ahol egy számlához több asztal, tetszés szerinti rendelései tartozhatnak. Az ilyen feladatok elvégzésére három módszer kínálkozik. Az első, amikor egy ablakra rakjuk ki az összes szükséges objektumot, melyekkel elvégezhető a műveletsor. Itt figyelni kell az átláthatóságra, és arra, hogy ne legyen az ablak zsúfolt. Ha nem elég nagy az ablak, és nem fér el rajta minden, akkor jöhet szóba a második lehetőség, az egymás után felbukkanó ablakok használata. Ezeknél fontos, hogy mindegyiken legyen 21 visszalépési lehetőség (Mégsem gomb). A harmadik módszer a legszebb, és manapság egyre nagyobb teret hódít magának: a varázsló. Ennek persze csak akkor van értelme, ha szabadon oda-vissza lépkedhetünk benne, illetve bármikor kiléphetünk belőle. Nagyon fontos, hogy kerüljük a felesleges szövegeket és oldalakat (üdvözlő oldal, elköszönő oldal, stb., ld legtöbb Microsoft varázsló), csak a lényegre
figyeljünk Jó, ha minden oldalon van valamilyen alapértelmezett beállítás (lehet ez az előző alkalommal használt érték), mert így még könnyebben végig lehet menni a varázslón. A Delphi sajnos varázsló komponenset nem tartalmaz, de nagyon egyszerűen készíthetünk magunknak egyet a TNotebook (Win 3.1 fül) segítségével, amely több lapból áll, és lapjaira bármit föltehetünk. Rendelés az éttermi rendszerrel (műveletvégző ablak): a típusokra egyszer kattintva kiválaszthatjuk a baloldalon megjelenítendő tételeket, azokra egyszer kattintva pedig felvehetjük a rendelésbe őket. Kimutatások Az adatbázis-kezelő programok fontos részét képezik a kimutatások. Ezek alatt általában valamilyen szempontból csoportosított, valamilyen szűrővel ellátott, valamilyen rendezési elvet követő adatmegjelenítést értünk. A feladat bonyolultságától függ, hogy egy kimutatásnak hogyan kell kinéznie, mit kell megmutatnia Az a jó kimutatás,
amellyel minél több szempontból lehet adatokat csoportosítani. Kimutatások készítésénél szinte elkerülhetetlen az SQL-lekérdezések (Select-ek) használata. A lekérdezésekkel természetesen lehet számított adatokat is megjeleníteni, összevonni, stb. (bővebbeket a Local SQL Guide-ban) Az SQL-lekérdezéseket használó kimutatások fontos jellemzője, hogy menet közben nem tudunk változtatni a feltételeken (kivéve a szűrőket), csak ha újrafuttatjuk a lekérdezést. Ezért célszerű a feltételeket a kimutatás megjelenítése előtt bekérni a felhasználótól, majd lefuttatni az SQL-eket, és aztán megjeleníteni azok eredményeit. Ezek után sem szabad azonban elfeledkezni a feltételekről: érdemes azokat kijelezni a kimutatásablakban is A kimutatás-ablak szerkezete tetszőleges lehet, de ha egyszerre több táblázatot jelenítünk meg, azt mindenképpen érdemes egy TPageControl megfelelően feliratozott lapjaira helyezni, hogy minél
áttekinthetőbb legyen. Gyakran használunk kapcsolt táblákat, amikor egy adat törzse egy táblában, az egyéb adatai (amelyből több rekord is lehet) pedig egy másik táblában szerepelnek. Ekkor a legszebb megoldás, ha mindkét táblázatot megjelenítjük, de úgy, hogy az egyik táblában egyszerre csak egy, a másik táblában kiválasztott elem részletei jelenjenek meg. Ezt pedig a legegyszerűbben úgy tehetjük meg, ha az egyik tábla MasterSource-át és MasterFields-ét a másik táblának megfelelő DataSource-ra, illetve mezőjére állítjuk. Ha pl. van két táblánk: TableNevek(személykód, név) (DataSourceNevek) TableLakas(személykód, lakhely) (DataSourceLakas) (Egy személyhez több lakhely is tartozhat.) Ekkor a következőket kell tennünk: 22 TableLakas.MasterSource:=DataSourceNevek; TableLakas.IndexFields:=’személykód’; TableLakas.MasterFields:=’személykód’; Példa a kimutatásra: zárás az Étterem programmal Folyamatkijelzők
(ProgressBar-ok) Előfordulhat, hogy a programunk olyan műveleteket végez, amelyek viszonylag időigényesek. Ekkor nem túl jó, ha ezt csupán egy homokórás egérkurzorral jelezzük (ezt egyébként az Application.Cursor crHourGlass értékre állításával tehetjük meg), hanem folyamatkijelzőket (ProgressBar-okat) használunk. Így a felhasználó meg tudja ítélni, hogy mennyi van még hátra, és az is nyilvánvalóvá válik számára, hogy a program nem fagyott le, hanem dolgozik. Erre a feladatra természetesen a TProgressBar komponens használható. A dolog egyszerű: megszámoljuk hány műveletet végzünk, és azt megadjuk a ProgressBar-nak (Max). Ezután minden műveletet követően léptetjük (StepIt). Ha tudjuk, hogy a műveletek nem lesznek folyamatosak (a műveletek végrehajtási ideje között nagyok a különbségek), célszerű a Smooth értéket kikapcsolva hagyni, így nem olyan feltűnő. Kalkulálhatunk azzal is, hogy egy hosszabb művelet után
esetleg többet léptetünk egyszerre. Ami nagyon fontos, hogy mindenképpen legyen valóság alapja a folyamatkijelzőnknek. Ha tehetjük, célszerű szövegesen is közölni, hogy éppen mit csinál a program (ez hibakeresésnél is jól jöhet). Mindez azonban semmit nem ér, ha nem jelenik meg. Normális esetben ugyanis a Windows nem dolgozza fel a komponenseknek küldött üzeneteket, miközben mi egy eljáráson belül műveleteket végzünk. Ezt nekünk kell megtenni, az Application.ProcessMessages segítségével (ezt minden StepIt után be kell írnunk) Ezzel megoldódik a probléma. Természetesen máshol is alkalmazhatjuk a ProcessMessages metódust, ahol zavaró, hogy sokáig nem frissülnek az ablakok. Nyomtatás, riport Gyakran van arra szükség, hogy amit az adatbázisunkban (vagy esetleg SQL lekérdezésünkben) tárolunk, papíron is megjelenítsük. Erre a Delphi is lehetőséget nyújt a QuickReport komponenscsomag segítségével (QReport fül). Az új riportot
elhelyezhetjük a form-on, de létrehozhatjuk önálló ablakként is (File/New/Report). Használata nagyon egyszerű: beállításait a jobb egérgombbal elérhető menüből és az Object Inspector segítségével tehetjük meg. A riportra ezek után rápakolhatjuk a megfelelő komponenseket a QReport eszköztárból. Használatukat a mellékelt példaprogramok segítségével könnyen megérthetjük (a Delphi könyvtárán belül a Demos/Quickrpt könyvtárban találjuk őket). A lényege az, hogy Band-eket kell elhelyeznünk, 23 amelyeknek különböző típusai lehetnek (BandType tulajdonság), majd ezekben helyezzük el az adatokat megjelenítő komponenseket (pl. QRDBText) A komponenseket széleskörűen konfigurálhatjuk, kényünkkedvünk szerint Programunkon belül a QuickReport Preview metódusát meghívva nyomtatási képet mutathatunk meg. Ezt célszerű alkalmazni, mert a felhasználók szeretik látni, mi fog a nyomtatott papírra kerülni. A Preview ablak
használata egyszerű, de célszerű magyarítani a Delphi könyvtárán belül a Lib/qrprev.dfm fájl megnyitásával és átírásával. Egy QuickReport-tal készített nyomtatási kép Project Ha már mindennel kész vagyunk, nem árt, ha végignézzük a Project beállításait (Project/Options). A Forms lapon beállíthatjuk, hogy melyik form legyen az alkalmazásunk fő formja (Main Form), valamint hogy mely formokat hozza létre automatikusan a program (amelyeket futásidőben, kézzel kívánunk létrehozni, azokat töröljük a listából). Az Application lapon megadhatjuk, mi legyen a program neve (ApplicationTitle, ez jelenik meg a tálcán) és hogy mi legyen az ikonja (Application.Icon) Bármennyire is lényegtelennek tűnik, nagy hangsúlyt kell fektetnünk az alkalmazás ikonjának kiválasztására. A felhasználók ugyanis az alapján azonosítják a programunkat, így annak utalnia kell funkciójára, vagy mindenképpen egyedinek, az általában használatos
ikonoktól eltérőnek kell lennie. A Compiler oldalon a fordítóra vonatkozó beállításokat tehetjük meg Végső fordítás esetén (ha már teljesen kész vagyunk a programmal, és nem óhajtunk több hibakeresést végezni) célszerű a Runtime errors és a Debugging panelen mindent kikapcsolni, ezzel is csökkentve a program erőforrásigényét. Hasonló okokból érdemes bekapcsolni a Code generation-ban az Optimization-t Még egy fül van, ami érdekes számunkra, ez pedig a Version info. Itt a programnak azon tulajdonságait állíthatjuk be, amelyeket a Windowsban, az exe tulajdonság-lapján is megtekinthetünk. A mezőket kitöltve megadjuk a verziószámot (a Build szám automatikus növelésére is van lehetőségünk), a szerzőt, a tulajdonost, stb. Ha már ezeken is túl vagyunk, jöhet az utolsó Build, majd a használat. Sok sikert! 24 V. Egyéb problémák Hibakezelés Ebben a részben a hibák kezelésének alapjaival ismerkedünk meg, hogy itt szerzett
tudásunkat felhasználhassuk az adatbázis-kezelő rendszerek készítése során, hiszen egy adatbázis kezelése során rengeteg hibalehetőség van, sokuk független attól, hogy a programot hogy írtuk meg. A Windows a hibakezelést ún. exception-ökkel oldja meg (Az exception jelentése: kifogás, kivétel, kivételes esemény, megszakítás. A magyar nyelvű Windows-okban kivételnek fordítják, holott a kivételes esemény, vagy a megszakítás jobban illene rá.) Kivétel akkor jön létre, ha egy hiba, vagy egyéb esemény megszakítja a program normál futását. A kivétel az irányítást egy kivétel-kezelőnek (exception handler) adja át, ami lehetőséget ad arra, hogy elkülönítsük a program normál futását a hibakezeléstől. A kivétel információt (ami általában egy hibaüzenet) tud átvinni attól a ponttól, ahol előidézték, addig a pontig, ahol lekezelik. Ha egy alkalmazás használja a SysUtils unitot, minden futtatási hiba (runtime error)
automatikusan kivétellé konvertálódik. Olyan hibák, amelyek más esetben megszakítanák a program futását – mint pl kevés memória, nullával való osztás, általános védelmi hiba – elfoghatók és kezelhetők. A kivételek is objektumok, így használatuk könnyen elsajátítható. (Delphi Help: Exceptions: Owerview) Saját magunk is létrehozhatunk ilyen kivételeket, a raise kulcsszó segítségével. A legegyszerűbben a következő példa alapján járhatunk el: raise EMathError.Create(‘Hiba történt! Kutykurutty!’); A hibák kezelése sem sokkal bonyolultabb ennél. A legegyszerűbb, ha a problémás műveleteket ún tryblokkba tesszük Ez a következőképpen néz ki: Try X := Y/Z; except on EZeroDivide do HandleZeroDivide; end; Az on után a hiba típusát (pontosabban a kivétel osztályának nevét) írjuk, a do után pedig meghívhatjuk a kezelő eljárásunkat. Ha azt szeretnénk, hogy a kritikus művelet után mindenképpen végrehajtódjanak bizonyos
műveletek, és csak azután keletkezzen kivétel, használhatjuk a try.finally szerkezetet is: Reset(F); try {.} // fájlműveletek az F fájllal finally CloseFile(F); end; A hibakezelésnek van még egy fontos része, ez pedig a komponensek hibaeseményei. Nevük általában a következőképpen néz ki: On*Error. Nevük utal mivoltukra, pl a TTable OnDeleteError eseménye akkor keletkezik, ha egy adat törlése meghiúsult. Ezek használatával rengeteg hibalehetőséget kiszűrhetünk, kezelhetünk. Ehhez jól kell tudnunk használni az Exit, az Abort, ill a Halt eljárásokat Az Exit kilép az aktuális eljárásból. Ha a főprogramban van, a program kilép, ha egy meghívott eljárásban van, visszaadja a vezérlést arra a pontra, ahol az eljárást meghívtuk. Akkor használjuk, ha olyan eset áll fenn, amely mellett az eljárás nem folytatható. Az Abort megszakítja az aktuális folyamatot anélkül, hogy hibát generálna. Használatát a következő példa jól mutatja:
procedure TForm1.Table1BeforePost(DataSet: TDataSet); begin if DBEdit1.Text = then Abort; end; Tehát a Table1 tábla mentés (Post) előtt meghívott eseményében (BeforePost) megnézzük, hogy üresen hagyta-e a felhasználó a DBEdit1 által reprezentált mezőt. Ha igen, az abort-tal megszakítjuk a műveletet, és így nem engedélyezzük a mentést. A Halt azonnal megállítja a program futását, és a paraméterként megadott hibakóddal kilép. Csak akkor használjuk, ha a program futtatása egyáltalán nem folytatható. A fentiek ismeretében nyugodtan elkezdhetünk hibaelhárító, kezelő programrészeket írni. 25 Hálózat használata Paradox rendszerrel Gyakori feladat, hogy egy PARADOX adatbázist több számítógépről szeretnénk egyszerre használni. Ehhez azonban még nem feltétlenül szükséges kliens-szerver alkalmazást készíteni, ha jól dolgozik az ember, megfelelő platform erre a PARADOX is. Úgy kell tehát megtervezni az alkalmazásunkat, hogy
ne okozzon neki gondot, ha vele egy időben egy másik program is dolgozik az adatbázisán. A probléma látszólag ugyanaz, mint amikor egy számítógépen egyszerre futó több program éri el ugyanazt az adatbázis. Az erre való felkészítés azonban nem elég. Az adatbázis ugyanis másként viselkedik, ha a programok nem egyazon számítógépen futnak Elsőnek arra kell figyelnünk, hogy ha egy program szerkesztésre megnyitott egy adatot, azt egy másik program csak olvasásra tudja megnyitni. Ezért le kell kezelnünk azt az esetet, amikor a másik program is megpróbálja szerkeszteni azt. Ennek a legegyszerűbb módja, ha a táblák OnEditError eseményébe beírjuk a következőt: E.Message:= Nem sikerült megnyitni az adatot Lehet, hogy egy másik felhasználó épp szerkeszti; Ezzel azt érjük el, hogy hiba esetén a Delphi ezt az üzenetet fogja kiírni. A többivel nem kell foglalkoznunk, hiszen a Delphi automatikusan megszakítja a hibát okozó folyamatot. Gyakori
eset, hogy egy adatbázisban egy egyed azonosítására egy számértéket (kódot) használunk, amit minden egyes új egyed felvételekor eggyel növelünk. Ez a növelés pedig úgy történik, hogy az új egyed a kódját úgy kapja meg, hogy a legnagyobb kódhoz hozzáadunk egyet. Ekkor azonban előfordulhat az, hogy két program egyszerre hoz létre egy-egy új egyedet (vagy az egyik megnyitja, de még nem menti, amikor a másik is megnyitja). Így mindkét program azonos kóddal szeretné elmenteni az adatot, ami nem túl szerencsés (és nem is lehet, ha ez a kód az egyedüli elsődleges kulcs). Az ehhez hasonló esetek megoldására az tűnik megfelelőnek, hogy mihelyst létrehozzuk az új egyedet, megkapja a megfelelő kulcsot (kódot), és rögvest mentjük is, majd újra megnyitjuk szerkesztésre. Így a felhasználó vagy a program korlátlan ideig „gondolkodhat” az adatok kitöltésén, az egyed helye biztosított az adatbázisban. Fontos kérdés hálózat
használata esetén a frissítés megvalósítása. Minden művelet előtt frissíteni, a műveleteket követően pedig rögzíteni kell az adatokat. A programok ugyanis nem veszik „maguktól” észre a változásokat, amiket egy másik program okozott. Ezért célszerű a táblák Refresh metódusát használni, mielőtt valamilyen műveletbe kezdünk. Mivel a BDE az adatokat a memóriában (bufferekben) tárolja, azok nem íródnak ki rögtön a merevlemezre, így a változásokat csak az adott számítógépen futó programok érzékelhetik (mivel egyszerre csak egy BDE futhat, és az szolgálja ki az összes BDE-programot). Gondolnunk kell tehát arra, hogy egy esetleges meghibásodás vagy áramkimaradás esetén, továbbá ha egy másik számítógép szeretné az aktuális adatokat lekérdezni, ez az elv nem követhető. Ezért minden művelet elvégzése után érdemes a bufferek tartalmát a merevlemezre írni a FlushBuffers metódus segítségével, ha pedig CacheUpdate
módot használunk, az ApplyUpdates-el (a CacheUpdate üzemmódról a TTable-nél volt szó, a IV. fejzetben) Ezeket betartani azonban még mindig csak fél siker. Ahhoz, hogy egyszerre több számítógépen futó programok (pontosabban BDE-k) megfelelő módon kezelhessék az adatállományokat, a BDE megfelelő beállításait is el kell végezni. A BDE ugyanis az adatbázis megnyitásakor annak könyvtárában ún lock-fájlokat (paradox táblák használata esetén ez a paradox.lck és a pdoxusrslck) helyez el Ezekben tárolja azt, hogy mely tábláknak mely rekordjait nyitották meg szerkesztésre, így azokhoz csak olvasási hozzáférést enged. Ahhoz viszont, hogy egyszerre több program (BDE) a hálózaton keresztül elérje ezt az adatbázist, össze kell hangolni őket. Erre szolgál a pdoxusrsnet fájl, amit a BDE a neki beállított NET DIR változó értékének megfelelő helyre helyez el, illetve ott keresi. Ezért fontos, hogy az összes BDE egy helyen (a hálózat
egyik megosztott könyvtárában) keresse ezt a fájlt. Tehát minden számítógépen megfelelően kell beállítani a BDE Administrtator programban a Configuration/Drivers/Native/Paradox elem NET DIR paraméterét (azon a gépen, ahol az adatok vannak, a C:-t érdemes beállítani, a többi gépen pedig értelemszerűen ezt a könyvtárat képviselő hálózati megosztás nevét). Ha a beállítások nem helyesek, a program leállhat azzal, hogy az adatbázist valaki más használja. Ha már a BDE beállításainál tartunk, ismét megemlítem, hogy a sikeres hálózati működés érdekében a Configuration/System/INIT/LOCAL SHARE változót TRUE-ra kell állítani, különben az adatokat nem tudják frissíteni a hálózaton keresztül az alkalmazások. Ha olyan programot írunk, amely hálózaton, több példányban működik, gondolnunk kell arra, hogy bizonyos műveletek kizárólagos megnyitást követelnek maguknak (ilyen pl. a tábla ürítése, az EmptyTable), azaz ekkor
csak egy példányban futhat a programunk. A legjobb és legegyszerűbb az, hogy a művelet elvégzése előtt közöljük a felhasználóval, hogy zárja be a többi alkalmazást. Ha pedig mégis hiba lép föl, még egyszer figyelmeztetjük erről. A fentiekből látszik, hogy a Paradox rendszereket nem arra találták ki, hogy egyszerre több száz program férjen hozzá egy adatbázishoz. Néhány program viszont tökéletesen működik hálózatban, ha betartjuk a szabályokat. A tapasztalatom azt mutatja, hogy megfelelő odafigyeléssel ezeket a hibalehetőségeket ki lehet iktatni, és végeredményben egy jól használható alkalmazást kapunk. 26 Biztonsági problémák Előfordulhat, hogy az adatbázis megsérül. Mit kell, mit lehet ilyenkor tenni? Milyen kilátásaink vannak, hogyan háríthatjuk el a hibát, és hogyan előzhetjük meg? Ezek a kérdések minden fejlesztőt foglalkoztatnak. Ebben a részben megoldást keresünk rájuk. Biztonsági mentés A károk
megelőzésének legegyszerűbb módja, ha biztonsági másolatot készítünk adatainkról. Egyszerűbb fájlsérüléseket kivédhetünk azzal, hogy az adott merevlemezre másoljuk le az adatokat. Biztosra azonban csak akkor mehetünk, ha ezt egy másik adathordozóra tesszük meg (történhetnek ugyanis olyan károk, amelyek a merevlemez adatainak teljes elvesztésével járnak együtt). Biztonsági másolat készítésére rengeteg módszer kínálkozik. Először is el kell döntenünk, hogy erről mi magunk kívánunk gondoskodni, vagy rábízzuk a felhasználóra. Ha nem akarjuk a felhasználókat ilyenekkel terhelni, érdemes a programunkba épített automatizmussal megoldani. Persze az is megoldás, ha külön programot készítünk erre a célra, amit a Windows Feladatütemezőjével adott időszakonként futtatunk. Ehhez azonban már ismerni kell a Feladatütemező vezérlését, vagy a telepítés során kézzel be kell konfigurálni. Ha a felhasználóra bízzuk a dolgot,
beépíthetjük a programunk karbantartó moduljába, vagy írhatunk rá külön programot, amelynek parancsikonjára kattintva automatikusan lefut. A legrosszabb megoldás, ha nem teszünk semmit, hanem a felhasználóra bízzuk, hogy készítsen kézzel (valamilyen fájlkezelővel, Intézővel) másolatot az adatbázis fájljairól. Ez ugyan nem valami felhasználóbarát, de ha tudjuk, hogy a felhasználó képes erre, minden további nélkül építhetünk rá. A biztonsági mentés kétféle lehet. Vagy mindig ugyanazokat a fájlokat írjuk felül az aktuális adatbázissal, vagy minden alkalommal egy külön dátummal ellátott könyvtárba másoljuk azokat, lehetővé téve, hogy egyszerre több biztonsági másolat is elérhető legyen. Arra kell ekkor figyelmet fordítanunk, hogy sok biztonsági mentés mérete már elég jelentős lehet, és nem túl szerencsés ezzel terhelni a háttértárakat. Meg kell tehát oldanunk az elavult biztonsági mentések törlését is. Ami
nagyon fontos: nem nekünk érdekünk az adatok biztonságos tárolása és mentése, hanem a felhasználónak. Ha már megoldottuk a biztonsági másolat készítését, már csak a visszatöltéssel kell törődnünk. A másolatot ugyanis azért készítjük, hogy probléma esetén könnyen vissza lehessen tölteni azt az eredeti adatfájlokat felülírva. A feladat programozása nem bonyolult, mindössze az adatbázis fájljait kell másolgatni a CopyFile (windows unit) függvény segítségével. A dolog jelentősége ott van, hogy gondoltunk rá, és nem a felhasználónak kell ezzel törődnie. Azt, hogy milyen időközönként mentünk, attól tegyük függővé, hogy mennyire gyakran változik az adatbázis, és ezek a változások mennyire rekonstruálhatók. Adatsérülések kezelése Az adatsérülés általában úgy mutatkozik meg, hogy program indításakor, vagy futásakor valamilyen hibaüzenetet kapunk a BDE-től, amelyben hibás adatfájlokról, megnyithatatlan
táblákról tájékoztat minket. Az esetek többségében a hiba javítható, mivel nem a táblák, csak a hozzájuk tartozó járulékos fájlok sérülnek. Ezeket a sérüléseket általában a program futásának váratlan megszakadása okozza, amely történhet a mi hibánkból, de lehet a Windows hibája is. Hálózati használat esetén egy váratlan kapcsolatszakadás (érintkezési hiba, stb.) is okozhat ilyen hibát, ugyanúgy, mint az áramszünet Hogy jobban lássuk a helyzetet, tisztázzuk, milyen fájlok tartoznak egy Paradox alapú adattáblához. Az első a .db fájl, ami maga a tábla Ha ez sérül, nem tudunk igazán mit kezdeni vele, a biztonsági mentéshez kell nyúlnunk. A többi, egy táblához tartozó fájlnak a pont előtti neve megegyezik a .db fájl pont előtti nevével Ha használjuk a tábla Validity Checks (érvényesség vizsgálat) lehetőségét, automatikusan létrejön egy .VAL fájl, ami ezeket a beállításokat tárolja Ha ez sérül meg, a 12036
kódú, VAL file is out of date (VAL fájl elavult) hibaüzenetet kapjuk, mikor megpróbáljuk megnyitni az adatállományt. Ezt úgy tudjuk kivédeni, ha letöröljük. Ekkor azonban elvesztjük az imént említett lehetőségeket, így inkább újra kell gyártanunk ezt a fájlt (hogy hogyan, azt majd a későbbiekben tisztázzuk). Az elsődleges indexszel kapcsolatos információk a .PX fájlban tárolódnak A többi, másodlagos indexinformációk a GX? és a GY? fájlokban vannak (a ? helyén szám, illetve betű található, attól függően, hogy hányadik az index). Ha ezek közül sérül valamelyik, a 8961-es kódú Corrupt table/index header (hibás tábla/index fejléc) hibaüzenetet kapjuk. Ezen a problémán a tábla újraindexelésével lehet segíteni (ha nem állítunk be indexet a táblának, hibás index-állományokkal is megnyithatjuk). Ahhoz, hogy az imént felsorolt hibákat ki tudjuk javítani, szükségünk van a táblák definícióira. Ezért azokat el kell
tárolnunk a hibajavító programban. Ezt nagyon egyszerűen úgy tehetjük meg, hogy létrehozzuk a megfelelő táblákat (TTable), majd jobb egérgombbal kattintva, az Update Table Definition menüpont segítségével beállítjuk a meződeklarációkat (amiket a tábla a FieldDefs tulajdonságában tárol) és az 27 indexdeklarációkat (ezek az IndexDefs-ben vannak). Ha mindezt elvégeztük, a Table-ök már ismerik a tábláink struktúráját, definícióját. Így már bármikor létrehozhatunk egy ennek megfelelő, üres táblát, a CreateTable metódus segítségével. Ezt a lehetőséget használhatjuk fel a sérült fájlok javítására, a következőképpen: ha sérültnek találunk egy állományt (a .db fájl kivételével), letöröljük, majd egy SQL lekérdezésben megnyitjuk a táblát (a SELECT * FROM tábla.db lekérdezést használva), és meghívjuk a táblához tartozó Table CreateTable metódusát. Ezzel létrejött egy új, üres tábla, amelynek
struktúrája hibátlan, a tábla előző adatait pedig átmenetileg a Query-ben tároljuk. A következő lépés az adatok átmásolása a Query-ből a Table-be Ezt a korábban szintén bemutatott TBatchMove segítségével végezhetjük el. Ha ezek után sem tudjuk megnyitni a táblát, sajnos nem tudunk rajta segíteni. A tábláknak vannak olyan tulajdonságai is, amelyeket nem tudunk a TTable-ben eltárolni, és ezáltal a CreateTable metódussal létrehozott tábla sem tartalmazza. A leggyakoribb ilyen tulajdonság, amivel találkozunk, a jelszó. Ezért ezt magunknak kell megadni, a tábla létrehozása után A dolog abból a szempontból nem egyszerű, hogy nincs meg a megfelelő eljárás, amivel egy táblának jelszót állíthatnánk be. Némi keresgetés után azonban találunk egy példát, méghozzá a Borland Database Engine Online Reference-ben (megtalálható a Start menüben is, a Delphi-n belül a Help/Borland Database Engine menüben, valamit a BDE könyvtárában).
Ha ebben az állományban rákeresünk az AddMasterPassword szóra, és a kiadott találatot megnyitva az oldal Example 4: Add a master password to a Paradox table alcímet viselő részből az eljárást kimásoljuk, máris van egy saját jelszóhozzáadó rutinunk (ami egyébként a DbiDoRestructure nevű BDE API függvényt használja). Sokszor nem elég az adatokat fizikailag ellenőrizni. Hibás működés esetén ugyanis előfordulhatnak kitöltetlen mezők, amik rengeteg bosszúságot okozhatnak. Érdemes tehát a táblák tartalmát is ellenőrizni, és lehetőség szerint a kellő helyen beavatkozni. Hogy be lehet-e avatkozni, és ha igen, milyen módon, azt a konkrét feladat illetve adatbázis határozza meg, ezért részletekbe nem bocsátkozunk. Egy szoftver fejlesztése során gondolnunk kell arra is, szükséges-e naplózni a műveleteket, és ha igen, ezt milyen módon oldjuk meg. A naplózás azért tölt be fontos szerepet egy szoftver működésében, mert
segítségével könnyen visszakereshetők a műveletek, és könnyebben megy a hibakeresés is (főleg ha a hiba egy egyszerű felhasználó „keze alatt” keletkezett, aki még a hibajelenséget, illetve a hibaüzenetet sem tudja felidézni). A naplózás legegyszerűbb módja, ha minden eseményt beírunk egy naplófájlba, dátummal, idővel ellátva. Használhatjuk például a TListBox-ot, melybe a LinesAdd metódussal beszúrhatunk egy sort, a Lines.SaveToFile-lal pedig egyszerűen kimenthetjük tartalmát a paraméterben megadott fájlba A várt adatforgalomtól függően óránként, esetleg naponta megnyitunk egy újabb naplófájlt, és abba folytatjuk a naplózást. Ha a fájljainknak txt kiterjesztést adunk, a Jegyzettömbbel könnyen megjeleníthetőek lesznek. Ügyelnünk kell azonban arra, hogy az elavult naplófájlokat néha törölni kell, mert az egy könyvtárban tárolható fájlok száma a legtöbb Windows-os rendszerben véges (bármilyen kicsik is ezek a
fájlok). A biztonság kérdése nagyon fontos egy programnál, főleg ha azt valaki üzemszerűen használja, és fontos adatokat tárol és kezel segítségével. A biztonsági intézkedések hosszútávon jelentenek előnyt, és beváltják a hozzájuk fűzött reményeket. Ezen védelmi mechanizmusok ellenére azonban nem engedhetjük meg magunknak azt a luxust, hogy kevésbé törődjünk a rendszerünk egyéb részeinek helyes, biztonságos működésével, hiszen ezeket a biztonsági intézkedéseket a program által okozott hibáknál nem igazán tudjuk kihasználni. Telepítő készítése Ha egy általunk készített programot felhasználás céljából más kezébe adjuk, gondoskodnunk kell egy telepítőprogramról. Ez főleg igaz azokra a programokra, amelyek használják a Borland Database Engine-t, mivel azt a számítógépekre külön telepíteni kell (persze ezt megtehetjük a Delphi telepítő CD-jéről is, de az nem az igazi). A Delphi programcsomag tartalmaz egy
InstallShield Express nevű programot, amellyel könnyen és gyorsan készíthetünk komplett telepítőkészletet. Előnye a többi, Interneten fellelhető ingyenes vagy olcsóbb telepítő-készítőkkel szemben, hogy kifejezetten Delphi-hez készült, így támogatja a BDE-t. A program nem tartozik közvetlenül a Delphi-hez, a Delphi telepítő CD-jéről, mint önálló programot tehetjük föl számítógépünkre. A programban, ha új telepítőt készítünk, először meg kell adnunk annak nevét és helyét. Ha testre szabható telepítőt (Custom Setup) szeretnénk készíteni, jelöljük be az Include a custom setup type jelölőnégyzetet. A Create gomb megnyomása után megjelenő Setup Checklist-ben különböző opciókat találunk Ezeken egyenként végig kell mennünk. A teljesség igénye nélkül most végignézzük a lehetőségeket Set the Visual Design Az App Info lapon a program általános jellemzőit adhatjuk meg. Itt kell kiválasztanunk, melyik fájl a
programunk futtatható állománya (Application Executable). A Main Widow-ban a telepítő hátterét, a Featuresben pedig az automatikus Uninstall-t kapcsolhatjuk be Specify InstallShield Objects for Delphi Ebben a csoportban állíthatjuk be, milyen Delphi komponenseket telepítsen az általunk készített telepítő. Számunkra a BDE a legfontosabb Ezt meg is találjuk a General fülön belül Kiválasztva egy beállító varázsló jelenik meg. Első lépésben a telepítendő BDE komponenseket adhatjuk meg Ha Full BDE Installationt választunk, a teljes BDE belekerül a készletbe, de a Partial-lal mi adhatjuk meg ezeket a részeket Ha Paradox 28 rendszerben dolgozunk, elég a Paradox Driver-t bejelölni. Ha használunk valami mást is (pl SQL-t), értelemszerűen azt is be kell jelölnünk. A következő lapon alias-okat hozhatunk létre Itt célszerű megadni a program által használt alias-okat. Ezeknek a tulajdonságait kettővel arrébb be is állíthatjuk Érdemes még
a komponensek közül kiválasztani a BDE Control Panel File-t, melynek segítségével a Vezérlőpultból is elérhető a BDE Administrator program. Specify Components and Files A telepítőben a fájlokat csoportosítjuk. Vannak programfájlok, adatbázisfájlok, súgófájlok, stb, melyeket ilyen módon csoportba szervezünk, így telepítéskor könnyen kiválaszthatóak a szükséges részek. A Groups lapon létre kell hoznunk a használni kívánt csoportokat, és azokhoz hozzáadni a megfelelő fájlokat. A Components-ben megadhatjuk, hogy ezek a csoportok milyen komponenseken belül legyenek, a Setup Types-ban pedig azt, hogy mely telepítési típus (Egyéni – Custom, Tipikus – Typical, Minimális – Compact) hogyan tartalmazza ezeket a komponenseket. Select User Interface Components A telepítőnek megmondhatjuk, hogy milyen dialógus-ablakok jelenjenek meg a telepítés folyamán. Itt helyezhetjük el a felhasználói licenszszerződést (Software Licence Agreement),
valamint az „olvass el” dokumentumot (Readme Information), stb. Specify Folders and Icons Itt parancsikonokat hozhatunk létre a programunk különböző fájljairól, amit a telepítő a megadott helyre létrehoz majd. Ha mindent beállítottunk, a Disk Builder segítségével létrehozhatjuk a telepítőt. Ha kész, nincs más hátra, mint lemezre másolni. Az InstallShield 29 Készítsünk CD-t! Manapság egy telepítőt CD-n illik kiadni. Emellett egyre nagyobb teret hódít az automatikus CD, amelyet a meghajtóba helyezve elindul egy héjprogram, amivel elindíthatjuk a CD-n lévő telepítőket. Ilyet természetesen mi is készíthetünk. Egy megfelelő telepítő-indító héjprogram megírása nem jelenthet gondot Néhány felirat és gomb. De hogyan futtatunk programot? Sajnos a Delphi-ben nincs egyszerű programfuttató rutin, ellenben a Delphi DemosDocFilemanex könyvtárában található egy fmxutils.pas nevű unit, melyet a Bin könyvtárba másolva, a
programunk uses-listájába írva használatba is vehetünk. Ebben találunk egy ExecuteFile függvényt, amellyel már könnyedén futtathatunk bármit (egyébként használhatnánk a ShellExecute API függvényt is, de ez egyszerűbb). Egy dologra viszont nagyon oda kell figyelni: ha automatikusan indul a CD-n lévő héjprogram, nem biztos, hogy a Windows a CD gyökérkönyvtárából futtatja. Ezért a futtatni kívánt telepítő fájlnevét célszerű a héjprogram fájlnevéből kiszámítani. Kiváló eszköz erre a nulladik paraméter (ParamStr(0)) Ez ugyanis mindig a teljes fájlnév. Ebből az ExtractFilePath függvénnyel kivesszük a könyvtárnevet, így megkapjuk a héjprogram könyvárát (a héjprogramot a CD gyökérkönyvtárában érdemes elhelyezni). Ebből már könnyen megvan a futtatni kívánt telepítő teljes fájlneve. ExecuteFile(ExtractFilePath(ParamStr(0))+ TelepítőSetup.exe, , ExtractFilePath(ParamStr(0)),0); Így ez a következő fájlt futtatja (ha a
program helye D:): D:TelepítőSetup.exe Most már csak azt kell megoldani, hogy a héjprogramunk automatikusan induljon. A CD gyökérkönyvtárában létre kell hozni egy autorun.inf nevű állományt, amelynek a következő sorokat kell tartalmaznia: [autorun] OPEN=Program.exe Ha pedig ikont is szeretnénk (amit szintén a CD gyökerében helyezhetünk el): ICON=Ikon.ico vagy használhatjuk a programunk ikonját is: ICON=Program.exe,0 A leírtak alapján már bárki elkezdhet telepítőt készíteni. Egy jó tanács azonban elkél: Mindig próbáld ki a telepítőt, mielőtt kiadod a kezedből! VI. Lezárás Az adatbázis-kezelő alkalmazások fejlesztésének rögös útján könnyű elbukni. Az ember mindig a saját tapasztalataiból (és főleg kárából) tanul a legjobban. Nincs olyan könyv vagy kiadvány, ami minden speciális problémával foglalkozna. Korunkban viszont egyre inkább a speciális, lokalizált, a felhasználó igényeire szabott rendszerekre van szükség.
Az általános szoftverek ideje lejárt Azokat már megírták mások (többek is) A mai programozóknak két lehetőségük van: vagy beolvadnak egy szoftvercégbe, és ott fejlesztik sokadmagukkal a különböző általánosabbnak mondható alkalmazásokat, vagy egyéni vállalkozásba fognak, és speciális, egyedi problémák megoldására szakosodnak. Zárásként nem áll szándékomban okoskodni, és elmondani, „miért jó ez a dolgozat” és „milyen tanulságai vannak”. Ezt az Olvasóra bízom Én csupán azt remélem, sikerült átadnom azt, amit szerettem volna Remélem dolgozatom elérte célját, és a Kedves Olvasó talált benne olyat, ami a hasznára vált. Köszönöm. 30 Irodalomjegyzék 1. 2. 3. 4. 5. 6. 7. Borland Delphi 5.0 Online Help (beleértve a BDE Help-et és a Win32 Programmers Reference-et is) Szelezsán János: Adatbázisok, LSI Oktatóközpont, Budapest Halassy Béla: Az adatbázistervezés alapjai és titkai, IDG Magyarországi Lapkiadó
Kft., Budapest, 1994 Thomas Binzinger: Delphi, Kossuth Kiadó, 1998 Dr. Szabó László: A Delphi 20-ról röviden, oktatási segédlet, Miskolc, 2000 Marco Cantú: Delphi 3 mesteri szinten, I-II. kötet, Kiskapu Kiadó, Budapest, 1998 Gray Cornell: Delphi tippek és trükkök, Panem Kft., Budapest, 1997 Ajánlott Internet-címek: - Delphi Software Online: www.animarehu/dso - Magyar Delphi levelezőlista: www.szechenyi-nkzsasulinethu/delphi - Borland Magyarország: www.borlandhu - Borland Home Page: www.borlandcom - Deplhi HomePage: www.borlandcom/delphi/indexhtml - Deplhi Developer Support: www.borlandcom/devsupport/delphi/ 31