Pál László, Máté Szilárd
Alkalmazásfejlesztés Delphiben - egyetemi jegyzet –
2009
1
TARTALOMJEGYZÉK BEVEZETŐ...................................................................................................................... 10 1. ALAPFOGALMAK ................................................................................................. 11 1.1. A Delphi, mint objektumorientált programozási nyelv .................................... 11 1.2. Objektumorientált programozási alapok........................................................... 12 1.2.1. OOP nyelvek jellemzői ............................................................................. 12 1.2.2. Osztály, objektum ..................................................................................... 13 1.2.3. Konstruktor és destruktor.......................................................................... 15 1.2.4. Hozzáférés az adatokhoz........................................................................... 16 1.2.5. Öröklés...................................................................................................... 17 1.2.6. Polimorfizmus........................................................................................... 18 1.2.7. Típusellenőrzés és típuskonverzió ............................................................ 19 1.2.8. A Self kúlcsszó ......................................................................................... 19 1.2.9. Osztályváltozók és osztálymetódusok ...................................................... 20 1.3. A Delphi alkalmazások felépítése..................................................................... 20 1.3.1. A projektállomány szerkezete................................................................... 22 1.3.2. Az űrlapterv szerkezete............................................................................. 22 1.3.3. Az űrlaphoz tartozó egység....................................................................... 23 1.4. A Delphi fejlesztői környezet ........................................................................... 24 1.4.1. Űrlapok tervezése...................................................................................... 25 1.5. Delphi adattípusok ............................................................................................ 27 2. DELPHI KOMPONENSEK ..................................................................................... 29 2.1. A komponensek osztályhierarchiája ................................................................. 29 2.2. Standard komponensek ..................................................................................... 34 2.3. Az Additional paletta fontosabb komponensei ................................................. 40 2.4. A WIN32 paletta fontosabb komponensei........................................................ 42 2.5. Más palettákon szereplő fontosabb komponensek............................................ 45 2.6. Saját komponensek létrehozása ........................................................................ 46 2.6.1. A létrehozás lépései .................................................................................. 47 3. ÜZENET- ÉS DIALÓGUSABLAKOK ................................................................... 51 3.1. Üzenetablakok................................................................................................... 51 3.1.1. ShowMessage ........................................................................................... 51 3.1.2. MessageDlg............................................................................................... 51 3.1.3. MessageDlgPos......................................................................................... 53 3.1.4. Az InputBox üzenetablak.......................................................................... 53 3.2. Dialógusablakok ............................................................................................... 54 3.2.1. OpenDialog, SaveDialog .......................................................................... 54 3.2.2. FontDialog ................................................................................................ 56 3.2.3. ColorDialog............................................................................................... 57 3.2.4. PrintDialog................................................................................................ 57 3.2.5. FindDialog, ReplaceDialog....................................................................... 57 4. TÖBB ŰRLAPOS ALKALMAZÁSOK .................................................................. 59 4.1. Alkalmazások űrlapjai ...................................................................................... 59 4.2. Az ablakok megjelenítési formái ...................................................................... 59 4.2.1. Modális űrlapok ........................................................................................ 59 4.2.2. Nem modális űrlapok................................................................................ 60 4.3. MDI alkalmazások készítése............................................................................. 61 2
5.
GRAFIKA................................................................................................................. 65 5.1. A rajzvászon fontosabb jellemzői ..................................................................... 65 5.2. A rajzvászon fontosabb metódusai ................................................................... 66 5.3. Grafikus nyomtatás ........................................................................................... 68 6. KIVÉTELEK KEZELÉSE........................................................................................ 70 6.1. Mi a kivétel? ..................................................................................................... 70 6.2. Kivételek kezelése: try-except .......................................................................... 70 6.3. Erőforrás védelem: try-finally........................................................................... 71 6.4. Kivétel generálása: raise ................................................................................... 73 7. ÁLLOMÁNY-KEZELÉS ......................................................................................... 74 7.1. Típusos állományok .......................................................................................... 74 7.2. Szöveges állományok........................................................................................ 76 7.3. Szöveges nyomtatás .......................................................................................... 78 7.4. Egyéb lehetőségek Delphiben........................................................................... 78 8. ADATBÁZIS-KEZELÉS DELPHIBEN .................................................................. 80 8.1. Adatbázis motor (BDE) .................................................................................... 80 8.2. BDE álnevek (Alias) ......................................................................................... 81 8.3. Adatelérési komponensek (Data Access).......................................................... 81 8.3.1. Fontosabb komponensek........................................................................... 82 8.3.2. Adathalmazok állapota.............................................................................. 83 8.3.3. Adathalmazok nyitása és zárása................................................................ 84 8.3.4. Mozgás az adathalmazban ........................................................................ 84 8.3.5. Adatelérési komponensek fontosabb közös tulajdonságai........................ 84 8.3.6. Adatelérési komponensek fontosabb közös metódusai............................. 85 8.3.7. Fontosabb közös események..................................................................... 85 8.4. Adatmegjelenítési komponensek (Data Controls) ............................................ 86 8.5. Egyszerű alkalmazáskészítés ............................................................................ 88 8.5.1. Adatmegjelenítés vezérlő objektumok segítségével ................................. 88 8.5.2. Adatmegjelenítés lekérdezés segítségével ................................................ 90 8.5.3. Adatmodulok használata........................................................................... 91 8.6. Egyéb hasznos technikák .................................................................................. 91 8.6.1. Rekordok szerkesztése, törlése, új rekordok felvitele............................... 91 8.6.2. Keresés az adathalmazban ........................................................................ 93 8.6.3. Egy adathalmaz szűrése ............................................................................ 94 8.6.4. Űrlapok gyors tervezése............................................................................ 95 9. HASZNOS TECHNIKÁK........................................................................................ 96 9.1. Adatbevitel konzol alkalmazás használatával................................................... 96 9.2. Eseményekkel kapcsolatos megjegyzések........................................................ 97 9.2.1. A főablak OnCreate eseménye.................................................................. 97 9.2.2. Egérrel kapcsolatos események ................................................................ 97 9.2.3. Billentyűzet események ............................................................................ 98 9.2.4. Drag and Drop technika ............................................................................ 99 9.3. Objektumok létrehozása programból .............................................................. 100 9.4. Dátum- és időkezelő rutinok........................................................................... 101 9.5. Akciólista (ActionList) használata.................................................................. 102 9.6. Multimédiás lehetőségek ................................................................................ 104 10. Megoldott feladatok és útmutatások ................................................................... 107 11. Kitűzött feladatok................................................................................................ 148 Szakirodalom .................................................................................................................. 156 3
Abstract ........................................................................................................................... 157 Rezumat .......................................................................................................................... 158 A szerzőkről .................................................................................................................... 159
4
CONTENTS INTRODUCTION .............................................................................................................. 8 1. Basic definitions.........................................................Error! Bookmark not defined. 1.1. Delphi as an object-oriented programming language ...... Error! Bookmark not defined. 1.2. Object-oriented programming basics.................Error! Bookmark not defined. 1.2.1. OOP language feauters ..............................Error! Bookmark not defined. 1.2.2. Class, object ...............................................Error! Bookmark not defined. 1.2.3. Constructor and destructor.........................Error! Bookmark not defined. 1.2.4. The access levels........................................Error! Bookmark not defined. 1.2.5. Inheritance..................................................Error! Bookmark not defined. 1.2.6. Polymorphism ............................................Error! Bookmark not defined. 1.2.7. Typecasting ................................................Error! Bookmark not defined. 1.2.8. The Self parameter .....................................Error! Bookmark not defined. 1.2.9. Class methods ............................................Error! Bookmark not defined. 1.3. The structure of Delphi application ...................Error! Bookmark not defined. 1.3.1. The project file structure............................Error! Bookmark not defined. 1.3.2. The structure of the form file .....................Error! Bookmark not defined. 1.3.3. The unit file structure.................................Error! Bookmark not defined. 1.4. The programming environment of Delphi .........Error! Bookmark not defined. 1.4.1. Designing forms........................................................................................ 23 1.5. Delphi data types............................................................................................... 23 2. Delphi components ....................................................Error! Bookmark not defined. 2.1. The component hierarchy. .................................Error! Bookmark not defined. 2.2. Standard components .........................................Error! Bookmark not defined. 2.3. Components from the Additional palette...........Error! Bookmark not defined. 2.4. Components from the Win32 palette .................Error! Bookmark not defined. 2.5. Other important components..............................Error! Bookmark not defined. 2.6. Writing custom components ..............................Error! Bookmark not defined. 2.6.1. The main steps ...........................................Error! Bookmark not defined. 3. Delphi messages and dialogs .....................................Error! Bookmark not defined. 3.1. Messages ............................................................Error! Bookmark not defined. 3.1.1. ShowMessage ............................................Error! Bookmark not defined. 3.1.2. MessageDlg................................................Error! Bookmark not defined. 3.1.3. MessageDlgPos..........................................Error! Bookmark not defined. 3.1.4. The InputBox component ..........................Error! Bookmark not defined. 3.2. Dialogs ...............................................................Error! Bookmark not defined. 3.2.1. OpenDialog, SaveDialog ...........................Error! Bookmark not defined. 3.2.2. FontDialog .................................................Error! Bookmark not defined. 3.2.3. ColorDialog................................................Error! Bookmark not defined. 3.2.4. PrintDialog.................................................Error! Bookmark not defined. 3.2.5. FindDialog, ReplaceDialog........................Error! Bookmark not defined. 4. Multi-form applications .............................................Error! Bookmark not defined. 4.1. Types of forms ...................................................Error! Bookmark not defined. 4.2. Displaying forms................................................Error! Bookmark not defined. 4.2.1. Modal forms...............................................Error! Bookmark not defined. 4.2.2. Non-modal forms .......................................Error! Bookmark not defined. 5
4.3. Writing MDI applications ..................................Error! Bookmark not defined. 5. Graphics .....................................................................Error! Bookmark not defined. 5.1. The canvas properties ........................................Error! Bookmark not defined. 5.2. The canvas methods ...........................................Error! Bookmark not defined. 5.3. Graphics printing .............................................................................................. 60 6. Exception handling ................................................................................................... 62 6.1. What is an exception? ....................................................................................... 62 6.2. Exception handling: try-except ......................................................................... 62 6.3. Resource protection: try-finally .........................Error! Bookmark not defined. 6.4. Raising an exception ......................................................................................... 62 7. Working with files .....................................................Error! Bookmark not defined. 7.1. Binary files.........................................................Error! Bookmark not defined. 7.2. Text files ............................................................Error! Bookmark not defined. 7.3. Printing...............................................................Error! Bookmark not defined. 7.4. Other possibilities in Delphi ..............................Error! Bookmark not defined. 8. Database development ...............................................Error! Bookmark not defined. 8.1. The BDE ............................................................Error! Bookmark not defined. 8.2. The BDE aliases.................................................Error! Bookmark not defined. 8.3. Data access components ....................................Error! Bookmark not defined. 8.3.1. The most important components................Error! Bookmark not defined. 8.3.2. Dataset properties.......................................Error! Bookmark not defined. 8.3.3. Opening and closing datasets.....................Error! Bookmark not defined. 8.3.4. Moving through a dataset...........................Error! Bookmark not defined. 8.3.5. Important properties of the data access components Error! Bookmark not defined. 8.3.6. Important methods of the data access components.. Error! Bookmark not defined. 8.3.7. Important common events..........................Error! Bookmark not defined. 8.4. Data control components ...................................Error! Bookmark not defined. 8.5. Writing a simple application..............................Error! Bookmark not defined. 8.5.1. Displaying data by using control objects ...Error! Bookmark not defined. 8.5.2. Displaying data from queries .....................Error! Bookmark not defined. 8.5.3. Using the Data Module ..............................Error! Bookmark not defined. 8.6. Other important techniques................................Error! Bookmark not defined. 8.6.1. Editing, removing, adding records.............Error! Bookmark not defined. 8.6.2. Searching through dataset ..........................Error! Bookmark not defined. 8.6.3. Filtering a dataset.......................................Error! Bookmark not defined. 8.6.4. Quick form designing ................................Error! Bookmark not defined. 9. Useful techniques.......................................................Error! Bookmark not defined. 9.1. Handling input using console application..........Error! Bookmark not defined. 9.2. Remarks on events .............................................Error! Bookmark not defined. 9.2.1. The OnCreate event ...................................Error! Bookmark not defined. 9.2.2. Mouse events .............................................Error! Bookmark not defined. 9.2.3. Keyboard events.........................................Error! Bookmark not defined. 9.2.4. The Drag and Drop technique....................Error! Bookmark not defined. 9.3. Creating dynamic objects.................................................................................. 90 9.4. Date and time routines .......................................Error! Bookmark not defined. 9.5. Using the ActionList component .......................Error! Bookmark not defined. 9.6. Multimedia tools ................................................Error! Bookmark not defined. 6
10. Problems with hints and solutions .........................Error! Bookmark not defined. 11. Problems to be solved ............................................Error! Bookmark not defined. Bibliography ......................................................................Error! Bookmark not defined. Abstract ..............................................................................Error! Bookmark not defined. Rezumat .............................................................................Error! Bookmark not defined. About the authors...............................................................Error! Bookmark not defined.
CUPRINS INTRODUCERE ................................................................................................................ 8 1. Noţiuni de bază ........................................................................................................... 9 1.1. Delphi, ca limbaj de programare orientată obiect ............................................... 9 1.2. Bazele programării orientată pe obiecte ........................................................... 10 1.2.1. Caracteristicile limbajelor POO................................................................ 10 1.2.2. Clasă, obiect.............................................................................................. 11 1.2.3. Constructori şi destructori......................................................................... 12 1.2.4. Accesarea datelor ...................................................................................... 13 1.2.5. Moştenire .................................................................................................. 14 1.2.6. Polimorfism............................................................................................... 15 1.2.7. Conversii ................................................................................................... 15 1.2.8. Variabila Self ............................................................................................ 15 1.2.9. Metode de tip clasă ................................................................................... 16 1.3. Structura aplicaţiilor Delphi.............................................................................. 19 1.3.1. Structura fişierului project ........................................................................ 15 1.3.2. Structura fişierului DFM........................................................................... 15 1.3.3. Structura unit-urilor .................................................................................. 15 1.4. Mediul de programare Delphi........................................................................... 20 1.4.1. Proiectarea formularelor ........................................................................... 20 1.5. Tipuri de date în Delphi .................................................................................... 23 2. Componente Delphi .................................................................................................. 23 2.1. Ierarhia de clase. ............................................................................................... 23 2.2. Componentele Standard .................................................................................... 26 2.3. Componentele paletei Additional ..................................................................... 33 2.4. Componentele paletei Win32............................................................................ 35 2.5. Alte componente importante............................................................................. 38 2.6. Crearea componentelor proprii ......................................................................... 39 2.6.1. Paşii de creare ........................................................................................... 39 3. Ferestre de mesaje şi de dialog ................................................................................. 43 3.1. Ferestre de mesaje............................................................................................. 43 3.1.1. ShowMessage ........................................................................................... 43 7
3.1.2. MessageDlg............................................................................................... 43 3.1.3. MessageDlgPos......................................................................................... 45 3.1.4. Fereastra de mesaje InputBox................................................................... 45 3.2. Ferestre de dialog.............................................................................................. 46 3.2.1. OpenDialog, SaveDialog .......................................................................... 46 3.2.2. FontDialog ................................................................................................ 48 3.2.3. ColorDialog............................................................................................... 48 3.2.4. PrintDialog................................................................................................ 49 3.2.5. FindDialog, ReplaceDialog....................................................................... 49 4. Aplicaţii cu mai multe formulare.............................................................................. 51 4.1. Formularele aplicaţiilor..................................................................................... 51 4.2. Tipuri de formulare ........................................................................................... 51 4.2.1. Formulare de tip modal............................................................................. 51 4.2.2. Formulare de tip nemodal ......................................................................... 52 4.3. Aplicaţii MDI.................................................................................................... 53 5. Elemente de grafică................................................................................................... 57 5.1. Proprietăţile obiectului TCanvas....................................................................... 57 5.2. Metodele obiectului TCanvas ........................................................................... 58 5.3. Printare grafică.................................................................................................. 60 6. Gestionarea excepţiilor ............................................................................................. 62 6.1. Ce este excepţia?............................................................................................... 62 6.2. Tratarea excepţiilor: try-except......................................................................... 62 6.3. Protecţia resurselor: try-finally ......................................................................... 63 6.4. Comanda raise................................................................................................... 63 7. Gestionarea fişierelor ................................................................................................ 65 7.1. Fişiere binare..................................................................................................... 65 7.2. Fişiere text......................................................................................................... 67 7.3. Tipărire.............................................................................................................. 69 7.4. Alte posibilităţi în Delphi.................................................................................. 69 8. Baze de date în Delphi .............................................................................................. 71 8.1. Setarea BDE...................................................................................................... 71 8.2. Pseudonimurile (Alias) BDE ............................................................................ 72 8.3. Componente pentru accesarea datelor (Data Access) ....................................... 72 8.3.1. Componente importante............................................................................ 73 8.3.2. Starea seturilor de date.............................................................................. 74 8.3.3. Deschiderea şi închiderea seturilor de date............................................... 75 8.3.4. Deplasarea în setul de date........................................................................ 75 8.3.5. Proprietăţile comune ale seturilor de date................................................. 75 8.3.6. Metode comune ale seturilor de date ........................................................ 76 8.3.7. Evenimente comune importante ............................................................... 76 8.4. Componente pentru vizualizarea datelor (Data Controls) ................................ 77 8.5. Crearea unei aplicaţii simple............................................................................. 79 8.5.1. Vizualizarea datelor prin obiecte de control ............................................. 79 8.5.2. Vizualizarea datelor prin interogare.......................................................... 81 8.5.3. Utilizarea modulului de date..................................................................... 81 8.6. Alte tehnici importante ..................................................................................... 82 8.6.1. Modificarea, ştergerea şi adăugarea înregistrărilor................................... 82 8.6.2. Căutarea datelor ........................................................................................ 83 8.6.3. Filtrarea datelor......................................................................................... 84 8
8.5.4. Proiectarea rapidă a formularelor.............................................................. 81 9. Tehnici utile .............................................................................................................. 86 9.1. Introducerea datelor prin aplicaţii consolă........................................................ 86 9.2. Observaţii în legătură cu evenimente................................................................ 87 9.2.1. Evenimentul OnCreate.............................................................................. 87 9.2.2. Evenimentele mouse ................................................................................. 87 9.2.3. Evenimente de tastatură ............................................................................ 88 9.2.4. Tehnica Drag and Drop............................................................................. 89 9.3. Crearea obiectelor dinamice ............................................................................. 90 9.4. Metode Date/Time ............................................................................................ 91 9.5. Utilizarea componentei ActionList ................................................................... 92 9.6. Posibilităţi multimedia ...................................................................................... 94 10. Probleme rezolvate şi comentate .......................................................................... 97 11. Probleme propuse................................................................................................ 138 Bibliografie ..................................................................................................................... 146 Abstract ........................................................................................................................... 147 Rezumat .......................................................................................................................... 148 Despre autori................................................................................................................... 149
9
BEVEZETŐ Alkalmazásfejlesztést manapság számos programcsomag és fejlesztő eszköz segítségével végezhetünk. A nyelv egyszerűségét, hatékonyságát és gazdag fejlesztői környezetét tekintve talán kiemelkedik ezek közül a Delphi programozási nyelv. A Delphi egy negyedik generációs, vizuális programozási nyelv, amely az objektumorientált programozás alapelveit követi. Legvonzóbb tulajdonságai közé tartozik az űrlap központúság, gyors fordítás, kiváló adatbázis-kezelés, a Windows operációs rendszerrel való együttműködés, komponenseken alapuló technológia. Többnyire desktop alkalmazások fejlesztésére használják, de ma már számos webalkalmazás fejlesztésére alkalmas komponenst is tartalmaz. A fejlesztő eszköznek számos verziója jelent meg az évek során, a legutolsó verzió 2008-ban jelent meg Delphi 2009 névvel. A Delphi egy összetett, sok különböző részből felépülő eszköz, ezért a jegyzetben összefoglaltuk a legfontosabb technikákat, amelyek ismeretében már lehetséges egyszerűbb alkalmazások elkészítése. Mivel a fejlesztő eszköz alapnyelve az Object Pascal, ezért a jegyzet Pascal programozási ismereteket feltételez, viszont nem volt célunk objektumorientált programozási technikák bemutatása. Ennek ellenére a jegyzet elején egy kis objektumorientált programozási ízelítőt adunk a hallgatóknak. A jegyzet 11 fejezetből áll, amelyekben az alapvető ismereteken túl áttekintjük a fontosabb komponenseket, az üzenet- és dialógus ablakokat, több űrlapos alkalmazásokat, a Delphi grafikai lehetőségeit. Továbbá olyan hasznos technikákat mutatunk be, mint a kivételkezelés, állomány- és adatbázis-kezelés, valamint egyéb hasznos trükkök. Valamennyi technika bemutatását példákkal szemléltetjük. A jegyzet tartalmaz egy megoldott feladatokból álló fejezetet. Az itt található feladatokat részletesen tárgyaljuk, így a hallgatóknak lehetőségük van ezen feladatok tanulmányozására és megoldására. Az utolsó fejezet megoldásra javasolt feladatokat tartalmaz. A jegyzetben használt kölönböző technikák bemutatására valamint a feladatok megoldására a Delphi 7 verzióját használtuk.
Csíkszereda, 2009.03.12
A szerzők
10
1. ALAPFOGALMAK 1.1. A Delphi, mint objektumorientált programozási nyelv A Delphi egy Windows grafikus felületen futó negyedik generációs programozási nyelv, amely gyors alkalmazásfejlesztő környezetet (RAD Rapid Application Development) biztosít a fejlesztők számára. A Delphi rendszer az Object Pascal objektumorientált nyelvre épül és fontosabb jellemzői közül kiemelhetjük a komponensalapú felépítését és a testreszabható adatbázis-hozzáférési lehetőségét. Ezenkívül a rendszer támogatja a legújabb technológiákat, mint az Internet-alkalmazások készítését, a multimédiás megjelenítést, az ügyfél-kiszolgáló adatbázis-kezelést. Kezdetben csak Windows operációs rendszerben lehetett használni a Delphit, majd később fejlesztették ki a Linuxra szánt verziót Kylix néven. Sajnos a Kylix nem volt túl népszerű a programozók körében, így fejlesztése leállt. A Delphi 2003-as verziójától kezdődően már .NET támogatást is tartalmaz. Az Objektumorientált Programozás (OOP) a természetes gondolkodást, cselekvést közelítő programozási módszer. Az objektumorientált programozás jobban megközelíti, utánozza a valóságot, és jobban igazodik a tárgyakhoz. Minden valóságos tárgyat nemcsak alakja, elhelyezkedése jellemez, hanem az is, hogy hogyan viselkednek bizonyos körülmények között. Így a tárgyakat minden jellemzőivel együtt komplex egészként tekintjük. Az OOP a programozási nyelvek tervezésének következetes fejlődése következtében alakult ki. Az így létrejött nyelv sokkal strukturáltabb, modulárisabb és absztraktabb, mint egy klasszikus programozási nyelv. Ahhoz, hogy egy programot írjunk, szükségünk van a valós világ modellezésére. Ez a következő alapelveken keresztül történik: • Absztrakció: Segítségével kiemeljük a feladat szempontjából lényeges dolgokat, a többit pedig figyelmen kívül hagyjuk. Tehát, a tárgyak csak olyan tulajdonságait vesszük figyelembe, amelyek a cél elérése érdekében (működő program) feltétlenül szükségesek. Ha például egy diákokkal kapcsolatos programot írunk (pl. vizsgajegyek nyilvántartása) és létrehozzuk a diák objektumot, akkor elég, ha a diák a valós világban meglévő összes tulajdonságaiból csak a nevét, címét, életkorát, szakát, jegyeit, stb. tároljuk, nincs szükségünk pl. a súlya, a szeme színe, barátainak száma, stb. tulajdonságokra. Ha viszont a diák objektumot egy orvosi programban használjuk, akkor az utóbbi tulajdonságok kerülhetnek előtérbe. • Megkülönböztetés: Minden körülöttünk lévő tárgyat megkülönböztetünk tulajdonságai és viselkedésük alapján. • Osztályozás: Az osztályozás a tárgyak rendszerezését, kategóriákba, osztályokba sorolását jelenti. Ez a művelet, a tárgy számunkra lényeges tulajdonságai alapján történik. Az 1.1.1. ábrán láthatunk egy osztályba sorolást.
11
1.1.1. ábra. Az objektumok osztályokba sorolása
A fenyő az udvarunkon illetve a másik fenyő a Hargitán egy-egy objektum és a Fenyő osztály példányait jelentik. A Fenyő osztály része egy osztályhierarchiának. • Általánosítás, specializálás: E két alapelv bővebb és szűkebb kategóriák felállítását jelenti a tárgyak hasonlósága és különbözősége alapján. Például a rózsa és a tulipán virágok (általánosítás), de a rózsának van tövise (specializálás). • Kapcsolatok felépítése: A tárgyak között kapcsolatok léteznek: o Ismeretségi vagy együttműködési kapcsolatok: az egymástól független objektumok között. o Tartalmazási vagy egész-rész kapcsolatok: ha az egyik objektum része a másik objektumnak. A fenti, természetes emberi gondolkodáson alapuló modellezési alapelveket követi az Objektumorientált Programozás (OOP). Egy OO program egymással kommunikáló objektumokból áll, ahol minden objektumnak meg van a jól meghatározott feladata és az objektumok egymást kérik a megfelelő feladatok elvégzésére.
1.2. Objektumorientált programozási alapok Delphi alkalmazásokat készíthetünk anélkül, hogy ismernénk az Object Pascal részleteit, ugyanis a program kód nagy részét a Delphi automatikusan generálja. Azonban, ha ismerjük a részleteket, az hozzásegíthet a nyelv minél jobb megértéséhez. A következő alfejezetekben az objektumorientált programozás alapelveit fogjuk áttekinteni Delphi megközelítésben, anélkül, hogy nagyon bele mennénk a részletekbe.
1.2.1. OOP nyelvek jellemzői
12
Az objektum az OOP nyelv alapegysége, amely információkat tárol és a felhasználó kérésére feladatokat hajt végre. Egy objektumokra épülő OOP nyelv az előző fejezetben megfogalmazott modellezési elveken túl a következő alapelvekre épül: •
•
•
Egységbezárás (Encapsulation): Az adatokat és a hozzájuk tartozó eljárásokat egyetlen egységben kezeljük. Az információkhoz nem lehet hozzáférni csak a metódusokon keresztül, amelyek kommunikálnak a külvilággal. Öröklés (Inheritance): Az objektum-osztályok továbbfejlesztésének lehetősége. Ennek során a származtatott osztály örökli ősétől azok attribútumait, és metódusait, de ezeket bizonyos szabályok mellett újakkal egészítheti ki, és meg is változtathatja. Sokalakúság (Polymorphism): Lényege, hogy ugyanarra a kérésre a különböző objektumok különbözőképpen reagálnak. A származtatás során az ősosztályok metódusai képesek lesznek az új átdefiniált metódusok használatára újraírás nélkül is.
A következőkben azt tekintjük át, hogy a három alapelv, hogyan jelenik meg az Object Pascalban illetve Delphiben. 1.2.2. Osztály, objektum Az OOP nyelv két legfontosabb alapeleme. •
Osztály: egy felhasználó által definiált összetett adatszerkezet, amely ugyanolyan tulajdonsággal és viselkedéssel rendelkező objektumok osztályozására szolgál. Az osztály definiálja, hogy egy létrehozandó objektumpéldány, milyen attribútumokkal, illetve funkciókkal rendelkezzen.
•
Objektum: az osztály egy példánya, más szóval egy adott osztály által definiált típusú változó. Az objektum az osztály egyede, amely a program futásakor memóriát foglal le. A kapcsolat az objektum és osztály között hasonló, mint egy változó és annak típusa között.
Az Object Pascalban az osztályok definiálására az class kulcsszó áll rendelkezésre. Korábban az object kulcsszót használták erre a célra, amit a Delphi megtartott kompatibilitási okokból, de nem ajánlja használatát. Új osztály létrehozásának a szintaxisa a következő, amely tetszőleges számú adattagot és metódust tartalmazhat: type SajatOsztaly = class MezoNev1:tipus; MezoNev2:tipus; procedure EljarasNev; function FuggvenyNev:tipus; end;
13
A következő példában egy TAlkalmazott nevű osztályt definiálunk. type TAlkalmazott = class nev: string; fizetes: integer; procedure fizetestEmel(f: integer); procedure Kiir; end;
Megjegyzés: Delphiben konvenció, hogy minden osztálynév T betűvel kezdődik, természetesen ettől el lehet tekinteni. Ahhoz hogy a fenti osztályt használni tudjuk a metódusokat is definiálni kell. A metódusok definiálásánál az Object Pascal a „.” (pont) operátort használja. Szintaxisa: Osztalynev.Metodus. procedure TAlkalmazott.fizetestEmel(f: integer); begin fizetes:= fizetes + f; end; procedure TAlkalmazott.Kiir; begin ShowMessage(Format('%s: %d', [nev, fizetes])); end;
A Kiir eljárásban az alkalmazott nevét és fizetését íratjuk ki formázva. A metódusok definiálása után most már kipróbálhatjuk ezeknek a tesztelését az alábbi példa szerint: var alk: TAlkalmazott; begin alk.nev:= ”Kiss Istvan”; alk.fizetes:=1000; alk.fizetestEmel(500); alk.Kiir; end;
A fenti példából az is kiderül, hogyan hivatkozunk az osztály adatára vagy metódusára. Itt is a „.” operátort használjuk a következőképpen: objektum.adat, objektum.metodus. Annak ellenére, hogy a fenti kód helyes, mégsem működik jól. A probléma megértéséhez ismerni kell a Delphi objektum-hivatkozási modelljét, aminek az alapgondolata az, hogy az osztály minden példánya, mint például az alk a fenti kódrészletben, nem tárolja az objektum értékét, csak egy hivatkozást (mutatót), ami egy memóriaterületet jelöl, ahol az objektum tárolódik. Magát az objektumot nekünk kell létrehoznunk a Create metódus segítségével, melyet konstruktornak neveznek (ez az eljárás szolgál a memória lefoglalására és az objektum inicializálására). Az előző példa konstruktort használva: var alk: TAlkalmazott; begin alk:= TAlkalmazott.Create;
14
alk.nev:= ”Kiss Istvan”; alk.fizetes:=1000; alk.fizetestEmel(500); alk.Kiir; alk.Free; end;
Fontos észrevétel, hogy a konstruktor az osztályra vonatkozik és nem az objektumra. Másfelől felvetődik a kérdés, hogy honnan van a Create metódus? A Delphiben definiáltak egy TObject nevű osztályt, amely minden osztály ősosztálya, azaz minden osztály innen örököl. Tehát, ha egy osztályban nem definiálunk saját készítésű konstruktort, akkor a TObject osztály Create konstruktorát fogja örökölni. Miután az objektumot létrehoztuk és használtuk, meg is kell szüntetnünk a Free metódus segítségével.
1.2.3. Konstruktor és destruktor Az előző részben láthattuk, hogy a Create metódus segítségével memóriát foglaltunk le az objektum számára. Ahhoz hogy az objektumot tudjuk használni további inicializálásra van szükség. Ilyen esetekben saját készítésű konstruktorokat szoktunk definiálni. Annyi dolgunk van, hogy egy új eljárást kell definiálnunk, viszont most a procedure kulcsszó helyett a constructor szót használjuk. A konstruktor egy specifikus eljárás mivel a Delphi maga foglalja le annak az objektumnak a memóriát, amelyen a konstruktort meghívjuk. Lényeges, hogy a konstruktort az osztályon hívjuk meg, mert csak ebben az esetben történik memóriafoglalás az objektum számára. Ha objektumra hívjuk meg, csak a kód fut le memóriafoglalás nélkül és ez kiszámíthatatlan következményekkel járhat. Nézzük meg a korábbi osztály definíciót saját készítésű konstruktorral: type TAlkalmazott = class nev: string; fizetes: integer; constructor Create(n: string; f: integer); procedure fizetestEmel(f: integer); procedure Kiir; end;
A konstruktor definíciója történhet például az alábbi módon: constructor TAlkalmazott.Create(n: string; f: integer); begin nev:= n; fizetes:= f; end;
Megjegyzés: a konstruktor neve nem feltétlen kell Create legyen, lehet teljesen más nevet is adni. 15
Egy osztálynak nemcsak konstruktora lehet, hanem lehet destruktora is. Ez egy olyan eljárás, amelyet a destructor kulcsszóval deklarálunk, és ami felszabadíthat bizonyos erőforrásokat, mielőtt az objektum megsemmisül. Tehát a konstruktorral ellenkezőleg, a destruktor felszabadítja a memóriát. Delphiben a destruktor neve Destroy, de saját készítésű destruktor esetén adhatunk más nevet is. A különböző problémák elkerülése végett, ajánlott a Destroy helyett a Free metódust használni, ugyanis ez előbb leellenőrzi az objektum-mutatót, és csak akkor hívja meg a destruktort, ha az nem a nil mutató, azaz ha az objektum létezik.
1.2.4. Hozzáférés az adatokhoz Egy osztály bizonyos adatokkal és metódusokkal rendelkezik. Az OOP egyik lényeges alapelve, hogy az osztály adataihoz ne lehessen kívülről közvetlenül hozzáférni, azaz a külvilág számára elérhetetlen legyen. Magát az osztályt úgy kell elképzelni mint egy „fekete dobozt”, amiből csak egy kis rész látszik. Ezt a részt az osztály interfészének is nevezzük, ahol megtalálhatók azok a metódusok, amelyekkel az adatok módosíthatók. Delphiben a következő hozzáférési szinteket definiálták: • • • •
public: olyan adatmezőket és metódusokat jelöl, amiket a program bármely részéről elérhetünk private: az adatmezők és metódusok elérése csak az objektum-osztályon belül lehetséges. Az itt felsorolt adatmezők és metódusok kívülről nem érhetők el. protected: részben védett adatmezőket és metódusokat takar, amiket csak az adott osztályból és abból származó alosztályokból lehet elérni. published: az itt szereplő adatmezők és metódusok nem csak a program futásakor, de tervezési időben is elérhetők. Az alkalmazás szemszögéből hasonlóan bárhonnan láthatóak, mint a public részben szereplő adatmezők és metódusok.
Általában az osztály mezőinek célszerű private elérést biztosítani a metódusoknak pedig public elérést. Természetesen ez nem egy általános szabály és bizonyos esetekben lehet használni más hozzáférési szinteket. A Delphi komponensei általában rendelkeznek a published hozzáférési szinttel. Általában ez a hozzáférési szint nem tartalmaz mezőket vagy metódusokat csak egy új nyelvi elemet: a tulajdonságot (property). Erről részletesebben lesz szó a saját készítésű komponenseknél. Példaként tekintsük a TAlkalmazott osztályt kiegészítve hozzáférési szintekkel: type TAlkalmazott = class private nev: string; fizetes: integer; public constructor Create(n: string; f: integer);
16
procedure fizetestEmel(f: integer); procedure Kiir; end;
A fenti példában az adataink most már private elérésűek, ami azt jelenti, hogy kívülről csak a Kiir és a fizetestEmel eljárások segítségével érhetjük el. Az alábbi utasítás például hibát okozna, mivel a fizetes adattagot direkt hozzáféréssel akarjuk módosítani: var alk: TAlkalmazott; begin … alk.fizetes:=1000; … end;
1.2.5. Öröklés Az öröklés az OOP másik alapelve, amelynek az a lényege, hogy egy új létrehozandó osztályt, egy már létező másik osztályból (ősosztály vagy szűlőosztály) származtatunk. A származtatás akkor indokolt, ha az új osztály hasonlóan kell viselkedjen mint a régi. Ebben az esetben örökli a régi osztály adatait és metódusait kiegészítve saját adattal illetve metódussal. Ez a technika egyik nagy előnye a kód újrahasznosítás. Származtatás esetén a származtatott osztály definíciójában meg kell adni az őszosztályt az alábbiak szerint: type TForm1 = class(TForm) end;
A fenti osztálydefiníció azt jelenti, hogy a TForm1 osztály a TForm szülőosztály minden adatát és metódusát örökli. Példaként az öröklésre tekintsük a TAlkalmazott osztály egy TFonok származtatott osztályát, amely rendelkezni fog egy saját adattaggal és metódussal. Lesz saját konstrukora és a Kiir eljárás a beosztottak számát is kifogja írni: type TFonok = class(TAlkalmazott) private beosztottakSzama: integer; public constructor Create(n: string; f, b: integer); procedure beosztottakotNovel; procedure Kiir; end;
Az új eljárások programkódjai: constructor TFonok.Create(n: string; f,b: integer); begin inherited Create(n, f); beosztottakSzama:= b;
17
end; procedure TFonok.beosztottakotNovel; begin beosztottakSzama:= beosztottakSzama + 1; end; procedure TFonok.Kiir; begin ShowMessage(Format('%s: %d BeosztottakSzama = %d', [nev, fizetes, beosztottakSzama])); end;
A származtatott osztály konstruktora meghívja egyszer a szülőosztály konstruktorát, majd utána inicializálja az új adattagot. 1.2.6. Polimorfizmus A polimorfizmus (többalakúság) az OOP nyelvek harmadik tulajdonsága, amelynek lényege, hogy a különböző objektumok ugyanarra az üzenetre különbözőképpen reagálnak. A polimorfizmus leggyakrabban a metódusok felüldefiniálásával (overriding) valósul meg. Ez azt jelenti, hogy egy osztályhierarchia különböző osztályai egy ős osztálybeli metódust felüldefiniálhatnak, így ugyanaz a nevű metódus a különböző osztályokban más-más feladatot hajthat végre. A polimorfizmus egyik megvalósítási eszköze a dinamikus vagy futás alatti kötés. A futás alatti kötés az OOP nyelvek egy másik nagyon fontos tulajdonsága, amely azt jelenti, hogy a meghívott metódus címe nem fordításkór, hanem futásközben derül ki. A polimorfizmus lényegét egy egyszerű példán mutatjuk be. Tekintsünk egy alk nevű TAlkalmazott osztálybeli változót. Ez a változó a program futása során a TAlkalmazott és TFonok osztályok bármelyikének az objektumára hivatkozhat, ugyanis a főnök is egy alkalmazott. Most képzeljük el, hogy erre a változóra meghívjuk a Kiir eljárást például statikus kötés esetén. Ebben az esetben biztosan a TAlkalmazott osztály Kiir metódusa fog lefutni, ugyanis az eljárás címe már fordításkor el volt döntve. Ha dinamikus kötést alkalmazunk, akkor két ugyanolyan nevű metódus közül, hogy melyik osztály metódusa lesz meghívva, mindig a program futása alatt dől el a konkrét helyzettől függően. A dinamikus kötést a virtual és dynamic kulcsszavak használatával definiálhatjuk. A két módszer között csak megvalósításbeli különbségek vannak. A megfelelő metódust az ős osztályban ellátjuk a virtual vagy a dynamic kúlcsszavakkal, valamint a leszármazott osztályokban az override kulcsszóval lássuk el. type TAlkalmazott = class … procedure Kiir; virtual; … end; TFonok = class(TAlkalmazott) … procedure Kiir; override; …
18
end;
Megjegyzés: statikus metódus újradefiniálásakor elegendő, ha létrehozunk az alosztályban egy ugyanolyan nevű metódust, aminek a paraméterezése megegyezhet vagy eltérhet az eredetitől, a virtuális metódus felülírásakor a paraméter listának meg kell egyeznie. 1.2.7. Típusellenőrzés és típuskonverzió Az Object Pascalban ismert tény, hogy egy ősosztálybeli objektumnak értékül adhatunk egy származtatott osztálybeli objektumot. Ez valahol természetes viselkedés ugyanis, a származtatott osztály örökli az ősosztály adattagjait és metódusait is. Tehát a gyermek-osztály kompatibilis a szülőosztállyal. Fordítva nem igaz. Például az alábbi műveletek érvényesek: var alk1, alk2: TAlkalmazott; fonok:TFonok; begin alk1:= TAlkalmazott.Create(...); alk2:= TFonok.Create(...); fonok:= TFonok.Create(...); alk1:= fonok; ...
Korábban már láttuk, hogy a TFonok származtatott osztálynak van egy beosztottakotNovel metódusa, amit az ősosztály nem tartalmaz. Ha egy TAlkalmazott osztálybeli változó egy főnökre vonatkozik, akkor nyugodtan meghívhatjuk az előbbi metódust, különben bajban vagyunk. A probléma megoldására a Delphi futás idejű típus információn alapuló technikáját használhatjuk. Arról van szó, hogy az is operátor segítségével megállapíthatjuk, hogy egy adott objektum egy adott osztályból származik-e. if
alk1 is TFonok then …
A then ágra akkor kerül a vezérlés, ha az alk1 objektum egy TFonok vagy annak valamelyik leszármazott osztályából létrehozott objektum. Az alk1 objektumra még mindig nem hívhatjuk a beosztottakotNovel metódust, ugyanis az is operátor csak egy ellenőrző függvény, nem alakítja át a TAlkalmazottat-at TFonok-é. A probléma megoldására alkalmazható az as típuskonverziós operátor. Használata: objektumnév AS osztálynév. Az előbbi példára alkalmazva: if
alk1 is TFonok then (alk1 as TFonok).beosztottakotNovel(...)
Lényeges, hogy az as operátort az is típusellenőrző operátor után használjuk, különben futás közbeni üzenetet kaphatunk. 1.2.8. A Self kúlcsszó
19
A Self egy alapértelmezett paraméter, amely átadódik minden meghívott metódusnak. Mindig az aktuális objektumpéldányra mutat és ezt a mutatót használja a metódus az adott objektum egyedeinek az elérésére. Korábban már használtuk az alábbi függvényt: procedure TAlkalmazott.fizetestEmel(f: integer); begin fizetes:= fizetes + f; end;
Tulajdonképpen a fenti függvényt a Delphi a következőképpen értelmezi: procedure TAlkalmazott.fizetestEmel(f: integer); begin Self.fizetes:= Self.fizetes + f; end;
A Self igazából egy nyelvi elem, amelyet a rendszer fordításkor használ, de számos esetben a programozó is használja különböző problémák megoldására. Ilyen például az objektumok dinamikus létrehozása, amelyre egy példa található a 9. Fejezetben.
1.2.9. Osztályváltozók és osztálymetódusok Objektumok létrehozásakor az osztály mezői minden egyedhez hozzáadódnak. Néha azonban szükség van olyan mezőkre, amelyeket az összes objektum közösen tud használni. Az ilyen típusú mezőket osztályváltozónak vagy globális változónak hívunk. Az osztályváltozókat az Object Pascalban, az implemantation részben kell deklarálni és kezdeti értéket lehet adni az inicialization részben. Az osztályváltozókat az egységen kívül kétféleképpen érhetjük el. Az első esetben az osztály egy metódusát használva úgy, hogy létrehozunk egy objektumpéldányt és erre meghívjuk az említett metódust. A másik megoldás, amikor osztálymetódust használunk. Az ilyen metódus az osztályhoz és nem annak valamely objektumához kapcsolódik. Az osztálymetódust az Object Pascalban a function, vagy a procedure szavak elé írt class szóval deklaráljuk: type MyClass = class class function fuggvenyNev: tipus;
Így most már példányosítás nélkül is tudjuk hívni az osztálymetódust: MyClass.fuggvenyNev
1.3. A Delphi alkalmazások felépítése Minden Delphi alkalmazás a következő összetevőkből áll: 20
• •
Főprogram vagy projektállomány, amely egy DPR (Delphi PRoject) kiterjesztésű állomány. Ez fogja össze az alkalmazáshoz tartozó valamennyi állományt (1.3.1. ábra). A Delphi alkalmazás egy vagy több ablakot tartalmaz. Minden ablakhoz két állomány tartozik: o Az ablak és a rajta levő komponensek, objektumok szerkezetét, tulajdonságait leíró DFM (Delphi ForM) kiterjesztésű állomány: űrlapterv. Magát az ablakot űrlapnak (Form) hívjuk. o Az ablak és a rajta levő komponensek, objektumok viselkedését (metódusok: eljárások és függvények segítségével) leíró PAS kiterjesztésű állomány: egység (unit).
1.3.1. ábra. A Delphi alkalmazás felépítése
•
•
Egy alkalmazás tartalmazhat olyan egységeket is, amelyek nem egy ablak viselkedését írják le, hanem a Pascal egységekhez hasonlóan, rutin és adatkönyvtárakat tartalmaznak. Ezekben olyan globális változókat, eljárásokat és függvényeket tárolhatunk, amelyeket az egész alkalmazásban lehet használni. Az alkalmazások tartalmazhatnak különböző külső erőforrásokat is: o Ikon-állományokat (ICO) o Kurzor-állományokat (CUR, ANI) o Kép-állományokat (BMP) o Súgó-állományokat (HLP) o A fenti erőforrás-állományokat egy közös RES (RESource) kiterjesztésű állományban is el lehet helyezni a Delphihez tartozó Image Editor segédprogram segítségével
Az alkalmazás fordításakor és a programszerkesztéskor (compiling and linking) a PAS kiterjesztésű egységekből DCU (Delphi Compilled Unit) kiterjesztésű állományok jönnek létre, amelyek a fent felsorolt erőforrás-állományokkal illetve az űrlaptervekkel együtt egy EXE kiterjesztésű, futtatható állományba szerkesztődnek össze.
21
1.3.2. ábra. A futtatható EXE állomány létrehozása az alkalmazás állományaiból.
Fontos, hogy minden Delphi alkalmazást külön könyvtárban tároljunk, így könnyebben tudjuk azonosítani az egy programhoz tartozó állományokat. A továbbiakban röviden átnézzük a fontosabb összetevők szerkezetét. 1.3.1. A projektállomány szerkezete Egy új alkalmazásnál a Delphi létrehoz egy projektállományt, egy űrlapot és a hozzátartozó egységet. A projektállomány semmiben nem különbözik egy sima Pascal programtól. Tartalmazza a programfejet, hivatkozási részt és a végreható részt. Egy alkalmazás főprogramja, a Project/View Source menüpont alatt tekinthető meg és egy üres alkalmazás esetén az alábbi kódot tartalmazza: program Project1; uses Forms, Unit1 in 'Unit1.pas' {Form1}; {$R *.res} begin Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run; end.
A fenti kód tartalmaz egy inicializáló részt, ezt követi az űrlap létrehozása és végül az alkalmazás üzenetkezelő ciklusa.
1.3.2. Az űrlapterv szerkezete Egy Delphi alkalmazás minden űrlapjához tartozik egy DFM kiterjesztésű állomány, amely az űrlap és a rajta elhelyezkedő egyéb objektumok tulajdonságait tárolja bináris állomány formájában. Az állomány megtekinthető szöveges formában, ha az űrlapon a jobb egérgombbal kattintva a felugró menüből kiválasszuk a View as Text parancsot. Egy üres alkalmazás űrlapjának leíró állománya: 22
object Form1: TForm1 Left = 239 Top = 129 Width = 870 Height = 500 Caption = 'Form1' Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] OldCreateOrder = False PixelsPerInch = 96 TextHeight = 13 end
1.3.3. Az űrlaphoz tartozó egység Az Object Pascal az osztályokat úgynevezett egységekben (unit) definiálja. Egy egység tulajdonképpen egy állomány, amely különböző szerkezeti elemeket tartalmaz. A Delphi szintén egységek segítségével definiálja az osztályokat és gyakorlatilag minden űrlaphoz generál egy egységet az alábbiak szerint: unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs; type TForm1 = class(TForm) private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} end.
Az illesztő részben, az interface kulcsszó után definiálhatjuk azt, amit más programrészek is láthatnak. Az itt nem definiált részek csak az adott unit számára elérhetők. Itt kell megadni azt is, hogy melyik más unitok tartalmát akarjuk használni. Ezt kell felsorolni a uses kulcsszó után. A deklarációs részben (type, var) a unit számára látható típus-, 23
változó-, konstans- és címkedeklarációkat kell megadnunk. A kifejtő (implementation) részben az unitot alkotó eljárásokat, függvényeket kell definiálni. Ezek közül kifelé csak azok láthatók, amelyek az interface részben voltak deklarálva. 1.4. A Delphi fejlesztői környezet A Delphi indításakor létrejön egy Project1 nevű főprogram és egy Form1 nevű űrlap (ablak).
1.4.1. ábra. A fejlesztői környezet elemei
A komponens paletta tartalmazza a különböző objektumokat, amelyeket elhelyezhetünk az űrlapokon. Ezek a komponensek több lapon vannak csoportosítva: Standard, Additional, Win32, stb. A pontozott ablak az űrlap. A pontozottság jelenti azt, hogy tervező nézetben vagyunk, ilyenkor és erre az ablakra helyezzük el a fent említett komponenseket. A kódszerkesztő ablak tartalmazza az egységek forráskódjait. A View menü Units parancsa segítségével választhatjuk ki, hogy melyik egység kódját jelenítjük meg és itt van lehetőségünk a főprogram forráskódjának megjelenítésére is. Az űrlap és a kódszerkesztő ablak általában takarják egymást, váltani a kettő között (el és vissza) az F12-es billentyűvel lehet. Fontos, hogy ne valamelyik ablakot zárjuk be, hogy hozzáférhessünk az alatta levőhöz, hanem a fent említett billentyűt használjuk! Baloldalt található az Objektum-felügyelő (Object Inspector), ami ha rejtve van, akkor az F11 billentyűvel jeleníthető meg. A Properties lapján lehet megtekinteni, illetve megváltoztatni az űrlap és a rajta elhelyezett komponensek tulajdonságait. Az Events lap tartalmazza azoknak az eseményeknek a listáját, amelyek az ablakkal illetve a rajta elhelyezett objektumokkal bekövetkezhetnek. A megfelelő esemény melletti üres mezőben dupla kattintással hozhatunk létre egy új eljárást (1.4.2. ábra). Ebbe az eljárásba beírt kódot (programrészt) a program minden alkalommal, amikor az esemény 24
bekövetkezik végrehajtja. Egy eseményhez rendelhetünk már létező metódust is, ilyenkor nem duplán kattintunk a mezőre, hanem a legördülő listát lenyitjuk és kiválasztjuk a megfelelő eljárást vagy függvényt.
1.4.2. ábra. A Button1 feliratú és nevű gomb kattintási eseményéhez (OnClick) rendelt eljárás.
Az alkalmazást futtatni az F9 billentyűvel lehet vagy az eszköztáron található jobbra mutató, zöld színű gomb segítségével. Fontos, hogy futtatás után a működő program ablakát zárjuk be és csak ezután folytassuk a program szerkesztését! Ha ezt nem tesszük meg, nem áll rendelkezésünkre az objektum felügyelő, sőt a program is lefagyhat, ilyenkor már csak a kódszerkesztő ablakban a CTRL-F2 billentyű-kombináció segítségével tudjuk bezárni az alkalmazást. Fontos lépés az alkalmazás megfelelő lementése is. Ajánlott a File menü Save All parancsát használva végezni ezt a műveletet, mert ez a parancs az, amely lementi az alkalmazás összes állományát. Mentéskor ügyeljünk arra is, hogy mindig hozzuk létre az új könyvtárat az alkalmazás számára, majd ebbe a könyvtárba belépve a Save gomb többszöri kattintásával mentsük le az egységeket, illetve a főprogramot. A Save és Save As parancs csak az aktuális egységet menti le, a Save Project As paranccsal pedig csak a főprogram lementésére van lehetőségünk. Létező alkalmazást megnyitni a DPR állományra való dupla kattintással, illetve Delphiből a File menü Open Project parancsával lehet. Ha csak egy egységet nyitunk meg (a PAS kiterjesztésű állományra kattintunk, vagy Delphiből az Open paranccsal ezt nyitjuk meg és nem a főprogramot), akkor ugyanúgy megjelenik az egységhez tartozó űrlap is illetve az egység forráskódja is, de ilyenkor a program nem futtatható. Lehetőségünk van lépésenkénti futtatásra is (F7 illetve F8 billentyűkkel), ilyenkor a Run menü Add Watch parancsával megjelenített ablakba beírt változók, illetve kifejezések értékeinek változását követhetjük figyelemmel a megjelenő Watchlist ablakban. 1.4.1. Űrlapok tervezése Korábban már láttuk, hogy az űrlapok a Delphi alkalmazások központi elemei. Alkalmazásunk külalakja nagymértékben függ ezeknek az objektumoknak a megtervezésétől. A továbbiakban néhány hasznos technikát mutatunk be az űrlapok tervezésével kapcsolatosan. 25
Egy új alkalmazás létrehozásakor alapértelmezetten létrejön egy űrlap. Természetesen lehetőségünk van további űrlapok létrehozására a File/New/Form menü alatt. Az űrlapokat úgy is el lehet képzelni, mint komponenstárolók, azaz több komponenst helyezhetünk el rajtuk. Egy komponenst az objektum palettáról többféleképpen is az űrlapra helyezhetünk: • A komponensre kattintva, vigyük az egeret az űrlapra. Itt a bal egérgomb lenyomásával húzhatjuk az egeret a komponens méretének a beállítására • Először kattintsunk a komponensre, majd az űrlap egy pozíciójába. A komponens meg fog jelenni az alapértelmezett méretekkel. • Duplán kattintva a komponenspalettán, a komponens megjelenik az űrlap közepén • Néha előfordul, hogy egy komponensből több példányt is az űrlapra kell helyezni. Ilyenkor a Shift gombot nyomva tartva kattintsunk a komponensre, majd utána az űrlapra annyiszor, ahány példányra szükségünk van. Gyakran előfordul, hogy űrlapunkon több komponenst is elhelyezünk. Ezeknek az elemeknek az elhelyezését megoldhatjuk az egér segítségével is, ugyanis az űrlapon tervezési nézetben látható pontozott rács nagyban segít. A komponensek igazítását megoldhatjuk a View menü Alignment Palette parancsa segítségével is. Tulajdonképpen ez egy eszköztár, igazítást segítő gombokkal, amellyel lehet vízszintesen, függőlegesen, középre igazítani.
1.4.1.1. ábra. Az Alignment Palette eszköztár
Az űrlapra helyezett komponenseknek van két fontos tulajdonsága: TabStop, TabOrder. A TabOrder tulajdonság egy szám, ami azt mondja meg, hogy a TAB billentyű leütésekor melyik objektum kapja meg a fókuszt. Lényeges, hogy egy komponens csak akkor fókuszálható, ha a TabStop tulajdonsága igaz (ez az alapértelmezett érték). E két tulajdonság hasznos lehet olyan űrlapok tervezésénél, ahol például valamilyen adatokat kell bevinni egy adatbázisba. Mint más objektumok, az űrlapok is számos tulajdonsággal és metódussal rendelkeznek. Nagyon gyakran alkalmazásainkban beállítjuk a Caption és Name jellemzőket. Az utóbbit azért szükséges beállítani, ugyanis egy nagyméretű alkalmazásban könnyebb az általunk megadott nevekkel dolgozni. Itt érdemes alkalmazni jelölési konvenciókat. Például az űrlap neve lehet frmAlkalmazottak, ahol a név első három betűje az objektum elnevezéséből származik. Érdekes, ha a Name jellemzőt a Caption előtt állítjuk be, akkor a Caption felveszi a Name értékét. Egy másik tulajdonság, ami lényegesen befolyásolja az űrlap kinézetét: a FormStyle. Lehetséges értékei: o fsNormal: az űrlap egy normál ablak, ez az alapértelmezett érték o fsMDIChild: az űrlap egy MDI gyerekablak o fsMDIForm: az űrlap egy MDI szülőablak o fsStayOnTop: az űrlap az összes többi ablakot eltakarja 26
Az MDI típusú ablakokkal részletesen foglalkozunk a 4. Fejezetben. 1.5. Delphi adattípusok Pascal nyelvben található bizonyos adattípusokat a Delphi egyrészt kiterjesztette, másrészt pedig számos új típust vezetett be. A fejezetben ezeket az új típusokat mutatjuk be. Egész -és valós adattípusok Az egész és valós adattípusokat tekintve a Pascal nyelvhez képest számos újdonságot tartalmaz a Delphi. Az alap egész típus az Integer, amely 4 byte méretű. Az Integer típusnak létezik egy megfelelő előjel nélküli változata a Cardinal. E két típus mérete operációs rendszer függő, így nem garantált méretük. A LongInt (előjeles) típus mérete garantált, amely mérete megegyezik az előző két típus méretével. Nagyon nagy méretű egész számok tárolására használhatjuk az int64 típust. Az int64 típus 8 byte-on tárolja az értékeket és ez a méret is garantált. A valós típusok esetén használható a Single (4 byte), Double (8 byte), Extended (10 byte) típusok. Létezik egy Currency (8 byte) típus is gazdasági problémákra, 4 helyi értékkel és 53 bites pontossággal. Tömbök Delphiben Delphiben két típusú tömb létezik: statikus, dinamikus. A statikus tömb nem más mint a jól ismert Pascal tömb. Lényege, hogy mérete előre ismert és a statikus memóriában foglal helyet. A dinamikus tömböknek nincsen méretük deklaráláskor. Méretüket futás közben változtathatjuk a SetLength eljárás segítségével. Olyan esetekben használjuk, ha előre nem tudjuk eldönteni a szükséges méretet. Lényeges, hogy a tömb a 0 indextől kezdődik. Példa: var dtomb: array of string; begin SetLength(dtomb, 3); end;
A fenti példában egy dinamikus karakterlánc tömböt deklaráltunk, amelynek méretét futás közben háromra növeljük. Létezik Delphiben egy másik típusú tömb, amelyet függvények és eljárások paramétereként használunk. Ezek a nyitott tömbök (open array). A nyitott tömbök használatára tekintsük az alábbi példát: procedure Listaz(otomb: array of integer) var i:integer; begin for i:=Low(otomb) to High(otomb) do Writeln(otomb[i]);
27
end
A fenti eljárás a tömb méretétül függetlenül jól működik minden egész típusú tömbre. A tömb méretét a Low és High függvények segítségével kérdezzük le. Habár a nyitott tömbök szintaxisa megegyezik a dinamikus tömbökével, a kettőt nem szabad összetéveszteni. A nyitott tömböknek létezik egy speciális esete a konstans tömbök: array of const. Lényege, hogy különböző típusú adatokat (string, integer, float, stb.) képes tárolni tetszőleges számban. Klasszikus példa a Delphi Format nevű függvénye, amely segítségével adatainkat formázni tudjuk: function string;
Format(const
Format:
string;
const
Args:
array
of
const):
Példa: Format(’%s egy sztring, %d egy egesz szam’, [’string’, 10]);
Karakterláncok A Delphi négy típusú karakterláncot képes kezelni: o Pascal típusú karakterlánc (ShortString) o Hosszú karakterláncok o Long String (String) típusú karakterlánc o Wide String (WideString) típusú karakterlánc o C típusú karakterláncok A ShortString típus megfelel a hagyományos Pascal karakterláncnak. Maximális hosszúsága 255 lehet. A Delphi kompatibilitási okokból tartotta meg ezt a típust. A Long (Ansi) String típusú karakterlánc, változó hosszúságú karakterláncot jelent. Az ilyen típusú karakterláncok számára a memória dinamikusan lesz lefoglalva, ami azt jelenti, hogy méretüket az aktuális memória mérete határozza meg. A 32-bites Delphi verziók alapértelmezetten ezt a karakterlánc típust használják. Példa:
var s: String; s := ’Az s karakterlanc tetszoleges meretu lehet...’;
Ez a típus esetén nem használható az s[0] a karakterlánc hosszának a lekérdezésére. A WideString típus hasonló a Long String típushoz, azzal a különbséggel, hogy itt a karakterlánc elemei WideChar típusúak. A WideChar típus, 16-bites Unicode típusú karaktert jelent. A C típusú karakterlánc egy nulla alsó indexű karaktertömb. A karaktertömb végét a nulla értékű bájt jelzi. Ez azt jelenti, hogy nincs különbség egy nulla végződésű karakterlánc és egy nulla végződésű karaktertömb ( array[0..meret] ) között. Többnyire a Windows API függvényeknél használjuk a nulla végződésű karakterláncokat. 28
Variant típus Egy flexibilis, általános célú adattípus, amely tetszőleges típusú adatot képes tárolni. A Variant típusa és értéke mindig a program futása közben dől el, ami erősen befolyásolja a program sebességét. Ezen kívül rontja a program olvashatóságát, tehát csak indokolt esetben használjuk. Példa: var v:variant; s:string; begin v:= 10; v:= ’Delphi’; v:= 45.5; s:= v; end;
A fenti példában az is látszik, hogy egy variant változó értéke egy vele inkompatibilis típusba is átmásolható. A konverziót a Delphi automatikusan elvégzi.
2. DELPHI KOMPONENSEK Előzőleg már említettük, hogy a Delphi egy komponens alapú fejlesztő eszköz. A komponens vagy más néven vezérlőelem egy osztály, amely valamely látható vagy nem látható erőforrás interface-ét valósítja meg. Tulajdonképpen egy elem, amelyet csatolhatunk a Delphi rendszerébe, ami azt jelenti, hogy megjelenik a komponens palettán, feltehető az űrlapra, beállítható a tulajdonságai tervezési időben. 2.1. A komponensek osztályhierarchiája A Delphi rendelkezik egy használatra kész osztályhalmazzal, amelyek egy része komponens osztály és megtalálható a komponens palettán. A komponensek egy vizuális komponenskönyvtárban (VCL-Visual Component Library) van összegyűjtve. A Delphi rendszer működését egy jól megtervezett osztályhierarchia biztosítja. A következőkben áttekintjük röviden a fontosabb komponensek osztályhierarchiában elfoglalt helyüket, a saját és örökölt tulajdonságait, metódusait, viselkedésüket, illetve használatukat. Az osztályhierarchia csúcsában a TObject osztály áll, ami azt jelenti, hogy minden osztály őse. A VCL hierarchia három fő részre osztható: komponensek, általános objektumok, kivételek. Mi többnyire csak a komponensek bemutatásával foglalkozunk. A komponensek a Delphi alkalmazás központi összetevői és a TComponent osztály leszármazottai. A TComponent osztály a TPersistent osztálytól örököl, ezáltal a komponensek átirányíthatóak DFM állományokba. A TPersistent osztály viszonylag kevés kódot tartalmaz és segítségével megvalósítható a vizuális programozás lényege, azaz a különböző objektumok kimenthetők állományokba, így később visszaállíthatók és 29
újra használhatók. A következő ábrán az ősosztályból származó osztályok egy része látható:
2.1.1. ábra. A TObject osztály és leszármazottai
A komponensek különálló csoportokba sorolhatók bizonyos szempontok alapján. Két nagy csoport létezik: • Kontrol komponensek vagy vizuális komponensek, amelyek a TControl osztály leszármazottai. Ezeknek a komponenseknek adott méretük és helyzetük van az űrlapon. Két fajtájuk van: ablak alapú és nem ablak alapú komponens. • Nem vizuális komponensek, mindazok amelyek a TComponent, de nem a TControl leszármazottai. A 2.1.2 ábra a komponensek osztályhierarchiájának egy részét mutatja.
2.1.2. ábra. A Delphi komponens osztályhierarchia egy része
A továbbiakban a komponensek fontosabb tulajdonságait és metódusait mutatjuk be. A TComponent osztályban van definiálva két fontos tulajdonság (amit minden komponensben megtalálunk, mert innen öröklődik) a Name és az Owner. A Name a komponensek programbeli nevét jelenti. Csak tervezési időben lehet megváltoztatni. Amikor egy komponenst elhelyezünk az űrlapunkon, akkor a megfelelő
30
egységben, mint az űrlap osztály (TForm1) tulajdonsága, a Name-ben megadott nevű változóként deklarálódik (2.1.3. ábra).
2.1.3. ábra. A komponens nevének megadása
Ha a nevet változtatjuk, akkor általában az objektum felirata is változik, ezért vigyázzunk arra, hogy nem ez a tulajdonság az, ami az objektumok feliratáért felel (ez általában a Caption vagy a Text)! Megtörténhet, hogy csak a feliratot szeretnénk megváltoztatni vagy letörölni egy komponensről és véletlenül a nevét változtatjuk meg vagy tüntetjük el, amire ezután már nem tudunk hivatkozni (vagy a változtatás előtt használt hivatkozásoknál fog hibákat jelezni a fordító). Az Owner tulajdonság az objektum tulajdonosát jelenti. A tulajdonos feladata, hogy megszűnésekor megszüntesse a tulajdonában levő elemeket is. Például a főűrlap tulajdonosa az alkalmazás, az űrlapokra elhelyezett komponensek tulajdonosa pedig maga az űrlap. Amint már láttuk korábban, a TComponent osztályból származnak a láthatatlan komponensek (TMainMenu, TPopupMenu, TTimer, stb.) illetve a látható komponensek közös ősosztálya a TControl. A TControl fontosabb tulajdonságai: • Parent: a komponens szülőobjektuma. Hasonló az Owner tulajdonsághoz, mindkettő a komponens „főnöke”, a szülő viszont azt az objektumot jelenti, amelyiken elhelyezzük az új komponenst (2.1.4. ábra). A Parent tulajdonság felelős a komponens megjelenítéséért is. • Align: a szülőkomponensen belüli igazítást jelenti. A lenyíló listából választható értékek: o alNone: nincs igazítás, ezzel tudjuk megszüntetni a korábban o megadott igazítást o alTop, alBottom, alLeft, alRight: a szülőobjektum felső, alsó, bal és jobb széléhez való igazítás
31
o alClient: kitöltő igazítás, a komponens alakja teljesen kitölti a szülőobjektum alakját, vagy a fennmaradó részt (ha a szülőobjektumon vannak más objektumok is valamelyik szélhez igazítva) Űrlapnál szülőobjektumnak lehet tekinteni a képernyőt, ehhez történik az igazítás, más komponenseknél az űrlap, vagy az a komponens, amelyen el van helyezve. A 2.1.4. ábrán látható Button2 gombot, ha például balra igazítanánk, akkor a Panel1 objektum bal széléhez „ragadna hozzá”, míg a Button1-et a Form1 nevű űrlap széléhez.
2.1.4. ábra. A tulajdonos és a szülő közötti különbség
• •
Top és Left: a komponens szülőobjektumán belüli pozícióját jelenti: a Top a felső szélétől mért távolság képpontokban (pixel) megadva, a Left pedig a bal szélétől. Height és Width: a komponens magassága és szélessége képpontokban mérve.
2.1.5. ábra. A Panel1 komponens pozíciója és méretei
•
Enabled: boolean típusú tulajdonság, ha értéke True, akkor a komponens fogadja az üzeneteket, ha False, akkor nem. Utóbbi esetben a komponens felirata halványabban látszik, ez jelzi, hogy a komponens nincs „működő” állapotban
32
• • •
•
Visible: boolean típusú tulajdonság, ha értéke True, akkor a komponens látszik, ha False, akkor nem. Csak a program futásakor lép érvénybe, tervezési időben a komponens nem tűnik el Color: az objektum színe, a lenyíló listából kell kiválasztani értékét. Delphiben a színeket konstansok jelölik: clRed, clBlue, clGreen, stb. Font: a komponensen levő felirat tulajdonságait jelenti. Ilyen tulajdonság a font neve: Font.Name (Páldául: „Arial”, „Times New Roman”, stb.), mérete: Font.Size, színe: Font.Color és stílusa: Font.Style (fsBold, fsItalic, fsUnderline) PopupMenu: az objektumhoz rendelt gyorsmenü
Az eseménykezelés a Delphi rendszernek egy nagyon fontos eleme, amely a Windows esemény-vezérelt modelljét követi. Egy esemény akkor következik be, ha egy program felhasználója hat a rendszerre. Ezt a külső történést, a Windows rendszer fogadja és üzenetté alakítja. Az esemény-vezérelt modell lényege, hogy nem kell egy adott esemény bekövetkezését folyamatosan figyelni, hanem, a program a Windows üzenetek segítségével automatikusan meghívja a kívánt eseményt a megfelelő időben. Ilyen esemény például, ha a felhasználó kattint egy gombra. A Delphiben található komponensek számos eseményre képesek reagálni, amelyeket a következő részben fogunk bemutatni. A TControl osztály fontosabb eseményei: • OnClick, OnDblClick: egérrel való kattintás illetve dupla kattintás. Például, ha kattintunk egy gombra, akkor annál a gombnál bekövetkezik az OnClick esemény. Ha ehhez nem rendeltünk semmit, akkor nem fog történni semmi, ha viszont hozzárendeltünk egy eljárást, akkor minden alkalommal, amikor a gombra kattintunk, végrehajtódik ez az eljárás • OnMouseDown, OnMouseUp, OnMouseMove: akkor következnek be, ha az egeret lenyomjuk, felengedjük, vagy mozgatjuk az objektumon • OnDragDrop,OnDragOver, OnEndDrag, OnStartDrag: A Drag&Drop (vonszolás) technika alkalmazásakor bekövetkező események Az eseményekhez rendelt eljárások különböző paraméterekkel is rendelkeznek. Például az OnMouseDown eseményhez rendelt eljárásban paraméterként továbbítódnak az egér kurzor aktuális pozicíójának koordinátái. Ha fontos, hogy az egeret hol nyomták le, melyik pozícióban, akkor ezzel az eseménnyel kell dolgoznunk, ha nem akkor elég az OnClick esemény is. Ha az is fontos, hogy az egeret hol engedtük fel, akkor természetesen az OnMouseUp eseményt kell használni, ha nem akkor az OnClick itt is elég, mert az kattintást, tehát lenyomást és felengedést is jelent. Egy másik fontos paraméter a Sender. Ez jelenti azt az objektumot, amelyikkel bekövetkezik az esemény és amelyik meghívja ezt az eljárást. Megtehetjük, hogy ugyanazt az eljárást rendeljük több objektum különböző eseményeihez. Ilyenkor a Sender paraméter segítségével kérdezhető le az eljárásban, hogy éppen melyik objektum hívta meg azt. A komponensek közös őseinek és az itt található (innen öröklődő) tulajdonságok és események áttekintése után konkrét komponensek tulajdonságaival, metódusaival, és eseményeivel fogunk megismerkedni a következő fejezetekben.
33
2.2. Standard komponensek •
TLabel – Címke
2.2.1. ábra. A TLabel helye a Standard komponens-palettán
o Leírás, szerep: passzív (nem szerkeszthető) szöveg megjelenítése o Fontosabb jellemzők: Caption: a címke szövegét tartalmazó String típusú tulajdonság Transparent: átlátszó-e vagy sem a szövegdoboz, típusa logikai
2.2.2. ábra. A Label1 nem átlátszó, a Label2 igen
•
TWinControl
Az ablakozott komponensek (az üzenetekre ablakszerűen reagáló objektumok) közös őse. Az ilyen objektumok fókuszálhatók. Ez azt jelenti, hogy az objektum aktívvá válik, például egy szövegmezőbe csak ilyenkor tudunk szöveget írni, vagy, ha egy gomb fókuszált, akkor az Enter billentyű hatására lenyomódik, stb. A fokuszálást, a program működése közben interaktívan el lehet végezni az egérrel (kattintunk a megfelelő objetumba/-ra) vagy a Tab billentyű sorozatos lenyomásával: sorban fókuszba helyezzük a különböző objektumokat. A fókuszálás elvégezhető a programon belülről is a SetFocus metódus segítségével. o Fontosabb jellemzők: TabOrder: a fókuszálás sorrendjét jelenti (0-tól számozva) TabStop: False-ra állítva értékét, a fokuszálás elmarad o Fontosabb események: OnEnter: fókuszáláskor (az objektumba való belépéskór) következik be OnExit: a fókusz elvesztésekor (az objektum elhagyásakor) következik be OnKeyPress: egy billentyű leütésekor következik be OnKeyDown: egy billentyű lenyomásakor következik be OnKeyUp: egy billentyű felengedésekor következik be Megjegyzés: Az utolsó három eseményhez rendelt eljárás fontos paramétere a Key változó, ami az adott billentyű kódját tartalmazza. •
TEdit – Szövegmező, szerkesztő-doboz 34
2.2.3. ábra. A TEdit helye a Standard komponens-palettán
o Leírás, szerep: adat (egysoros szöveges információ) megjelenítése és bekérése. Ha numerikus értékek kiírására vagy beolvasására használjuk, alkalmazni kell a konvertáló függvényeket (StrToInt, StrToFloat, IntToStr, FloatToStr) o Fontosabb jellemzők: Text: a szövegmező szövegét tartalmazza MaxLength: a maximálisan megengedett karakterek száma o Fontosabb metódusok: SelectAll: kijelöli a teljes szöveget ClearSelection: törli a kijelölt szöveget Clear: törli a teljes szöveget CutToClipboard, CopyToClipboard, PasteFromClipboard: a Cut/Copy/Paste (kivágás/másolás/beillesztés) műveletek elvégzése a kijelölt szövegrésszel o Fontosabb események: OnClick: rákattintáskor következik be OnChange: a szöveg-doboz tartalmának változásakor következik be •
TMemo – többsoros szövegmező
2.2.4. ábra. A TMemo helye a Standard komponens-palettán
o Leírás, szerep: többsoros szöveg szerkesztése o Fontosabb jellemzők: Text: a teljes szöveg Lines: TStrings típusú tulajdonság, amely az objektum sorait tárolja (Lines[0] jelenti az első sort, Lines[1] a másodikat, stb.) MaxLength: a maximálisan megengedett karakterek száma SelText: a kijelölt szöveg SelStart: a kijelölés kezdete SelLength: a kijelölés hossza o Fontosabb metódusok: SelectAll: kijelöli a teljes szöveget ClearSelection: törli a kijelölt szöveget Clear: törli a teljes szöveget CutToClipboard, CopyToClipboard, PasteFromClipboard: a Cut/Copy/Paste (kivágás/másolás/beillesztés) műveletek elvégzése a kijelölt szövegrésszel 35
Lines.SaveToFile(szövegállománynév): a memo sorait lementi a paraméterként megadott szövegállományba Lines.LoadFromFile(szövegállománynév): a memo sorait feltölti a paraméterként megadott szövegállományból o Fontosabb események: OnClick, OnChange •
TButton – Gomb
2.2.5. ábra. A TButton helye a Standard komponens-palettán
o Leírás, szerep: kattintható gomb, kattintásra induló tevékenység elvégzésére használjuk o Fontosabb jellemzők: Caption: a gomb felirata o Fontosabb események: OnClick •
TCheckBox – Jelölőnégyzet
2.2.6. ábra. A TCheckBox helye a Standard komponens-palettán
o Leírás, szerep: két állapotú értékek (Igaz/Hamis, Igen/Nem, stb.) jelölésére használjuk o Fontosabb jellemzők: Caption: a jelölőnégyzet mellett megjelenő szöveg Checked: ha értéke True, akkor a jelölőnégyzet ki van jelölve o Fontosabb események: OnClick •
TRadioGroup – Választógomb-csoport
2.2.7. ábra. A TRadioGroup helye a Standard komponens-palettán
o Leírás, szerep: egymást kölcsönösen kizáró opciók (kiválasztható karikák) létrehozására használjuk o Fontosabb jellemzők: Caption: a választógomb-csoport keretének felirata 36
Items: TStrings típusú tulajdonság, itt adjuk meg az opciók feliratát soronként Items.Count: az opciók száma
2.2.8. ábra. Választógomb-csoport
ItemIndex: a kiválasztott elem (karika) sorszáma (a számozás 0-tól kezdődik). Ha értéke -1, akkor egyik sincs kiválasztva o Fontosabb események: OnClick – Az eseményhez rendelt eljárásban egy case utasítással vizsgáljuk meg az ItemIndex értékeit (azt, hogy kattintás után melyik elem lett kiválasztva) és végezzük el a megfelelő utasítást vagy utasításokat •
TListBox – Lista
2.2.9. ábra. A TListBox helye a Standard komponens-palettán
o Leírás, szerep: elemekből (szöveg, számok) álló lista, az elemek kijelölhetők. o Fontosabb jellemzők: Items: itt adjuk meg az elemeket soronként Items.Count: az elemek száma ItemIndex: a kiválasztott elem indexe Selected[i:integer]: értéke True ha az i. elem ki van választva
Mivel a harmadik és az ötödik elem ki van választva a Selected[2] és a Selected[4] értéke True. (a számozás 0-tól kezdődik )
2.2.10. ábra. Kiválasztott elemek a listában
MultiSelect: ha értéke True, a listában több elem is kiválasztható Sorted: ha értéke True, a lista elemei rendezve vannak o Fontosabb metódusok:
37
Items.Insert(index:integer, elem:string) – egy elem beszúrása a lista egy index pozíciójába Példa: listbox1.items.insert(0, ’alma’) – a listbox1 nevű lista első pozíciójába beszúrjuk az ‘alma’ szöveget. Items.add(elem:string) – egy elem hozzáadása a lista elemeihez. Példa: listbox1.items.add(’alma’) – a listbox1 nevű listához hozzáadódik az alma elem. Items.delete(index:integer) – az index sorszámú elem törlése Példa: listbox1.items.delete(3) – a listbox1 nevű listából törli a 3-as sorszámú (tehát a 4.) elemet. o Fontosabb események: OnClick •
TComboBox – lenyíló (legördülő) lista
2.2.11. ábra. A TComboBox helye a Standard komponens-palettán
o Leírás, szerep: ugyanúgy működik mint a TListBox, különbség: az elemek csak akkor látszanak, ha a listát lenyitjuk.
2.2.12. ábra. A lenyíló lista állapotai
o Fontosabb jellemzők: Text: a lenyíló lista felirata Items: itt adjuk meg az elemeket soronként Items.Count: az elemek száma ItemIndex: a kiválasztott elem sorszáma Selected[i:integer]: értéke True ha az i. elem ki van választva MultiSelect: ha értéke True, a listában több elem is kiválasztható Sorted: ha értéke True, a lista elemei rendezve vannak o Fontosabb metódusok: Items.add(elem:string): egy elem hozzáadása a lista elemeihez Items.delete(index:integer): az index sorszámú elem törlése o Fontosabb események: OnClick
38
•
TPanel
2.2.13. ábra. A TPanel helye a Standard komponens-palettán
o Leírás, szerep: egy téglalap alakú felület, amelyen elhelyezhetünk más objektumokat is. o Fontosabb jellemzők: Caption: a Panel felirata Color: a Panel színe, típusa TColor
•
TMainMenu – Főmenü
2.2.14. ábra. A TMainMenu helye a Standard komponens-palettán
o Leírás, szerep: az alkalmazás főmenüje, több almenüvel. Az ablakban való elhelyezés után duplán kell kattintani a komponensre, amelynek során megnyílik a menüszerkesztő ablak. A menüszerkesztő segítségével pedig létrehozhatjuk a menüpontokat illetve testre szabhatjuk azokat.
2.2.15. ábra. A Menü és a menüszerkesztő ablak
o Fontosabb jellemzők: Items: TStrings típusú tulajdonság, a menüpontok az Items jellemzőben tárolódnak Példa: MainMenu1.Items[0].Caption – így hivatkozunk a menü
első főmenüpontjának feliratára és a következőképpen az első főmenüpont almenüjének első almenüpontjának a feliratára: MainMenu1.Items[0][0].Caption
39
o Fontosabb események: OnClick •
TPopupMenu – Gyorsmenü, helyi-menü
2.2.16. ábra. A TPopupMenu helye a Standard komponens-palettán
o Leírás, szerep: Egy-oszlopos menü, amelyet hozzárendelünk valamilyen más komponenshez. Erre a komponensre jobb gombbal kattintva, megjelenik a gyorsmenü. A komponensekhez való hozzárendelés úgy történik, hogy a komponens PopupMenu jellemzőjénél kiválasztjuk a listából a megfelelő gyorsmenüt (több is lehet egy alkalmazásban). o Fontosabb jellemzők: Items: itt tárolódnak a menüpontok o Fontosabb események: OnClick 2.3. Az Additional paletta fontosabb komponensei •
TBitBtn
2.3.1. ábra. A TBitBtn helye az Additional komponens-palettán
o Leírás, szerep: kattintható gomb, kattintásra induló tevékenység elvégzésére használjuk. Annyiban különbözik a TButton-tól, hogy tartalmazhat egy kis grafikus szimbólumot. o Fontosabb jellemzők: Caption: a gomb felirata Glyph: a gomb grafikus szimbóluma, kattintva a gombra, majd a Load gombra betölthetünk egy kisméretű (pl. 16x16 pixel), bmp képet. Ha a kép 16 pixel magas és 32 pixel széles, akkor csak a kép első fele jelenik meg, a második csak akkor, ha a gomb le van tiltva (Enabled=False) Kind: egy listából kiválaszthatjuk a gomb típusát (felirat és kép)
40
2.3.2. ábra. A Kind jellemző használata
o Fontosabb események: OnClick •
TSpeedButton – Gyorsgomb
2.3.3. ábra. A TSpeedButton helye az Additional komponens-palettán
o Leírás, szerep: Olyan nyomógombok, amelyeket általában eszköztárak létrehozására használunk. Ehhez egy panelen kell elhelyezni őket. Lehetnek beragadt és felengedett állapotban, grafikát és szöveget tartalmazhatnak. A gombokat lehet csoportosítani, ilyenkor az egy csoportban levő gombok egymást kölcsönösen kizárják. o Fontosabb jellemzők: Caption: a gomb felirata gombra, majd Glyph: a gomb grafikus szimbóluma, kattintva a a Load gombra betölthetünk egy kisméretű, bmp képet NumGlyphs: képek száma, maximálisan 4 lehet, ezek egy bitmapban kell legyenek egymásután: az első akkor jelenik meg, amikor a gomb nincs lenyomva, a második akkor, ha a gomb le van tiltva (nem kattintható, Enabled=False) a harmadik kattintáskor, a negyedik pedig a gomb lenyomott állapotában látszik GroupIndex: ezzel lehet csoportosítani a gombokat. Az egy csoportba tartozó gomboknál értéke ugyanaz kell legyen. Ha értéke 0, akkor a gomb nem ragad be Down: ha értéke True, akkor a gomb be van nyomva AllowAllUp: ha értéke True, akkor a csoportban levő gombok közül mindegyiket fel lehet engedni, ha False, akkor a gombok közül egy kötelező módon benyomva kell maradjon o Fontosabb események: OnClick •
TImage – Kép
41
2.3.4. ábra. A TImage helye az Additional komponens-palettán
o Leírás, szerep: egy kép megjelenítése o Fontosabb jellemzők: Picture: ennél a jellemzőnél adjuk meg a képet kattintva a gombra, majd a Load gombra. A kép az Image objektum Picture.Graphic jellemzőjébe kerül (így kell hivatkozni rá) Stretch: ha True, a kép felveszi az image méretét. Ha False, akkor a kép az Image bal felső sarkában lesz, eredeti méretben (ha az Image kisebb mint a kép, levágja) o Fontosabb metódusok: Picture.LoadFromFile(állománynév: string): a képet betölti a paraméterként megadott állományból Picture.SaveToFile(állománynév: string): a képet lementi a paraméterként megadott állományba •
TShape – Alakzat
2.3.5. ábra. A TShape helye az Additional komponens-palettán
o Leírás, szerep: egy alakzat megjelenítése o Fontosabb jellemzők: Shape: az alakzat formája. Kiválasztható egy listából. Lehetséges értékek: • StCircle: kör • StEllipse: ellipszis • StRectangle: téglalap • StRoundRect: lekerekített sarkú téglalap • StSquare: négyzet • StRoundSquare: lekerekített sarkú négyzet Pen: az alakzat körvonalának (a rajzoló toll) tulajdonságai: • Pen.Color: szín • Pen.Style: a toll stílusa: psSolid – folytonos, psDash – szaggatott, psDot – pontozott, stb. • Pen.Mode: a rajzolási mód (bővebben lásd a Grafika című fejezetben) • Pen.Width: a toll vastagsága Brush: az alakzatot kitöltő, kifestő ecset tulajdonságai: szín, és stílus 2.4. A WIN32 paletta fontosabb komponensei •
TTabControl 42
2.4.1. ábra. A TTabControl helye a Win32 komponens-palettán
o Leírás, szerep: fülsor egy lappal o Fontosabb jellemzők: Tabs: a fülek felirata, TStrings típusú tulajdonság TabIndex: az aktuális fül sorszáma TabPosition: a fülek pozíciója, lehetséges értékei: tpTop – fent, tpBottom – lent, tpLeft – bal oldal, tpRight – jobb oldal o Fontosabb események: OnChange: a fülek váltásakor következik be •
TPageControl
2.4.2. ábra. A TPageControl helye a Win32 komponens-palettán
o Leírás, szerep: fülsor több lappal. A lapokat az objektumon jobb gombbal kattintva és a New Page parancsot választva lehet létrehozni. A lapok önálló objektumokként jönnek létre TabSheet1, TabSheet2, stb. névvel. A lapok kiválasztása a megfelelő fülre (ilyenkor csak a PageControl objektum választódik ki) majd belül, a lapra való kattintással történik. Feliratukat, kiválasztás után, a Caption jellemzőben lehet beállítani. o Fontosabb jellemzők: TabPosition: a fülek pozíciója, lehetséges értékei: tpTop – fent, tpBottom – lent, tpLeft – bal oldal, tpRight – jobb oldal o Fontosabb események: OnChange: a fülek váltásakor következik be •
TRichEdit
2.4.3. ábra. A TRichEdit helye a Win32 komponens-palettán
o Leírás, szerep: többsoros szöveg szerkesztése, használata megegyezik a TMemo használatával a következő plusz lehetőségekkel: A SelAttributes jellemző size, color, style és name tulajdonságait felhasználhatjuk arra, hogy a kijelölt szöveg tulajdonságait módosítsuk (a Memo-nál csak a teljes szöveget lehetett) A Paragraph jellemző Alignment (bekezdések igazítása), Numbering (felsorolás-jelek alkalmazása) FirstIndent, LeftIndent, RightIndent (Longint típusú értékek, a bekezdés első sora illetve 43
•
bal és jobb pozíciójának megadására) tulajdonságait a bekezdések formázására használhatjuk: • az Alignment lehetséges értékei: taLeftJustify (balra igazítás), taRightJustify (jobbra igazítás), taCenter (középre igazítás) • a Numbering lehetséges értékei: nsNone (felsorolás jelek nélkül) és nsBullet (felsorolás jelek: pontok alkalmazása) Az OpenFromFile és SaveToFile metódusok segítségével megnyithatók és menthetők az RTF kiterjesztésű állományok
TProgressBar
2.4.4. ábra. A TProgressBar helye a Win32 komponens-palettán
o Leírás, szerep: Folyamatjelző: egy üres téglalap, amelyet folyamatosan kitöltenek kék négyzetek, hosszan tartó műveletek haladási állapotát lehet vele szemléltetni o Fontosabb jellemzők: Max: a lépések maximális száma Step: egy lépés értéke (hány lépésből kell elérni a maximumot, a ProgressBar végét) o Fontosabb metódusok: StepIt: egy lépés végrehajtása (a Step-nél megadott értékkel lép előre) •
TAnimate
2.4.5. ábra. A TAnimate helye a Win32 komponens-palettán
o Leírás, szerep: animációk megjelenítése o Fontosabb jellemzők: CommonAvi: standard Windows animációkat lehet megjeleníteni vele: • aviNone: nincs animáció • aviCopyFile: az állománymásolás animációját jeleníti meg • stb. FileName: egy AVI kiterjesztésű állományt lehet megnyitni és megjeleníteni Active: ha értéke True, akkor játszódik le az animáció Repetitions: hányszor ismételje az animációt, ha értéke 0, akkor végtelenül ismétli StartFrame: a képsorban levő hányadik képpel kezdje az animációt 44
•
StopFrame: a képsor hányadik képe legyen az utolsó kép az animációban. (pl. ha a képsor hossza 20, ami azt jelenti, hogy van egy 20 képet tartalmazó animációnk és beállítjuk a Startframe-t 1re, a StopFrame-t pedig 10-re, akkor az animációt csak a feléig játssza le.)
TMonthCalendar
2.4.6. ábra. A TMonthCalendar helye a Win32 komponens-palettán
o Leírás, szerep: naptár, dátum kiválasztására használjuk o Fontosabb jellemzők: Date: a kiválasztott dátum MaxDate: a maximális, legkésőbbi dátum, amit még meg lehet jeleníteni MinDate: a legkorábbi dátum, amit még meg lehet jeleníteni ShowToday: ha értéke True, alól megjelenik a mai dátum ShowTodayCircle: ha értéke True, be van karikázva a mai dátum WeekNumbers: ha értéke True, a naptár balfelén megjelenik a hetek száma is
2.4.7. ábra. A kiválasztott dátum: 2006-10-25, a mai dátum: 2006-10-22.
•
TDateTimePicker
2.4.8. ábra. A TDateTimePicker helye a Win32 komponens-palettán
o Leírás, szerep: naptár, dátum kiválasztására használjuk. Annyiban különbözik a TMonthCalendar típusú objektumtól, hogy a dátum egy szerkesztődobozban szerepel és ezt lenyitva jelenik meg a naptár.
2.4.9. ábra. TDateTimePicker típusú objektum
2.5. Más palettákon szereplő fontosabb komponensek 45
•
System paletta TTimer komponense
2.5.1. ábra. A TTimer helye a System komponens-palettán
o Leírás, szerep: Időzítő: szabályos időközönként végrehajtandó tevékenységekre használjuk. o Fontosabb jellemzők: Interval: az az időintervallum, amennyi időközönként ismétlődik a tevékenység. Ezredmásodpercben kell megadni. Enabled: boolean típusú jellemző, ha True, akkor működik az időzítés o Fontosabb események: OnTimer: az Interval jellemzőnél megadott időközönként következik be, a hozzá rendelt eljárásba kell beírni az ismétlődő tevékenységet •
Samples paletta TSpinEdit komponense
2.5.2. ábra. A TSpinEdit helye a Samples komponens-palettán
o Leírás, szerep: egy egész számot tartalmazó szerkesztődoboz, értékét a le/fel mutató háromszögekkel lehet csökkenteni/növelni. Egész számú értékek megadására, beolvasására használjuk, előnye, hogy nem kell alkalmazni szöveg-szám átalakító függvényt. o Fontosabb jellemzők: Value: az objektum értékét jelenti MaxValue: az objektum maximális értéke, ennél tovább nem lehet növelni MinValue: az objektum minimális értéke, ennél tovább nem lehet csökkenteni Interval: ezzel az értékközzel nő/csökken az objektum értéke o Fontosabb események: OnChange: akkor következik be, ha az objektum értékét megváltoztatjuk
2.6. Saját komponensek létrehozása Delphiben lehetőség van arra is, hogy saját komponenseket hozzunk létre, kiegészítve a létező komponenseket újabb jellemzőkkel, metódusokkal, illetve felülírva, módosítva a már meglévőket. 46
Egy példán keresztül vizsgáljuk meg, hogyan, milyen lépésekkel valósíthatjuk meg ezt. Létre fogunk hozni egy új komponens palettát és erre helyezünk majd el egy saját fejlesztésű komponenst. Az első komponensünket, a TEdit (szövegmező) komponensből származtatjuk, úgy módosítva, hogy egész számokat lehessen beírni a szerkesztődobozba. A komponenst kiegészítjük egy ertek (érték) nevű jellemzővel, ez fogja tartalmazni a beírt szám értékét. Előnye az lesz a szövegmezővel szemben, hogy átalakítás nélkül lehet használni a komponenst egész számok beolvasására és megjelenítésére. A feladat megoldható valós számokra is, létrehozható egy új komponens valós számok beolvasására és megjelenítésére is. 2.6.1. A létrehozás lépései Az új komponensek létrehozása 4 fő lépésből áll: 1. Lépés: Komponens definiálása: • Component menü / New component parancs: a megjelenő párbeszédablak első mezőjében kiválasztjuk a TEdit komponenst, úgyanis azt fogjuk specializálni. A második mezőbe beírjuk a komponens nevét, valamint a harmadikba a komponens paletta nevét (vagy kiválasztunk egy létező palettát). A többi mezőt változatlanul hagyjuk, azok automatikusan kitöltődnek.
2.6.1. ábra. A New Component párbeszédablak
•
Az OK gombra való kattintás után létrejön a Delphi program könyvtárában levő Lib könyvtárban a [komponens neve az első T nélkül].pas egység. Ebben kell megírni a komponens „viselkedését” (2.6.1. ábra).
2. Lépés: Az egység megírása
47
2.6.2. ábra. Az egység kezdeti tartalma
•
a published deklarációnál írjuk be a következő utasításokat: property ertek:integer read olvas write ir;
Ez azt jelenti, hogy deklaráltunk egy publikált (tehát az Object Inspector-ban megjelenő és értékét beállítható) ertek nevű, integer típusú jellemzőt. A read és a write kulcsszavak után egy-egy metódusnevet kell megadni, amelyekben azt kell majd megírni, hogy hogyan kerül érték a jellemzőbe (hogyan olvasódik be), illetve, hogyan jelenik meg itt az érték (hogyan íródik ki). •
a public deklarációknál adjuk meg a következő konstruktort:
constructor Create(aOwner:TComponent); override;
Ez azt jelenti, hogy az új komponens konstruktorát (létrehozó metódusát), amelyet örököl az ős-komponenstől (TEdit) megfogjuk változtatni, felülírjuk (override). •
a private kulcsszónál adjuk meg az alábbi eljárás illetve függvény deklarációkat: procedure ir(value:integer); function olvas:integer;
•
Az egység implementation részében megírjuk a három metódust (Create, olvas, ir).
Constructor TEgeszSzam.Create(aOwner:TComponent); begin Inherited Create(aOwner); Text:=’0’;
48
Ertek:=0; end; procedure TEgeszSzam.ir(value:integer); begin Text:=inttostr(value); end; function TEgeszSzam.olvas:integer; begin Result:=strtoint(text); end;
3. Lépés. Bitmap készítés: Ez a lépés kihagyható, de akkor az új komponens a komponens-palettán pont úgy fog kinézni, mint az ős-komponens. • Indítsuk el a Delphi erőforrás szerkesztőjét: Tools menü / Image Editor parancs. • Az Image Editor programnak válasszuk a File | New | Component Resource file parancsát. • Kattintsunk jobbgombbal a Contents feliratra és válasszuk a New | Bitmap parancsot. • Az új bitmap legyen 24x24 képpont méretű és VGA (16 colors) típusú. • Kattintsunk jobbgombbal a Bitmap1 feliratra és válasszuk a Rename parancsot. A név szerkeszthetővé válik és a Bitmap1 helyére írjuk be: TEGESZSZAM. Fontos, hogy csupa nagy betűkkel írjuk és az új komponens nevét írjuk be (T-vel az elején). • Kattintsunk duplán a TEGESZSZAM feliratra, megjelenik az üres kép, amit most megszerkeszthetünk. Írjuk bele, hogy int és •
•
rajzoljunk köréje egy téglalapot: . Be kell zárni a szerkesztő ablakot és a File | Save parancssal mentsük le a rajzot. Fontos, hogy abba a könyvtárba mentsünk, ahol az egység is van (Lib) és az állomány neve pedig egyezzen meg az egység nevével: egeszszam (nem kell T betű az elejére). Zárjuk be az Image Editor programot.
4. lépés: A létrehozott komponens telepítése: • Component menü / Install component parancs. • Mivel új komponens-palettát hozunk létre lépjünk át az Into new package fülre, majd az alábbi ábrán látható beállításokat végezzük el.
49
2.6.3. ábra. Az Install Component párbeszédablak
•
A komponens-paletták végén megjelenik a Sajat komponensek nevű új paletta. Válasszuk ki ezt és láthatjuk az új komponensünket.
2.6.4. ábra. Az új komponens paletta az új komponenssel
Az új komponens tesztelésére, hozzunk létre egy új alkalmazást. Helyezzünk el az ablakba egy TSpinEdit típusú objektumot a Samples palettáról illetve egy TEgeszSzam típusú objektumot a Sajat komponensek palettáról. Az legyen a feladat, hogy az EgeszSzam1 objektumba írt egész szám jelenjen meg a SpinEdit1 értékeként, illetve, amikor a SpinEdit1 értékét változtatjuk, akkor az érték jelenjen meg az EgeszSzam1 objektumban. A megoldás az, hogy mindkét objektum OnChange eseményéhez eljárást fogunk rendelni és ezekbe beírjuk egy-egy utasítást: SpinEdit1.value:=EgeszSzam1.ertek; {EgeszSzam1 esetén} EgeszSzam1.ertek:= SpinEdit1.value; {SpinEdit1 esetén}
50
3. ÜZENET- ÉS DIALÓGUSABLAKOK 3.1. Üzenetablakok Windows-os alkalmazásokban nagyon gyakran használt technika, hogy probléma esetén a program a felhasználót értesíti egy megfelelő üzenetablak segítségével. Az üzenetablakok különböző típusaival figyelmeztetések, hibajelzések jeleníthetők meg, esetleg a kényesebb műveletek végrehajtására vonatkozó engedélyező vagy tiltó kérdéseket tehetünk fel. Az üzenetablakban található különböző nyomógombok választási lehetőséget kínálnak a felhasználó számára. A bemutatásra kerülő üzenetablakok kettő kivételével modális, azaz a futó alkalmazás megszakad, az üzenetablak lesz aktív és a folytatáshoz be kell zárni az üzenetablakot. Az ablakok megjelenítésével kapcsolatosan a következő fejezet foglalkozik részletesen.
3.1.1. ShowMessage A ShowMessage ablakhívó eljárás hatására egy OK nyomógombbal ellátott olyan üzenetablak jelenik meg a képernyő közepén, amelynek a fejlécében az alkalmazás neve szerepel, míg a nyomógomb felett jelenik meg az üzenet szövege. Az ablaknak nyugtázási szerepe van, ugyanis a felhasználónak nincs választási lehetősége. Többnyire figyelmeztetésre, hibajelzésre használható. Szintaxisa: procedure ShowMessage(const Msg:string);
Például: ShowMessage(’Ez egy egyszeru üzenetablak’);
3.1.1.1. ábra. A ShowMessage üzenetablak
3.1.2. MessageDlg Ez egy több választási lehetőséget (több nyomógombot) is tartalmazó modális üzenetablak, amely ugyancsak a képernyő közepén, az űrlaptól független módon jelenik 51
meg. A MessageDlg tulajdonképpen egy Word típusú függvény, amely mindig a felhasználó által választott gomb értékét adja vissza, amellyel az ablakot bezárta. Szintaxisa: function MessageDlg(const Msg:string; DlgType: TMsgDlgType; AButtons: TMsgDlgButtons;HelpCtx: Longint): Word;
A paraméterek jelentése: • •
Msg: a szöveg, amit meg szeretnénk jeleníteni (lehet több soros is) DlgType: a lehetséges ablakfajták típusát jelenti: o mtWarning: figyelmeztetést jelző sárga-fekete ikon o mtError: hibát jelző piros „stoptábla” o mtInformation: információt jelző kék „i” betű o mtConfirmation: kérdést jelző kék kérdőjel o mtCustom: az üzenetablakon nem lesz kép
3.1.2.1. ábra. A MessageDlg Confirm, Error és Warning típusú ablakai
•
AButtons: az ablakra kerülő gombok halmazát jelenti. Mivel halmaz típusról van szó, ezért a szükséges gombokat, egymástól vesszővel elválasztva szögletes zárójelben kell felsorolni. A lehetséges értékek: mbYes, mbNo, mbOK, mbCancel, mbAbort, mbRetry, mbIgnore, mbAll, mbNoToAll, mbYesToAll, mbHelp.
3.1.2.2. ábra. A MessageDlg lehetséges gombjai
•
HelpCtx: a súgó azon témájának azonosítója, amely akkor jelenik meg, ha megnyomjuk az F1 billentyűt. Ha ezt nem akarjuk használni, adjunk 0 értéket.
Amint már korábban említettük, a MessageDlg visszaadja annak a nyomógombnak az értékét, amellyel a felhasználó bezárta az üzenetablakot. Lehetséges értékek: mrNone, mrAbort, mrYes, mrOk, mrRetry, mrNo, mrCancel, mrIgnore, mrAll. Példa: 52
Nagyon sok alkalmazás bezárásakor megerősítést kér. Delphiben ezt az űrlap OnCloseQuery eseményében tehetjük meg az alábbi módon: procedure TForm1.FormCloseQuery(Sender:TObject; var CanClose:Boolean); begin CanClose := MessageDlg('Valóban kilép?', mtConfirmation, [mbYes,mbNo], 0 ) = mrYes; end;
3.1.2.3. ábra. A példában megjelenő dialógusablak
Ha a felhasználó az mbNo gombot választja, akkor a függvény visszatérési értéke mrNo és az igaz ágban a CanClose értékét False-ra állítjuk.
3.1.3. MessageDlgPos A MessageDlg függvény egy javított változata, amelyben a felhasználónak van lehetősége a dialógusablak pozíciójának a megadására. A függvény paraméterezése megegyezik az előző függvény paraméterezésével, kiegészítve a pozíció megadásának lehetőségével. Szintaxis: function MessageDlg(const Msg:string; DlgType: TMsgDlgType; AButtons: TMsgDlgButtons;HelpCtx: Longint; X, Y: Integer): Word;
•
X, Y: az ablak bal felső csúcsának koordinátái pixelben megadva
3.1.4. Az InputBox üzenetablak
Egysoros adatok beolvasására használhatunk az InputBox tulajdonképpen egy függvény, amelynek a szintaxisa a következő:
üzenetablakot.
function InputBox(const ACaption, APrompt, ADefault: string): string;
A függvény paramétereinek jelentése: • ACaption: a dialógusablak felirata. • APrompt: a dialógusablakban megjelenő szöveg. • ADefault: a beviteli mezőben megjelenő kezdeti szöveg. 53
Ez
Az ablak tartalmazni fog egy OK és egy Cancel gombot is. Ha az OK gombra kattintunk, akkor a függvény értéke a szövegmezőben levő szöveg lesz, ha pedig a Cancel gombra kattintunk vagy bezárjuk az ablakot, akkor a visszatérített érték a harmadik paraméter értéke lesz. Példa: nev := InputBox(’Adatbevitel’, ’Add meg a neved:’, ’’);
3.1.4.1. ábra. Adatbevitel InputBox-al
3.2. Dialógusablakok Egy összetett alkalmazásban általában szükség van különböző állományok megnyitására, elmentésére, nyomtató beállítására. A Delphi lehetőséget ad ezeknek a műveleteknek az elvégzésére párbeszédablakok segítségével. A különböző dialógusablakok megjelenítésére használt komponensek a Dialogs nevű palettán találhatók és mindegyik a Windowsból jól ismert szabványos párbeszédablakot jelenít meg.
3.2.1. ábra. A Dialogs paletta
Több közös vonás is van a dialógusablakok között. Itt kiemelnénk, hogy kettő kivételével mindegyik modális ablak és valamennyi az Execute utasítás segítségével hívódik meg. A következőkben a paletta fontosabb elemeit tekintjük át. 3.2.1. OpenDialog, SaveDialog Állományok tallózására és kiválasztására valamint állományok lementési helyének és nevének megadására alkalmas ablakok. A két komponens tulajdonságai gyakorlatilag megegyeznek. Fontosabb tulajdonságai: •
DefaultExt: alapértelmezett állomány kiterjesztés, amely abban az esetben csatolódik az állomány nevéhez, ha a felhasználó azt nem adja meg 54
• •
FileName: a kiválasztott állomány teljes nevét tartalmazza Filter: az állományok szűrése oldható meg a segítségével (megadott maszk alapján, pl. *.txt, *.doc, stb.). A program tervezési fázisában ez a tulajdonság a Filter Editor segítségével adható meg
3.2.1.1. ábra. A szűrő megadása: az Open-ablak csak a txt illetve az avi kiterjesztésű fájlokat fogja „látni”
• • •
FilterIndex: az aktuális szűrő sorszáma InitialDir: kezdeti könyvtár Options: a dialógusablak különböző beállítási lehetőségeit tartalmazza. Nézzünk egy párat ezek közül: o ofOverwritePrompt: létező fájl felülírása esetén figyelmeztet o ofHideReadOnly: nem jelenik meg a „megnyitás csak olvasásra” lehetőség a dialógusablakon o ofAllowMultiSelect: több állomány egyszerre történő kiválasztására ad lehetőséget. A kiválasztott fájlokat a TString típusú Files tulajdonságban kapjuk vissza
Legfontosabb metódus az Execute, amellyel a dialógusablakot megjelentetjük. Példa: if OpenDialog1.Execute then begin utasitások; end;
55
3.2.1.2. ábra. Az OpenDialog párbeszédablak
3.2.2. FontDialog A szövegtulajdonságok beállítására alkalmas ablak, amellyel már az MsOffice csomag keretén belül találkozhattunk. Fontosabb tulajdonságai: • •
Font: a kiválasztott tulajdonságok együttese MinFontSize, MaxFontSize: a betűméret nagyságát korlátozhatjuk segítségükkel
Példa: Az alábbi példában az Edit1 szövegmező szövegének a jellemzői felveszik a Fontablakban kiválasztott jellemzőket. procedure TForm1.Button1Click(Sender: TObject); begin if FontDialog1.Execute then Edit1.Font := FontDialog1.Font; end;
3.2.2.1. ábra. Az MsOffice alkalmazásokból ismert Font-ablak
56
3.2.3. ColorDialog A színkiválasztó ablak megjelenítését teszi lehetővé. Legfontosabb tulajdonsága a Color, ahol a egy konkrét szint lehet kiválasztani.
3.2.3.1. ábra. A színkiválasztó Color-ablak
3.2.4. PrintDialog Nyomtatási paraméterek megadását értékeket vagy ki lehet olvasni beállított másolatok számát (Copies tulajdonság), a Szintén be lehet határolni (korlátozni) a MaxPage).
teszi lehetővé. Be lehet állítani kezdeti értékeket, melyek megadhatják például: a leválogatás módját (Collage tulajdonság). kinyomtatandó oldalak számát (MinPage,
3.2.4.1. ábra. A PrintDialog párbeszédablak
3.2.5. FindDialog, ReplaceDialog Olyan dialógus ablakok, amelyekben megadhatjuk a keresendő szót, viszont a keresést nekünk kell megoldani. Az előző ablakoktól eltérően ezek nem modális ablakok, azaz a felhasználónak nem kell bezárnia, ahhoz hogy más tevékenységet is folytasson. 57
Mindkét komponens rendelkezik a FindText tulajdonsággal. Itt lehet megadni a keresendő szöveget, ami meg fog jelenni a dialógusablakban. A RepleaceDialog objektum pluszban rendelkezik egy RepleaceText tulajdonsággal is. Az itt megadott szöveg fogja majd helyettesíteni a FindText-ben található szöveget. Az Options tulajdonság fontosabb beállítási lehetőségei: • frDown: keresés iránya, true értéke azt jelenti, hogy lefele történik a keresés, ami egyben az alapértelmezett irány • frMatchCase: nagy és kisbetűk közötti különbséget lehet itt bekapcsolni Az eseményeknél ki lehet emelni az OnFind eseményt, amely akkor következik be, ha a felhasználó a „Find Next” nyomógombra kattintott. A ReplaceDialog még rendelkezik egy OnReplace eseménnyel is, amely a „Replace” nyomógomb megnyomásakor következik be. Mindkét objektum az Execute metódus segítségével jeleníthető meg.
3.2.5.1. ábra. A Find dialógusablak
58
4. TÖBB ŰRLAPOS ALKALMAZÁSOK Alkalmazás fejlesztéskór nagyon gyakran találkozunk azzal a problémával, hogy a sok információ megjelenítésére már nem elegendő az aktuális űrlap. Ilyen esetben több lehetőség közül választhatunk. Az egyik lehetőség az, amikor több űrlapot használunk, és a főűrlapról jutunk el a többi űrlapra. A másik esetben a logikailag összetartozó információkat egy űrlapon több oldalra helyezzük el. A következz részben a több űrlapos alkalmazások készítésest mutatjuk be. 4.1. Alkalmazások űrlapjai Az űrlapok létrehozását tekintve, két típusról beszélhetünk: • auto-create típusú űrlap: a program indításának pillanatában létrejön, és ettől kezdve megszűnéséig a memóriába marad. A létrehozást és a megszűntetést teljesen a rendszer felügyeli, azaz a felhasználónak nincsen beleszólása. Leggyakrabban ilyen típusú űrlapokat használunk. • dinamikus űrlap: nekünk kell kódból létrehoznunk, megjelenítenünk. A ritkán használt űrlapokat ajánlatos dinamikusan létrehozni. A Project/Options párbeszédablak Forms fülén állíthatjuk be azt, hogy az alkalmazás összes űrlapja közül melyik legyen főűrlap, melyek legyen automatikusan létrehozva és melyik dinamikusan. Az alábbi ábrán a Form1 űrlap automatikusan van létrehozva, ami egyben főűrlap is. A Form1 és Form2 űrlapokat dinamikusan lehet létrehozni.
4.1.1. ábra. Űrlapok típusának beállítása
4.2. Az ablakok megjelenítési formái Az alkalmazásunkban az űrlapokat (ablakokat) két féle módon jeleníthetjük meg: modálisan (Modal), nem modálisan (Modeless). 4.2.1. Modális űrlapok A modális ablakok legfontosabb tulajdonsága, hogy megjelenésük idejére az alkalmazás többi ablaka nem elérhető. Ha egy űrlap modális, arról könnyedén 59
meggyőződhetünk, úgy hogy az űrlap mellé kattintunk. Ezt egy rövid figyelmeztető sípszó fogja jelezni. Ilyen típusú ablakok az üzenetablakok, speciális párbeszédablakok: fájl megnyitás, mentés. A modális űrlapok megjelenítéséhez az űrlap ShowModal metódusát használjuk. Ez egy függvény, amelynek visszatérési értéke az űrlapon lévő nyomógombok ModalResult tulajdonságától függ. Ennek a technikának nagy előnye, hogy egyszerűbbé teheti a kódot, mivel a visszatérési érték egyértelműen azonosíthatja a gombot (ha a ModalResult tulajdonság megfelelően be van állítva). Ha azt akarjuk, hogy csak egy kiválasztott nyomógomb használatakor záródjon be az ablak, kizárólag a választott gomb ModalResult tulajdonságát állítsuk 0-tól különbözőre. Ha egy Form1 nevű űrlapon található gomb segítségével egy Form2 nevű űrlapot szeretnénk modálisan megjeleníteni az alábbi kódot használjuk: procedure TForm1.Button1Click(Sender: TObject); begin if Form2.ShowModal = mrOk then begin műveletek end; end;
Ha feltételezzük, hogy a Form2 űrlapon található gomb ModalResult tulajdonsága a mrOk-ra van állítva akkor erre a gombra kattintva a szűlőűrlap ugyanilyen nevű paramétere automatikusan megkapja a gombnál beállított értéket és bezáródik. Ennek a technikának az az előnye, hogy erre a gombra nem kell OnClick eseményt írni. Ugyanezt a feladatot megoldhatjuk úgy is, hogy a második űrlapot dinamikusan hozzuk létre: procedure TForm1.Button1Click(Sender: TObject); var Form2: TForm2; begin Form2 := TForm2.Create(Self); if Form2.ShowModal = mrOk then begin műveletek end; Form2.Free; end;
Első lépésként meghívtuk a Create konstruktort az űrlap létrehozására, majd utána megjelenítettük. Ebben az esetben az űrlap felszabadításáról is nekünk kell gondoskodnunk. 4.2.2. Nem modális űrlapok Az ilyen űrlapok előnye, hogy az alkalmazás többi ablakával is tudunk dolgozni anélkül, hogy a megjelenített űrlapot bezárnánk. A Delphi keretrendszerben ilyen például az objektum-felügyelő ablak, a kódszerkesztő ablak. 60
A nem modális űrlapok megjelenítéséhez az űrlap Show metódusát használjuk. Ez a metódus csupán megjeleníti és előtérbe helyezi az űrlapot, nincsen visszatérítési értéke. A Show és ShowModal utasítások közti lényeges különbséget az alábbi ábrán szemléltetjük. A Show metódus megjeleníti az űrlapot és rögtön utána végrehajtja az U1 utasítást. A ShowModal is megjeleníti az űrlapot, de utána vár ennek bezárásáig, így az U2 utasítás csak az ablak eltűnése után fog lefutni. Form1.Show; U1;
Form2.ShowModal; U2;
4.2.2.1. ábra. A Show és ShowModal metódusok összehasonlítása
4.3. MDI alkalmazások készítése
Egy MDI (Multiple Document Interface) alkalmazásban a fő (parent) ablak keretein belül több gyerek (child) ablak nyitható meg. Számos MDI típusú alkalmazással találkoztunk már, ilyen például a MS Word szövegszerkesztő vagy az MS Excel táblázatkezelő, de az ügyviteli programok legnagyobb része is MDI szemlélettel készül. Az MDI alkalmazás főablaka úgy viselkedik, mint egy tároló vagy keret, rendelkezik saját menüvel, és ha a főablakot mozgatjuk, akkor minden egyes objektum, ami benne található vele együtt mozog. A főablakot szülőablaknak vagy keretnek nevezzük, míg a keretablakban nyíló ablakokat gyerekablakoknak. A gyerekablakok csak a keretablak területén belül helyezkedhetnek el, és bármilyen típusúak lehetnek. A gyerekablakok ugyanazokkal a tulajdonságokkal rendelkezhetnek, mint a szülőjük, semmilyen korlátozás nincs rájuk, viszont a keretablakuk nélkül nem létezhetnek. Tehát ha bezárjuk a főablakot, akkor az összes gyerekablak is bezáródik. Delphiben természetesen van lehetőség több ablakot kezelő alkalmazások készítésére, tulajdonképpen a Delphi is ilyen alkalmazás. A következő részben megnézzük egy példán keresztül, hogy milyen lépésekben készíthetünk egy egyszerű MDI alkalmazást. Egy Delphi alkalmazás egyetlen MDI konténert tartalmazhat és ez általában az alkalmazás főűrlapja. Alkalmazásunk főablakának az elkészítéséhez a következő lépéseket hajtsuk végre: • Egy új Delphi alkalmazást kezdünk: File | New Application. • Az alkalmazás főűrlap Name tulajdonsága legyen például frMain valamint a FormStyle tulajdonság értéke legyen fsMDIform. Ez az jelenti, hogy a főúrlap lesz az alkalmazás konténer űrlapja. Egy főablak általában egy vagy több gyerekablakot tartalmaz. Ezek a gyerekablakok is egyszerű űrlapok, viszont eltérően a dialógus ablakoktól a szülőűrlap keretén belül találhatók. Továbbá, ha a gyerekablakot lekicsinyítjük, akkor annak ikonja a szülőablakban fog maradni.
61
Továbbá hozzunk létre egy új űrlapot: File | New Form. Legyen az űrlap neve frChild, valamint a FormStyle tulajdonság fsMDIChild. Ezáltal az új űrlapunk egy gyerekablak lesz. Tervezési időben nincs különbség a szülő és gyerekablak között. Az utóbbi természetesen tartalmazhat különböző objektumokat. A következő lépésben néhány beállítást végzünk a projektünkben. Válasszuk a Project | Option menüpontot. A megjelenő dialógusablakban a gyerekűrlapot a baloldali listából helyezzük át a jobboldaliba. A jobboldali lista az alkalmazás azon űrlapjait tartalmazza, amelyeket nem hozza létre automatikusan. Alapértelmezetten minden gyerekűrlap az alkalmazás indításakor létrejön. Mi ezeknek a létrehozását a programban fogjuk megoldani.
4.3.1. ábra. A Project Options párbeszédablak
Mivel a gyerekűrlapokat programból hozzuk létre, szükség lesz egy eljárásra, amely létrehozza a frChild egy példányát. A következő CreateChildForm eljárást és annak prototípusát a frMain űrlap egységében hozzuk létre: type TfrMain = class(TForm) procedure CreateChildForm(const childName : string); … end; … uses uchild; … procedure TfrMain.CreateChildForm(const childName : string); var Child: TfrChild; begin Child := TfrChild.Create(Application); Child.Caption := childName; end;
A gyerekűrlapokat használat után be lehet zárni, viszont alapértelmezetten bezáráskor azok minimalizálódnak a szülőűrlap aljára. Ahhoz, hogy ezt elkerüljük a gyerekűrlap FormClose eseményét fogjuk lekezelni: procedure TfrChild.FormClose (Sender: TObject; var Action: TCloseAction); begin Action := caFree; end;
62
Minden MDI alkalmazás tartalmaz egy főmenüt is, ahonnan különböző műveleteket hajthatunk végre. A mi esetünkben a főmenü két menücsoportot fog tartalmazni. Az egyik segítségével gyerekablakokat fogunk létrehozni, a másik segítségével pedig ezeket fogjuk igazítani. A főmenü az alábbi ábrán látható:
4.3.2. ábra. Az MDI alkalmazásunk főmenüje
A File menü két almenüjének OnClick eseményét kezeljük le: procedure TfrMain.NewChild1Click(Sender: TObject); begin CreateChildForm('Child '+IntToStr(MDIChildCount+1)); end; procedure TfrMain.CloseAll1Click(Sender: TObject); var i: integer; begin for i:= 0 to MdiChildCount - 1 do MDIChildren[i].Close; end;
Az eljárásokban használjuk a MDIChildCount tulajdonságot, amely csak olvasható és a gyerekűrlapok számát tartalmazza. Az MDIChildren tömb a létrehozott gyerekűrlapoknak tartalmazza a referenciáját. A Windows menü a gyerek ablakok elhelyezését kezeli. A megfelelő utasítások: procedure TfrMain.Cascade1Click(Sender: TObject); begin Cascade; end; procedure TfrMain.Tile1Click(Sender: TObject); begin Tile; end; procedure TfrMain.ArrangeAll1Click(Sender: TObject); begin ArrangeIcons; end;
Az MDI alkalmazásunk most már teljesen készen van és tesztelhetjük.
63
4.3.3. ábra. Az MDI alkalmazásunk futás közben
64
5. GRAFIKA Tervezési időben megadható grafikus elemekkel megismerkedtünk már (TShape, TImage), most nézzük, milyen lehetőségeket kínál a Delphi a programunk futása közben létrehozható grafikus objektumok terén. Futás közben, Delphiben azoknak a komponenseknek a felületére rajzolhatunk, amelyek rendelkeznek canvas („rajzvászon”) jellemzővel. Ilyen komponens például a TImage, TBitmap, TPrinter, TForm, stb. A canvas tulajdonképpen egy objektum, amely tartalmazza a rajzoláshoz szükséges tulajdonságokat, metódusokat. Mivel ez a jellemző csak publikus (public) és nem publikált (published) tervezési időben nem érhető el (nem jelenik meg az Object Inspectorban a jellemzőknél), csak a program futása közben. A komponensek rajzvásznának az osztálya TCanvas, amely tartalmazza a rajzoláshoz szükséges jellemzőket és metódusokat. A következő részben ezeket fogjuk áttekinteni.
5.1. A rajzvászon fontosabb jellemzői •
Pen: a rajzoló toll. Tulajdonságai: o Pen.Color: szín o Pen.Style: a toll stílusa: psSolid: folyamatos vonal. psDash: szaggatott vonal. psDot: pontozott vonal. psDashDot: pontvonal. psDashDotDot: dupla pontvonal (két pont és egy rövid szakasz ismétlődik) psClear: nincs vonal. Ezt akkor használhatjuk, ha egy objektumnak nem szeretnénk körvonalat rajzolni. o Pen.Mode: a rajzolási mód: pmBlack: a vonal színe mindig fekete lesz. pmWhite: a vonal színe mindig fehér lesz. pmNop: nem történik rajzolás. pmNot: a rajzvászon színének az inverzével történik a rajzolás. pmCopy: a Color jellemzőnél megadott színt használja rajzoláshoz. Ez az alapértelmezett beállítás. pmNotCopy: a Color jellemzőnél megadott szín inverzével történik a rajzolás. pmMerge: A vászon színének és a Color jellemzőnél megadott színnek a kombinációja. pmXor: XOR művelet (kizáró vagy) a vászon színe, és a Color jellemzőnél megadott szín között. pmNotXor: NotXOR művelet a vászon színe, és a Color jellemzőnél megadott szín között (az előző művelet eredményének a fordítottja). Törlésre használjuk: ha egy objektumot ilyen rajzmódban újra 65
megrajzolunk (rárajzolunk saját magára), akkor az eltűnik, a háttérszínt kapjuk vissza. o Pen.Width: a toll vastagsága. •
Brush: a rajzvászonra rajzolt objektumokat (kör, téglalap, stb.) kitöltő, kifestő ecset tulajdonságai: o Brush.Color: szín o Brush.Style: az ecset stílusa: bsSolid: nincs minta, folyamatos kitöltés a Color jellemzőben megadott színnel. bsClear: nincs kitöltés. Akkor használjuk, ha például az objektumnak csak a körvonalát szeretnénk megjeleníteni, vagy a felirat hátterét átlátszóvá tenni. bsHorizontal: vízszintes csíkozás. bsVertical: függőleges csíkozás. bsFDiagonal: 135 fokos ferde csíkozás bsBDiagonal: 45 fokos ferde csíkozás. bsCross: vízszintes és függőleges csíkozás kombinációja. bsDiagCross: a bsFDiagonal és bsBDiagonal minták kombinációja. o Brush.Bitmap: egy TBitmap típusú kisméretű grafikus objektum, amivel a vászonra rajzolt grafikus objektumokat kitöltjük.
•
Font: A rajzvászonra szöveget is lehet írni (rajzolni), ez a jellemző határozza meg a szöveg tulajdonságait. PenPos: TPoint (x,y pár) típusú érték, a toll aktuális pozícióját jelenti. Pixels: egy kétdimenziós tömb, ami a rajzvászon képpontonkénti színét jelenti.
• •
Példa: az űrlap rajzvásznának egy bizonyos téglalap alakú tartományát piros színűre fessük. for i:=10 to 100 do for j:=10 to 100 do Canvas.Pixels[i,j]:= clRed;
5.2. A rajzvászon fontosabb metódusai • • •
MoveTo(x,y:integer): a tollat a vászon (x,y) koordinátájú pontba helyezi LineTo(x,y:integer): vonalat húz a toll aktuális pozíciójából az (x,y) koordinátájú pontba TextOut(x,y:integer,s:string): az s paraméterben megadott szöveget, elhelyezi az (x,y) koordinátájú pontba Példa: gomb kattintásra az űrlap rajzvásznára egy egyenest rajzolunk és kiírunk egy szöveget egy adott pozícióba: procedure TForm1.Button1Click(Sender: TObject); begin
66
with Form1.Canvas do begin MoveTo(10,10); LineTo(100,50); TextOut(150,150,'Helló!'); end; end;
• • • •
•
Rectangle(x1,y1,x2,y2:integer): téglalap rajzolása, (x1,y1) jelenti a bal felső sarok koordinátáját, (x2,y2) a jobb alsó sarokét. RoundRect(x1,y1,x2,y2,x3,y3:integer): lekerekített sarkú téglalap, az első két koordináta-pár jelenti a sarkakat, az utolsó két paraméter annak az ellipszisnek a nagy és kistengelye, amely meghatározza a sarkak görbületét. Ellipse(x1,y1,x2,y2:integer): a koordináták által határolt téglalapba beférő ellipszist rajzol, ha ezek négyzetet alkotnak, akkor kört. Draw(X, Y: Integer; Graphic: TGraphic): a vászon (x,y) koordinátájú pontjában elhelyezi a Graphic paraméterben megadott grafikus objektumot. Ez lehet egy TBitmap típusú objektum is, ilyenkor a CopyMode jellemzőben megadott módon történik a kép elhelyezése. StretchDraw(Rect: TRect; Graphic: TGraphic): hasonló a Draw eljáráshoz, a Graphic paraméterben megadott grafikus objektumot a Rect paraméterben megadott téglalapba beszorítva helyezi el. A kép nem az eredeti méretben (mint a Draw eljárásnál), hanem a téglalap által meghatározott méretben jelenik meg. Példa: Image1.Canvas.StretchDraw(Rect(100, 50, 325, 120), kep);
•
FillRect(p:TRect): a megadott téglalap alakú tartományt kitölti az aktuális ecsettel Példa: külső állományból egy bitképet töltünk be és jelenítünk meg az ecset segítségével: procedure TForm1.Button1Click(Sender: TObject); var bmp: TBitmap; begin bmp := TBitmap.Create; bmp.LoadFromFile('kep.bmp'); Canvas.Brush.Bitmap := bmp; Canvas.FillRect(Rect(0,0,Width,Height)); bmp.Free; end;
•
CopyRect(Dest: TRect; Canvas: TCanvas; Source: TRect): ha egy képnek csak egy bizonyos részletét szeretnénk a vászonra másolni, akkor erre a CopyRect eljárást használjuk. Ennek első paramétere TRect típusú, és a célterület koordinátáit adja meg. A második paraméter az a vászon, amiről a képrészletet másolni szeretnénk. Az utolsó paraméter szintén TRect típusú, és a másolandó terület koordinátáit tartalmazza. Az alábbi példában a az első kép (x1,y1,x2,y2) 67
által határolt területét, a második kép (10, 10, 80, 80) által határolt részére másoljuk be. Példa: Image2.Canvas.CopyRect(Rect(10, 10, 80, 80), Image1.Canvas, Rect(x1, y1, x2, y2));
•
FloodFill(x,y:integer, Color:TColor, FillStyle:TFillStyle): zárt területek kifestésére alkalmas eljárás, a kifestés az (x,y) koordinátájú pontból indul, a Color paraméter egy színt jelent, a negyedik paraméter pedig a kitöltés módja. A szín paraméter felhasználási módja ez utóbbi paramétertől függ. A kitöltés módja kétféle lehet: • fsSurface: a kitöltés addig folytatódik, amíg a paraméterként megadott színű összefüggő területet talál. • fsBorder: a paraméterként megadott szín annak az alakzatnak a körvonal színe, amit ki szeretnénk festeni. Ha az alakzatban vannak más színű pontok is, azok is ki lesznek festve. Az alábbi példák közül az első a Canvas (10,10) pozíciójában lévő pixeltől kezdve kifesti azt az alakzatot, aminek színe a megadott koordinátában lévő szín. A második példa egy olyan alakzatot fest ki, aminek a körvonala fekete színű. A megadott koordinátának az alakzaton belül kell lennie. Példa: Image1.Canvas.FloodFill(10,10,Image1.Canvas.Pixels[10, 10], fsSurface); Image1.Canvas.FloodFill(40, 50, clBlack, fsBorder);
Megjegyzés: az előző példákban, gombkattintásra rajzoltunk az űrlapra valamilyen alakzatot. Ennek a rajzolásnak az a hátránya, hogy a rajz csak addig látszik, amíg az űrlap újra nem frissül. Ezért az a szokás, hogy a rajzolási műveleteket, az űrlap OnPaint eseményébe helyezzük, így az űrlap frissítésekor az esemény lefut és a rajzolás újra megtörténik. 5.3. Grafikus nyomtatás Ha alkalmazásunkból grafikát szeretnénk kinyomtatni, akkor ezt megtehetjük a Printers egységben deklarált TPrinter típusú Printer objektummal. Ez rendelkezik Canvas jellemzővel, tehát a nyomtatás úgy fog történni, mintha bármilyen más vászonnal rendelkező objektumra rajzolnánk. Első lépésként a Printers egységet beírjuk fent a Uses kulcsszó utáni egységek listájába, majd a nyomtatás a Printer.Canvas metódusaival történik. A Printer fontosabb jellemzői: • Canvas: a rajzvászon.
68
• • • •
Printing: boolean típusú, ha értéke True, azt jelenti, hogy a nyomtatás folyamatban van. PageNumber: egész típusú jellemző, a nyomtatás alatt álló oldal számát jelenti. PageHeight, PageWidth: a lap méretei. Copies: nyomtatási példányszám.
A Printer fontosabb metódusai: • BeginDoc: ezzel az utasítással indul a nyomtatás, elvégzi a nyomtatási előkészületeket. • EndDoc: nyomtatás befejezése. • Abort: nyomtatás megszüntetése. • NewPage: lapdobást, új lap kezdését jelenti. A nyomtatás lépései: • Előkészületek: használhatjuk a Dialogs komponens-paletta két dialógus ablakát a PrintDialog-ot és PrinterSetupDialog-ot a nyomtatási paraméterek és a nyomtató beállítására. • Printer.Begindoc; • Rajzolás, írás a nyomtató vászonára a vászon metódusainak segítségével. • Printer.EndDoc; Példa: procedure TForm1.Button1Click(Sender: TObject); begin if PrintDialog1.Execute then with Printer do begin BeginDoc; Canvas.TextOut(’Nyomtatás’); EndDoc; end; end;
69
6. KIVÉTELEK KEZELÉSE 6.1. Mi a kivétel? A kivétel (exception) olyan esemény, amely valamilyen program futási hiba miatt keletkezik. Az ilyen váratlan, a normál futást megzavaró események bekövetkezésekor az aktuális programágat megszakítva valamilyen magasabb szintre kell visszaadni a vezérlést. A hiba kezelése hagyományos módon általában feltételek segítségével történik, melyekben valamilyen változók, hibakódok, függvények és eljárások visszaadási értékeit figyeljük. Ennek a módszernek több hátránya is van, például a függvények visszatérítési értékét figyelni kell és hiba esetén fel kell szabadítani az erőforrásokat, jelenteni kell a hívónak. A hívási lánc minden eleme felelős azért is, hogy a hiba információt továbbadja a fölötte levőnek, ami nem mindig sikerül. A kivételek ezt a problémát úgy oldják meg, hogy továbbító mechanizmust kiveszik a kódból. A kivételek többletet jelentenek abból a szempontból is, hogy általános és egységes hibajelentési mechanizmust alkotnak, amit a Delphi használ. Ha valami hiba történik futási időben a Delphi egy kivételt vált ki. Ha a program helyesen van megírva a felmerülő hibát képes lehet megoldani, különben a kivétel továbbkerül a hívóhoz. Ha a programunkban nincs kivételkezelés, a Delphi megteszi helyettünk, hogy megjelenít egy szabványos hibaüzenetet és megpróbálja folytatni a programot. A kivételek egész mechanizmusa négy kulcsszón alapszik: • try: a védett kód kezdetét jelzi, amelyben előreláthatóan bekövetkezhet a hiba • except: a védett kód végét jelenti, a kivételek kezelésére szolgáló parancsokat tartalmazza a következő formában: on kivételtípus do utasítás
• •
finally: annak a programrésznek az elejét jelzi, amely minden esetben végrehajtódik, akár bekövetkezett a kivétel, akár nem. Ezt a részt általában a lefoglalt memória felszabadítására, megnyitott fájlok bezárására használjuk. raise: kivétel generálására szolgál. A legtöbb kivételt, amivel a Delphiben találkozunk, a rendszer generálja, de bizonyos esetekben van értelme saját kivételek kiváltására is.
A Delphi a kivételek kezelésére alapvetően két fajta szerkezetet bocsát a programozók rendelkezésére. Bár a két szerkezet felépítése és szintaktikája nagyban hasonlít egymásra, alapvetően eltérő feladatokra használhatóak. Az alábbiakban a két szerkezetet mutatjuk be. 6.2. Kivételek kezelése: try-except A try-except-end szerkezetet abban az esetben érdemes használnunk, amennyiben a kivételt továbbterjedését megakadályozni szeretnénk. Ez a szerkezet lehetőséget ad az egyes kivételek típusa alapján történő megkülönböztetésre is, és arra, hogy a kezelni nem 70
tudott kivételek továbbterjedését ne akadályozzuk meg, vagy további vizsgálatoktól függően annak folytatását kérjük. A szerkezet szintaktikája a következő: { megelőző blokk } try { védett blokk } except [ [on [
:<]kivétel-típus1> do { kivételkezelő blokk1 }] [on [:<]kivétel-típus2> do { kivételkezelő blokk2 }] ] | [{kivételkezelő blokk}] end; { követő blokk }
A try-except úgy működik, hogy amennyiben a szerkezet védett blokkjában kivétel keletkezik, úgy a vezérlés az except-end közötti részre adódik. Amennyiben a védett blokk végéig nem keletkezik kivétel, úgy az except-end közötti rész nem kerül végrehajtásra, hanem a védett blokk utolsó utasítása után a követő blokk első utasítására kerül a vezérlés. A try-except szerkezetnél opcionálisan megadható az elfogni kívánt kivétel(ek) típusa(i) is. Ebben az esetben az except után az on..do szerkezetet kell használni, a kulcsszavak között egy kivétel-típust (osztálynevet) megadva. A következő példában két egész szám osztása esetén használjuk a try-except szerkezetet. procedure Form1.Button1Click(Sender: TObject); var a, b, c: Integer; begin a := 0; b := 0; try c := a div b; except on EdivByZero do ShowMessage(’Nullával nem lehet osztani!’); end; end;
A fenti példában nullával való osztás esetén a kivételkezelő elkapja a kivételt és megjeleníti a megfelelő üzenetet a képernyőn. A kivételkezelő részben az EdivByZero (nullával való osztás) kivételt fogjuk el, amit a Delphi definiál. A Delphi saját kivételei mellett a felhasználónak lehetősége van saját kivételek definiálására is.
6.3. Erőforrás védelem: try-finally Az osztott, vagy csak átmenetileg lefoglalt erőforrások esetén fontos, hogy a rutinok azonnal elengedjék vagy felszabadítsák azokat, miután befejezték a műveletvégzést azokon. Ilyen erőforrás lehet például egy állomány, egy adatbázis. 71
A hagyományos megoldás, amely esetében a rutin elején az erőforrás lefoglalásra, a végén pedig felszabadításra kerül, az osztott erőforrások indokolatlan foglalásához és a korlátozott erőforrások gyors kimerüléséhez vezethet, ha a rutinon belül kivétel keletkezik, hiszen ebben az esetben az eljárás végén lévő felszabadító kódra nem kerül már a vezérlés. Az ilyen jellegű erőforrás-vesztéses problémák elkerülésére alkalmas a try-finallyend kivételkezelő szerkezet, amellyel gondoskodhatunk arról, hogy az erőforrást felszabadító, vagy a rendszert újból konzisztens állapotba hozó műveletek minden esetben (kivétel keletkezése és normál futás esetén is) végrehajtódjanak az eljárásból történő kilépés előtt. { megelőző műveletek } { erőforrás lefoglalása } try { műveletvégzés } finally { erőforrás felszabadítása } end; { követő műveletek }
A try-finally-end szerkezet lényege, hogy a finally-end közötti műveletsor végrehajtódik abban az esetben is, ha a try-finally közötti rész normálisan ér véget, valamint akkor is, ha abban kivétel keletkezik. Az előbbiek szemléltetésére tekintsük az alábbi példát: procedure Form1.Button1Click(Sender: TObject); var a, b, c: integer; begin a := 0; b := 0; try c := a div b; finally Form1.Caption := ’finally’; end; end;
Ebben az esetben a finally-end törzsében található utasítás minden körülmények között lefut. Ebben az esetben, annyi a probléma hogy nincs kivételkezelés, viszont ez is megoldható kód egymásba ágyazással az alábbiak szerint: procedure Form1.Button1Click(Sender: TObject); var a, b, c: Integer; begin a := 0; b := 0; try try c := a div b; except on EdivByZero do ShowMessage(’Nullával nem lehet osztani!’);
72
end; finally Form1.Caption := ’finally’; end; end;
6.4. Kivétel generálása: raise A raise segítségével egy kivétel előidézését érhetjük el. Leggyakrabban a saját készítésű kivételeink kiváltására alkalmazzuk. A következő példában ennek a használatát mutatjuk be. Egy szövegmezőben bekérünk egy egész számot a felhasználótól, majd gombkattintásra megvizsgáljuk, hogy 1 és 10 között található-e? Ha nem akkor kiváltunk egy saját tervezésű kivételt. A továbbiakban hozzunk létre egy új kivételosztályt ENemJoSzam névvel, a főűrlap egységében: ENemJoSzam = class(Exception);
Az űrlapon található gomb OnClick eseménye a következő lesz: procedure Form1.Button1Click(Sender: TObject); var a: Integer; begin try a:=StrToInt(Edit1.Text); if (a < 1) Or (a > 10) then Raise ENemJoSzam.Create(’Nem jo szam!’); {Itt jon a feldolgozas} … except on E:ENemJoSzam do ShowMessage(E.Message); end; end;
73
7. ÁLLOMÁNY-KEZELÉS Delphiben az állománykezelés a Pascal nyelvből jól ismert állománykezeléssel vagy a vizuális komponenskönyvtár állománytámogatása segítségével történhet, amely apró eltérésekkel megegyezik a Pascal nyelvben használt állománykezeléssel. Az eltérés három utasítást érint: a Text (szöveg állomány típusa), Assign (állomány hozzárendelése), Close (állomány bezárása) parancsok a Delphiben TextFile, AssignFile és CloseFile parancsokkal vannak helyettesítve. Minden program egy „doboz”-nak tekinthető, bemenő adatokkal (amelyeket beolvasunk) és kijövő adatokkal (amelyeket kiszámolunk és kiíratunk). Ha az adatok száma megnövekszik, akkor célszerű ezeket valamilyen állományban a lemezen tárolni. Például, ha egy programot többször futtatunk, ugyanazokkal a bemenő adatokkal, akkor a felhasználó számára sokkal könnyebb, ha nem kell minden alkalommal beírni az adatokat, csak egyszer az első beolvasáskor. Ilyenkor megoldható az, hogy a program egy állományba helyezze az adatokat, és a program következő végrehajtásakor innen olvassa be őket. Ugyanígy, ha sok kijövő adatunk van, ezeket a további felhasználás céljából érdemes lementeni. Kétféle állománnyal lehet dolgozni: • •
Típusos állományokkal, amikor binárisan, azonos típusú adatokat tárolunk. Ezek lehetnek egyszerű típusok (pl. egész számok tárolására: Integer), vagy összetett típusok (record adattípus) Szöveges állományokkal, amikor az adatok soronként, szöveg formájában vannak tárolva
Minden állománynak van egy fizikai neve: ezzel szerepel a lemezen, és a programban egy logikai neve: egy változó, ami az állományra utal. 7.1. Típusos állományok •
Deklaráció: var állomány_változó: file of típus;
Példa: var f: file of integer;
Az fenti példában az f változó az állomány logikai neve, ezzel a változóval fogunk hivatkozni az állományra a programban, a típusa pedig integer, ami azt jelenti, hogy az állományunkban egész típusú adatokat lehet tárolni. • Az állományok felépítése: 0
1
...
Filesize(f)-1
... 74
Az adatok az állományban szekvenciálisan (egymás után) vannak tárolva, minden adatkomponensnek van egy sorszáma, az elsőnek 0. Az állományban levő elemek, adatkomponensek száma lekérdezhető a Filesize(f) függvénnyel. Mivel a számozás 0-tól kezdődik, az utolsó komponens sorszáma a Filesize(f)-1 érték lesz. •
Állománymutató: o Minden típusos állomány tartalmaz egy állománymutatót, ami egy Longint típusú szám, és az aktuális adatkomponensre mutat, annak a sorszámát tartalmazza o Értéke lekérdezhető a Filepos(f) függvénnyel o Állítható: Seek(f,n); - a mutató az n. sorszámú elemre fog mutatni o Az állomány megnyitásakor értéke 0 ( az első elemre mutat) • Fizikai és logikai név egymáshoz rendelése: Az állomány-változó deklarálása után az első lépés az, hogy megadjuk, hogy a lemezen az állomány milyen néven szerepel. Példa: AssignFile(f, ’C:\Aruk.dat’); - az f változóhoz hozzárendeltük a C:
lemez Aruk.dat nevű állományát. •
Állomány megnyitása: Az állományt kétféle módon lehet megnyitni: o Rewrite(f): akkor használjuk, ha az állomány még nem létezik, ez hozza létre és megnyitja írásra. Vigyázat! - ha az állomány már létezett, akkor azt felülírja, tehát a tartalma eltűnik o Reset(f): létező állomány megnyitása • Olvasás állományból: Read(f, állomány_változó); - Az állomány aktuális elemét (amelyre az állománymutató mutat) beolvassa a állomány_változó nevű változóba. • Írás állományba: Write(f, állomány_változó); - A állomány_változó nevű változó értékét beírja az állomány aktuális elemébe. • Az olvasás és írás műveletek után a mutató egyet előre ugrik. • Állomány vége: Ha beolvasás során az utolsó elemet is beolvastuk, akkor a mutató az utolsó elem utáni elemre mutat. Ez jelenti azt, hogy az állomány végére értünk. Ilyenkor az EOF(f) függvény értéke True-ra vált. • Állomány zárása: CloseFile(f); • Létrehozás, kiírás állományba: Az alábbi példában egy egész számokat tartalmazó állományt hozunk létre. A számokat inputbox objektummal olvassuk be.
procedure TForm1.Button1Click(Sender: TObject); var f:file of integer;
75
a,i:integer; begin AssignFile(f,’egeszek.dat’); Rewrite(f); for i:=1 to 10 do begin a:=StrToInt(InputBox(’’,’egesz szam’,’’)); Write(f,a); end; CloseFile(f); end.
•
Állomány bővítése: Hasonló a létrehozáshoz, a különbség az, hogy az állományt megnyitjuk (reset(f)) és mielőtt beírnánk az új adatokat, az állománymutatót a végére állítjuk: Seek(f, filesize(f)); (a mutató átállítása az utolsó utáni helyre). • Állomány átnevezése: Rename(f, újnév); • Állomány törlése: Erase(f); Megjegyzés: átnevezés illetve törlés előtt az állományt be kell zárni. •
Állomány módosítása: két lehetőség van o A módosított komponensek visszaírása az eredeti pozícióba. Mivel olvasáskor a mutató eggyel előre lép, visszaírás előtt a mutatót eggyel hátrább kell állítani o Az összes komponens átmásolása egy új állományba (olvasás, módosítás, kiírás az új file-ba), a régi törlése, az új átnevezése
7.2. Szöveges állományok • • • •
az adatok soronként, szöveg formájában vannak tárolva minden sor végén egy nem látható EOLN jel van, az állomány végén pedig egy szintén láthatatlan EOF jel speciális szöveges állománynak tekinthető a nyomtató is, amelybe csak írni lehet. Deklaráció: var t:TextFile;
•
• •
Nyitás, zárás: o Reset(t); - megnyitás csak olvasásra o Rewrite(t); - megnyitás csak írásra o Append(t); - megnyitás bővítésre. o Close(t); - bezárás Nem használhatók a Seek, Filepos, Filesize függvények Írás/olvasás karakterenként: var t:TextFile; ch :char ; o Write(t,ch) ; o Writeln(t) – EOLN jel írása, ezután a következő sorba írhatunk o Read(t,ch) ; 76
o Readln(t) ; ezután az olvasás a következő sorból történik •
Írás/olvasás soronként: var t:TextFile; s:string ; o Writeln(t,s); o Readln(t,s);
A következő példa segítségével szemléltetjük a szöveges állományokkal kapcsolatos műveleteket. A példa egy gomb megnyomására az aktuális könyvtárban található „data.txt” állomány tartalmát beolvassa egy Memo komponensbe: procedure TForm1.Button1Click(Sender: TObject); var fajl: TextFile; sor: String; begin AssignFile(fajl,’data.txt’); Reset(fajl); while not Eof(fajl) do begin ReadLn(fajl,sor); Memo1.Lines.Add(sor); end; CloseFile(fajl); end;
Az állományokkal való munka közben hibák is felléphetnek. A leggyakoribb hibák állomány megnyitásánál, írásánál, olvasásánál keletkezhetnek. Delphiben az ilyen típusú hibák kezelésére az EInOutError kivétel objektumot használhatjuk. Az előző feladat megoldása kivételkezeléssel a következőképpen alakul: procedure TForm1.Button1Click(Sender: TObject); var fajl: TextFile; sor: String; begin AssignFile(fajl,’data.txt’); try Reset(fajl); try while not Eof(fajl) do begin ReadLn(fajl,sor); Memo1.Lines.Add(sor); end; finally CloseFile(fajl); end;
77
except on E:EInOutError do case E.ErrorCode of 2: ShowMessage(’Nincs meg a fájl!’); 103: ShowMessage(’A fájl nem volt megnyitva!’); else ShowMessage(’Hiba: ’ + E.Message); end; end; end;
A keletkező hiba kódját a program az ErrorCode váltózóba helyezi. 7.3. Szöveges nyomtatás A nyomtatót egy speciális szövegállománynak is lehet tekinteni, így az ebbe a szövegállományba írt szöveg megjelenik kinyomtatva. Módszer: • Deklarálni kell egy szövegállomány-változót. Például: var p: text; • Az AssignPRN(p) utasítással a p szövegállományt hozzárendeljük az alapértelmezett nyomtatóhoz • A reset(p) utasítással megnyitjuk az állományt, elkezdjük a nyomtatást • A write és writeln utasításokkal szöveges vagy numerikus változókat írhatunk az állományba (küldhetünk a nyomtatóra). Például: •
writeln(p,sor);
A CloseFile(p) utasítással befejezzük a nyomtatást
7.4. Egyéb lehetőségek Delphiben Ha nincs szükségünk igényesebb állománykezelésre, akkor állomány olvasásra és írásra használhatjuk a Delphi által biztosított LoadFromFile és a SaveToFile metódusokat is. Mindkét metódusnak egyetlen paramétere van, az állomány neve ahonnan olvasunk, vagy ahová kiírunk. Az előző példa megvalósítása a LoadFromFile utasítással: procedure TForm1.Button1Click(Sender: TObject); begin Memo1.Lines.LoadFromFile(’data.txt’); end;
Más hasznos metódusok: • • •
FileExist(név): értéke true, ha a megadott nevű állomány létezik DeleteFile(név): kitörli a megadott nevű állományt és true értéket ad vissza, ha a törlés sikeres volt RenameFile(réginév, újnév): átnevezi az állományt és true értéket ad vissza, ha az átnevezés sikeres volt 78
• • •
ChangeFileExt(név, kiterjesztés): megváltoztatja az állomány kiterjesztését és visszaadja az állomány új nevét ExtractFileName(teljes_név): a teljes útvonalból kiveszi csak a állomány nevét, melyet visszaad ExtractFileExt(név): az adott állomány kiterjesztését adja vissza
79
8. ADATBÁZIS-KEZELÉS DELPHIBEN Az adtabázis-kezelés a Delphi egyik kulcsfontosságú eleme. Nagyon gazdag eszköztárral rendelkezik, ami szükséges egy modern adatbázis készítéséhez. Egyszerűségének köszönhetően nagyon népszerű az adatbázis fejlesztők körében. A következő részben betekintést nyújtunk a Delphi adatbázisokkal kapcsolatos lehetőségeibe, amelyben az adatbázis tervezés elméleti bemutatásától eltekintünk. Először azt fogjuk áttekinteni, hogyan működik Delphiben az adatelérés, majd bemutatjuk az adatbázisokkal kapcsolatos fontosabb komponenseket, néhány egyszerűbb példán keresztül illusztrálva azok működését.
8.1. Adatbázis motor (BDE) A BDE a Borland Database Engine rövidítése, amely egy Windows alapú 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. A BDE egységes felületet biztosít a különböző formátumú adatbázisok eléréséhez. Tehát a Delphi adatbázis-alkalmazások nem közvetlenül kommunikálnak az adatforrásokkal, hanem a BDE segítségével, amely a megfelelő meghajtóprogram használatával közvetlen kapcsolatot tud kiépíteni különböző típusú adatbázisokkal. A BDE a következő adatbázisokat támogatja: dBASE, Paradox, Text, FoxPro, Access, InterBase, Oracle, Sybase, Microsoft SQL, ODBC(Microsoft Open Database Connectivity). Az adatbázis-szerverek eléréséhez a BDE az ún. SQL-links csomagot használja. Az SQL-links gyakorlatilag kiegészítő drivereket tartalmaz a BDEhez. Az adatbázis motor telepítése a Delphi környezet telepítésével együtt történik, de szükséges a telepítése minden olyan gépen ahol az adatbázis alkalmazásunkat futtatni szeretnénk.
8.1.1. ábra. Adatelérés Delphi-ben
80
8.2. BDE álnevek (Alias) 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.). 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. A BDE Administrator esetén úgy történik, hogy az Object/New menüpontot kiválasztva megadjuk az adatbázis típusát majd beírjuk az alias nevét, és kitöltjük a jobb oldalon a Path mezőt.
8.2.1. ábra. Aliasok kezelése a BDE Administrator segítségével
A különböző adatbázis műveleteket a fenti ábrán is látható DBDEMOS alias adattábláin fogjuk bemutatni. Ez egy előre elkészített példa adatbázis, amelyet a Delphi a környezetével egyszerre telepít. 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. Maguk a komponensek két fő csoportra bonthatóak: látható, és nem látható komponensekre. A nem látható komponensek fogják a kapcsolatokat a háttérben felépíteni, majd az összeköttetést biztosítani a látható komponenseink, és a háttérben lévő adatbázis között. Funkcionalitásukat tekintve adatelérési és adatmegjelenítési komponenseknek is nevezzük.
8.3. Adatelérési komponensek (Data Access) Az adatkezelési komponensek a Data Access valamint a BDE komponenspalettán találhatók. Ezek közül csak a legfontosabbakat tekintjük át.
81
8.3.1. Fontosabb komponensek TDataSource Delphiben egy adatbázis eléréséhez mindenekelőtt szükségünk van egy adatforrásra, amelyet egy TDataSource komponenssel adhatunk meg. Ez az elem azonban nem közvetlenül mutat az adatbázisra, hanem egy táblára, egy lekérdezésre vagy egy tárolt eljárásra vonatkozik. Ezért szükségünk van még egy Table (tábla), Query (lekérdezés) vagy StoredProc (tárolt eljárás) komponensre. Az adatelérési és adatmegjelenítési komponensek nem tudnak közvetlenül egymáshoz kapcsolódni. Ahhoz hogy az adatelérési komponens által reprezentált adatokat meg tudjuk jeleníteni egy adatmegjelenítési komponenssel, szükség van a TDataSource-ra, amely először kapcsolódik egy TDataSet komponenshez, majd ezután egy adatmegjelenítési komponens DataSource tulajdonságát tudjuk ráállítani az adatforrásunkra.
8.3.1.1. ábra. A TDataSource komponens kapcsolata az adatelérési valamint az adatmegjelenítési komponenssel
TTable A legegyszerűbb módszer, amivel adathozzáférést biztosíthatunk a Delphiben, a TTable komponens használata. Ha felhelyezünk egy ilyen komponenst az űrlapra, az első dolog, amit tennünk kell, hogy megadjuk a használt adatbázis nevét a tábla DatabaseName tulajdonságánál. Itt megadhatunk egy nevet, egy álnevet, vagy egy olyan könyvtár elérési útját, amely a használni kívánt táblákat tartalmazza. Itt általában megjelennek az elérhető adatbázisok, amelyek előzőleg a BDE-ben voltak telepítve. Másfelől meg kell adni a helyes táblanevet a TableName tulajdonságnál. Itt is megjelennek az aktuális adatbázis választható táblái, így mindig érdemes előre a DatabaseName tulajdonságot beállítani.
82
A TTable komponensnek számos tulajdonsága van. 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. Az Active tulajdonság segítségével már akár szerkesztő módban is bekapcsolhatjuk a táblát, de használhatjuk futásidőben az Open illetve a Close helyett is. 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.
TQuery A másik fontos adatelérési komponens Delphiben. 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 végezhetünk. Itt is találunk a táblákhoz hasonló tulajdonságokat és eseményeket. Ebből is látszik, hogy csak abban különbözik a táblá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ünk adatmódosító műveleteket (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. Metódusai közül az ExecSQL nevűt említhetjük meg, mellyel lefuttathatjuk a lekérdezésünket. Természetesen a táblákhoz hasonlóan itt is működik az Open és a Close metódus is.
8.3.2. Adathalmazok állapota Az adathalmazoknak (tábla, lekérdezés) különböző állapotai lehetnek, amelyek közül az éppen aktuálisat a State tulajdonság jelöl. Egyszerűbb esetekben az állapotváltozás automatikusan megtörténik, mégis lényeges ezeknek a jelentését megérteni. A State tulajdonság fontosabb értékei és jelentése: • •
• • •
dsBrowse: az adathalmaz normál böngésző üzemmódban van, amelyet az adatok nézegetésére, vagy a közöttük való keresésre használhatjuk dsEdit: az adathalmaz szerkesztő üzemmódban van. Egy adathalmaz akkor kerül ilyen állapotba, amikor a programunk meghívja az Edit metódust, és a felhasználó egy adatfüggő vezérlőt, például egy DBGrid, vagy egy DBEdit komponenst használ éppen. Az adathalmaz kilép ebből az állapotból, mikor a szerkesztés befejeztével a változásokat a program elküldi az adatbázisnak. dsInsert: egy új rekordot szúrtunk be a meglévők közé. Ez akkor történhet meg, ha meghívjuk az Insert metódust vagy kiadjuk a DBNavigator megfelelő parancsát. dsInactive: az adathalmaz le van zárva dsCalcFields: az adathalmaz rekordjai között valamilyen számolási műveletet hajtunk végre, amelyet az OnCalcFields eseménykezelővel hívtunk meg 83
•
dsFilter: egy szűrőt állítunk be
8.3.3. Adathalmazok nyitása és zárása
Az adathalmazokat tervezési és futási időben is meglehet nyitni és be lehet zárni. Tervezéskor csak az Active jellemzőjük segítségével tehetjük ezt, míg futáskor használhatjuk az Active jellemzőt valamint az Open és Close metódusokat is. Tervezési időben Nyitás Active:= True Zárás Active:= False
Futási időben Open vagy Active:= True Close vagy Active:= False
8.3.4. Mozgás az adathalmazban Minden nyitott adathalmazban létezik egy rekordmutató, ami az aktuális rekordon áll. Ennek mezőértékeire vonatkozik a szerkesztés és törlés. Az aktuális rekord megváltoztatását az ún. navigációs metódusokkal érhetjük el. Ezek a következőek: • • • •
First: a mutatót az adathalmaz első rekordjára helyezi. Last: a mutatót az adathalmaz utolsó rekordjára helyezi. Next: a következő rekordra ugrik. Ha nincs következő, akkor a mutató a helyén marad. Prior: az előző rekordra ugrik. Ha nincs előző, akkor a mutató a régi helyen marad.
8.3.5. Adatelérési komponensek fontosabb közös tulajdonságai • • • • • • • • • •
Active: egy adathalmaz megnyitására, zárására szolgál. Ha True, akkor van nyitva az adathalmaz, egyébként zárva. Bof: értéke True, ha a kurzor az adathalmaz elején van, egyébként False. CanModify: azt jelzi, hogy módosítható-e az adathalmaz adata. DatabaseName: az adatbázis neve, melyhez kapcsolódik a komponens (lehet fizikai út, vagy alias) Eof: értéke True, ha a kurzor az adathalmaz végén van, egyébként False. FieldCount: az adathalmaz mezőinek számát adja. Fields: egy tömb, mely TField objektumokat tartalmaz. Filter: szűrési feltétel adható meg, az adathalmaz rekordjaira. Filtered: ha értéke True, akkor csak a szűrési feltételnek megfelelő rekordok jelennek meg, ha False akkor az összes rekord FilterOptions: a szűrés tulajdonságainak beállítása.
84
• • • •
Found: jelzi, ha egy keresés sikeres volt. Modified: jelzi, ha egy rekord módosítása megtörtént. RecNo: az aktuális rekord számát adja vissza az adathalmazban. RecordCount: az adathalmaz összes rekordjának számát adja.
Természetesen ezeken kívül egyes komponensek további egyéb tulajdonságokkal rendelkeznek, de ezek egyenkénti részletes felsorolása nem célunk. 8.3.6. Adatelérési komponensek fontosabb közös metódusai • • • • • • • • • • • • • • • • • • • • • • • • •
Append: létrehoz egy üres rekordot és az adatbázis végéhez fűzi. AppendRecord: létrehoz egy üres rekordot, és azt feltölti az átadott értékekkel. Cancel: egy rekord szerkesztésekor lehet érvényteleníteni a módosításokat. ClearFields: törli az adott rekord összes mezőjének értékét. Close: az adathalmaz lezárása. Delete: törli az aktuális rekordot. Edit: engedélyezi az aktuális rekord szerkesztését. FindFirst: megkeresi a szűrési feltételnek megfelelő első rekordot. FindNext: megkeresi a szűrési feltételnek megfelelő következő rekordot. FindLast: megkeresi a szűrési feltételnek megfelelő utolsó rekordot. FindPrior: megkeresi azt a megelőző rekordot, amely megfelel a szűrési feltételnek. First: a kurzort az adathalmaz első rekordjára állítja. GetFieldNames: visszaadja egy adathalmaz mezőinek nevét. Insert: beszúr egy rekordot az adathalmazba, az aktuális pozícióban. InsertRecord: beszúr egy rekordot és feltölti az átadott értékekkel. Last: az adathalmaz utolsó rekordjára ugrik. Locate: keresés. Ha van találat, a feltételnek eleget tevő rekord lesz az aktuális. Lookup: keresés, amely a keresett értéket adja vissza, a rekordmutató nem mozdul el. MoveBy: a rekordmutatót az aktuális pozícióból megadott értékkel elmozgatja. Next: a rekordmutatót a következő rekordra viszi. Open: megnyit egy adathalmazt. Post: szerkesztés után ezzel a metódussal lehet az adatok módosítását véglegesíteni. Prior: a rekordmutatót a megelőző rekordra viszi. Refresh: az adathalmaz által reprezentált adatokat frissíti az adatbázisból. SetFields: egy adathalmaz egy rekordjának összes mezőjét egyszerre lehet módosítani segítségével. 8.3.7. Fontosabb közös események
•
AfterCancel: rekord szerkesztéskor Cancel után hívódik meg. 85
• • • • • • • • • • • • • • • • • • •
AfterClose: adathalmaz zárása után hívódik. AfterDelete: rekord törlése után hívódik. AfterEdit: rekord szerkesztése után hívódik. AfterInsert: rekord beszúrása után hívódik. AfterOpen: adathalmaz megnyitása után hívódik. AfterPost: egy rekord módosításának Post meghívása után hívódik. BeforeCancel: rekord szerkesztéskor Cancel előtt hívódik meg. BeforeClose: adathalmaz lezárása előtt generálódik. BeforeDelete: rekord törlése előtt generálódik. BeforeEdit: azelőtt generálódik, mielőtt egy adathalmaz szerkeszthető állapotba kerülne. BeforeInsert: rekord beszúrása előtt hívódik. BeforeOpen: azelőtt generálódik, mielőtt egy adathalmaz megnyitott állapotba kerül. BeforePost: a Post metódus meghívása előtt generálódik. OnCalcFields: egy mező számításakor hívódik (ide kell elhelyezni a képletet, ami a számított mező értékét számolja) OnDeleteError: egy rekord törlésének hibájakor generálódik. OnEditError: akkor generálódik, ha szerkesztéskor valami hiba lép fel. OnFilterRecord: adathalmazok szűrésére használjuk. OnNewRecord: egy új rekord felvitelekor hívódik. OnPostError: akkor generálódik, ha a Post metódus meghívásakor hiba lép fel.
8.4. Adatmegjelenítési komponensek (Data Controls) Az adatmegjelenítési komponensek a Data Controls komponenspalettán találhatók. Ezen komponensek használata feltételezi egy valamilyen adatkezelési komponens létezését, ezért ha elhelyeztünk az űrlapon egy ilyen komponenst, az első dolgunk, hogy beállítjuk a DataSource jellemzőjét. A továbbiakban csak a legfontosabb komponenseket tekintjük át. TDBGrid Egyszerű táblamegjelenítő komponens. Egy TTable (vagy TQuery) objektum tartalmát jeleníti meg. A megjelenített rácsban lépkedhetünk a sorok és oszlopok között, görgethetjük az információkat és szerkeszthetjük is a tartalmát. 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énk 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. 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 86
DBGrid oszlopszerkesztője (Column Editor), amit a Columns tulajdonság gombjára kattintva érhetünk el.
8.4.1. ábra. DBGrid komponens
TDBNavigator Egy gombgyűjtemény, 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 őket. A VisibleButtons tulajdonság segítségével megadhatjuk, hogy mely gombok legyenek láthatóak.
8.4.2. ábra. A navigátorsor gombjai és szerepük
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észletezzük, csupán a standard változattól való eltérésekre és az adatbázis-kezelő vonatkozásaira térünk ki. TDBText A standard TLabel objektumhoz 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
87
Szövegét egy tábla mezőjének aktuális értékéből veszi, illetve szerkesztésénél ezt a mezőértéket módosítja. TDBListBox Segítségével egy adatforrás egy adott mezőjének értékét jeleníthetjük meg, illetve szerkeszthetjük át. Az adott mező értékének szerkesztése úgy történik, hogy a listából 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.
8.5. Egyszerű alkalmazáskészítés
A különböző adatbázis-kezelő komponensek megismerése után azok használatát mutatjuk be egyszerű alkalmazásokon keresztül. Ezek az alkalmazások többnyire adatmegjelenítéssel, navigálással foglalkoznak. Az összetettebb műveletek tárgyalása nem célunk. 8.5.1. Adatmegjelenítés vezérlő objektumok segítségével Az első példánkban megmutatjuk, hogyan lehet megjeleníteni egy DBGrid rács komponens segítségével egy adatbázis adattábláját. Ahogy már korábban említettük a példák során a Delphi DBDEMOS nevű példa adatbázisát fogjuk használni. Első lépésként helyezzünk az űrlapra egy Table, egy DataSource és egy DBGrid komponenst. Ezután kapcsoljuk össze a komponenseket, vagyis állítsuk a DBGrid objektum DataSource tulajdonságát DataSource1-re, a DataSource összetevő DataSet tulajdonságát Table1-re, végül a tábla komponens DatabaseName tulajdonságát DBDEMOS-ra, a TableName tulajdonságát pedig COUNTRY.DB-re. Utolsó lépésként állítsuk a tábla Active tulajdonságát igazra, ezáltal az adatok már tervezési időben megjelennek a rácson.
88
8.5.1.1. ábra. Adattábla megjelenítése tervezési időben
Ezután az alkalmazást lehet futtatni, amelynek során ugyanazokat az adatokat fogjuk látni, mint tervezéskor, azzal a különbséggel, hogy az adatok most már szerkeszthetőek. A meglévő értékek megváltoztatása mellett új rekordok beszúrására és hozzáfűzésére is van lehetőség. Például, ha egy új rekordot akarunk beszúrni, akkor nyomjuk meg az Insert billentyűt a megfelelő kurzorpozícióban. Tudjuk már, hogy adatmódosításkor az adattábla különböző állapotokba kerülhet. Ezeknek az állapotoknak a nyomon követését megtehetjük például az adatforrás (nem tábla) OnStateChange eseményében. A következő forráskód segítségével az űrlap címében megjelenítjük a tábla megfelelő állapotát: procedure TForm1.DataSource1StateChange(Sender:TObject); var cim:String; begin case Table1.State of dsBrowse: cim := ’Browse’; dsEdit: cim := ’Edit’; dsInsert: cim := ’Insert’; else cim:= ’Egyéb állapot’; end; Caption:= cim; end;
Az előző példában a tábla minden adatát megjelenítettünk. Más komponensek segítségével megtehetjük, hogy csak a számunkra fontos adatokat jelenítjük meg. Másfelől az adatmódosítást kényelmesebben is végre lehet hajtani például a navigátorsor segítségével. A következő példa a tábla és az adatforrás mellett DBEdit komponenseket valamint DBNavigator objektumot is fog tartalmazni.
89
8.5.1.2. ábra. Adattábla megjelenítése DBEdit komponenssel
Ebben a példában is össze kell kapcsolnunk az adatfüggő vezérlőket az adatforrással, úgy hogy beállítjuk a DataSource tulajdonságukat. A szerkesztőmezők esetén pluszba meg kell adni a DataField tulajdonságát (Name, Capital, Continent a példában). Ha a tábla Active tulajdonságát igazra állítottuk, az első rekord értékei megjelennek a szerkesztőmezőkben. A navigátorsor segítségével kényelmesen böngészhetjük és módosíthatjuk adatainkat.
8.5.2. Adatmegjelenítés lekérdezés segítségével A táblákhoz hasonlóan a lekérdezések is az adatbázis bizonyos adatait képesek megjeleníteni. A következő alkalmazásunkban a TTable komponens helyett TQuery komponenst fogunk használni, amelyre ugyanazokat a beállításokat kell elvégezni, mint a tábla esetén. A lekérdezésnek van egy SQL jellemzője, ahol megadhatjuk a lekérdezés szövegét úgy tervezési, mint futási időben. Ha tervezési időben szeretnénk megadni a lekérdezési parancsot, az objektum felügyelőben az SQL jellemzőre kattintva, a megjelenő szerkesztő ablakba beírhatjuk az utasítást. Például az alábbi paranccsal a Country tábla összes mezőjét megjeleníthetjük: select * from Country
Miután megadtuk az utasítást és aktivizáltuk a lekérdezést az Active tulajdonság igazra állításával, megjelennek az adatok a megfelelő vezérlő objektumban (DBGrid, DBEdit, stb.). Természetesen a lekérdezés csak akkor fog lefutni, ha helyesen volt beírva, különben hibaüzenetet kapunk. A lekérdezéseket általában nem tervezési időben szoktuk lefuttatni, hanem futási időben. Az előző lekérdezés megvalósítását például hozzárendelhetjük egy választógomb OnClick eseményéhez az alábbiak szerint: procedure TForm1.RadioButton1Click(Sender:TObject); begin Query1.Close; Query1.Sql.Clear;
90
Query.Sql.Add(’select from Country’); Query.Open; end.
Lényeges, hogy minden alkalommal, amikor végrehajtunk egy lekérdezést, egy Close valamint egy Open műveletet kell végrehajtani. Egy másik fontos dolog, hogy alapértelmezés szerint a lekérdezés eredményét nem szerkeszthetjük. Ha ezt szeretnénk elérni, akkor a komponens RequestLive tulajdonságát igazra kell állítani. Ennek is csak akkor van értelme, ha a lekérdezés egy táblára vonatkozik, különben nem lehetséges az adat módosítás. 8.5.3. Adatmodulok használata A Data Module egy speciális űrlap, amelyre tervezési időben ráhelyezhetjük az adatelérési komponenseket. Létrehozni a File/New/DataModule menüben tudjuk. Segítségével az adatbázis-elérési komponenseket a különböző űrlapoktól elkülönítve tudjuk tárolni, ezáltal megvalósul az adatelérési logika és a felhasználói felület elkülönítése. Ez azért is fontos, mert így egy összetett adatbázis esetében elegendő, ha egyetlen felületen hozzuk létre a szükséges elemeket, majd hivatkozunk rájuk a kezelőfelületen. Az előbbi példákat természetesen meg lehet oldani adatmodul használatával is. Annyi a teendőnk, hogy a létrehozott adatmodulra ráhelyezzük és beállítjuk a megfelelő adatelérési komponenseket. A főűrlapon megadjuk a uses után az adatmodul egységét, miután már elérhetők az adatbázis-elérési komponensek. Adatmodulok használatára egy összetettebb példa található a megoldott feladatoknál.
8.5.3.1. ábra. A Data Module űrlap egy Table és egy DataSource komponenssel
8.6. Egyéb hasznos technikák 8.6.1. Rekordok szerkesztése, törlése, új rekordok felvitele Már korábban is láttuk, hogy egy adathalmaznak mindig az aktuális rekordját szerkeszthetjük, ha az adathalmaz dsEdit állapotban van. Ezért egy rekord mezőjének módosítása előtt meg kell győződnünk arról, hogy a tábla dsEdit állapotban van-e. A
91
tábla szerkeszthetőségét a CanModify jellemzőből tudhatjuk meg. Ha nincs ebben az állapotban, átállíthatjuk az Edit utasítással az alábbiak szerint: with Table1 do if CanModify then begin if not (State in [dsEdit, dsInsert]) then Edit; FieldByName(’Mező1név’).Value := érték1; FieldByName(’Mező2név’).Value := érték2; … Post; end;
A példában a módosítások mentése a postázó (Post) metódus meghívásával történik. Ha a módosításokat vissza szeretnénk vonni, akkor a postázás előtt meg kell hívni a Cancel eljárást. Rekordok törlése a Delete eljárás meghívásával történik: Table1.Delete;
A törlés sajnos nem minden esetben lesz sikeres, amelynek a különböző okaira itt nem térünk ki. A lehetséges problémák miatt érdemes a törlést kivételkezeléssel társítani. A következő példában egy gombkattintással egy adattábla aktuális rekordját szeretnénk törölni: procedure TForm1.Button1Click(Sender: TObject); begin try Table1.Delete; Except On EDatabaseError do begin ShowMessage(’A rekord nem törölhető’); Abort; end; end; end;
Új adat felvételére az adathalmaz Insert és Append metódusait használhatjuk. Ezek használata a következő példában látható: with Table1 do begin Insert; {vagy Append} FieldByName(’Mező1név’).Value := érték1; FieldByName(’Mező2név’).Value := érték2; … Post; end;
92
Új rekord felvételére használhatjuk az InsertRecord metódust is, amely paraméterként egy konstans tömböt vár. A tömb értékei az adattábla definíciójában megadott típusokat és sorrendet kell kövesse. Ha egy értéket nem szeretnénk megadni, helyette használhatjuk a null értéket. A metódus a megadott értékeket automatikusan postázza, nincs szükség a Post-ra. with Table1 do begin InsertRecord([érték1, érték2,..., értékn]); end;
Az adattábla aktuális rekordját úgy is módosíthatjuk, hogy a tábla mezőket hozzáadjuk az osztályhoz, majd az új osztálymezőknek értékeket adunk. A mezők osztályhoz való hozzáadása úgy történik, hogy kattintunk jobb egérgombbal a TTable komponensen, majd a Fields Editor segítségével (Add fields... parancs), hozzáadjuk a megfelelő mezőket. Ha például a DBDemos adatbázis, animals.dbf nevű adattáblája estén az összes mezőt az osztályhoz adjuk, akkor az osztály a következőképpen alakul: type TForm1 = class(TForm) DataSource1: TDataSource; Table1: TTable; Table1NAME: TStringField; Table1SIZE: TSmallintField; Table1WEIGHT: TSmallintField; Table1AREA: TStringField; Table1BMP: TBlobField;
... Az új mezők segítségével az adattáblát lehet módosítani: procedure TForm1.Button1Click(Sender: TObject); begin Table1.Edit; Table1Size.Value:= 20; Table1.Post; end;
8.6.2. Keresés az adathalmazban Nagyon gyakran szükségünk van egy adathalmaz egy olyan rekordjára, amely eleget tesz bizonyos feltételeknek. Egy ilyen rekordot keresés segítségével találhatunk meg. Delphiben több metódus is található, amelyekkel adathalmazokon lehet keresni. Ezek közül mi kettőt említünk meg, a Locate és Lookup metódusokat. A Locate metódus, ha van találat, akkor logikai igaz értéket ad vissza, és az első a keresési feltételnek eleget tevő rekordra áll a rekordmutató. Két keresési opció beállítása lehetséges, az egyik hogy megkülönböztesse-e a kis és nagybetűket (loCaseInSensitive), illetve, hogy csak teljes egyezés esetén jelezzen találatot, vagy részleges egyezés esetén is
93
(loPartialKey). Arra is lehetőség van, hogy egyszerre több mezőre adjunk keresési feltételt. Példa a Locate metódus használatára: with Table1 do begin if Locate(’Name’, ’Canada ’,[loCaseInSensitive]) then begin {Találat esetén itt dolgozhatjuk fel a rekordot.} end else ShowMessage(’Nincs találat!’) end;
A példában a COUNTRY táblában keresünk rá a ‘Canada’ névre a ‘Name’ mezőben. A keresés során nem különböztetjük meg a nagy és kis betűket. A Lookup nem mozgatja el a rekordmutatót, hanem a keresett mező értékét adja vissza, egyébként úgy működik, mint a Locate. Példa a LookUp használatára: Nepesseg:=Table1.LookUp(’Name’,’Canada’,’Population’);
A példában a COUNTRY táblában keressük a ‘Canada’ nevű ország lakosságának számát. 8.6.3. Egy adathalmaz szűrése Ha egy adathalmaz tartalmát valamilyen szempont szerint le kell szűkítenünk, akkor szűrést kell alkalmaznunk. Erre Delphiben két féle lehetőségünk van: • a Filter jellemző segítségével, vagy • az OnFilterRecord eseményjellemző felhasználásával Ha a Filter jellemzőt használjuk, akkor meg kell adni azt a kifejezést, ami alapján szűrjük az adathalmazt, és a Filtered jellemzőt igazra kell állítani. A kifejezés megadásánál természetesen használhatjuk a jól ismert relációs valamint a logikai műveleteket. A karakterlánc típusú mezők esetén beállíthatunk bizonyos szűrési opciókat is a FilterOptions jellemző segítségével. Ez egy halmaz típusú jellemző, amelynek maximálisan két eleme lehet: foCaseInSensitive, foNoPartialCompare. A foCaseInSensitive hatására a szűrés folyamán nem lesznek megkülönböztetve a kis és nagybetűk. Ha a FilterOptions halmaznak eleme a foNoPartialCompare is, akkor ha a feltételben ’*’ karaktert használunk, az joker karakterként fog funkcionálni, egyébként sima ’*’ karakternek számit. Példa Filter használatára: 94
with Table1 do begin FilterOptions:=FilterOptions - [foNoPartialCompare]; Filter := ’Name = ’’C*’’’; Filtered := true; end;
A szűrési feltétel megadása azt jelenti, hogy csak a feltételnek megfelelő rekordok jelennek meg az eredményhalmazban. Tehát a példában csak a ‘C’ karakterrel kezdődő ország nevek fognak megjelenni a táblában. Az OnFilterRecord eseményjellemzőt használva lehetőség nyílik arra, hogy egy rekordon belül különböző mezőértékeket hasonlítunk össze. Az eseményjellemzőt használva vigyázni kell arra, hogy ne végezzünk olyan műveletet, ami az esemény ismételt bekövetkezéséhez vezetne. Például nem szabad a FilterOptions jellemzőt átállítani. Példa az OnFilterRecord használatára: procedure TForm1.Table1FilterRecord(DataSet: TDataSet; var Accept: Boolean) begin Accept := Table1.FieldByName(’Continent’).Value = ’South America’; end;
A példában csak a Dél-Amerikai országokat jelenítjük meg.
8.6.4. Űrlapok gyors tervezése A TTable komponens mezőszerkesztőjét (Fields Editor) használhatjuk az űrlap gyors tervezésére is. Miután kiválasztottuk az adattábla mezőit, lehetőség van ezeknek az űrlapra való helyezésére Drag and Drop technika segítségével. Tehát az egér segítségével ezeket az űrlapra vonszolhatjuk. A vonszolást követően, a rendszer minden mezőnek egy DBEdit adatmegjelenítési komponenst fog generálni, amely az adattábla aktuális rekordjának mezőértékét fogja tartalmazni. A következő ábra a DBDemos adatbázis animals.dbf két mezőjéből generált DBEdit komponenst mutatja:
8.6.4.1. ábra. Űrlap tervezés Drag and Drop technikával
95
9.
HASZNOS TECHNIKÁK
Ebben a fejezetben olyan technikákat mutatunk be, amelyek az eddigi ismereteket kiegészítve nagymértékben segíthetik a felhasználót hatékony alkalmazások készítésében. 9.1. Adatbevitel konzol alkalmazás használatával Egy kezdő felhasználóban feltevődik a kérdés, hogy hogyan történik az információ bevitele Delphiben. Kérdés hogy a Pascal-ban megismert beviteli eszközök továbbra is használhatók-e? A Pascal-ban ismert adatbeviteli és megjelenítése műveleteket (readln, writeln) csak konzol alkalmazás (File | Other...| Console | Application) formájában használhatjuk. Ez egyben azt is jelenti, hogy egy Pascal nyelven megírt programot kisebb változtatásokkal futtathatunk Delphi környezetben. Az alábbi példában szereplő {$APPTYPE CONSOLE} direktíva utasítja a Delphi környezetet konzol alkalmazás generálására, azaz ebben az esetben nem jön létre grafikus felület. program olvas_ir; {$APPTYPE CONSOLE } uses SysUtils; var nev : string; ev : integer; begin write('Neve : '); readln(nev); write('Szuletesi eve : '); readln(ev); readln; end.
9.1.1. ábra. Konzol ablak Delphiben
Természetesen konzol alkalmazást nem csak adatbevitel céljából készítünk Delphiben, hanem számos más esetben alkalmazhatjuk.
96
Mivel a Delphi egy OOP nyelv, ezért kézenfekvő, hogy az információ bevitele és megjelenítése objektumok segítségével történjék. Ezért az utóbbi megoldást ritkán használjuk. Az előző fejezetekből láthattuk, hogy a leggyakrabban használt adatbeviteli és megjelenítési objektum az egyszerű szövegmező (Edit). Ebben az esetben, ha számokkal dolgozunk, mindig kell használni a konverziós függvényeket. A számokkal való munkánkat megkönnyíti a SpinEdit objektumnak a használata, mivel itt nem kell konverziót alkalmazni.
9.2. Eseményekkel kapcsolatos megjegyzések Az eseménykezelés a Delphi alkalmazások készítésének egyik legfontosabb eleme. A következő részben az ezekkel kapcsolatos megjegyzéseinket mutatjuk be. 9.2.1. A főablak OnCreate eseménye Ez a legelső esemény, amely az alkalmazásunk indításánál bekövetkezik. Tulajdonképpen az űrlapunk létrehozásakor hívódik meg, ezért olyan tevékenységeket lehet itt megadni, amelyek a globális változók kezdeti értékét állítják be, illetve más, a program elején végrehajtandó feladatokat. Itt szoktuk létrehozni a saját készítésű osztályok objektumait. 9.2.2. Egérrel kapcsolatos események Alkalmazásainkban talán a legtöbbet előforduló események az egérhez kapcsolódnak. Ezek közül a leggyakoribb az OnClick esemény, amellyel az objektumok nagytöbbsége rendelkezik. Ez az esemény egy komponensre való kattintáskor következik be, de ezen kívül vannak más esetek is amikor aktiválódik. Ilyen eset például, ha a felhasználó megnyomja az Enter billentyűt és az aktív komponens egy nyomógomb, vagy hasonlóan a szóköz leütésekor az aktív komponens egy nyomógomb vagy egy jelölőnégyzet. Szintén az OnClick esemény aktiválódik, ha a felhasználó egy lenyíló listában kiválaszt egy elemet. Az egérrel kapcsolatos események közül kiemelnénk még az OnMouseDown, OnMouseUp és az OnMouseMove eseményeket. Ezek akkor következnek be, amikor a felhasználó lenyomja, felengedi az egér gombját, illetve mozgatja az egérkurzort. Ezek az események hasonló paraméterezésüek, példaként tekintsük az OnMouseDown eseményt. procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
Az eljárás paramétereinek jelentése a következő:
97
• • •
•
Sender: azt a komponenst jelöli, amelyen bekövetkezett az esemény Button: a lenyomott egérgombot tartalmazza. Lehetséges értékei: mbLeft (bal egérgomb), mbRight (jobb egérgomb), mbMiddle (középső egérgomb). Shift: bizonyos gombok állapotát tartalmazza az esemény bekövetkezésekor. Típusa halmaz típus és lehetséges értékei: ssShift (Shift billentyű le volt nyomva), ssAlt (Alt billentyő), ssCtrl (Ctrl billentyő), ssLeft (bal egérgomb), ssRight (jobb egérgomb), ssMiddle (középső egérgomb), ssDouble (duplakattintás következett be) X, Y: az egérkurzor koordinátáit adják meg pixelben azon a komponensen belül, amelyen az esemény bekövetkezett
A következő példában minden űrlapra való kattintáskor megjelenik egy üzenet, hogy melyik egérgombbal kattintottunk. procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin case Button of mbLeft: ShowMessage(’Bal egérgomb.’); mbRight: ShowMessage(’Jobb egérgomb.’); mbMiddle: ShowMessage(’Középső egérgomb.’); end; end;
9.2.3. Billentyűzet események A leggyakoribb billentyűzet események az OnKeyDown, OnKeyUp és OnKeyPress. Az OnKeyPress egy ASCII kóddal rendelkező billentyű megnyomásakor következik be. Az eseménynek van egy char típusú Key paramétere, amely a lenyomott billentyű ASCII kódját tartalmazza. Az OnKeyDown és OnKeyUp események segítségével az ASCII kóddal rendelkező billentyűk mellett figyelni tudjuk az ASCII kóddal nem rendelkező speciális billentyűket, mint például Shift, Ctrl, F1, stb. Az első esemény akkor következik be, ha a felhasználó lenyom egy billentyűt, a második amikor felengedi. Mindkét esemény eljárásában van Key paraméter, amely a lenyomott billentyű kódját tartalmazza. A nem alfanumerikus billentyűk esetén használhatjuk a Delphi által definiált virtuális billentyűkódokat, például VK_CONTROL (Ctrl), VK_BACK (Backspace), VK_RETURN (Enter) stb. Továbbá használhatjuk az eljárás Shift paraméterét is, amely megadja, hogy a Shift, Ctrl, Alt gombok közül melyik volt lenyomva az esemény bekövetkezésekor. A következő példában egy űrlap esetén figyeljük a karakterek leütését az OnKeyPress eseményben. Ha a leütött karakter ASCII kódja nagyobb, mint az ‘A’ karakter kódja vagy megegyezik a space karakterrel, akkor megjelenik az űrlap címében. Abban az esetben, ha a ‘Backspace’ billentyűt ütjük le akkor az űrlap címét rövidítjük. Természetesen az utóbbit az OnKeyDown eseményben figyeljük. procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
98
begin if (Ord(key)>47) or (Ord(key)=32) then Form1.Caption:=Form1.Caption+Key; end; procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin if (key=VK_BACK) and (length(Caption)>0) then caption:=copy(caption,1,length(caption)-1); end;
9.2.4. Drag and Drop technika A vonszolás-dobás (Drag and Drop) technika a windows operációs rendszer egy nagyon népszerű és hasznos eleme. Vonszolás (dragging) történik, amikor egy objektumot az egér lenyomásával elmozdítunk. Az egérgomb felengedésével pedig bekövetkezik a dobás (dropping). A Delphi támogatja ezt a technikát és viszonylag könnyen megvalósíthatjuk alkalmazásainkban. A Drag and Drop technika megvalósítása három lépésben történik: •
A vonszolás elkezdése: ha az egeret lenyomjuk a vonszolandó elem fölött bekövetkezik az OnMouseDown esemény, ehhez rendelünk eljárást és beírjuk: vonszolandó_objektum_neve.BeginDrag(false);
A False azt jelenti, hogy nem egyből, hanem legalább egy 5 pixeles elmozdulásra kezdődik meg a vonszolás. A következő lépésben az objektum DragMode jellemzőjét kell beállítani. Az egyik lehetséges érték a dmManual (kézi vonszolás). Ebben az esetben a felhasználónak saját kezűleg kell megírni a BeginDrag illetve EndDrag metódusokat. A másik lehetőség a dmAutomatic, amikor a vonszolás automatikusan elkezdődik. • A vonszolás fogadása: Ezt annál az objektumnál kell beállítani, amelyikre vonszolunk. Annál az objektumnál, amelyik fölé érünk vonszolás közben, bekövetkezik az OnDragOver esemény. Amelyik objektum fogadhatja a vonszolt elemet, annak a fenti eseményéhez rendelt eljárásába beírjuk: Accept:=True;
Ez azt jelenti, hogy az objektum fogadja a vonszolt elemet. • A vonszolás befejezése (a vonszolt elem elengedése- dobása): Ha felengedjük az egér gombját egy fogadó objektum fölött, akkor az kap egy OnDragDrop üzenetet. Ehhez az eseményhez rendelt eljárásban írjuk meg, hogy mi történjen a vonszolt objektummal. (legtöbbször áthelyezésről van szó) Egy példa, amelyben egy lista elemét egy szövegmezőbe húzzuk: 99
A lista DragMode tulajdonságát automatikusra állítjuk. ListBox.DragMode = dmAutomatic;
Megírjuk a szövegmező DragOver és DragDrop metódusát. procedure TForm1.Edit1DragOver(Sender, Source: TObject; X,Y: Integer; State:TDragState; var Accept: Boolean) ; begin Accept := True; end; procedure TForm1.Edit1DragDrop (Sender, Source: TObject; X,Y: Integer) ; begin (Sender as TEdit).Text := (Source as TListBox).Items [(Source as TListBox).ItemIndex] end;
9.3. Objektumok létrehozása programból Gyakran szükségünk van objektumok létrehozására a program futása közben. Ennek a technikának a következő lépései vannak: •
Deklarálunk egy megfelelő típusú globális változót Példa: egy címke (TLabel) létrehozása: var cimke: TLabel;
•
Létrehozzuk az objektumot Példal: cimke:= TLabel.Create(Self);
ahol Self az objektum tulajdonosát jelenti (például az űrlapot) • Az objektum tulajdonságainak kezdő értéket adunk Kérdés, hogy a változó deklarációt és az objektum létrehozását hol végezzük el. Több lehetőség is van. Például helyezhetjük egy eseménybe. A következő példában az űrlapra akárhányszor kattintunk megjelenik egy címke az aktuális pont koordinátáival. procedure TfrmCimkek.FormMouseDown(Sender: TObject; Button:TMouseButton;Shift: TShiftState; X, Y: Integer); var cimke:TLabel; begin
100
cimke:= TLabel.Create(Self); with cimke do begin Parent := Self; Top:= Y; Left:= X; Caption := '('+IntToStr(X)+', '+IntToStr(Y)+')'; end; end;
•
Ha az objektummal kapcsolatos eseményeket szeretnénk feldolgozni, a megfelelő eseményhez hozzá kell rendelni a megfelelő eljárást. Példa: cimke.OnClick:=Eljaras;
ahol az OnClick az eseményt jelenti, az Eljaras pedig egy olyan eljárást, amit meg kell írni és deklarálni az űrlap (form) osztályának eljárásai között vagy az objektumok eseményeihez rendelt eljárások közül kell kiválasztani. 9.4. Dátum- és időkezelő rutinok Munkánk során szükségünk lehet az aktuális idő és dátum lekérdezésére. A Delphi számos dátum és időkezelő rutint tartalmaz. Lássuk ezek közül a legfontosabbakat: • • • • • • • •
• •
Date: függvény, ami a mai dátumot adja vissza dátum formátumban (TDateTime). Now: függvény, ami a mai dátumot és a pontos időt adja vissza Time: függvény, ami a pontos időt adja vissza DateToStr(datum): függvény, ami a paraméterként megadott dátum típusú változót szöveggé alakítja DateTimeToStr: függvény, ami a paraméterként megadott dátum típusú változót olyan szöveggé alakítja, amelyben a dátum és az idő is szerepel TimeToStr(ido): függvény, ami a paraméterként megadott dátum típusú változóból az időt szöveggé alakítja DecodeDate(Dátum,Év,Hónap,Nap): eljárás, ami az első paraméterként megadott dátum típusú változót felbontja és az Év, Hónap, Nap, word (számtípus) típusú paraméterekben adja vissza DecodeDateTime(Dátum, Év, Hónap, Nap, Óra, Perc, Másodperc, Ezredmásodperc): eljárás, ami az első paraméterként megadott, időt is tartalmazó, dátum típusú változót felbontja és a következő hét, word típusú paraméterben adja vissza EncodeDate(Év,Hónap,Nap): függvény, ami a paraméterként megadott számokat, dátummá alakítja EncodeDateTime(Év,Hónap,Nap,Óra,Perc,Másodperc,Ezredmásodperc): függvény, ami a paraméterként megadott számokat, időt is tartalmazó dátummá alakítja
101
A következő példában a program indulásakor kiíratjuk az aktuális időt és dátumot: procedure TForm1.FormCreate(Sender: TObject); begin Label1.Caption := 'Mai dátum: ' + DateToStr(Date); Label2.Caption := 'Ido: ' + TimeToStr(Time); end;
9.4.1. ábra. Az aktuális dátum és idő megjelenítése
9.5. Akciólista (ActionList) használata Egy alkalmazásban a felhasználónak felkínált parancsokat célszerű összegyűjteni egy csoportba, azután az egyes vezérlőkhöz, mint például menüelemek, gombok ebből a csoportból (listából) csak ki kell választani a társított parancsot. A lista elemeit akcióknak nevezzük, amelyek dinamikusan kötődnek a vezérlőkhöz, tehát a vezérlők tulajdonságainak módosítása helyett csak akción kell változtatnunk, és az érvényes lesz a vezérlőkre is. Az akciólisták megvalósítására a Delphi a TActionList komponenst hozta létre, amely a Standard komponenspalettán található. Az akciólistáknak a használata viszonylag egyszerű és a legtöbb esetben még csak kódot sem kell írni a megvalósításukhoz. A továbbiakban az akciólisták és akciók használatát egy példán keresztül mutatjuk be. Az első lépés egy TActionList komponens példányának az űrlapon való elhelyezése. Duplakattintással a komponensre előhívjuk az akciólista szerkesztőjét (Action List Editor). A szerkesztő bal oldali ablakában az akciók csoportjai (Categories), jobboldalt egy csoport elemei vannak felsorolva (Actions). Akció felvételéhez jobb egérkattintással hívjuk elő a szerkesztő helyi menüjét, innen választhatjuk a New Action és a New Standard Action parancsot. Utóbbi az Edit, Window, Help menük, továbbá az adathalmazon (DataSet) végezhető műveletek akcióit tartalmazza, melyek a Windows alkalmazásokban megszokott módon működnek. Tehát a standard akciókhoz nem kell programkódot írni, például még a vágólapra másolás (EditCopy) parancs aktív voltát sem kell figyelni. Az újonnan létrehozott listába az Edit kategória három akcióját vesszük fel: EditCut, EditCopy, EditPaste.
102
9.5.1. ábra. Az üres valamint a három akciót tartalmazó lista
Továbbá helyezzünk az űrlapra egy TToolBar komponenst a Win32 palettáról, majd ezen hozzunk létre három gombot (Jobb egér kattintás a TToolBar komponensen, majd válasszuk a New Button parancsot). Az ilyen típusú gombokhoz kis képeket szoktak rendelni, amit a Win32 palettán található TImageList komponenssel szoktak megoldani. A felhasználó képeket talál a Program Files\Common Files\Borland Shared\Images\ Buttons mappában. A kép listát hozzácsatolhatjuk az akciólistához annak ImageList tulajdonságával valamint a ToolBar komponenshez annak Images tulajdonságával. Továbbá helyezzünk egy főmenü komponenst három almenüponttal a következő feliratokkal: Cut, Copy, Paste. A következő lépés az akciók hozzárendelése a vezérlőkhöz, a mi esetünkben a menüpontokhoz és az eszköztár gombjaihoz. Ezt úgy oldjuk meg, hogy minden objektumnak az Action jellemzőjét beállítjuk a megfelelő akcióra. Hogy tesztelni tudjuk az alkalmazásunkat helyezzünk az űrlapra egy szövegmezőt, majd futtassuk az alkalmazást.
9.5.2. ábra. Inaktív komponensek a program indításakor
Észrevehető, hogy kezdetben minden komponens inaktív, ugyanis nincs szöveg a vágólapon és nincs szöveg kijelölve. Abban a pillanatban ahogy beírunk egy szöveget a szövegmezőbe és egy részét kijelöljük a másolást és kivágást megvalósító eszköztár gombok és a megfelelő menüpontok aktívvá válnak.
103
9.5.3. ábra. Aktív komponensek a szöveg kijelölésekor
9.6. Multimédiás lehetőségek Egy összetett alkalmazásban nélkülözhetetlen multimédiás lehetőségek alkalmazása. A multimédia magában foglalja a wave, MIDI audiófájlok valamint AVI videófájlok lejátszását. A továbbiakban a Delphi multimédiás lehetőségeit tekintjük át. Természetesen Delphiben használhatjuk a Windows API hang lejátszással kapcsolatos függvényeit. Gyakran használjuk a MessageBeep függvényt, amely az alapértelmezett Windows hangokat tudja visszaadni. Egy másik API metódus a PlaySound, nem csak a beépített hangokat tudja lejátszani, hanem tetszőleges wave hangfájlt is. A MessageBeep függvény egy egész típusú paramétert vár, amelynek 0 és -1 értékére a számítógép belső hangszóróját szólaltatja meg, a további lehetséges értékekkel a Windows alapértelmezett hangjai játszhatók le. Példa: MessageBeep(mb_Ok);
A példában az alapértelmezett hang fog lejátszódni. Példa: PlaySound(’test.wav’, 0, snd_Async);
A PlaySound metódus első paramétere a lejátszandó állomány neve, a második paraméter megadja, hogy a fájl hol található, valamint a harmadik paraméter a lejátszás folyamatát szabályozza. A hangok és videók lejátszására sokkal több lehetőséget biztosít a Delphi Médialejátszó (MediaPlayer) komponense. A komponens a System palettán található és segítségével viszonylag könnyen tudunk wave -és MIDI fájlokat valamint AVI videókat lejátszani.
9.6.1. ábra. A MediaPlayer komponens
104
A médialejátszó fontosabb tulajdonságai: FileName, DeviceType, VisibleButtons, AutoOpen. A FileName tulajdonságban kell megadni egy érvényes multimédia fájl nevét. A DeviceType lehetséges értékei a FileName tulajdonságban megadott fájl kiterjesztésének megfelelő eszközt tételez fel. Ismertebb értékei: dtCDAudio, dtAVIVideo, dtWaveAudio. Az eszköztípus és fájl megadása után, megnyithatjuk az eszközt, vagy az AutoOpen tulajdonságot igazra állítjuk. Ezekkel a beállításokkal elérhetővé válnak a MediaPlayer gombjai. A VisibleButtons a megjelenő gombokat tartalmazza, az EnabledButtons ezek közül az engedélyezetteket. Hangfájlok lejátszására, helyezzünk egy MediaPlayer komponenst az űrlapra, egy OpenDialog komponenst, egy gombot, amelyre kattintva tallózhatunk egy hangfájlt és egy címkét, amelyben megjelenítjük a hangfájl nevét. A gomb OnClick eseményét kezeljük le az alábbiak szerint: procedure TForm1.Button1Click(Sender: TObject); begin if OpenDialog1.Execute then begin MediaPlayer1.FileName:= OpenDialog1.FileName; Label2.Caption:= OpenDialog1.FileName; MediaPlayer1.Open; end; end;
9.6.2. ábra. Hangfájl lejátszása
A multimédiás fájlok lejátszására nem szükséges a MediaPlayer gombjainak a használata, ugyanis a megfelelő utasításokat programból is kiadható. A következő példában egy fájlnak csak bizonyos részét játszuk le. MediaPlayer1.FileName := ’test.wav’; MediaPlayer1.Open; MediaPlayer1.StartPos := 1000; MediaPlayer1.EndPos := 3000; MediaPlayer1.Play;
A komponens eseményei közül kiemeljük az OnClick eseményt, amely kissé furcsán működik, ugyanis a két paramétere közül az első meghatározza, hogy melyik gombot
105
nyomtuk le, a másodikkal az alapértelmezett működést kikapcsolhatjuk. Az OnNotify esemény értesíti a komponenst arról, hogy sikeres volt-e az elindított folyamat. Videóklipek lejátszásakor hasonlóan járunk el mint hangfájlok lejátszásánál: megadjuk a megfelelő avi fájlt a komponens FileName tulajdonságában, állítsuk az AutoOpen tulajdonságot igazra, majd megnyomjuk a Play gombot. A Play gomb megnyomása után a Delphi egy külön ablakban játsza le a fájlt. Ha saját ablakban akarjuk lejátszani, akkor a Display tulajdonságában kell megadni a megfelelő objektumot.
9.6.3. ábra. Videóklip lejátszása külön ablakban és egy Panel ablakban
106
10. Megoldott feladatok és útmutatások
1. Feladat. Hello – üdvözlő program. Helyezzünk el az űrlapon egy szerkesztődobozt (szövegmezőt - Edit) és két gombot OK és Kilépés feliratokkal. Az elsőre kattintva a szövegmezőben jelenjen meg a Hello szöveg, a másikra kattintva lehessen kilépni az alkalmazásból. Helyezzünk el egy újabb gombot, Törlés felirattal, amire, ha kattintunk a szövegmező váljon üressé. Megoldás: • A Standard komponens-palettáról elhelyezünk egy szövegmezőt (Edit) és három gombot (Button). • Kijelöljük a szövegmezőt, a Text tulajdonságánál töröljük az Edit1 szöveget (a program indulásakor legyen üres a szövegmező). • Kijelöljük rendre a gombokat és a Caption tulajdonságnál beírjuk a feliratokat: OK, Kilépés, Törlés. • Kiválasztjuk az OK feliratú gombot (Button1), megkeressük az OnClick eseményét és itt dupla kattintással egy új eljárást hozunk létre. Ez az eljárás fog végrehajtódni akkor, amikor a gombra kattintunk az alkalmazás működése közben. Beírjuk: Edit1.Text:=’Hello’; (az Edit1 szövegmező szövege vegye fel a ’Hello’ értéket) • Eljárást rendelünk a Kilépés feliratú (Button2) gomb OnClick eseményéhez és beírjuk: Close; (ez bezárja a főablakot) • Eljárást rendelünk a Törlés feliratú (Button3) gomb OnClick eseményéhez és beírjuk: Edit1.Text:=’’; (az edit1 szövegmező szövege vegye fel az üres karakterlánc értéket) 2. Feladat. Események gyakorlása Hozzuk létre az alábbi űrlapot. Az első szövegmezőbe írt szöveg egyből jelenjen meg, a másodikban, a harmadikban pedig csak akkor, ha elhagyjuk (pl. TAB-bal) az első szövegmezőt, a negyedikben pedig a Gomb feliratú gombra való kattintás eredményeképpen .
107
10.1. ábra. A 2. Feladathoz tartozó űrlapterv
Megoldás: • Elhelyezzük a Standard komponens-palettáról a négy címkét (Label), a négy szövegmezőt (Edit) és a két gombot (Button). A címkéknél illetve a gomboknál a Caption tulajdonságban állítjuk be a feliratokat, a szövegmezőknél a Text tulajdonságnál töröljük a szöveget. • Az első szövegmező (Edit1) OnChange eseményéhez rendeljük az első eljárást. Beírjuk: Edit2.Text:=Edit1.Text; (a második szövegmező szövege legyen egyenlő az első szövegmező szövegével). Minden karakter beírásakor (vagy törlésekor) a szövegmező tartalma változik, bekövetkezik az OnChange esemény és végrehajtódik az eljárás. • Az első szövegmező (Edit1) OnExit eseményéhez rendeljük a következő eljárást. Ez akkor következik be, ha elhagyjuk a szövegmezőt (nem lesz fókuszban). Beírjuk: Edit3.Text:=Edit1.Text; (a harmadik szövegmező szövege legyen egyenlő az első szövegmező szövegével) • Az első gomb (Button1) OnClick eseményéhez rendeljük a következő eljárást. Ez akkor következik be, ha kattintunk a gombra. Beírjuk: Edit4.Text:=Edit1.Text; (a negyedik szövegmező szövege legyen egyenlő az első szövegmező szövegével). • Eljárást rendelünk a második gomb (Button2) OnClick eseményéhez és beírjuk: Close; (a főablak s ezzel az alkalmazás bezárása) 3. Feladat. Beolvasás, kiírás gyakorlása. Írjunk egy programot, ami bekéri egy kör sugarát, majd kiszámolja és kiírja a területét.
10.2. ábra. A 3. Feladathoz tartozó űrlapterv
Megoldás: • Elhelyezünk a Standard komponens-palettáról két címkét (Label), két szövegmezőt (Edit) és egy gombot (Button). A címkéknél illetve a gomboknál a Caption tulajdonságban állítjuk be a feliratokat, a szövegmezőknél a Text tulajdonságnál töröljük a szöveget.
108
• Eljárást rendelünk a gomb (Button1) OnClick eseményéhez és beírjuk a megfelelő utasításokat az alábbiak szerint: procedure TForm1.Button1Click(Sender: TObject); var var r,t:real; begin r:=StrToFloat(Edit1.Text); t:=pi*r*r; Edit2.Text:=FloatToStr(t); end;
• A deklarációs részben deklaráltunk 2 valós típusú változót. Az r változóban fogjuk tárolni a kör sugarát, a t-ben fogjuk kiszámolni a területét. Az első utasításban átalakítjuk az Edit1 szövegét valós számmá és ezt az értéket veszi fel az r változó, a t változóban kiszámoljuk a kör területét, majd ezt az értéket átalakítjuk szöveggé és ezt az értéket kapja meg az Edit2 szövege. 4. Feladat. Beolvasás, kiírás, számítások gyakorlása. Írjunk egy programot, amiben kiszámoljuk a havonta visszafizetendő összeget, ha a bankból felveszünk egy kölcsönt, adott kamattal és meghatározott időszakra (hónapra). Megoldás: • Megtervezzük az alkalmazás ablakát, elhelyezünk négy szövegmezőt (Edit), hat címkét (Label) és egy gombot (Button):
10.3. ábra. A 4. Feladathoz tartozó űrlapterv
• Eljárást rendelünk a „Kiszámol” feliratú gomb OnClick eseményéhez. • A létrejött eljárásban deklarálunk négy valós típusú változót: var o,k,h,hfo:real;
• Beolvassuk az első három változót, alkalmazva a típuskonverziót (szövegből valós számot hozunk létre): 109
o:=StrToFloat(Edit1.Text); - összeg beolvasása k:=StrToFloat(Edit2.Text); - kamat beolvasása h:=StrToFloat(Edit3.Text); - hónapok számának a beolvasása
• Kiszámoljuk a havonta fizetendő összeget, mint az összeg plusz a kamatok és a hónapok száma hányadosát: hfo:=(o + (o * k * h) / 1200) / h;
Az (o * k * h) / 1200 kifejezés a következő meggondolás alapján jött létre: az egy évi kamat egyenlő az összeg szorozva a kamat/100, és ezt be kell még szorozni az évek számával, vagyis a h/12 – vel (hónapok száma/12). • A negyedik szövegmezőben megjelenítjük a kiszámolt érték szöveges változatát: Edit4.text:=FloatToStr(hfo);
5. Feladat. Jelölőnégyzet és rádiógombok használata Készítsünk programot egy kávéautomata modellezéséhez! Rádiógombokkal lehessen megadni az italt (kávé, tea, kakaó), jelölőnégyzetekkel a hozzávalókat (citrom, cukor, tej, tejszín). A program számolja ki és jelenítse meg a fizetendő összeget! Teához ne lehessen tejszint, kávéhoz citromot, kakaóhoz se citromot, se tejszint kérni. Megoldás: Helyezzünk az űrlapra két GroupBox objektumot a Standard palettáról. Az elsőbe helyezzünk három RadioButton objektumot, a másodikba négy CheckBox objektumot az alábbiak szerint:
10.4. ábra. A 5. Feladathoz tartozó űrlapterv
Az űrlap aljára helyezzünk egy címkét (Label), amely a fizetendő összeget fogja tartalmazni.
110
A feladatot úgy oldjuk meg, hogy valamelyik ital kiválasztása esetén először beállítjuk a lehetséges hozzávalókat (a jelölőnégyzet Enabled tulajdonságát igazra állítjuk), majd utána kijelölve a szükséges hozzávalót lefuttatunk egy saját készítésű eljárást, amely a számolásokat elvégzi. Kezdetben, az űrlap OnCreate eseményében állítsuk a négy jelölőnégyzet Enabled tulajdonságát hamisra. Például a kávé választása esetén az alábbi eljárás hajtódik végre: procedure TForm1.RadioButton1Click(Sender: TObject); begin CheckBox1.Checked := false; CheckBox1.Enabled := false; CheckBox2.Enabled := true; CheckBox3.Enabled := true; CheckBox4.Enabled := true; Szamol; end;
A többi ital esetén hasonlóan járunk el. A Szamol eljárás egy saját készítésű eljárás, aminek a definícióját az egység többi eljárásához tesszük. procedure Szamol; var ar:real; begin ar:=0; if Form1.RadioButton1.Checked then ar:=1; if Form1.RadioButton2.Checked then ar:=0.5; if Form1.RadioButton3.Checked then ar:=1; if Form1.CheckBox1.Checked then ar:=ar+0.25; if Form1.CheckBox2.Checked then ar:=ar+0.25; if Form1.CheckBox3.Checked then ar:=ar+0.5; if Form1.CheckBox4.Checked then ar:=ar+0.75; Form1.Label1.Caption := 'Fizetendo: ' + FloatToStr(ar) + ' RON'; end;
Megjegyzés: a fentihez hasonló eljárások estén az űrlap objektumaira csak az űrlap nevén keresztül hivatkozhatunk.
10.3. ábra. A program futás közben
111
6. Feladat. Szöveges objektumok tulajdonságai. Írjunk egy programot, amiben egy szövegmező szövegének tulajdonságait lehet változtatni az ábrán látható módon.
10.4. ábra. A 5. Feladathoz tartozó űrlapterv
Megoldás: • Elhelyezünk a Standard komponens-palettáról két címkét (Label) a Mit: és a Méret: feliratokkal, egy szövegmezőt (Edit) és egy gombot (Button). A címkéknél illetve a gombnál a Caption tulajdonságban állítjuk be a feliratokat, a szövegmezőnél a Text tulajdonságnál töröljük a szöveget. A szín és a font beállításához elhelyezünk a Standard komponens-palettáról két választógomb-csoportot (RadioGroup). Ezek Caption jellemzőjébe írjuk be a Szín és a Font szöveget, az Items jellemzőjénél (duplán kattintva ott) adjuk meg az opciók szövegeit.
10.5. ábra. A RadioGroup elemeinek a szerkesztése
112
• A Stílus beállításához helyezzünk el a Standard komponens-palettáról egy GroupBox objektumot (Caption- be: Stílus) és rá három jelölő-négyzetet (CheckBox). Ezek Caption tulajdonságánál adjuk meg a feliratokat. • A méret beállításához helyezzünk el a Samples komponens-palettáról egy SpinEdit objektumot. A Value (érték) tulajdonságát állítsuk 10-re, a MinValue-t (minimális érték) 8-ra a MaxValue-t (maximális érték) 72-re, az Increment-et (mennyivel nő vagy csökken az érték) 2-re. • A Mit: felirat után elhelyezünk egy lenyíló-listát (ComboBox), amivel azt lehet majd szabályozni, hogy a szín a szövegre vagy annak hátterére vonatkozzon. Az Items jellemzőnél adjuk meg a lista 2 elemét: Előtér és Háttér. Ezzel elkészültünk az ablak megszerkesztésével, következik az objektumok viselkedésének a megírása. • Szöveg méretének a változtatása. A SpinEdit1 OnChange eseményéhez rendelünk eljárást és beírjuk: Edit1.Font.Size:=SpinEdit1.Value; (a SpinEdit1 értékének változásakor a szövegmező szövegének mérete legyen egyenlő a SpinEdit1-ben levő értékkel) • A színek alkalmazása: a RadioGroup1 OnClick eseményéhez rendelünk eljárást (mert ha kiválasztunk egy színt, akkor kattintani kell erre az objektumra) és beírjuk: if ComboBox1.ItemIndex=0 then case RadioGroup1.ItemIndex of 0: Edit1.Font.Color:=clRed; 1: Edit1.Font.Color:=clBlue; 2: Edit1.Font.Color:=clGreen; end else case RadioGroup1.ItemIndex of 0: Edit1.Color:=clRed; 1: Edit1.Color:=clBlue; 2: Edit1.Color:=clGreen; end;
Először megvizsgáltuk, hogy a ComboBox1 melyik eleme van kiválasztva. (ha az ItemIndex=0 akkor az első, ha nem akkor a második) és ennek függvényében a színt vagy a szövegmező szövegére alkalmaztuk (Edit1.Font.Color) vagy csak a szövegmezőre (Edit1.Color). A megfelelő szín alkalmazása a RadioGroup1.ItemIndex értékének megvizsgálásával történt, ennek értéke adta meg, hogy melyik opció (karika) van kiválasztva (a számozás 0-tól kezdődik). • A Font megadásához eljárást rendelünk a másik választógomb-csoport (RadioGroup2) OnClick eseményéhez és beírjuk: Edit1.Font.Name:=RadioGroup2.Items[RadioGroup2.ItemIndex];
Használhattuk volna itt is a Case-es megoldást, megvizsgálva a RadioGroup2 ItemIndex értékét és alkalmazva a megfelelő nevű fontot. De itt felhasználjuk azt, hogy a font megadásához a Font.Name jellemző a font nevét, mint szöveget (string típusú változó) kell kapja (pl. Edit1.Font.Name:=’Arial’), így elég, ha hivatkozunk az aktuális opció szövegére. A RadioGroup2.ItemIndex értéke 0,1 vagy 2 (attól
113
függően, hogy melyik opció van kiválasztva) és a RadioGroup2.Items pedig az a tömb, amely pont ezt a három font nevet, mint szöveget tartalmazza. • A Stílus megadásához mind a három jelölő-négyzet OnClick eseményéhez eljárást rendelünk. Az elsőbe beírjuk: if CheckBox1.Checked then Edit1.Font.Style:= Edit1.Font.Style + [fsBold] else Edit1.Font.Style:= Edit1.Font.Style - [fsBold];
Megvizsgáljuk, hogy a jelölőnégyzet ki van-e választva. Ha ki van választva, akkor a szövegmező fontjának stílushalmazához hozzáadjuk az fsBold elemet, ha nincs, akkor azt jelenti, hogy pont most szüntettük meg a kiválasztást és ebben az esetben a stílushalmazból kivonjuk az fsBold elemet. Hasonlóan kell eljárni a másik két esetben is használva az fsItalic (dőlt) és az fsUnderline (aláhúzott) elemeket. 7. Feladat. Listák használata. Hozzunk létre két listát, melynek elemeit egyikből a másikba lehet rakosgatni. Legyen két gomb amivel a kiválasztott elemeket lehet át- és visszahelyezni, míg a két másik gombbal minden elemet lehessen mozgatni. A listát ne tervezéskor, hanem programból töltsük fel.
10.6. ábra. A 6. Feladathoz tartozó űrlapterv
Megoldás: • Elhelyezünk a Standard komponens-palettáról 2 listát (ListBox), a feliratoknak két címkét (Label) és öt gombot (Button). A gombokra a Caption jellemzőnél beírjuk a következő feliratokat: >, >>, <, <<, Bezár. • A listákat nevezzük l1-nek illetve l2-nek (Name jellemzőnél állítjuk be) • A listaelemeket az Items jellemzőnél lehetne megadni, de mivel programból kell a listát feltölteni, a Form1 OnCreate eseményéhez rendelünk eljárást. Ez a program indulásakor fog végrehajtódni. Deklarálunk egy i:Integer változót és beírjuk:
114
for i:=1 to 5 do l1.Items.Add(StrToInt(i)+’. elem’);
A StrToInt(i) függvény segítségével rendre létrehozzuk az ‘1’, ‘2’,... karaktereket, amihez hozzáadjuk az ‘. elem’ karakterláncot és az Items.Add eljárással ezeket a karakterláncokat adjuk hozzá a lista elemeihez. • Eljárást rendelünk az első gomb (Button1) OnClick eseményéhez. Ez a gomb kell átrakja az első listában kiválasztott elemeket a második listába. A módszer a következő lesz: végigmegyünk a lista elemein, egyenként megvizsgálva, hogy melyik van kiválasztva. Amelyik elem ki van választva, azt hozzáadjuk a második lista elemeihez és töröljük az első lista elemei közül: for i:=l1.Items.Count-1 downto 0 do if l1.Selected[i] then begin l2.Items.Add(l1.Items[i]); l1.Items.Delete(i); end;
Az l1.Items.Count jelenti az első lista elemeinek számát. Mivel a lista elemeinek számozása 0-tól kezdődik, ezért az i változó rendre felveszi az utolsó elem sorszámától az első elem sorszámáig terjedő értékeket. (Pl. ha a listának 5 eleme van, akkor i=4..0). Az elemek végigjárása azért történik alulról felfele (az utolsó elemtől az elsőig), mert csak ekkor biztosítható az összes elem végigjárása, hiszen az elemek átrakásával az elemek fogynak és ezáltal a sorszámuk is megváltozik (Pl. ha áttesszük a 2-es sorszámú elemet, akkor a sorszámváltozás csak a már végignézett elemeket érinti, a hátralevőket (1-est és 0-st) nem.). A Selected[i]-vel vizsgáljuk meg, hogy az i. elem ki van választva vagy sem, az l2.Items.Add(l1.Items(i)) pedig a 2. lista elemeihez hozzáadja az 1. lista elemei közül az i.-et, az l1.Delete(i) pedig törli az 1. listából ezt az i. elemet. • A második gomb az összes elemet kell átrakja az első listából a másodikba, ezért az ehhez rendelt eljárásba ugyanazt kell beírni, mint az előzőbe, azzal a különbséggel, hogy le kell törölni azt az utasítást (az if utasítást tartalmazó sort), amelyik azt vizsgálja meg, hogy az i. elem ki van-e választva. Ugyanis az összes elemet átrakjuk, függetlenül attól, hogy ki van vagy sem választva. • A másik két gomb ugyanazt valósítja meg, mint a két előző gomb, azzal különbséggel, hogy az irány most fordított. Tehát a harmadik és negyedik gomb OnClick eseményéhez rendelt eljárásokba bemásoljuk az első és második gombhoz rendelt eljárások tartalmát és a listák indexeit felcseréljük: mindenhol l1 helyett l2-t írunk és fordítva. 8. Feladat. Drag&Drop technika. Oldjuk meg az előző feladatot Drag&Drop módszerrel. Megoldás:
115
• Az űrlap megszerkesztésekor kihagyjuk a listák közötti gombokat, hiszen most egérrel fogjuk áthúzni az elemet, egyik listából a másikba. • Három lépésben valósul meg a Drag&Drop technika: 1. Az első lista OnMouseDown eseményéhez rendelünk eljárást és ide beírjuk: l1.BeginDrag(false);. Ez azt jelenti, hogy ha a listán az egeret lenyomjuk és legalább 5 pixeles mozdítást végzünk, akkor elkezdődik a vonszolás. 2. A második, fogadó lista OnDragOver eseményéhez rendelünk eljárást és ide beírjuk: Accept:=True;. Ez azt jelenti, hogy ha a listára érünk a vonszolt objektummal, akkor az fogadhatja ezt az objektumot. 3. Szintén a második lista, ezúttal az OnDragDrop eseményéhez rendelünk eljárást és itt valósítjuk meg a kijelölt elemek átrakását. Ugyanazokat az utasításokat kell beírni, mint az előző feladat első gombjának az OnClick eseményéhez rendelt eljárásba. • Ha azt szeretnénk, hogy a vonszolás fordított irányban is működjön, akkor ugyanezeket a lépéseket kell követni, azzal a különbséggel, hogy a második lista OnMouseDown, illetve az első lista OnDragOver és OnDragDrop eseményeit dolgozzuk fel. 9. Feladat. Drag&Drop technika gyakorlása. Helyezzünk el az űrlapra két szövegmezőt és egy panel objektumot. Drag&Drop módszerrel lehessen az első szövegmezőbe írt szöveget áthúzni a panelre (a panel felirata vegye fel a szövegmező szövegét), és a második szövegmezőbe. Ha az ablakra húzzuk ki a szöveget, akkor az ablak felirata vegye fel a szöveg értékét és egyúttal jelenjen is meg egy felirat (Label), ami ezt a szöveget tartalmazza.
10.7. ábra. A 8. Feladathoz tartozó űrlapterv
Megoldás: Alkalmazzuk a Drag&Drop technika 3 lépését: 1. Az Edit1 OnMouseDown eseményéhez rendelünk eljárást és ide beírjuk: Edit1.BeginDrag(false);
2. A három fogadó objektum a Panel1, az Edit1 és a Form1 objektumok OnDragOver eseményéhez rendelt eljárásokba írjuk be rendre: Accept:=True;
3. A fenti 3 objektum OnDragDrop eseményéhez rendelünk eljárást és itt rendre beírjuk:
116
Panel1.Caption:=Edit1.Text; - a Panel1-nél. Edit2.Text:= Edit1.Text; - az Edit2-nél Form1.Caption:= Edit1.Text; - a Form1-nél.
A feladat utolsó részének megoldása érdekében az utolsó eljárást kiegészítjük a következőkkel: • Deklarálunk egy TLabel típusú változót: var L:TLabel; • Az eljárás törzsébe beírjuk: L:=TLable.Create(Self); - létrehozzuk az L objektumot. L.Parent:=Form1; - a szülőobjektum a főablak lesz. L.Left:=x; - az L felirat pozícióját az OnDragDrop eseményhez rendelt
eljárás x és y paramétere határozza meg, ezek azt a pozíciót jelentik, ahol a vonszolás befejeződött. L.Top:=y; L.Caption:=Edit1.Text; - a felirat szövege megkapja a szövegmező
szövegét. 10. Feladat. A Sender paraméter. Hozzuk létre az alábbi űrlapot. A gombokra kattintva jelenjen meg a szövegmezőben a megfelelő gomb felirata. A feladatot oldjuk meg úgy, hogy csak egy eljárást használjunk.
10.8. ábra. A 9. Feladathoz tartozó űrlapterv
Megoldás: • Elhelyezzük a Standard komponens-palettáról a megfelelő elemeket. • Mivel a három gomb szinte ugyanazt kell végrehajtsa és a három eljárás (a gombok OnClick eseményei) szerkezete is azonos, felvetődik a kérdés, hogyan lehetne ezt egy eljárással megoldani. Ez megoldható, ha felhasználjuk a (minden eljárásban megjelenő) Sender paramétert. Ugyanis ez nem más, mint az objektum, amelyik meghívja az eljárást, amelyikkel az esemény bekövetkezik. Ezért csak első gombhoz rendelünk eljárást és ugyanezt az eljárást rendeljük a másik két gomb OnClick eseményéhez is. Ez úgy oldható meg, hogy az objektum-felügyelő Events (események) lapján a megfelelő eseményhez nem rendelünk új eljárást (dupla kattintással), hanem egyszerűen listából választjuk ki a már létező eljárást és ezt
117
rendeljük hozzá az eseményhez. Ebben az esetben, viszont az eljárást úgy kell megírni, hogy megfelelően viselkedjen attól függetlenül, hogy melyik gomb hívja meg. Ezért a következőt kell tartalmazza az eljárás: Edit1.Text:=(Sender as TButton).Caption;
A (Sender as TButton) jelenti azt a gombot, amelyik meghívja az eljárást. Tehát, ha például a második gombra kattintunk, akkor ez helyettesítődik a Button2-vel. Az as TButton típus-konverziót jelent. Ezt azért kell használni, mert a Sender paraméter az TObject típusú, aminek nincs Caption jellemzője és emiatt hibát adna fordításkor. 11. Feladat. TabControl/PageControl használata Írjunk egy programot, amelynek főablakát két részre osztjuk, a felső részben használjunk TTabControlt felső fülekkel és egy szövegmezőt, ahol megjelenik a kiválasztott fül neve, az alsó részben pedig TPageControlt két alsó füllel, ahol az egyik fülhöz tartozó lapon egy álló kép legyen a másikon pedig valamilyen animáció. Megoldás: • Elhelyezünk a Win32 komponens-palettáról egy TabControl elemet. A Tabs jellemzőnél megadjuk a fülek feliratait: Első, Második, Harmadik. • Mivel azt szeretnénk, hogy ahogyan fület váltunk, a szövegmezőben jelenjen meg az aktuális fül felirata, a TabControl OnChange eseményéhez rendelünk eljárást és ide beírjuk: Edit1.Text:=TabControl1.Tabs[TabControl1.TabIndex];
Fül váltáskor a TabIndex jellemző tartalmazza az aktuális fül indexét (sorszámát), a Tabs pedig az a tömb, ami a feliratokat tartalmazza. Például, ha kattintunk a második fülre, akkor tabindex=1 és a tabs[1]=’második’. • Ahhoz, hogy a TabControl elfoglalja az ablak felső felét, az Align jellemzőjénél kiválasztjuk az alTop értéket. • Elhelyezünk alól egy PageControlt (szintén Win32 paletta). • Jobb gombbal rákattintunk és a New Page paranccsal hozzuk létre az új lapot s a hozzá tartozó fület. Ezt megismételjük, mert két lapra lesz szükségünk. Ezzel valójában már három objektumunk van: a PageControl1 és a két füles lapunk: TabSheet1 és TabSheet2. Amikor valamelyik fülre kattintunk, akkor a PageControl van kiválasztva, ha pedig kattintunk a fülhöz tartozó lapra is, akkor lesz kiválasztva a megfelelő lap (TabSheet). A feliratok megadásához jelöljük ki a megfelelő lapot és a Caption jellemzőbe írjuk be a feliratokat (Kép és Animáció). • Ahhoz, hogy a PageControl elfoglalja az ablak alsó felét, az Align jellemzőjénél kiválasztjuk az alClient értéket. • Lépjünk át a Kép lapra és helyezzünk el rajta az Additional palettáról egy Image objektumot. A Picture jellemzőnél lehet egy képet betölteni.
118
• Lépjünk át az Animáció lapra és helyezzünk el rajta a Win32 palettáról egy Animate objektumot. Válasszunk ki egy animációt a CommonAvi jellemzőnél egy avi kiterjesztésű animációt a FileName jellemzőnél. Az animációk megindításához az Active jellemzőt kell True-ra állítani. 12. Feladat. Mászkáló gomb. Hozzunk létre egy gombot, ami nagyon gyorsan mozog ide-oda az ablakban. Ha sikerül elkapni a gombot (rákattintani) álljon meg, ha újból kattintunk rá, induljon el.
10.9. ábra. A 11. Feladathoz tartozó űrlapterv
Megoldás: • A Standard komponens-palettáról elhelyezünk egy gombot, a System palettáról pedig egy Timer-t. A Timer1 objektum Interval jellemzőjénél megadjuk az időköz értékét: 100 (ez 0.1 másodpercet jelent), és az OnTimer eseményéhez eljárást rendelünk. Ez az Interval jellemzőnél megadott időközönként fog végrehajtódni. Itt fogjuk megadni, hogy a gomb véletlenszerűen mozduljon el valamilyen irányba. Az OnTimer eljárás a megfelelő utasításokkal: procedure TForm1.Timer1Timer(Sender: TObject); var dx,dy:ineteger; begin Randomize; dx:=10*(random(7)-3); dy:=10*(random(7)-3); if (Button1.Left+dx>0) and (Button1.Left+dx+Button1.Width0) and (Button1.Top+dy+Button1.Height
•
Az eljárás elején generáltunk két számot véletlenszerűen a -30, -20, -10, 0, 10, 20, 30 értékek közül. (random(7) értéke egy 0 és 6 közötti szám, ebből kivonva 3-at -3 és 3 között van az érték, ezt megszorozva 10-el kapjuk a -30 és 30 közti értékeket. Ezzel a két értékkel változtatjuk meg a gomb 119
• •
koordinátáit: ha az érték negatív a gomb balra illetve felfele fog elmozdulni, ha pozitív akkor jobbra illetve lefele. A feltételek azért szükségesek, hogy a gomb ne hagyhassa el az ablakot (a dx vagy dy értéket, akkor adjuk csak hozzá a left vagy top jellemzőhöz, ha ezután a gomb még mindig az ablakon belül marad). Az utolsó utasítás célja, hogy a mozgó gombra való kattintáskor az megálljon illetve az álló gombra való kattintáskor az újra kezdjen mozogni.
13. Feladat. Jelszó rendelése az alkalmazáshoz. Írjunk egy programot, amely a főablak megjelenése előtt egy jelszót kér és csak a helyes jelszó begépelése után folytatódik a program, egyébként írjon ki egy üzenetet és álljon le. A jelszó begépelésére használjunk adatbeviteli üzenetablakot (InputBox), az üzenet kiírására pedig egyszerű üzenetablakot (ShowMessage). Megoldás: • Mivel a jelszóbekérő ablak a főablak előtt kell megjelenjen, nyissuk meg a főprogramot: View/Units – és válasszuk ki a projekt állományt (Project1). A kódszerkesztő ablakban megjelenik a főprogram. Ebben az Application.Run utasítás az, amelyik hatására elindul a program és megjelenik a főablak. Ez elé kell beírni a jelszó bekérő ablakot megvalósító utasításokat az alábbiak szerint: if InputBox(’’,’Jelszó:’,’’)=’sajat jelszo’ then begin Application.Initialize; Application.CreateForm(...); Application.Run; end else ShowMessage(’Rossz jelszó’);
• Mivel az InputBox függvény és a ShowMessage eljárás a Dialogs egységben van deklarálva, fent az egységek felsorolásánál (uses után) írjuk be: Dialogs.
10.10. ábra. A jelszó bekérő ablak
• Az InputBox függvény hatására megjelenik a jelszóbekérő ablak és a beírt szöveg értékét fogja visszaadni a függvény. Ha ez megegyezik a főprogramban megadott saját jelszóval, csak akkor fogja végrehajtani az Application.Runt-t, vagyis a
120
program elindulását. Ha nem talál a beírt és a megadott saját jelszó, akkor megjelenik egy egyszerű üzenetablak a Rossz jelszó szöveggel. 14. Feladat. Létrehozás programból. Hozzunk létre egy menüt, amellyel különböző tulajdonságú panel és felirat objektumokat lehet létrehozni programból. A program menüje legyen a következő: • Panel / nagy piros, kicsi zöld • Felirat / 32-es kék, vastag Megoldás: • Elhelyezünk az ablakba egy főmenüt (MainMenu) és duplán kattintva rá megnyitjuk a menüszerkesztő ablakot. A Caption jellemzőbe beírjuk az első menüpont szövegét, majd kattintunk a mellette vagy alatta levő téglalapra és folytatjuk a menüpontok szövegének beírását. Bezárjuk a menüszerkesztő ablakot. • Kattintunk a Panel / nagy piros menüpontra. Létrehoztunk ezzel egy eljárást, ami akkor fog végrehajtódni, ha a program működése közben is ezt a menüpontot választjuk. Az eljárás a következő utasításokat tartalmazza: ... var P:TPanel; begin P:=TPanel.Create(Self); P.Parent:=Self; P.Top:=10; P.Left:=10; P.Height:=100; P.Width:=200; P.Color:=clRed; end;
• Mivel a TPanel típus az ExtCtrls egységben van deklarálva (megtekinthető a Helpben: a kurzort vigyük a TPanel feliratra és F1), ezt írjuk be a uses utáni egységek listájába. • Kattintunk a Panel / kicsi zöld menüpontra. Ugyanazt kell elvégezni, mint az előző menüpontnál, azzal a különbséggel, hogy: • A panel legyen lennebb: P.Top:=120; • Mérete legyen kisebb: P.Height:=10;P.Width:=20; • Színe legyen zöld: P.Color:=clGreen; Kattintunk a Felirat / 32-es kék menüpontra és írjuk be az alábbi utasításokat: ... var Sz:TLabel; begin
121
Sz:=TLabel.Create(Self); Sz.Parent:=Self; Sz.Top:=10; Sz.Left:=400; Sz.Caption:='32-es meretu szoveg'; Sz.Font.Size:=32; Sz.Font.Color:=clBlue; end;
• A vastag felirat létrehozásához az előzőn a következőket kell változtatni: • Helyezzük máshova (top, left). • Legyen a szöveg más (caption). • Nem kell méret és szín (size, color). • Meg kell adni a stílust: Sz.Font.Style:=[fsBold]; 15. Feladat. Szövegszerkesztő. Írjunk egy egyszerű szövegszerkesztő programot a következő menürendszerrel: • File/New, Open, Save, Exit • Edit/Cut, Copy, Paste • Format/Font, Background Megoldás: • Elhelyezünk az ablakba egy főmenüt (MainMenu) és duplán kattintva rá megnyitjuk a menüszerkesztő ablakot, majd elkészítjük a kért menüpontokat. • Elhelyezünk az ablakba egy Memo objektumot. Az Align jellemzőjénél kiválasztjuk az alClient-et, ezzel a szövegmező felveszi az ablak méreteit. • Számos menüpont működésének megvalósításánál dialógusablakokat fogunk használni, ezért a TDialog palettáról helyezzük el a következőket: egy állomány megnyitó ablakot (OpenDialog), egy állomány-mentő ablakot (SaveDialog), egy szövegformázó ablakot (FontDialog), és egy szín kiválasztó ablakot (ColorDialog). • A New menüpontot választva egy új, üres dokumentumot kell létrehozni, ezt úgy oldjuk meg, hogy töröljük a szövegmező tartalmát. Rendeljünk eljárást a menüponthoz és írjuk be: Memo1.Lines.Clear; • Az Open menüpont segítségével egy txt kiterjesztésű szövegállományt tudunk majd megnyitni. Rendeljünk eljárást a menüponthoz és írjuk be: if OpenDialog1.Execute then Memo1.Lines.LoadFromFile(OpenDialog1.FileName);
Ez azt jelenti, hogy ha az Open ablak megjelenik (OpenDialog1.Execute) és ott kiválasztunk egy állományt (OpenDialog1.Execute értéke true), akkor a Memo1 sorait (Lines) töltse be egy állományból (LoadFromFile), mégpedig az ablakban megadott állományból (OpenDialog1.FileName). Ahhoz, hogy az open ablak csak a txt kiterjesztésű állományokat ”lássa”, kiválasztjuk az OpenDialog1 objektumot és a Filter jellemzőjénél duplán kattintva,
122
a megjelenő táblázatba beírjuk: a Filter Name-hez: szövegállomány, a Filter-hez: *.txt. • Rendeljünk eljárást a Save menüponthoz és írjuk be: if SaveDialog1.Execute then Memo1.Lines.SaveToFile(SaveDialog1.FileName);
Ez azt jelenti, hogy ha a Save ablak megjelenik és ott megadunk egy állományt, akkor a Memo1 sorait mentse le a megadott állományba. • A Cut, Copy és Paste menüpontok a kivágás, másolás, beillesztés parancsokat kell megvalósítsák. Rendeljünk eljárásokat e menüpontokhoz és rendre írjuk be: • • •
A cut-nál: Memo1.Lines.CutToClipboard; A copy-nál: Memo1.Lines.CopyToClipboard; A paste-nél: Memo1.Lines.PasteFromClipboard;
• A Font menüpont segítségével a szöveget formázzuk. Rendeljünk eljárást a menüponthoz és írjuk be: if FontDialog1.Execute then Memo1.Font:=FontDialog1.Font;
Ez azt jelenti, hogy ha a Font ablak megjelenik és ott kiválasztunk egy pár szövegtulajdonságot , akkor a Memo1 szövege vegye fel a kiválasztott tulajdonságokat. • A Background menüpont segítségével a szövegmező hátterének színét lehessen megváltoztatni. Rendeljünk eljárást a menüponthoz és írjuk be: if ColorDialog1.Execute then Memo1.Color:=ColorDialog1.Color;
Ez azt jelenti, hogy ha a színkiválasztó ablak megjelenik és ott kiválasztunk egy színt, akkor a Memo1 háttérszíne legyen egyenlő a kiválasztott színnel. 16. Feladat. Dialógusablakok használata. Írjunk egy programot, amiben egy gyorsmenü segítségével lehessen változtatni egy panelnek a színét, illetve a feliratának a fontját. A szín és felirat tulajdonságainak megadásához használjunk dialógusablakokat. A tulajdonságok alkalmazása előtt a program kérdezzen rá, hogy biztos ezt akarjuk?! Egészítsük ki a gyorsmenüt egy új menüponttal, amelyre ha kattintunk jelenjen meg egy piros színű ablak. Megoldás: • Elhelyezünk az ablakba egy Panel objektumot és egy gyorsmenüt (PopupMenu). A gyorsmenüre duplán kattintva megnyitjuk a menüszerkesztő ablakot és a Caption jellemezőbe rendre beírjuk: szín, font, piros ablak. Ahhoz, hogy a gyorsmenü a
123
panelre kattintva (jobbgombbal) jelenjen meg, kijelöljük a panelt és a PopupMenu jellemzőjénél kiválasztjuk a PopupMenu1-et. • Elhelyezünk a Dialogs komponens-palettáról egy FontDialog-ot és egy ColorDialog-ot. • Eljárást rendelünk a szín menüponthoz és beírjuk: if ColorDialog1.Execute then if MessageDlg(’Biztos?’,mtConfirmation,[mbYes,mbNo],0)=mrYs then Panel1.Color:=ColorDialog1.Color;
A kiválasztott szín alkalmazása függ a párbeszédablakban választott opciótól. • A Font megváltoztatásánál hasonlóképpen járunk el, csak itt a FontDialog komponenst használjuk. • A piros ablak megjelenítéséhez először létrehozunk egy új űrlapot. Létrejön egy Form2 nevű ablak, aminek a Color jellemzőjénél kiválasztjuk a piros színt. Eljárást rendelünk a piros ablak menüponthoz és beírjuk: Form2.ShowModal; 17. Feladat. Színkeverés Egy panelnek a színét lehessen változtatni, úgy hogy azt „kikeverjük” a három alapszín (piros, zöld, kék) segítségével. Megoldás: • Elhelyezünk az ablakba egy Panel objektumot, három feliratot (Label) és három SpinEdit objektumot. • Nevezzük el a három SpinEdit-et sp1, sp2, sp3-nak
10.11. ábra. A 16. Feladathoz tartozó űrlapterv
• Mindhárom SpinEdit MaxValue jellemzőjét beállítjuk 255-re. (maximális színintenzitás) • Eljárást rendelünk az első SpinEdit OnChange eseményéhez, majd ugyanezt az eljárást rendeljük a másik két SpinEdit OnChange eseményéhez is (ami azt jelenti, hogy mind a három objektum értékváltozásakor ugyanaz az eljárás fog végrehajtódni). Beírjuk ebbe az eljárásba: if (sp1.Text<>'') and (sp2.Text<>'') and (sp3.Text<>'') then Panel1.Color:=RGB(sp1.Value,sp2.Value,sp3.Value);
124
• A feltételre azért van szükség, hogy a komponensekből a számokat ki lehessen törölni és helyettük más értéket lehessen bevinni. Ha ezt nem tesszük meg, akkor törlés után, mivel nincs értéke a SpinEdit-nek, hibaüzenettel leáll a program. Ilyenkor, amikor nincs érték benne, akkor a Text jellemzője „él” az objektumnak (nem a Value) és ez az üres string. Tehát a feltétel azt fogalmazza meg, hogy csak akkor számítsunk a Value értékekkel, ha nem üres string a SpinEdit Text jellemzője, magyarán, amikor szám van benne. • A Panel színét pedig az RGB eljárással keverjük ki, ahol paraméterként a színek intenzitását kell megadni 0 és 255 közötti értékkel. 18. Feladat. Tömbök kezelése. Írjunk egy tömbkezelő programot. a) Menüpontok: • Tömb/generálás, kiírás • Statisztika/átlag, max, min, 3-al osztható elemek száma b) A generálás menüponttal a tömb elemeit véletlenszerűen töltsük fel 0 és 500 közti számokkal c) A kiírás menüponttal jelenítsük meg a tömb elemeit. d) A Statisztika menü menüpontjaival jelenítsük meg a megfelelő értéket. Megoldás: • Elhelyezünk az ablakba egy főmenüt (MainMenu) és a Caption jellemzőjébe rendre beírjuk a menüpontokat. • Globális változót deklarálunk egy 10 elmemű, egész számokat tartalmazó tömbnek. Az implementation fölé írjuk be: t:array[1..10] of integer; • Eljárást rendelünk a generálás menüponthoz, a következő utasításokkal: ... var i: integer; begin randomize; for i:=1 to 10 do t[i]:=random(501); end;
• Eljárást rendelünk a kiírás menüponthoz az alábbi utasításokkal: ... var i: integer; s:string; begin s:=’A tömb elemei:’; for i:=1 to 10 do s:=s+IntToStr(t[i])+’ ’; ShowMessage(s); end;
125
• Eljárást rendelünk az átlag menüponthoz a következő utasításokkal: ... var i: integer; atlag:real; begin atlag:= 0; for i:=1 to 10 do atlag:=atlag+t[i]; atlag:=atlag/10; ShowMessage(’Átlag=’+FloatToStr(atlag)); end;
• Eljárást rendelünk a max menüponthoz az alábbi utasításokkal: ... var i,max: integer; begin max:= t[1]; for i:=2 to 10 do if t[i]>max then max:=t[i]; ShowMessage(’Max=’+IntToStr(atlag)); end;
• Eljárást rendelünk a min menüponthoz, és hasonlóan járunk el mint a max esetén. • Eljárást rendelünk a 3-al osztható elemek száma menüponthoz a következő utasításokkal: ... var i,hoesz: integer; begin hoesz:= 0; for i:=1 to 10 do if t[i] mod 3 = 0 then inc(hoesz); ShowMessage(’3-al osztható elemek száma=’+IntToStr(hoesz)); end;
19. Feladat. Dátum- és időkezelés. Írjunk egy programot, amivel kiszámíthatjuk, hogy hány napot éltünk. A mai dátum automatikusan jelenjen meg, a születésnapi dátumot lehessen kiválasztani egy naptárból.
126
10.12. ábra. A 18. Feladathoz tartozó űrlapterv
Megoldás: • Elhelyezünk az ablakba három feliratot (Label) és a Caption jellemzőjébe rendre beírjuk a megjelenítendő szövegeket, elhelyezünk az első és a harmadik felirat után két szövegmezőt (Edit), amit üresen hagyunk, illetve elhelyezünk egy TDateTimePicker típusú objektumot a második felirat után. • Mivel a mai dátum automatikusan kell megjelenjen a program indulásakor, a Form1 OnCreate eseményéhez rendelünk eljárást és írjuk be: Edit1.Text:=DateToStr(Date);
A Date függvény által megadott mai dátumot átalakítjuk a DateToStr függvénnyel szöveges formába és ezt kapja meg az Edit1. • Az eltelt napok száma, akkor kell megjelenjen, ha kiválasztunk egy dátumot a naptárból (megadva a születési dátumot), ezért a DateTimePicker1 objektum OnChange eseményéhez rendelünk eljárást és beírjuk: Edit2.Text:=IntToStr(DaysBetween(Date,DateTimePicker1.Date));
A DaysBetween függvénnyel a mai dátum és a DateTimePicker1-ben levő dátumok közt eltelt napok számát számoljuk ki, és ezt alakítjuk át szöveggé, amit megkap az Edit2. • Mivel a DaysBetween függvény a DateUtils egységben van deklarálva, és ez nem jelenik meg automatikusan az egységek listájában, ezt mi kell beírjuk. 20. Feladat. A folyamatjelző (ProgressBar) használata. Írjunk egy programot, amiben generálunk 100 darab, egyenként 1.000.000 elemszámú, véletlenszerű értékeket tartalmazó tömböt. A folyamat működését egy folyamatjelzővel (ProgressBar) szemléltessük. Megoldás: • Elhelyezünk az ablakba egy gombot, amire ha kattintunk, elindul a folyamat, illetve egy szöveget, aminek a felirata a folyamat végén „Kész!”-re változik. 127
• Deklaráljunk globális változóként egy egész számokat tartalmazó mátrixot, ennek oszlopai fogják tartalmazni a 100 tömböt: a: array[1..100,1..1000000] of integer;
• Rendeljünk eljárást a gomb OnClick eseményéhez és írjuk be: for i:=1 to 100 do begin tomb(i); ProgressBar1.StepIt; end; Label1.Caption:='Kész!';
A tömb eljárás generálja az i. egymillió elemű tömböt, majd utána folyamatjelzővel lépünk egyet. • Utolsó lépésként meg kell írni a tömb nevű eljárást. Először az eljárásnak egy deklarációját kell elhelyezni az illesztő részben: procedure tomb(sorszam:integer);
• Az eljárás definíciója: procedure TForm1.tomb(sorszam:integer); var i:longint; begin randomize; for i:=1 to 1000000 do a[sorszam,i]:=random(15000); end;
21. Feladat. Alakzatok használata. Helyezzünk el egy alakzatot (Shape) az űrlapra. Rendeljünk hozzá egy gyorsmenüt a következő menüpontokkal: kör, négyzet, piros, kék. Az első két menüponttal az alakzat alakját a másik kettővel a színét lehessen változtatni. Lehessen kiválasztani, hogy a szín a körvonalra vagy az alakzatot kitöltő színre vonatkozik. Lehessen változtatni a körvonal vastagságát is. Megoldás: • Elhelyezünk az ablakba az Additional palettáról egy Shape objektumot és a Standard palettáról egy gyorsmenüt (PopupMenu). A gyorsmenüre duplán kattintva megnyitjuk a menüszerkesztő ablakot és a Caption jellemezőbe rendre beírjuk: kör, négyzet, piros, kék. Ahhoz, hogy a gyorsmenü az ablakra kattintva (jobbgombbal) jelenjen meg, kijelöljük az ablakot (Form1) és a PopupMenu jellemzőjénél kiválasztjuk a PopupMenu1-et.
128
• Ahhoz, hogy megadhassuk, hogy a szín mire vonatkozik, helyezzünk el az ablakba egy RadioGroup-ot a Standard palettáról. Az Items jellemzőjénél adjuk meg a két opció feliratát: körvonal és belső. Induláskor legyen a körvonal opció kiválasztva, ezért az ItemIndex jellemző értékét állítsuk be 0-ra. • A körvonal vastagságát egy a Samples palettáról elhelyezett SpinEdit objektummal fogjuk változtatni. A Value jellemzőjét állítsuk 1-re. • Rendeljünk eljárást a kör menüponthoz és írjuk be: Shape1.Shape:=stCircle;
• Rendeljünk eljárást a négyzet menüponthoz és írjuk be: Shape1.Shape:=stSquare;
• Rendeljünk eljárást a piros menüponthoz és írjuk be: if RadioGroup1.ItemIndex=0 then Shape1.Pen.Color:=clRed else Shape1.Brush.Color:=clRed;
Ha a körvonal opció van kiválasztva, akkor az alakzat tollának színe legyen piros egyébként pedig az ecset színe. Hasonlóan kell eljárni a kék menüpontnál is, különbség: clRed helyett clBlue. • Rendeljünk eljárást a SpinEdit1 OnChange eseményéhez és írjuk be: Shape1.Pen.Size:=SpinEdit1.Value;
22. Feladat. Grafikus objektumok és a rajzvászon jellemzői. Írjunk egy programot, amivel egy Image objektum rajzvásznára lehet kattintással feliratokat és négyzeteket elhelyezni a beállított toll és ecset tulajdonságokat figyelembe véve. Megoldás: Tervezzük meg a következő ablakot:
129
10.13. ábra. A 21. Feladathoz tartozó űrlapterv
• Helyezzük el az ablakba a következő komponenseket: • Egy TGroupBox típusú komponenst (Caption=”Toll tulajdonsagai”), amellyel a toll tulajdonságait változtató komponenseket fogjuk elhelyezni: o Egy gombot a toll színének változtatására o Egy TShape típusú objektumot (Shape=stRectangle) a kiválasztott színnek a jelzésére o Egy TSpinEdit típusú objektumot a toll vastagságának az állítására. o Egy TRadioGroup típusú objektumot, ami tartalmazza a toll rajzolási módjait. • Egy újabb TGroupBox típusú komponenst (Caption=”Ecset tulajdonsagai”), amellyel az ecset tulajdonságait változtató komponenseket fogjuk elhelyezni: o Egy gombot az ecset színének változtatására o Egy TShape típusú objektumot (Shape=stRectangle) a kiválasztott színnek a jelzésére o Egy TRadioGroup típusú objektumot, ami tartalmazza az ecset stílusait. o Egy Bitmap feliratú gombot, amivel az ecset kitöltő grafikus képét egy állományból lehet megadni. • Egy TColorDialog típusú objektumot a színek kiválasztására. • Egy TOpenDialog típusú objektumot állományok megnyitására. • Egy TRadioGroup típusú objektumot, amiben azt választjuk ki, hogy négyzetet, vagy szöveget fogunk elhelyezni a vászonra, és ami tartalmaz egy szövegmezőt is, amelyben levő szöveget helyezzük el a vászonra. • Egy Háttérszín feliratú gombot, amivel a vászon színét lehet változtatni • Egy TImage típusú objektumot, aminek a vásznára fogunk rajzolni. • Rendeljünk eljárást a toll színkiválasztó gombjának OnClick eseményéhez és írjuk be: 130
if ColorDialog1.Execute then begin Image1.Canvas.Pen.Color:=ColorDialog1.Color; Shape1.Brush.Color:=ColorDialog1.Color; end;
A színkiválasztó ablakban kiválasztott szint alkalmazzuk a vászon tollszínére valamint az ecset színére. • Rendeljünk eljárást a SpinEdit1 objektum OnChange eseményéhez és írjuk be: Image1.Canvas.Pen.Width:=SpinEdit1.Value;
• Rendeljünk eljárást a RadioGroup1 objektum (toll rajzolási módjai) OnClick eseményéhez és írjuk be: with Image1.Canvas.Pen do case RadioGroup1.ItemIndex of 0: mode:=pmBlack; 1: mode:=pmWhite; 2: mode:=pmNop; 3: mode:=pmNot; 4: mode:=pmCopy; 5: mode:=pmNotCopy; 6: mode:=pmMerge; 7: mode:=pmXor; 8: mode:=pmNotXor; end;
Alkalmazzuk a toll mode jellemzőjére a megfelelő rajzolási módot. • Rendeljünk eljárást az ecset színkiválasztó gombjának OnClick eseményéhez és írjuk be: if ColorDialog1.Execute then begin Image1.Canvas.Brush.Color:=ColorDialog1.Color; Shape2.Brush.Color:=ColorDialog1.Color; end;
• Rendeljünk eljárást a RadioGroup2 objektum (ecset stílusa) OnClick eseményéhez és írjuk be: Image1.Canvas.Brush.Style:=TBrushStyle(RadioGroup2.ItemIndex);
Mivel a RadioGroup2 elemeiben az összes ecsetstílust beírtuk, és ugyanabban a sorrendben, ahogyan a TBrushStyle-ban (ecsetstílus típus) szerepel, használhatjuk a típus_neve(sorszám) kifejezést, ami – felsorolás jellegű típusról lévén szó – a megfelelő sorszámú elemet jelenti. Vagyis a RadioGroup2-ben kiválasztott elem
131
sorszáma (ItemIndex) megegyezik az ecsetstílus megfelelő stílusának sorszámával, ezért a fenti utasítást beírva az ecset stílusa felveszi a kiválasztott stílust. • Rendeljünk eljárást a Bitmap feliratú gomb OnClick eseményéhez és írjuk be: ... var b:TBitmap; begin if OpenDialog1.Execute then begin b:=TBitmap.Create; b.LoadFromFile(OpenDialog1.FileName); Image1.Canvas.Brush.Bitmap:=b; end; end;
Dinamikusan létrehozunk egy TBitmap objektumot, amibe betöltjük a kiválasztott képet. • Rendeljünk eljárást a Háttérszín feliratú gomb OnClick eseményéhez és írjuk be: if ColorDialog1.Execute then begin Image1.Canvas.Brush.Color:=ColorDialog1.Color; Image1.Canvas.FloodFill(1,1,Image1.Canvas.Pixels[1,1], fsSurface); end;
A FloodFill eljárással kitöltjük a teljes vászont, az (1,1) koordinátájú pontból kiindulva, addig amíg ennek a pontnak a színeit (Pixels[1,1]) tartalmazó pontokat találunk. • Rendeljünk eljárást az Image1 OnMouseDown eseményéhez (ez az eljárás fogja tartalmazni az x,y paraméterekben az egér kattintási pozícióját) és írjuk be: if RadioGroup3.ItemIndex=0 then Image1.Canvas.Rectangle(x,y,x+50,y+50) else Image1.Canvas.TextOut(x,y,Edit1.Text);
Ha az első elem (négyzet) van kiválasztva, akkor rajzoljunk a kattintási pontba egy 50x50-es téglalapot, egyébként (a szöveg elem van kiválasztva) a szövegmezőben szereplő szöveget írjuk ki a kattintási pontban. 23. Feladat. Rajzolás. Készítsünk egy rajzoló programot. Megoldás:
132
• Elhelyezünk az ablakba egy Panel objektumot. Erre fogjuk helyezni az alkalmazás gombjait, ezért a Panel1 objektum Align jellemzőjét állítsuk be alTop-ra. • Elhelyezünk az ablakba egy Image objektumot az Additional palettáról. Ezen lesz a rajzlapunk, ezért az Image1 objektum Align jellemzőjét állítsuk be alClient-re és így az Image1 az ablak többi részét fogja betölteni. • Helyezzük el a panelre a következő objektumokat: • öt darab SpeedButton objektumot (Additional paletta) • egy SpinEdit objektumot (Samples paletta) • két kis méretű panelt • két gombot • három feliratot (Label) a SpinEdit és két panel elé. • A rajzlapra (Image1) helyezzünk el a Dialogs palettáról egy-egy ColorDialog, OpenDialog és SaveDialog objektumot. • A SpeedButton objektumokra írjuk fel a következő feliratokat: Sz (szabadkézi rajzolás), V (vonal), K (kör), T (téglalap), F (kifestés). • Az öt SpeedButtont csoportosítani fogjuk, annak érdekében, hogy egyszerre csak egyet lehessen benyomni közülük (tehát egymást kölcsönösen zárják ki). Ezért kijelöljük mind az ötöt (kijelöljük az elsőt és a Shift gombot nyomva tartva, rendre kattintunk a másik négyre is) és a GroupIndex jellemzőjüket 1-re állítjuk. Hogyha az AllowAllUp jellemzőjük értéke False, akkor azt jelenti, hogy egy az öt gomb közül mindig be kell legyen nyomva. Beállítjuk, hogy kezdetben az első legyen benyomva, ezért kijelöljük az első gombot és a Down jellemzőjét a True értékre állítjuk. • A két kisebb panel a toll illetve az ecset színét kell jelezze ezért az első színét állítsuk be feketére a másodikét fehérre. • A feliratok szövegét változtassuk meg: Toll vastagsága, Toll színe, Ecset színe. • A két gomb feliratait változtassuk meg: Megnyitás, Mentés. • A SpinEdit értékét állítsuk be 1-re. • A három panel feliratát töröljük le.
10.14. ábra. A 22. Feladathoz tartozó űrlapterv
• Első lépésként az Image1 objektumunkra el fogunk helyezni a programból egy Bitmap típusú objektumot, erre azért van szükség, hogy el tudjuk menteni majd a rajzot egy bmp (bitmap) kiterjesztésű állományba. Ezt a program indulásakor kell elvégezni, ezért a Form1 objektum OnCreate eseményéhez rendeljünk eljárást. ... var b:TBitmap; begin b:=TBitmap.Create; b.Height:=ClientHeight;
133
b.Width:=ClientWidth; Image1.Picture.Graphic:=b; end;
• Deklaráljuk a következő globális változókat: gomb, rajz, kp, rp. gomb:byte=1; rajz:boolean=False; kp,rp:TPoint;
A byte típusú gomb változó kezdő értéke 1. Ez a változó fogja jelezni azt, hogy az öt gomb (SpeedButton) közül melyik van benyomva. A boolean típusú rajz változó kezdő értéke False. Ennek a változónak az értéke fogja jelenteni, hogy rajzolunk (True) vagy sem (False). A TPoint típusú (pont típus, van x és y komponense) kp és rp változókban bizonyos pontok koordinátáit fogjuk tárolni: kezdőpont és régi pont. • Rendeljünk eljárásokat az öt SpeedButon OnClick eseményeihez és rendre írjuk be: Gomb:=1; , ..., Gomb:=5; • A rajzolás az Image1 objektum három eseményével történik: az egér lenyomása (OnMouseDown), az egér mozgatása (OnMouseMove), és az egér gombjának a felengedése (OnMouseUp). Ezekben az eljárásokban megvizsgáljuk, hogy melyik gomb van benyomva és ennek függvényében végezzük el a különböző rajzoló utasításokat. • Rendeljünk eljárást az Image1 objektum OnMouseDown eseményéhez és írjuk be: rajz:=true; with Image1.Canvas do case gomb of 1: MoveTo(x,y); 2,3,4:begin kp:=Point(x,y); rp:=kp; pen.Mode:=pmNotXor; end; end;
Az eljárásban a gomb változó értékének függvényében, kézi rajzolás történik vagy vonal, kör, téglalap rajzolása. A végén átállunk pmNotXor rajzolási módba. Ez azt jelenti, hogy ha majd később valamit rárajzolunk saját magára, akkor visszakapjuk a hátteret (törlés). • Rendeljünk eljárást az Image1 objektum OnMouseMove eseményéhez és írjuk be: if rajz then with Image1.Canvas do case gomb of 1:LineTo(x,y); 2:begin
134
MoveTo(kp.X,kp.Y); LineTo(rp.X,rp.Y); MoveTo(kp.X,kp.Y); LineTo(X,Y); rp:=Point(x,y); end; 3:begin Ellipse(kp.X,kp.Y,rp.x,rp.Y); Ellipse(kp.X,kp.Y,x,y); rp:=Point(x,y); end; 4:begin Rectangle(kp.X,kp.Y,rp.x,rp.Y); Rectangle(kp.X,kp.Y,x,y); rp:=Point(x,y); end; end;
A gomb változó értékének függvényében különböző rajzokat készítünk. Így, ha értéke 1 (szabadkézi rajzolás), akkor a toll pozíciójából az x,y pontba (az egér új pozíciója) húzunk egy vonalat. Tehát az egér minden mozdulatára összekötjük egy vonallal az egér régi és új pozícióját, így kapjuk a szabadkézi rajzolást. Ha értéke 2 (vonal húzása), akkor a kezdőpontba helyezzük a tollat és vonalat húzunk a régi pontba. Ezzel valójában (mivel pmNotXor rajzolási módban vagyunk) töröljük az előző vonalat. Utána visszahelyezzük a kezdőpontba a tollat, majd vonalat húzzunk abba a pontba, ahova az egér elmozdult. Ezzel rajzoltuk meg az aktuális vonalat, ami a kezdőpont és az egér pozíciója között helyezkedik el. Ha a változó értéke 3, akkor kört rajzolunk, illetve, ha 4 akkor téglalapot. • Rendeljünk eljárást az Image1 objektum OnMouseUp eseményéhez és írjuk be: rajz:=false; Image1.Canvas.Pen.Mode:=pmCopy; with Image1.Canvas do case gomb of 2:begin MoveTo(kp.X,kp.Y); LineTo(X,Y); end; 3:Ellipse(kp.X,kp.Y,x,y); 4:Rectangle(kp.X,kp.Y,x,y); 5:FloodFill(x,y,pixels[x,y],fsSurface); end;
Az egérgomb felengedésekor a rajzolás befejeződik és a gomb változó értékének függvényében kirajzoljuk a végleges alakzatot. Ráadásul, ha a gomb értéke 5 (kifestés), akkor az x, y pontból kiindulva (a kattintási és felengedési hely), amíg olyan folytonos területet (fsSurface) találunk amelynek színe megegyezik az x, y pont színével (pixels[x,y]), addig ezeket a pontokat átfestjük az ecset színével.
135
Ezzel az öt rajzoló gomb működését megírtuk, következik az az öt eljárás, amivel a toll vastagságát, színét, illetve az ecset színét lehet változtatni, és menteni illetve, megnyitni lehet a rajzot: • Rendeljünk eljárást a SpinEdit1 OnChange eseményéhez és írjuk be: Image1.Canvas.Pen.Width:=SpinEdit1.Value;
• Rendeljünk eljárást a Panel2 OnClick eseményéhez és írjuk be: if ColorDialog1.Execute then begin Image1.Canvas.Pen.Color:=ColorDialog1.Color; Panel2.Color:=ColorDialog1.Color; end;
Az eljárásban a színkiválasztó dialógus ablakban kiválasztott színt alkalmazzuk a toll színére és a panel színére. • Rendeljünk eljárást a Panel3 OnClick eseményéhez és írjuk be: if ColorDialog1.Execute then begin Image1.Canvas.Brush.Color:=ColorDialog1.Color; Panel3.Color:=ColorDialog1.Color; end;
Az eljárásban a színkiválasztó dialógus ablakban kiválasztott színt alkalmazzuk az ecset színére és a panel színére. • Rendeljünk eljárást a Megnyitás feliratú gomb OnClick eseményéhez, amelyben az Image1 képét a kiválasztott állományból töltsük be: if OpenDialog1.Execute then Image1.Picture.LoadFromFile(OpenDialog1.FileName);
• Rendeljünk eljárást a Mentés feliratú gomb OnClick eseményéhez, amelyben az Image1 képét mentsük le a kiválasztott állományba: if SaveDialog1.Execute then Image1.Picture.SaveToFile(SaveDialog1.FileName);
24. Feladat. Állománykezelés. Írjunk egy állománykezelő programot, amelyben típusos állományt használva, diákok jegyeit tartjuk nyílván. A program a következő menürendszerrel rendelkezzen: File
136
főmenü a következő almenüpontokkal: Létrehozás, Megnyitás, Kiírás, Bővítés, Módosítás, Törlés, Átírás szövegfájlba, Beolvasás szövegfájlból, Kilépés. Megoldás: • Elhelyezünk az ablakba egy MainMenu-t és a Caption jellemzőbe rendre beírjuk a menüpontokat. • Elhelyezünk az ablakba egy Memo objektumot, ebben fogjuk majd az állomány tartalmát megjeleníteni. • Helyezzünk el az ablakba még egy OpenDialog és egy SaveDialog típusú dialógusablakot. A Filter jellemzőjüknél adjuk meg a következő két állományszűrőt: szövegfájl - *.txt és típusos fájl - *.dat. • A programot úgy tervezzük meg, hogy bizonyos menüpontok csak akkor lesznek kiválaszthatók, ha teljesülnek bizonyos feltételek: például, amíg nincs létrehozva vagy megnyitva egy állomány, addig nem lehet kiírni, módosítani, stb. Induláskor csak az első kettő, illetve az utolsó két menüpont lesz aktív, ezért a többi menüpont Enabled jellemzőjét állítsuk át False-ra. Ezt viszont a legvégén érdemes csak, miután megírtuk a menüpontokhoz rendelt eljárásokat.
10.15. ábra. A 23. Feladathoz tartozó űrlapterv
• Az űrlap-egységben deklarálunk egy új típust és egy pár globális változót az alábbiak szerint: TDiak=record nev:array[1..25] of char; jegy:integer; end; ... var diak:TDiak; f,g:file of TDiak; t:text; N:integer; s,snev,fnev:string;
A TDiak egy record adattípus, amely egy diák nevét és jegyét fogja tárolni. A diak változónk, ami TDiak típusú, azt jelenti, hogy ezt a változót lehet majd beírni az állományba, illetve az állományból ebbe a változóba lehet beolvasni az értékeket.
137
• Rendeljünk eljárást a Létrehozás menüponthoz és írjuk be: ... var i,j:integer; begin SaveDialog1.FilterIndex:=2; if SaveDialog1.Execute then begin fnev:=SaveDialog1.FileName+'.dat'; AssignFile(f,fnev); Rewrite(f); n:=StrToInt(InputBox('Diakok beolvasasa', 'Hany diakot fogsz beolvasni?:','')); for i:=1 to n do begin s:=InputBox(IntToStr(i)+'. diak','Nev:',''); for j:=1 to length(s) do diak.nev[j]:=s[j]; for j:=length(s)+1 to 25 do diak.nev[j]:=' '; diak.jegy:=StrToInt(InputBox(IntToStr(i)+'. diak','Jegy:','')); Write(f,diak); end; CloseFile(f); for i:=2 to 7 do MainMenu1.Items[0][i].Enabled:=true; Form1.Caption:=fnev; end; end;
Az eljárásban párbeszédablak segítségével létrehozunk egy típusos állományt, amelybe beírunk bizonyos számú diák adatait. Az adatbekérés InputBox komponenssel történik. Az eljárás végén a megfelelő menüpontokat aktívvá teszünk. • Rendeljünk eljárást a Megnyitás menüponthoz és írjuk be: ... var i:integer; begin OpenDialog1.FilterIndex:=2; if OpenDialog1.Execute then fnev:=OpenDialog1.FileName; for i:=2 to 7 do MainMenu1.Items[0][i].Enabled:=true; Form1.Caption:=fnev; end;
Az eljárásban az OpenDialog ablak segítségével kiválasztjuk a szükséges tipusos állomány nevét, majd betesszük az fnev globális változóba. • Rendeljünk eljárást a Kiírás menüponthoz és írjuk be: ... var i:integer; begin Memo1.Lines.Clear;
138
AssignFile(f,fnev); Reset(f); n:=0; repeat Read(f,diak); Inc(n); Memo1.Lines.Add(IntToStr(n)+'. '+IntToStr(diak.jegy)+' - '+diak.nev); until Eof(F); CloseFile(f); end;
Az eljárásban megnyitjuk az fnev változóban tárolt állományt olvasásra, majd a tartalmát betöltjük egy Memo komponensbe a következő formában: sorszám, jegy, név. • Rendeljünk eljárást a Bővítés menüponthoz és írjuk be: ... var i,j:integer; begin AssignFile(f,fnev); Reset(f); Seek(f,FileSize(f)); n:=StrToInt(InputBox('Diakok beolvasasa','Hany diakot fogsz beolvasni?:','')); for i:=1 to n do begin s:=InputBox(IntToStr(i)+'. diak','Nev:',''); for j:=1 to length(s) do diak.nev[j]:=s[j]; for j:=length(s)+1 to 25 do diak.nev[j]:=' '; diak.jegy:=StrToInt(InputBox(IntToStr(i)+'. diak','Jegy:','')); Write(f,diak); end; CloseFile(f); Kiiras1Click(Sender); end;
Az eljárásban hozzáadás céljából megnyitjuk az fnev változóban található fájlt. A Létrehozás menüponthoz hasonlóan beolvassuk az új elemeket és ezeket beírjuk a fájl végére. Az eljárás végén meghívjuk a Kiiras1Click eljárást, ami a kiírás menüponthoz rendelt eljárás, kiíratva ezzel a fájl tartalmát. • Rendeljünk eljárást a Módosítás menüponthoz és írjuk be: n:=StrToInt(InputBox('modositas','sorszam:','')); AssignFile(f,fnev); Reset(f); Seek(f,n-1); Read(f,diak); diak.jegy:=StrToInt(InputBox(diak.nev,'uj jegy:',IntToStr(diak.jegy)));
139
Seek(f,n-1); Write(f,diak); Closefile(f); Kiiras1Click(Sender;
Az eljárásban a fájlt megnyitjuk módosítás céljából. Megkeressük a módosítandó elemet, majd megadva a diák új adatait visszaírjuk a fájlba. • Rendeljünk eljárást a Törlés menüponthoz és írjuk be: ... var i:integer; begin n:=StrToInt(InputBox('Torles','sorszam:','')); AssignFile(f,fnev); Reset(f); AssignFile(g,'uj'); Rewrite(g); for i:=1 to n-1 do begin Read(f,diak); Write(g,diak); end; Read(f,diak); for i:=n+1 to FileSize(f) do begin Read(f,diak); Write(g,diak); end; CloseFile(f); CloseFile(g); Erase(f); Rename(g,fnev); Kiiras1Click(Sender); end;
Az eljárásban megnyitjuk az állományt törlés céljából. A törlés úgy történik, hogy a törlendő elem kivételével minden elemet átmásolunk egy új fájlba, majd az új állományt átnevezzük az eredeti fájl nevére. • Rendeljünk eljárást az átirás szövegfájlba menüponthoz és írjuk be: AssignFile(f,fnev); Reset(f); SaveDialog1.FilterIndex:=1; if SaveDialog1.Execute then snev:=SaveDialog1.FileName+'.txt'; AssignFile(t,snev); Rewrite(t); repeat Read(f,diak); Writeln(t,diak.nev,' ',diak.jegy); until Eof(f); CloseFile(f);
140
CloseFile(t);
Az eljárásban a típusos fájl tartalmát átírjuk egy szöveges állományba soronként a következő formában: diák.név, ‘ ‘, diák.jegy. • Rendeljünk eljárást a beolvasás szövegfájlból menüponthoz és írjuk be: ... var i:integer; ch:char; begin OpenDialog1.FilterIndex:=1; if OpenDialog1.Execute then snev:=OpenDialog1.FileName; AssignFile(t,snev); Reset(t); SaveDialog1.FilterIndex:=2; if SaveDialog1.Execute then fnev:=SaveDialog1.FileName+'.dat'; AssignFile(f,fnev); Rewrite(f); repeat for i:=1 to 25 do Read(t,diak.nev[i]); Readln(t,diak.jegy); Write(f,diak); until Eof(t); CloseFile(t); CloseFile(f); for i:=2 to 7 do MainMenu1.Items[0][i].Enabled:=true; Form1.Caption:=fnev; end;
Az eljárásban a szöveges fájl tartalmát átírjuk egy tipusos állományba.
25. Feladat. Adatbázis-kezelés – kikeresett mezők használata. Jelenítsük meg egy közös táblában a DBDemos adatbázis Orders és Customer táblájából a CustNo, OrderNo, SaleDate (Orders tábla) és a Company, Addr1 (Customer tábla) mezőket tudva azt, hogy a CustNo a közös, kapcsolódó mező. A Customer tábla mezőit mint kikeresett mezők (Lookup) jelenítsük meg. Megoldás: • A BDE komponens palettáról helyezzünk el két Table objektumot. • Állítsuk be a Table1 jellemzőit: DatabaseName: DBDemos, TableName: Orders.db, Active: True. • Állítsuk be a Table2 jellemzőit: DatabaseName: DBDemos, TableName: Customer.db, Active: True.
141
• A Data Access komponens palettáról helyezzünk el két DataSource objektumot. • A DataSource1 objektum Dataset jellemzőjét állítsuk be Table1-re a DataSource2jét pedig Table2-re. Ezzel összekapcsoltuk a két táblát és a két adatforrást. • A Data Control komponens palettáról helyezzünk el egy DbGrid objektumot és a DataSource jellemzőjét állítsuk be DataSource1-re. Ez azt jelenti, hogy a DbGrid1 az első adatforrás adatait fogja megjeleníteni (az Orders tábla adatait). • Kattintsunk duplán a Table1 objektumra, megjelenik egy üres ablak, ez a mezőszerkesztő. Kattintsunk ebben jobbgombbal és válasszuk az Add Field parancsot. Itt lehet megadni, hogy a tábla mely mezőit kívánjuk megjeleníteni. Válasszuk ki (shift vagy ctrl billentyűket használva) a három mezőt (CustNo, OrderNo, SaleDate). • A másik két mező a Table2-ben van, ezeket mint kikeresett mező (lookup field) fogjuk megjeleníteni a táblázatban. Ehhez kattintsunk jobbgombbal a mezőszerkesztő ablakban és válasszuk az New Field parancsot. A megjelenő ablakban töltsük ki (ill. válasszuk ki, ahol lehet) a következő mezőket: o Name: Cég (ez lesz az új mező neve) o Type: String (meg kell adni a mező típusát) o Key fields: Custno (ez a közös mező a Table1-ben) o DataSet: Table2 (ez az a tábla neve, ahonnan átvesszük a mezőt) o Lookup keys: Custno (ez a közös mező a Table2-ben) o Result field: Company (ezt a mezőt vesszük át a Table2-ből) • Zárjuk be az ablakokat és jelöljük ki a Table1-et, majd az Active jellemzőjét tegyük False-ra majd ismét True-ra. (újra kell nyitni a táblát, ahhoz hogy megjelenjenek az új mezők) • Hasonló módon kell megjeleníteni az Addr1 mező értékét is. 26. Feladat. Adatbázis-kezelés, saját adatokkal Készítsünk egy telefon nyilvántartó alkalmazást, amelyben saját készítésű táblát használunk. Megoldás: Először elkészítjük az adatbázistáblát. Ehhez indítsuk el a Database Desktop-ot, és válasszuk a New/Table menüpontot, ezen belül pedig a dBASE IV tábla formátumot. Az itt megjelenő ablakban tudjuk megadni a meződefiníciókat az alábbiak szerint: Field Name VEZ_NEV KER_NEV IRSZAM VAROS CÍM TELEFON
Type C C C C C C
Size Dec 20 20 4 20 30 11
142
EMAIL
C
30
Ezek után hozzuk létre az indexeket a következő mezőköz: VEZ_NEV, KER_NEV, IRSZAM, VAROS, TELEFON. Ehhez nyomjuk meg a jobb felső részen, a lenyíló menü alatt lévő Define gombot, majd a mezők közül válasszuk azt, amelyikhez indexet kell rendelni. Ezek után a létrehozott táblát mentsük le abba a könyvtárba, ahová a programunkat is lementjük „phone.dbf” néven. A továbbiakban definiáljunk egy aliast az adatbázisunkhoz. Ehhez a DataBase Desktop Tools menüjében indítsuk el az Alias Managert. Az új alias legyen „phone”, a típusa maradjon STANDARD, az elérési útvonal a létrehozandó alkalmazásunk mappájára mutasson. A továbbiakban az alkalmazásunkat fogjuk elkészíteni. Indítsunk egy új alkalmazást, amelyet mentsünk abba a mappába ahová mentettük az adattáblánkat, az unitot Uphone néven, a projectet pedig Pphone néven. Adjunk egy DataModul-t a projekthez (File/New/Data Module). A neve legyen DM és mentsük le DMUnit néven. Továbbá helyezzünk egy Table és egy DataSource komponenst a DataModulra, az alábbi beállításokkal: • Table o Database Name: Phone o Name: Phone o TableName: Phone.dbf o Active: True • DataSource o DataSet: Phone o Name: PhoneDS A felhasználói felület egyetlen űrlapból fog állni, amelyen egy PageControl komponens segítségével külön oldalakon jelenítjük meg a program különböző lehetőségeit. Helyezzünk az űrlapra egy PageConrol elemet három lappal. Az első lapon kereshetünk ismerőseink telefonszámai között, a második lapon a részletes adatokat jeleníthetjük meg, a harmadikon pedig adatokat vihetünk fel. Ennek megfelelően állítsuk be a Caption tulajdonságokat "Kereses", "Reszletes Adatok" és "Adatfelvitel" -re. Első körben a kereső lapot fogjuk elkészíteni. Ennek érdekében vegyünk fel egy DBGrid, és egy DBNavigator elemet a „Kereses” lapunkra. A DBGrid DataSource tulajdonságánál nem látható egyelőre az adattábla DataSource komponense, mivel a DataModul-re helyeztük. A megoldás: deklaráljuk az űrlapunk egységében a DMUnit egységet az uses után. Most már beállíthatjuk a DBGrid forrásának a DM.PhoneDS-t, így megjelennek a mezők. Továbbá egészítsük ki a lapunkat keresési funkcióval. Ezért helyezzünk az űrlapra két Edit objektumot, egy ComboBox-ot, két cimkét, ami a következő ábrán látszik:
143
10.16. ábra. A „Kereses” lap terve
A bal oldali lenyíló lista fog felelni az adatok rendezettségéért. Items tulajdonságát töltsük fel a következő elemekkel: Vezetéknév, Keresztnév, Irányítószám, Város, Telefon. Text tulajdonsága legyen a Vezetéknév, ami az alapértelmezett rendezettséget jelenti, majd a Table komponens IndexName property-jét állítsuk VEZ_NEV-re. A ComboBox OnChange eseménye a következőképpen alakul: procedure TfrmTelefon.ComboBox1Change(Sender: TObject); begin case ComboBox1.ItemIndex of 0: DM.Phone.IndexName:='VEZ_NEV'; 1: DM.Phone.IndexName:='KER_NEV'; 2: DM.Phone.IndexName:='IRSZAM'; 3: DM.Phone.IndexName:='VAROS'; 4: DM.Phone.IndexName:='TELEFON'; end; end;
Ennek hatására a kiválasztott érték szerint lesz indexelve a tábla, így a sorrend is ilyen lesz. A keresés úgy történik, hogy a keresendő szöveget az Edit1 komponensbe kell beírjuk. A keresés akkor indul, ha Entert nyomunk, ezért a kereső kódunkat az Edit1 komponens OnKeyDown eseményébe írjuk: procedure TfrmTelefon.Edit1KeyDown(Sender: TObject; var Key: Word;Shift: TShiftState); begin if Key = VK_RETURN then if DM.Phone.FindKey([Edit1.Text]) then Edit1.SelectAll; end;
144
A FindKey, az aktuális Index szerinti mezőben keres a megadott értékre. A Reszletes Adatok feliratú lapon tulajdonképpen a tábla adatait egyenként tekinthetjük meg és navigálhatunk közöttük. Helyezzünk az űrlapra négy címkét, négy DBText komponenst valamint egy DBNavigator-t. Mind a négy DBText komponens DataSource tulajdonsága legyen DM.PhoneDS, a DataField-et pedig válasszuk a listából.
10.16. ábra. A „Reszletes Adatok” lap terve
Az Uj Adatok feliratú lapon lehetőségünk lesz új adatok felvételére, valamint módosítására. A lapra Label, DBEdit és nyomógomb objektumokat helyezünk el:
10.17. ábra. Az „Uj Adatok” lap terve
A DBEdit komponensek esetén be kell állítani a megfelelő DataSource és DataField tulajdonságokat. Az első két gomb OnClick eseménye az új adat hozzáadást illetve módosítást fogja megoldani:
145
procedure TfrmTelefon.Button1Click(Sender: TObject); begin DBEdit1.SetFocus; DM.Phone.Append; end; procedure TfrmTelefon.Button2Click(Sender: TObject); begin DBEdit1.SetFocus; DM.Phone.Edit; end;
A másik két gomb a létrehozás és a módosítás műveleteket vonja vissza vagy jóváhagyja. procedure TfrmTelefon.Button4Click(Sender: TObject); begin DM.Phone.Cancel; end; procedure TfrmTelefon.Button3Click(Sender: TObject); begin if DM.Phone.State in [dsEdit, dsInsert] then DM.Phone.Post; end;
Kódunkban először megvizsgáljuk, hogy az adathalmaz dsEdit (szerkesztés) vagy dsInsert (beszúrás) állapotban van-e, és ha igen, akkor meghívjuk a Post metódust. 27. Feladat. Adatbáziskezelés – SQL lekérdezések. Jelenítsük meg SQL parancsot használva a DBDemos adatbázis Orders és Customer táblájából a CustNo, OrderNo, SaleDate (Orders tábla) és a Company, Addr1 (Customer tábla) mezőket tudva azt, hogy a CustNo a közös, kapcsolódó mező. Alkalmazzunk szűrést is: csak azok az adatsorok jelenjenek meg amelyeknél a SelDate mező értéke nagyobb mint 01/01/1995. Megoldás: • • •
A BDE komponens palettáról helyezzünk el egy Query objektumot. Állítsuk be a DatabaseName jellemzőjét DBDemos-ra. Az SQL jellemzőbe kell beírni a Select parancsot: SELECT CustNo, OrderNo, SaleDate, Company, Addr1 FROM Orders, Customer WHERE (Orders.Custno=Customer.Custno) and (Seldate>’01/01/1995’)
146
• • • •
Az Active jellemzőt állítsuk True-ra. A Data Access komponens palettáról helyezzünk el egy DataSource objektumot. A DataSource1 objektum DataSet jellemzőjét állítsuk be Query1-re. A Data Control komponens palettáról helyezzünk el egy DbGrid objektumot és a DataSource jellemzőjét állítsuk be DataSource1-re.
28. Feladat. Adatbázis-kezelés – SQL lekérdezések. Jelenítsük meg SQL parancsot használva a DBDemos adatbázis Items és Parts táblájából a PartNo, Qty, Discount (Items tábla) és a Description, Cost (Parts tábla) mezőket tudva azt, hogy a PartNo a közös, kapcsolódó mező. Alkalmazzunk szűrést is: csak azok az adatsorok jelenjenek meg amelyeknél a Cost mező értéke kisebb mint 1000. Megoldás: SELECT Partno,Qty,Discount, Description, Cost FROM Items, Parts WHERE (Items.Partno=Parts.Partno) and (Cost<1000)
29. Feladat. Adatbázis-kezelés – SQL lekérdezések. Jelenítsük meg SQL parancsot használva a DBDemos adatbázis Employee táblájából az összes mezőt, amelyek teljesítik a következő feltételt: a Salary mező értéke 25000 és 35000 között van. Megoldás: SELECT * FROM Employee WHERE (Salary>25000) and (Salary<35000)
147
11. Kitűzött feladatok 1. Készitsél Delphiben konzol alkalmazást az alábbi feladatok megoldására: a. Egy pozitív számról döntsd el, hogy osztható-e 2-vel vagy ha nem, akkor osztható-e 5-tel. b. Egy beolvasott karakterről állapítsd meg, hogy kisbetű ('a'..'z'), nagybetű ('A'..'Z'), számjegy ('0'..'9') vagy speciális karakter (''..'/',':'..'@','['..'`'). c. Kérjél be egy számot és írasd ki a neki megfelelő hónapot. d. Számold ki az első N darab páros szám összegét. e. Olvassál be egy karakterláncot, majd add meg a nagybetűk számát. f. Bankba tesszük a pénzünket kamatozni, és addig tartjuk bent, amíg milliomosok nem leszünk. A program számolja ki, hogy hány évet kell várnunk. g. Keresd meg egy tömb legnagyobb elemét, és jegyezd meg ennek a tömbbeli indexét. h. Írjál eljárást, amely a parameterként kapott számnak megfelelő napot írja ki a képernyőre. i. Írálj függvényt, amely a paraméterként megadott karakterről megállapítja, hogy szamjegye. j. Diákok tanulmányi átlagának számítása és maximális kreditpontjainak megkeresése unit-ok segitségével. 2. Számítsd ki a négyzet területét, kerületét és a kocka felszínét, térfogatát. A számításhoz definiáljál objektumokat valamint származtatott objektumokat. 3. Egy szövegmezőbe (Edit) írjuk be a nevünket, amely azonnal megjelenik egy címke (Label) feliratában, majd gomb kattintásra az űrlap címében. 4. Az előző feladatot úgy modosítsad, hogy az űrlapra kattintva a címkét rejtsd el, duplán kattintva pedig jelenjen meg újra. 5. Négy nyomógomb (Button) segítségével lehessen növelni illetve csökkenteni az űrlap hosszát és magasságát, másik két gommbal pedig az űrlapot lehessen elmozdítani vizszintesen és függőlegesen. 6. Helyezzál az űrlapra két nyomógombot. Az űrlapra kattintva az egyik gomb mozduljon el az űrlap balfelső sarkába, a másik gomb a jobbalsó sarkába. Dupla kattintáskor a gombok kerüljenek vissza az eredeti poziciójukba. 7. Készítsél egy programot, amely egy megadott évszámról eldönti, hogy szökőév-e és annak megfelelően megjelenit egy üzenetet. 8. Készítsél egy szám faktoriálisát, kiszámító programot. A számot egy szövegmezőben adjuk meg, majd egy gombra kattintva az eredmény jelenjen meg egy címkében.
148
9. Készítsünk programot egy valutaváltó működésének a szemléltetésére. Az egyszerűség kedvéért feltételezzük, hogy csak eurót adunk vagy veszünk. 10. Írjunk programot, amelyben (RadioButton) állítjuk be.
az
űrlap
háttérszínét
valasztógombokkal
11. Írjunk programot, amely kiszámolja a kör kerületét és területét. A számításhoz használjunk kétállapotú jelölőnégyzetet.
11.1. ábra. A 11. Feladat futás közben
12. Írjunk programot a gömb felszínének és térfogatának kiszámítására. A számítások kijelölését a RadioGroup vezérlőben létrehozott választógombokkal oldjuk meg.
11.2. ábra. A 12. Feladat futás közben
13. Készítsünk programot, amely jelölőnégyzetekkel szabályozza a „Delphi” felirat betűtípusának stílusát (dőlt, kövér, aláhúzott stb.).
11.3. ábra. A 13. Feladat futás közben
149
14. Egy listadoboz előre fel van töltve különböző autómárkák neveivel. Minden egyes autómárkának a programban tároljuk az árát. Az egyes autók kiválasztásakor jelenjen meg az autó ára a képernyőn. 15. Készítsünk programot, amelyben a nevek egyoszlopos listában és rendezetlenül jelennek meg. Legyen lehetőség a lista elemeinek ábécé sorrendben való megjelenítésére. A kiválasztott listaelemet jelenítsük meg a szövegmezőben.
11.4. ábra. A 15. Feladat futás közben
16. Helyezzünk az űrlapra egy listát, ami kezdetben üres. Egy szövegmezőbe írt szöveget lehessen a listába tenni, valamint lehessen a lista egy adott sorszámú elemét törölni. 17. Készítsünk egy egyszerű számológép programot.
11.5. ábra. A 17. Feladat futás közben
18. Helyezzél az űrlapra egy listát (List), egy Memo objektumot. A lista esetén engedélyezett a többelemes szelekció. A kijelölt elemeket átmásoljuk a Memo-ba. Egy választó-gombcsoport segitségével lehetőségünk legyen a Memo betű tipusát változtatni.
150
11.6. ábra. A 18. Feladat futás közben
19. Egy kódoló eljárással egy Memo objektumba töltött szöveget kódoljunk és mentsünk el, illetve visszatöltve tudjuk dekodólni azt (kódolási módszer például: Ceasar módszer). 20. Egy Memo objektumban nyissunk meg egy szöveget és készítsünk a tartalmából karakter statisztikát. A statisztika eredményeit jelenítsük meg egy második Memo-ban. Lehessen menteni és visszatölteni. 21. Tervezzünk egy olyan menürendszert, melynek főmenüje (MainMenu) a Menü, legördülő menüjének két eleme a "Szöveg megjelenítése" és a "Kilépés". A "Szöveg megjelenítése" menüponthoz kételemű almenü tartozik: a "Kisbetűs szöveg" menüpont az gyorsítóbillentyűvel, a "Nagybetűs szöveg" menüpont pedig az gyorsítóbillentyűvel. A megfelelő menüpontot választva az űrlapon megjelenik vagy egy kis betűs szöveg vagy egy nagybetűs. 22. Készítsünk programot, amelyben felbukkanó menüből (PopUpMenu) választva megjelenítjük egy szövegmezőben 2 négyzetét, négyzetgyökét. Az eredményt lehessen törölni is. 23. Készítsünk programot, amely szövegszerkesztőként működik és a szövegeket állományba képes menteni, illetve beolvasni onnan.
11.7. ábra. A 23. Feladat futás közben
151
24. Használjál két UpDown vezérlőt két szám szorzatának és négyzetgyökének kiszámítására.
11.8. ábra. A 24. Feladat futás közben
25. Készítsünk alkalmazást futó reklámszöveg készítésére. Legyen lehetőség a szöveget megadni egy InputBox segitségével. Használjuk a Timer komponenst. 26. Helyezzél az űrlapra két szövegmezőt. Az első szövegmezőbe írt szöveget gombkattintásra át szeretnénk másolni a második szövegmezőbe. Ha az első mező tartalma üres, a program figyelmeztessen, különben a program kérdezzen rá, hagy valóban akarunk-e másolni. 27. Készítsünk egy valutakonvertáló programot. használjunk InputBox objektumot, vagy űrlapot.
Az
árfolyam
bekéréséhez
11.9. ábra. A 27. Feladat futás közben
28. Készítsünk egy faktoriálist számító programot. A számot egy szövegmezőben adjuk meg, az eredmény egy címkében jelenjen meg. A program figyelmeztessen arra, ha túl nagy (pl. >12) számot írtunk be és ekkor ne is számolja ki a faktoriális értékét. A program kilépéskor kérdezzen rá, hogy valóban ki akarunk-e lépni. 29. Készítsünk programot, amely tartalmazni fog egy Memo komponenst és három nyomógombot. Az első nyomógomb egy dialógusablak segítségével válasszon ki egy TXT fájlt, majd olvassa be a program a Memo komponensünkbe a fájl tartalmát. A második nyomógomb mentse el a fájlt (dialógusablakkal lehessen megadni a fájl nevét és helyét), a harmadik nyomógomb segítségével lehessen megváltozatni a Memo komponens betűtípusát. Az alkalmazást bővítsük ki menüvel (MainMenu), ahonnan szintén elérhető legyen ez a három funkció.
152
11.10. ábra. A 29. Feladat futás közben
30. Készítsünk egy programot, amelyben bemutatjuk a modális és nem modális ablakok megjelenítését. 31. Írjunk programot, amely az elsőfokú egyenlet megoldását végzi. Az együtthatók beolvasására használjunk egy második űrlapot. Próbáljuk ki a modális és nem modális ablak megjelenítést is. 32. Készítsünk programot, amely másodfokú egyenlet megoldását végzi. Az együtthatók beolvasására használjunk egy második űrlapot. Próbáljuk ki a modális és nem modális ablak megjelenítést is. 33. Készítsünk programot, amely kiír 5 db véletlenszámot a képernyőre. Annak beállítása, hogy a számokat milyen intervallumból generálja, történjen egy új űrlap segítségével. 34. Készitsünk programot, képek megtekintésére. A kép kereséshez használjál dialógus ablakot. 35. Írjunk programot egy téglalap megrajzolására. A téglalap méreteit egy új űrlap segitségével kérjük be. A teglalap keretszínét és kitöltő színét dialógusablak segítségével adjuk meg. 36. Rajzoljunk különböző alakzatokat az űrlapra (téglalap, kör, négyzet, stb.). A megfelelő alakzat kiválasztása történjen választó-gombcsoport segítségével. 37. Rajzoljunk az űrlap rajzlapjára különböző méretű és színű téglalapokat és köröket véletlenszerű pozíciókba. 38. Készítsünk menüvezérelt programot, amellyel piros és kék színű kört vagy téglalapot rajzolhatunk. A menüpontokhoz tervezzünk eszköztárban (ToolBar) nyomógombokat is.
153
11.11. ábra. A 38. Feladat futás közben
39. Rajzoljunk piros és kék színű kört vagy téglalapot. A kiválasztott szint az állapotsor (StatusBar) Szín, a kiválasztott alakzat nevét pedig az Alakzat helyén jelezzük ki.
11.12. ábra. A 39. Feladat futás közben
40. Helyezzünk az űrlapra egy Shape objektumot. Egy választógomcsoport segítségével változtassuk a az objektum formáját, egy másokkal a rajzoló toll színét, egy harmadikkal az ecset stílusát. 41. Készítsél egy kis rajzoló programot.
11.13. ábra. A 41. Feladat futás közben
42. A 16, 19, 21, 22 feladatoknál használjunk kivételkezelést osztás és négyzetgyökvonás esetén.
154
43. Készítsünk programot, amely alkalmas számadatok megadására, az adatok szöveges fájlba történő mentésére, illetve állományból való olvasására. 44. Készítsünk programot, amely alkalmas számadatok megadására, az adatok tipusos fájlba történő mentésére, illetve állományból való olvasására. 45. Készítsünk programot halkatalógus tartalmának megjelenítésére. Használjuk a Delphi DBDEMOS adatbázisának biolife.db tábláját. 46. Készítsünk alkalmazást a Table komponens használatának bemutatására. Használjuk a Delphi DBDEMOS adatbázisát. 47. Készítsünk alkalmazást különböző SQL lekérdezések végrehajtására. Használjuk a Delphi DBDEMOS adatbázisát. 48. Készítsünk alkalmazást, amelyben SQL lekérdezés segitségével megkereshetjük az összes olyan bejegyzést egy adattáblában, amelynek egyik mezőértéke megegyezik egy másik táblázatból kiválasztott értékkel. Használjuk a Delphi DBDEMOS adatbázisának customer.db és orders.db tábláját. 49. Készítsünk programot telefonkönyv nyilvántartására. Lehessen új adatokat bevinni, lehessen módosítani és törölni. 50. Készítsünk programot saját könyvtárunk nyilvántartására. Lehessen új adatokat bevinni, lehessen módosítani és törölni.
155
Szakirodalom
ANGSZTER Erzsébet 1999 Programozás tankönyv : I. : strukturált tervezés : Turbo Pascal. Budapest, 4 Kör Bt. ANGSZTER Erzsébet 1999 Programozás tankönyv : II. : strukturált tervezés : Turbo Pascal. Budapest, 4 Kör Bt. ANGSZTER Erzsébet 2000 Az objektumorientált tervezés és programozás alapjai : UML Turbo Pascal C++. Budapest, 4 Kör Bt. BAGA Edit 1999 Delphi másképp. Martonvásár, Akadémiai Nyomda CANTÚ, Marco 2003 Delphi 7 Mesteri szinten. I-II. kötet. Budapest, Kiskapu Kft. SAS Tibor 1998 Delphi abszolút kezdőknek. Budapest, LSI Oktatóközpont
156
Abstract Delphi is a programming language and a software development environment for Microsoft Windows applications. It combines the easy to used visual environment with the fast optimized single pass compiler and with accessing a database engine. Delphi has also the advantage that it is easy to used and based on the well designed language Pascal. The main scope of this book is to present the most important techniques in order to write simple Windows applications. It is assumed that the students have good Pascal knowledge but no object oriented programming experience required. The book describes the development environment and the most important Delphi components. There are also covered techniques like exception handling, working with multiple forms, working with files, database development. At the end of the book there is a chapter with solved problems and one with proposed problems for practicing.
157
Rezumat
Delphi este un limbaj de programare şi un mediu de dezvoltare pentru programe, care combină uşurinţa utilizării unui mediu vizual de dezvoltare, viteza unui compilator pe 32 de biţi optimizat şi accesul la un motor de baze de date. Nu în ultimul rând puterea mediului Delphi îşi are originea şi limbajul de programare care stă la baza sa, şi care este o versiune orientată obiect a limbajului Pascal. Scopul principal al cărţii este prezentarea tehnicilor mai importante în baza cărora pot fi realizate aplicaţii simple Windows. Pentru a putea înţelege şi folosi tehnicile prezentate este necesar ca utilizatorul să cunoască bine limbajul Pascal, dar nu sunt necesare cunoştinţe de programare orientată pe obiecte. Cartea cuprinde prezentarea componentelor mai importante, ferestrelor de dialog şi a mesajelor. De asemenea sunt descrise tehnici ca tratarea excepţiilor, lucrul cu ferestre multiple şi fişiere, dezvoltarea aplicaţiilor de baze de date. La finalul cărţii este un capitol cu probleme rezolvate şi una cu probleme propuse pentru a exersa tehnicile prezentate.
158
A szerzőkről Pál László 1975 szeptember 27.-én születtet Csíkszeredában. A kolozsvári BabeşBolyai Tudományegyetem matematika szakán szerzett egyetemi oklevelet 1998-ban. Ugyanitt fejezte be informatika tanulmányait 2001-ben, valamint magiszteri oklevelet szerzett 2004-ben. 2006-tól a szegedi Tudományegyetem Matematika ás Számítástudományok doktori iskola doktorandusz hallgatója. Az egyetem elvégzése után három évet általános iskolában oktatott, majd 2004-től a Sapientia EMTE csíkszeredai Gazdaság- és Humántudományok Karának főállású tanársegéde. Kutatási témája a globális optimalizálás. Máté Szilárd 1971 augusztus 9.-én születtet Csíkszeredában. A kolozsvári BabeşBolyai Tudományegyetem matematika-informatika szakán szerzett egyetemi oklevelet 1995-ben. 2001-ben a Babeş-Bolyai Tudományegyetem gyergyószentmiklósi főiskoláján szerzett mérnöki oklevelet topográfia-kataszter-térképészet szakon. 2003-tól a bukaresti Műszaki Egyetem doktorandusz hallgatója. 2001-től a Sapientia EMTE csíkszeredai Gazdaság- és Humántudományok Karának főállású tanársegéde, 2007-től óraadó adjunktusa. Kutatási témája a térinformatika.
159