Umíme to s Delphi, 6. díl - struktura programu, VCL, standardní

Umíme již vytvořit jednoduchou aplikaci, vybrat komponenty, ošetřit události, najít v programu chybu - zkrátka vše podstatné, chtělo by se říct. Dnes si nejprve povíme několik detailů ke správě většího projektu, Popíšeme si podrobněji knihovnu vizuálních komponent a ještě něco navíc..
Umíme již vytvořit jednoduchou aplikaci, vybrat komponenty, ošetřit události, najít v programu chybu - zkrátka vše podstatné, chtělo by se říct. Dnes si nejprve povíme několik detailů ke správě většího projektu (a štábní kultuře programu), poté poprvé trochu zabrousíme do teorie (ale nebojte se, opravdu jen trochu). Popíšeme si podrobněji knihovnu vizuálních komponent (VCL). Pak se raději hned vrhneme na ryze praktickou záležitost - na popis standardních boxů, které umožňují základní uživatelskou interakci typu „ano, ne, storno“.

Řešení domácího úkolu

V minulém díle (5. kapitola) se na konci článku vyskytoval jakýsi námět k zamyšlení pro vás. Ptal jsem se, proč „nezabere“ breakpoint umístěný dovnitř jednoduchého for cyklu v našem jednoduchém příkladě. Nuže, důvodem je to, že Delphi provádí při překladu (nenastavíte-li jinak) optimalizaci. Překladač zjistil, že proměnnou, do které jsme uvnitř cyklu přičítali číslo 10, nikde za cyklem v programu nevyužijeme (výslednou hodnotu z jí nečteme), proto celý cyklus vyřadil a ten se v přeloženém programu neobjevil. Breakpoint umístěný do jeho těla tudíž nemohl „zabrat“. Když jsme potom do cyklu přidali test na hodnotu proměnné j, cyklus se ve výsledku objevil a breakpoint „zabral“.

Struktura programu v Delphi

A teď už k dnešnímu tématu. Ze všeho nejdříve se podívejme na to, jak vypadá struktura souborů projektu, tedy těch souborů, které použije Delphi k vytvoření spustitelného programu.

Program v Delphi je „zkonstruován“ ze zdrojových souborů (lidově zdrojáků), které se nazývají units (programové jednotky, soubory *.PAS). Každá jednotka je uložena v separátním souboru a je kompilována samostatně. Když jsme v předchozích kapitolách psali libovolný programový kód, psali jsme jej právě do jednotky *.PAS. Zkompilované jednotky (soubory *.DCU) jsou již přímo spojeny s vlastním vytvářením aplikace (tzv. linkování).

Výhody jednotek jsou zřejmé. Jednotky umožňují (kromě jiného):

  • rozdělení většího programu do modulů, které tvoří logické celky a mohou být editovány samostatně,
  • vytváření knihoven, které můžeme používat více aplikacemi,
  • šíření knihoven mezi ostatní vývojáře, aniž bychom uvolnili zdrojový kód.
V tradičním Pascalském programu je veškerý zdrojový kód (včetně „hlavního“ programu) uložen v souboru *.PAS. Delphi využívá k uložení „hlavního“ programu souboru *.DPR, přičemž valná většina ostatního zdrojového textu se nachází v jednotce (jednotkách) - *.PAS. Kód, který je v souboru projektu <.DPR), je vytvářen automaticky a v drtivé většině případů jej sami nijak needitujeme. Tento soubor především organizuje soubory jednotek v aplikaci. Vše, co sami po aplikaci chceme, uvádíme zpravidla do jednotek <.PAS).

Poznámka pro pokročilé uživatele:

Soubor *.DPR z technického hlediska hlavně vytváří instance tříd formulářů (form) a po jejich úspěšném vytvoření spouští aplikaci.

Každá aplikace (nebo projekt) sestává z jednoho souboru projektu <.DPR) a jedné nebo více jednotek <.PAS). K vytvoření spustitelného programu potřebuje Delphi buď zdrojové kódy nebo již kompilované soubory všech jednotek <.PAS nebo *.DCU). Jak asi víte, seznam používaných jednotek se uvádí za klausuli Uses.Většinou jej ovšem Delphi spáchají automaticky.

