Třída, komponenta a formulář
Před začátkem bych rád upozornil, že pokud používáte KDE, tak kombinace CTRL+F9 místo kompilace přepne na plochu 9. Doporučuji změnit.
Třída
V jedné knížce o Delphi jsem četl, že když u Borlandů přemýšleli o první verzi Delphi, tak někdo při brainstormingu napsal na tabuli Button.Caption:=‚text‘ a tím byl základ Delphi položen.
Minule jsem se snažil vysvětlit, co je to property. Abych se přiznal, tak podle mne je to úžasný nápad, který dává programátorovi do ruky mocný nástroj. Mimochodem v C# jsou „properties“ také (a kromě toho i spousta dalších věcí z Object Pascalu). Je to také možná tím, že hlavním architektem C# (a spoluautorem .NET) je Anders Hejsberger. Pokud vám to jméno nic neříká, tak to byl hlavní softwarový architekt Borlandů (Turbo Pascal a autor Delphi, u MS např. Visual J++), kterého v roce 1996 přetáhnul Microsoft, aby vytvořil „Delphi for Java“ (podrobnosti jsou docela zajímavé, ale to sem už fakt nepatří).
Abych se vrátil k tomu příkladu. Když například napíšeme uvedený kód, tak to není jen prosté uložení řetězce, ale zároveň dojde k překreslení popisku tlačítka. Je to umožněno tím, že pro zápis je definována metoda, která patřičné chování implementuje.
Ale ke čtení už nic kreslit nemusíme, takže může probíhat přímo z paměti. Při čtení tedy není žádná časová ztráta.
Druhý důsledek tehdejšího brainstormingu: Jak jsem uvedl minule, tak objekt je vlastně pointer. Tedy by se logicky mělo psát:
Button^.Caption:='text'
Ale kompilátor při zjištění, že se odkazujeme na objekt, dereferenci odpustí (respektive nepovolí).
Základní třídou je TObject. Třída kromě jiného zavádí constructor Create a destructor Destroy a několik metod. Mezi ně patří i metoda Free, která interně volá destruktor a uvolňuje vytvořený objekt.
Pro nás to znamená jediné: Pokud vytváříme následníka TObject, je nutné (kromě speciálních případů) také implementovat vlastní destruktor. Kompilátor pak zajistí, že při volání Free se zavolá správný destruktor.
interface type TMyClass=Class (TObject) // pokud neuvedeme nic, tak to značí TObject constructor Create; // náš konstruktor destructor Destroy; override; // náš destruktor end; implementation constructor TMyClass.Create; begin // zde vytvoříme, vše co potřebujeme end; destructor TMyClass.Destroy; begin // provedeme potřebné akce (uvolnění zdrojů, které jsme vytvořili) inherited //zavoláme předchůdce (pokud je to třeba) end;
Komponenta
Komponenta je objekt s určitými vlastnostmi (následník TComponent – viz help). Pokud tedy vytvoříme následníka této třídy, tak se s ním bude dát manipulovat na formuláři a může být přidán do palety komponent. Dále může vlastnit jiné komponenty a v neposlední řadě bude umět zapsat a načíst svá data do a ze streamu.
Formulář
Poslední vlastnost je velmi důležitá, jelikož celý popis formuláře je složen právě z dat komponent. Pokud například pohneme komponentou doleva, tak se u dané komponenty změní vlastnost Left. Při ukládání komponenty (do souboru *.xfm) je hodnota Left zapsaná (pokud není aktuální hodnota hodnotou výchozí).
Tedy soubor *.xfm obsahuje textový popis komponent na formuláři. Je generovaný IDE.
Při kompilaci se textový popis převede na binární a přilinkuje se k aplikaci. V objektu formuláře jsou uvedeny objekty, které formulář obsahuje. Při jejich vytváření jsou ze spustitelného souboru nataženy uložené vlastnosti (tedy i to zmíněné Left). No a jelikož je Left property, dojde (skrze zápisovou metodu) k posunutí komponenty na správné místo určené při návrhu.
Ukážeme si popis formuláře s jednou komponentou TMemo. Formulář má jméno frmMain a komponenta Editor. Formulář je vlastníkem Editoru.
Popis formuláře v souboru fMain.xfm:
object frmMain: TfrmMain Left = 200 Top = 157 Width = 783 Height = 540 ActiveControl = Editor Caption = 'Hlavní okno' Color = clBackground PixelsPerInch = 75 TextHeight = 16 TextWidth = 7 object Editor: TMemo Left = 0 Top = 0 Width = 783 Height = 540 Align = alClient TabOrder = 0 end end
a třída formuláře v fMain.pas:
unit fMain; interface type TfrmMain = class(TForm) Editor: TMemo; end; var frmMain:TfrmMain; implementation {$R *.xfm} // přilinkujeme výše uvedený xfm soubor end.
Teď se určitě ptáte, kdo vytváří formuláře. Při pohledu do souboru dpr (menu Project/View Source) uvidíme následující:
program Test1; uses QForms, fMain in 'fMain.pas' {frmMain}; begin Application.Initialize; // inicializace objektu Application Application.CreateForm(TfrmMain, frmMain); // vytvoření formuláře Application.Run; // jdeme na to end.
V případě více formulářů se prostřední řádek opakuje (samozřejmě s jinými parametry).
Pokud nechceme při startu aplikace vytvářet všechny formuláře (a to určitě nechceme, protože to u složitějších formulářů chvilku trvá a zbytečně to zabírá paměť), můžeme tyto řádky smazat. Druhou možností je říct IDE, že si to nepřejeme (menu Project/Options/Forms). V tom případě musíme vytvářet formuláře sami v okamžiku potřeby.
První vytvořený formulář se stane hlavním formulářem aplikace. Pokud se tedy po startu zobrazuje jiné okno, než je vaše ctěná libost – zkontrolujte pořadí vytváření formulářů.
Visual Form Inheritance
Velmi vhodné je používat Visual Form Inheritance. Pod tímto tajuplným názvem se skrývá dědění formulářů.
Představme si, že máme aplikaci, která obsahuje deset oken lišících se pouze v detailech. Jestliže pro všechny okna nalezneme společné prvky (a metody), je výhodné použít dědění formulářů.
Společné prvky budou tvořit předchůdce. Těchto předchůdců může být i více a mohou být několikanásobně děděni.
Úplně cítím nechápavé pohledy, a proto uvedu příklad: mějme formulář (nazvěme ho třeba TIniForm) jehož jediným úkolem bude při svém vytvoření načíst informace o své pozici a velikosti z ini souboru a při rušení je tam zase uložit. Tento formulář je následníkem standardního TForm.
Nyní mějme další formulář, který nazveme třeba TBasicForm. Tento formulář bude obsahovat třeba nějaká tlačítka (OK, Cancel …) a jejich obsluhu, ale jelikož je následníkem TIniForm, tak bude umět číst a zapisovat svoji konfiguraci do ini.
No a konečně mějme formulář TMainForm, který bude následníkem TBasicForm. Bude umět vše co předchůdce a navíc tam přidáme požadované prvky.
Výhod je při dobrém návrhu několik:
- Všechny změny v předchůdcích se automaticky promítají do následníků (zákazník chce konfiguraci oken v XML? No problem, prostě změníme dvě metody v TIniForm).
- Neduplikují se nám zdroje (bitmapy, komponenty …), tudíž je aplikace menší.
- Lehčeji udržovatelný kód (změnil jsem to ve všech oknech, nebo nezměnil??).
- Atd.
Spuštění aplikace bez Kylixe
Pokud chceme spustit program mimo Kylix, tak se nám to asi nepovede. Je to způsobeno tím, že aplikace vytvořená v Kylixu potřebuje ke své činnosti dvě knihovny (resp. toto platí pokud nepoužíváme balíčky – viz někdy příště).
Tyto knihovny jsou (pro Kylix 2) libqt.so.2 a libqtintf-6.5-qt2.3.so a od nás se očekává, že k nim laskavě uvedeme cestu.
Standardně jsou tyto knihovny uloženy v adresáři bin ve stromě Kylixu.
V případě, že svoji v potu vytvořenou aplikaci budeme někam přenášet (nedej bože prodávat), tak není vhodné knihovny kopírovat někam, kde k nim mají přístup všichni. Hezky ji uložte tam, kde k ní budou mít přístup všechny vámi vytvořené aplikace, a pouze k nim exportujte cestu.
Pro spuštění použijeme následující skript (vytvořený program se jmenuje Test1):
#!/bin/bash LD_LIBRARY_PATH="./:$LD_LIBRARY_PATH" #LD_LIBRARY_PATH="/usr/kylix2/bin/:$LD_LIBRARY_PATH" export LD_LIBRARY_PATH ./Test1 -ns
Parametr -ns jsem zjistil ve zdrojácích (QOpenBanner.pas) a znamená, že nechci zobrazit splash screen. Může být uveden jako kterýkoliv parametr (splash screen se zobrazuje pouze u Open Edition).
Ve skriptu je vidět, že exportuji cesty k aktuálnímu adresáři, kde mám symlinky do /usr/kylix/bin. V druhém případě exportuji cestu přímo do stromu Kylixu. Vy samozřejmě exportujte cesty k místu uložení patřičných knihoven.
Zajímavé komponenty
Jelikož je programování v Kylixu někdy hodně o vybrání správných komponent, tak budu uvádět odkazy na ověřené nebo alespoň dostatečně známé komponenty pro Kylix.
Pokud tedy znáte nějaké dobré komponenty pro Kylix, naváhejte a napište mi. A začneme hned teď, a to hnedle výjimkou. První záznam nebude balík komponent, ale tříd. Jelikož to nejsou komponenty, tak se nemusí instalovat.
Synapse a Synaser
Synapse je volně šiřitelná knihovna tříd (nikoliv komponent) zapouzdřující TCP/IP komunikaci.
Jejich použití se neomezuje jen na GUI aplikace, dobře se s nimi pracuje i v konzolových aplikacích.
Synapse implementuje několik různých protokolů. Balík dále obsahuje funkce např. pro zaslání mailu, stažení a poslání souboru přes různé protokoly, konverzi diakritiky a mnoho dalšího. Doporučuji se podívat na vlastnosti http://www.ararat.cz/synapse/features.htm.
Napsal ji Lukáš Gebauer a o jejích kvalitách svědčí například i to, že ji použil autor svého času populárního viru pro Windows (z čehož měl autor SYNAPSE opravdu velkou radost).
Na stejné stránce lze stáhnout také SYNASER, což není odpověď na neakceptovatelnou žádost, ale pěkná skupina tříd, které zapouzdřují sériovou komunikaci jak ve Windows, tak v Linuxu. Vím, že pro guru, jako jste vy, to není moc složité, ale proč nepoužít něco, co je už hotové a odladěné. Výhody si uvědomíte zvláště v případě vývoje pro obě platformy.
Podporovány jsou Delphi 2–6, C++Builder a Kylix 1–2.
Licence: Mozilla Public Licence 1.1
Zdrojové kódy: ANO
Homepage: http://www.ararat.cz/synapse/
Zajímavá aplikace
Při procházení internetu jsem narazil na window manager napsaný v Kylixu, který emuluje vzhled Windows 2000. Zdrojové kódy a binárky jsou k dispozici na http://xpwm.qadram.com/.
(autor je zvědav, jak dlouho mu vydrží nadšení)