Klausule uses poskytuje kompilátoru informaci o vztazích mezi moduly. Protože tato informace je uložena přímo v jednotkách (modulech) samotných, Object Pascal nevyžaduje makefiles, hlavičkové soubory (headers) nebo instrukci preprocesoru „include“, jak to známe třeba z jazyka C.

Poznámky pro pokročilé uživatele:

  • Project Manager v Delphi generuje makefile pokaždé, když je projekt načten v IDE, ale ukládá tyto soubory jen pro skupiny projektů (které obsahují více než jeden projekt).
  • Také „include“ soubory lze v Delphi použít. Používá se k tomu direktiva překladače {$I název_souboru}. Neuvedete-li příponu, předpokládá se *.PAS. Text z uvedeného souboru se přímo vloží před překladem.

Soubory projektu

Abych shrnul výklad z předchozí kapitoly, uvedu nyní seznam všech souborů používaných Delphi při překladu a vytváření projektu:

„Pascalské“ zdrojové soubory

Přípona Typ Obsah a význam
*.PAS soubory jednotek (units) většina zdrojového kódu aplikace. Jedna aplikace jich zpravidla obsahuje větší počet.
*.DPR soubor projektu aplikace obsahuje právě jeden. Organizuje soubory jednotek.
*.DPK soubory balíků (packages) tyto soubory jsou podobné souborům projektu, ale jsou používány ke konstrukci speciálních dynamicky linkovaných knihoven zvaných packages. V jednoduchých aplikacích zpravidla nejsou použity.

Automaticky generované, „nepascalské“ soubory

Přípona Typ Obsah a význam
*.DFM soubory formulářů zpravidla textové soubory obsahující informace o formulářích, vč. řetězců, bitmap, atd.
*.RES resource soubory standardní Windows-resource soubor, zde je kvůli ikoně aplikace
*.DOF soubor s volbami projektu obsahuje nastavení kompilátoru a linkeru, adresáře, informace o verzi apod.

Poznámka pro pokročilé uživatele:

Kromě těchto nejdůležitějších souborů se v adresáři s aplikací nalézá mnoho dalších souborů. Mnohé jsou sice vcelku důležité, ale málokdy je uživatel ručně edituje. Stručně si je vyjmenujme:

  • *.CFG – konfigurační soubor projektu,
  • *.DCI – změny Code Insight,
  • *.DCT – změny šablon komponent,
  • *.DMT – změny šablon menu,
  • *.DSK – desktop settings - obsahuje informace o uspořádání oken na obrazovce a o další konfiguraci,
  • *.RPS, *.DFN – údaje o lokalizaci zdrojů (generuje nástroj Integrated Translation Environment),
  • *.TDS – externí tabulka symbolů ladění (debug symbol table)
  • *.TODO – soubor to-do, obsahuje aktuální seznam operací, jež je třeba vykonat, vztažené k aktuálnímu projektu.

Správa většího projektu

V této kapitole si povíme několik základních zásad správy projektu a štábní kultury.

Pojmenování komponent

Není vhodné ponechávat komponentám jejich implicitní jména, která jim přiděluje Delphi. Pokud vytváříte jednoduchou aplikaci, jejímž jediným úkolem je spustit výpočet po stisku jediného přítomného tlačítka, je to celkem jedno; takové aplikace ovšem nejsou příliš časté. Mnohem lepší je přejmenovávat komponenty podle nějakého transparentního klíče, tedy např. tlačítka na btnXXX, kde XXX popisuje funkci tlačítka, např. btnKonec, btnVypocti, apod. Formuláře je dobré pojmenovávat frmXXX (a možná ještě lépe wndXXX), editační okna třeba edtXXX, apod. Jde o to, aby byl program čitelný s odstupem času pro vás samotné (a ještě spíše pro někoho, kdo bude číst „zdrojáky“ po vás).

Více formulářů

Není problémem mít v aplikaci více oken. Je to zcela běžné a většina aplikací disponuje více okny (formuláři). Chcete-li přidat do návrhu aplikace nový formulář, vyberte jednoduše z menu File – New Form. Delphi automaticky vytvoří nové soubory související s přidávaným formulářem. Pokud např. do jednoho modulu ze svého programu doplníte volání funkce (metody) z jiného vašeho modulu a zapomenete v sekci uses uvést jméno modulu, ve kterém se funkce (metoda) nachází, Delphi se zeptají (při kompilaci), zda to mají provést, a vám stačí jednou kladně odpovědět.

Poznámka pro pokročilé uživatele:

Přesněji řečeno, dojde k tomu, že Delphi přidají sekci uses do části implementation (nikoliv interface), a pokud tam již existuje, přidají do ní nový modul.

Není ovšem dobré zbytečně formuláři „hýřit“. Na spoustu věcí vůbec nemusíte navrhovat nová okna, stačí použít některý standardní dialog nebo box. Uživatelské prostředí vaší aplikace by mělo být vytvářeno v souladu s principem prvořadosti uživatele. To znamená, že primárním cílem vývojáře není vyřádit se při návrhu designu aplikace, ale využít uživatelových návyků při určitých činnostech a umožnit mu aplikovat je i v novém produktu. Pokud je uživatel zvyklý na standardní způsob, jakým mu pět aplikací něco sděluje, je nanejvýš vhodné v šesté aplikaci využít zcela stejného způsobu.

Štábní kultura kódu

O štábní kultuře by spíše měl pojednávat seriál vyučující Pascal, proto se omezím skutečně jen na naprosté minimum. Následující doporučení by vám měla umožnit psát čitelné a přehledné zdrojové texty. Jde skutečně pouze o doporučení a nemusíte je dodržovat.

  • Velká a malá písmena. Delphi (a vůbec Pascal) není case-sensitive jazyk. Přesto je dobré zavést si v malých a velkých písmenech pořádek a dodržovat určitou logiku (název „bntKonecClick“ vypadá rozhodně lépe než „btnkonecclick“ nebo (nedej Bože) dokonce „BTNKONECCLICK“. Totéž platí o názvech proměnných, konstant a objektů.
  • Komentáře. Hojným (avšak účelným) používáním komentářů si ušetříte mnoho problémů v budoucnu. Komentář vytvoříte buď uzavřením příslušného textu do {složených závorek} nebo napsáním dvou lomítek // na začátek řádky.
  • Odsazení, volné řádky, mezery. Nešetřete volnými řádkami a maximálně využívejte odsazení. Také mezery okolo operátoru udělají své!

Nyní se podívejte na příklad jedné metody. Nejprve bude napsána „ošklivě“:

procedure TForm3.FormActivate(Sender: TObject);
var i,x,y:integer;
begin
i:=-1;
repeat
inc(i);
until(Form2.ListBox1.Selected[i]);
Edit1.Text:=IntToStr(PreparujX(Form2.ListBox1.Items[i]));
Edit2.Text:=IntToStr(PreparujY(Form2.ListBox1.Items[i]));
end;

Nyní „hezká“ verze:


procedure TwndSeparuj.FormActivate(Sender: TObject);
var
  i : integer;

begin
  i := -1;

  repeat    {* najdeme vybranou položku *}
    inc (i);
  until (wndData.lstKomplex.Selected[i]);

  edtSourX.Text := IntToStr(PreparujX(wndData.lstKomplex.Items[i]));
  edtSourY.Text := IntToStr(PreparujY(wndData.lstKomplex.Items[i]));
end;

Úplně nejlepší je podívat se, jakým způsobem řeší „štábní kulturu“ samotné Delphi. Budete-li pokračovat v jeho stylu (pokud jde např. o velikost písmen), bude zdroják vypadat konzistentně a čitelně.

Hierarchie tříd

Následující text může začátečníkovi připadat poněkud složitý. Stane-li se vám při čtení něco podobného, neházejte flintu do žita. Hierarchie tříd není věc, kterou byste při programování jednoduchých aplikací museli nezbytně znát. Přesto jsem její popis zařadil, protože jednoho dne nastane okamžik (a ještě ke všemu u každého programátora jindy), kdy se může hodit, že o ní něco víte.

Systémová knihovna Delphi se nazývá Knihovna vizuálních komponent (Visual Component Library, VCL). Srdcem Delphi je hierarchie tříd. Každá třída v systému je potomkem datového typu TObject, takže celá hierarchie má jediný kořen. Tím je umožněno používat TObject jako náhradu za jakýkoliv datový typ v systému. K detailnímu vysvětlení tohoto jevu bychom museli zabrousit do hloubi objektově orientovaného přístupu, na což bohužel nemáme prostor. Stačí, když budete vědět, že při používání komponent vlastně vytváříme instance (tj. cosi jako konkrétní výskyty) koncových tříd hierarchie - listů ze stromu hierarchie. Dvojice Object Inspector a paleta komponent umožňuje umisťovat komponenty VCL na formulář, stejně jako měnit jejich vlastnosti, bez nutnosti psát kód.

Poznámka pro pokročilé uživatele:

Například metody reagující na událost obvykle obsahují parametr Sender typu TObject. Z výše uvedeného důvodu tedy můžeme jako odesílatele uvést prvek libovolné třídy VCL, protože je určitě odvozen od třídy TObject. TObject je abstraktní třídou, jejíž metody zapouzdřují základní chování, jako jsou např. vytvoření, zrušení a manipulace se zprávami.

Abyste nebyli z chaosu popsaného v předchozím odstavci úplně znechuceni, pokusím se jemně osvětlit rozdíl mezi instancí a třídou na krátkém příkladu:

var
  Neco : Tneco  // TNeco - třída

begin     Neco := TNeco.Create;  //  Neco - nově vytvořená instance
    ... práce s instancí ..// nyní můžu pracovat s Neco
    Neco.Free;    // zrušení instance end;

Tímto způsobem je také možné přidávat komponenty do programu za jeho běhu, tedy nikoliv ve fázi návrhu, ale run-time. V jakékoliv metodě formuláře můžeme například vytvořit nové tlačítko následujícím způsobem:

var
  btnNovy : TButton

begin
  btnNovy := TButton.Create(self);
  btnNovy.Parent := self;
  btnNovy.Left := 100;
  btnNovy.Top := 200;
  btnNovy.Visible := True;
end;

V okamžiku, kdy vytváříme novou komponentu, v zásadě knihovnu VCL rozšiřujeme, a proto používáme množství nástrojů Object Pascal, které uživatelé komponent zřídka potřebují. Problematice tvorby vlastních komponent bude věnována samostatná kapitola tohoto seriálu. Velkou výhodou knihovny VCL (a Delphi vůbec) je, že k používání komponent nepotřebujeme znát detailně strukturu hierarchie tříd. Potřebujeme pouze rozumět listům stromu, tj. komponentám.

Standardní boxy aneb „Ano, Ne, Storno“

V aplikacích velmi často potřebujeme získat od uživatele nějaký vstup. Komplexně se na zadávání informací a uživatelský vstup podíváme v příštím díle, ale už dnes začneme jednodušší variantou problému. Jedním z nejčastějších uživatelských vstupů je „parlamentní hlasovací“ odpověď - potvrzení, odmítnutí, nebo zrušení (zdržení se rozhodnutí).

Kromě toho (ne úplně zřídka) nastává situace, kdy je třeba uživatele informovat o aktuálním stavu výpočtu, upozornit jej na chybu nebo mu vynadat za špatný vstup. Pro oba zmíněné případy disponuje Delphi velmi elegantním řešením - standardními dialogovými boxy. Jak uvidíme za chvíli, použití těchto boxů je naprosto triviální, přesto poskytují širokou škálu voleb a možností zobrazení.

Nejjednodušší je ShowMessage

Při popisu boxů začneme od toho nejjednoduššího. Potřebujeme-li vypsat jednu krátkou informaci (zpravidla větu, alespoň se to tak doporučuje), nejsnáze použijeme proceduru ShowMessage. Člověk nemusí být zrovna expertem na angličtinu, aby si název funkce přeložil a aby jej pochopil. Syntaxe je následující:

procedure ShowMessage(const Msg: string);

Příklad:

ShowMessage(’Toto je krátká informace, milý uživateli.’);

Více umí MessageDlg

Vše, co umí ShowMessage, ale ještě mnohem víc, nabízí funkce MessageDlg. Umožňuje poměrně velkou variabilitu při návrhu vzhledu boxu. Syntaxe:

function MessageDlg(const Msg: string;
    DlgType: TMsgDlgType;
    Buttons: TMsgDlgButtons;
    HelpCtx: Longint): Word;

Popis parametrů:

  • Msg: řetězec, který chcete zobrazit
  • DlgType: indikuje účel dialogu. Možné hodnoty:
    • mtWarning – žlutočerná ikonka značící výstrahu,
    • mtError – červená „stopka“ značící chybu, všichni ji dobře známe :),
    • mtInformation – modré písmeno "i" značící informaci,
    • mtConfirmation – modrý otazník značící dotaz (žádost o potvrzení),
    • mtCustom – na boxu nebude standardně zhola nic.
  • Buttons: indikuje, která tlačítka se na dialogu objeví. Možné hodnoty:
    • mbYes, mbNo, mbOK, mbCancel, mbAbort, mbRetry, mbIgnore, mbAll, mnNoToAll, mbYesToAll, mbHelp
    • Pozor, jde o množinu, proto musíte zvolená tlačítka uvádět vždy v hranatých závorkách, např. [mbAbort, mbRetry, mbIgnore].
    • Jedinou výjimkou je použití některé z předdefinovaných konstant (např. mbOKCancel má stejný význam jako [mbOK, mbCancel]).
  • HelpCtx: „context ID“ tématu nápovědy, který se má objevit, stiskne-li uživatel F1 (nebo klikne na tlačítko Help) při otevřeném dialogu. Nechcete-li použít, nechte 0.

MessageDlg vrací hodnotu tlačítka, jehož stisknutím uživatel dialog opustil. Možné hodnoty: mrNone, mrAbort, mrYes, mrOk, mrRetry, mrNo, mrCancel, mrIgnore, mrAll.

Titulek okénka bude stejný jako název spustitelného souboru s aplikací (tedy název projektu v Delphi). To platí i o boxíku vytvořeném procedurou ShowMessage.

Příklad:

if MessageDlg(`Soubor byl změněn. Chcete jej uložit?`,
    mtConfirmation, [mbYes, mbNo, mbCancel], 0) = mrYes then
    uloz_soubor(aktualni);

Poslední vylepšení: MessageDlgPos

Jedinou větší nevýhodou funkce MessageDlg je, že okénko se otevře uprostřed obrazovky. To je někdy velmi nevhodné, naštěstí v Delphi existuje rozšířená funkce MessageDlgPos, která má úplně stejné parametry jako MessageDlg, ale navíc disponuje možností zadat souřadnice, na kterých se má boxík objevit:

function MessageDlgPos (const Msg: string;
    DlgType: TMsgDlgType;
    Buttons: TMsgDlgButtons;
    HelpCtx: Longint;
    X, Y: Integer): Word;

Funkcí je více

Funkcí pro výpis krátké informace a pro získání „jednobitové“ informace o dalším postupu je samozřejmě více. Pokud byste chtěli experimentovat s dalšími podobnými funkcemi, doporučuji především funkci MessageBox, což je zapouzdřená funkce Windows API MessageBox.

Příště budeme zadávat hodnoty

V následujícím díle se podíváme komplexně na možnosti uživatelského vstupu a na komponenty, které jsou pro to vhodné.
Diskuze (3) Další článek: Boj o první místo v on-line letenkách

Témata článku: , , , , , , , , , , , , , , , , , , , , , , , , ,