Umíme to s Delphi, 7. díl – zadávání informací, uživatelský vstup

Uživatel může s aplikacemi komunikovat mnoha způsoby. Komunikace může mít dva směry – od uživatele k aplikaci a naopak. Dnes se podíváme na uživatelský vstup a na to, jak zajistit, aby byl komfortní pro uživatele a efektivní pro aplikaci.

Zadávání logických hodnot
Začneme od toho nejjednoduššího. Potřebujeme-li získat od uživatele pouze logickou hodnotu (tj, „ano“ nebo „ne“, příp. „1“ nebo „0“, apod.), je situace poměrně jednoduchá. Často stačí třeba použít obyčejný „MessageBox“ (nebo jiný standardní nástroj popsaný v 6. kapitole našeho seriálu).

Někdy to ovšem není příliš elegantní řešení. Naproti tomu elegancí oplývá typický představitel logické hodnoty – zatrhávací pole (CheckBox). Jeho výhody jsou nesporné: je-li zobrazen stále na formuláři nebo v menu, může uživatel kdykoliv změnit hodnotu jediným kliknutím. Je přehledný, na uživatele stále nevyskakují další a další okénka (jako by tomu bylo u soustavného používání MessageBoxu, apod.).

Jak CheckBox na formulář dostat? Je to snadné:

  • na formulář umístěte příslušnou komponentu (tedy CheckBox, paleta Standard),
  • nastavte důležité vlastnosti:
    • AllowGrayed – je-li povoleno (True), může mít CheckBox tři hodnoty: Checked, Unchecked, Grayed. Jinak má standardní dvě hodnoty.
    • Caption – titulek, tedy to, co bude u CheckBoxu napsáno,
    • Checked – jaký bude stav CheckBoxu při spuštění programu
    • State – podobné předchozí vlastnosti, navíc je možno nastavit hodnotu Grayed.

Velmi jednoduché je také čtení hodnot ze zatrhávacích boxů. Chcete-li např. zjistit, zda si uživatel přeje automaticky ukládat soubor, provedete to takto:

if chbAutoSave.Checked = True then …

Poznámka: podmínku lze použít z také „zkráceně“:

if chbAutoSave.Checked then …

Tento tvar už sám o sobě dostatečně vyjadřuje, oč v podmínce jde, a není nijak matoucí. Velmi často se proto používá.

Kromě vstupu lze samozřejmě s výhodou použít komponentu CheckBox také k zobrazení „jednobitové“ informace (uvědomuji si, že pojem „jednobitové“ není přesný, zvláště povolíte-li třetí stav (Grayed)).

Přepínací boxy
Komponenta RadioButton pro nás také není žádnou novinkou. Tato přepínací tlačítka se ale zpravidla vyskytují ve skupinách, protože lze zároveň vybrat jen jednu možnost. Je možné, aby nejprve (na startu aplikace) nebylo vybráno žádné tlačítko, nicméně tato možnost nebývá příliš obvyklá.

Jedinou důležitou vlastností je Checked, která signalizuje, že tlačítko bylo vybráno.

Komponenty RadioButton se (skoro) vždy sdružují na některou kontejnerovou komponentu, nejčastěji na GroupBox, Panel, příp. samotný formulář. Místo použití několika RadioButtonů ovšem raději využijte jednu komponentu RadioGroup.

Skupina přepínacích tlačítek:

Vložíte-li na formulář komponentu RadioGroup, nastavte vlastnost Items. Ta je stejného typu (TStrings) jako vlastnost Lines komponenty Memo. Podrobný popis naleznete níže v této kapitole. Do editoru napište pouze popisky jednotlivých RadioButtonů, kolečka budou doplněna automaticky. Editací vlastnosti Columns volíte počet sloupců, ve kterých budou tlačítka zobrazena.

Zjišťování, které tlačítko je vybráno, se provádí testováním hodnoty vlastnosti ItemIndex. Mnohým programátorům ale tento způsob nevyhovuje, protože si musí pamatovat, jakou číselnou hodnotu má které „přepínátko“, navíc tato hodnota se může (teoreticky) za běhu programu měnit, budete-li seznam editovat (což není, pravda, příliš šikovné).

Dávají proto přednost testování textu (popisku) tlačítka na základě tohoto indexu (rdgpPozdrav je komponenta RadioGroup):

if rdgpPozdrav.Items.Strings[rdgpPozdrav.ItemIndex] = `Nazdar` then …

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

Je třeba podotknout, že tento způsob je poněkud „drsný“. Řetězce u jednotlivych tlačítek jsou zpravidla delší než krátké pozdravy v příkladu. Proto lze tuto metodu „vylepšit“, a to například nadefinováním konstant místo čísel ItemIndex (NAZDAR, AHOJ, ...) a následné testování podmínek typu

if rdgpPozdrav.ItemIndex = NAZDAR then ...

Jednořádkový vstup
Ne vždy si ale vystačíme se zatrhávacími boxy, představte si je třeba v situaci, kdy uživatel má zadat číslo (byť celé) z intervalu <0, 1000>. Nestačí ani přepínací tlačítka.

Nejjednodušší metodou je použití dalšího typu „MessageBoxu“ (podrobný popis podstatných boxů viz 6. díl seriálu). Konkrétně jde o funkci InputBox. Používá se velmi snadno:

function InputBox(const ACaption, APrompt, ADefault: string): string;

Význam jednotlivých parametrů:

  • ACaption – titulek dialogového boxu,
  • APrompt – text, který se objeví nad editačním okénkem,
  • ADefault - text, který se objeví v editačním okénku (jakýsi přednastavený text).
Mocnějším nástrojem pro vložení jednoho řádku textu je ovšem komponenta Edit. Má mnoho specifických vlastností, jimiž můžeme tento jednořádkový vstup omezit či formátovat (např. lze místo zadávaných znaků vypisovat hvězdičky; použití si jistě dovedete živě představit).

Vizte tabulku s nejdůležitějšími vlastnostmi komponenty Edit.

Vlastnost Význam
Text Nejdůležitější vlastnost; v návrhové fázi je výhodné vložit do ní počáteční text.
MaxLength Maximální počet znaků, které lze do Editu zadat. Doporučuji vyhradit na formuláři dostatek místa, aby Edit nemusel scrollovat (uživatele mate, když mu část textu kamsi ujíždí).
Modified Slouží ke zjištění, zda došlo ke změně.
AutoSelect Podle ní je/není při vstupu do pole stávající text automaticky vybrán. Nastavte podle toho, předpokládáte-li spíše zadání zcela nové hodnoty nebo editaci stávající.
ReadOnly Určuje, může-li uživatel hodnotu v Editu měnit.
PasswordChar Hodnotou této vlastnosti je znak, který se bude vypisovat do Editu místo skutečně zadávaných znaků.
Komponenta Edit oplývá také několika skvělými metodami. Jen pro ukázku – jedna z metod se jmenuje CopyToClipboard. Hádejte, co dělá!

Potřebujete-li získat řetězec (tedy vstup typu String), je situace nejjednodušší. Na formulář umístěte komponentu Edit (paleta Standard). Doporučuji k ní rovnou umístit štítek (komponenta Label), do něhož napíšete, co vlastně má uživatel do Editu zadat. Toť vše, pak už jen v kterékoliv metodě (např. v metodě OnClick tlačítka na tomtéž formuláři) vstupní řetězec zkontrolujete (je-li to nutné) a použijete (kamsi přiřadíte apod.).

Situace je zajímavější v případě, že potřebujete od uživatele získat číselný vstup. Možností je několik, například:

použít komponentu Edit, standardně získat řetězec a ten následně konvertovat na číslo dostupnými funkcemi. Tento postup je možný, ale má několik nevýhod. Nedisciplinovaný (nebo prostě hloupý) uživatel může zadat (takže stoprocentně zadá) místo čísel písmena. V takovém případě samozřejmě nastává chyba, kterou je nutno testovat a ošetřovat (pozn.: použití funkce val je tak trochu přežitek ze starého dobrého Pascalu. V Delphi existuje efektivnější a rychlejší funkce StrToInt. Jejím problémem ovšem je, že k ošetření chybového stavu musíme pracovat s výjimkami. Pokud na ně nemáte náladu, použijte klidně Val, která vrací výsledek konverze v posledním parametru. Popis funkce StrToInt, včetně reakce na chybu, naleznete v závěru tohoto dílu).

Příklad:

val (edtPlat.Text, plat, vysledek);

if vysledek <> 0 then
begin
  edtPlat.SetFocus;
  MessageDlg(`Výše platu musí být číselný údaj!`, mtError, [mbOk], 0);
end;

použít komponentu Edit, ale ze vstupního řetězce zrušit všechny nečíselné hodnoty. Toto „rušení“ však musíme provést velmi důsledně, jinak uživatel - hnidopich může dostat text do pole přes schránku. Navíc celková koncepce tohoto řešení je taková poněkud ... no, nepříliš „hezká“. Nicméně obecně funguje dobře a snadno se implementuje.

Příklad:

if not (key in [`0`..`9`, #8]) then
begin
  key := #0;
  messagebeep($FFFFFFFF);
end;

použít komponentu MaskEdit. Ta oproti komponentě Edit oplývá vlastností EditMask, která představuje omezení aplikovaná na vstupní data. Řada formátů je již předdefinována v Delphi a je samozřejmě možné vytvořit libovolný jiný formát. V Delphi k tomu existuje nástroj (Input Mask Editor), který spustíte kliknutím na tlačítko se třemi malými tečkami v Object Inspectoru při kliknutí na vlastnost EditMask.

Podrobný popis tvorby masek naleznete k nápovědě.

Vlastní text je uložen ve vlastnosti Text (jako u komponenty Edit). EditText obsahuje text formátovaný aplikací masky (tedy tak, jak jej vidí uživatel). Vlastnost IsMasked umožňuje testovat, zda byla maska definována.

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

Funkce MessageBeep zasluhuje krátké vysvětlení. Jde totiž o funkci Windows API, a proto ji v nápovědě Delphi příliš popsanou nenaleznete (záleží ovšem na konkrétní instalaci, někdy bývá v menu Help k nalezení položka Windows SDK). Tato funkce se používá pro přehrání zvuku definovaného ve Windows pro určitou událost. Možné hodnoty jsou 0xFFFFFFFF, MB_ICONASTERISK, MB_ICONEXCLAMATION, MB_ICONHAND, MB_ICONQUESTION, MB_OK.

Víceřádkový vstup
Komponenta Edit (včetně její rozšířené odrůdy – MaskEdit) může obsloužit omezené množství textu, a to pouze na jednom řádku. Potřebujeme-li něco podobného jako Edit (tedy ne moc lepšího), použijeme komponentu Memo. Ta se liší především možností obsáhnout několik řádků (a v důsledku toho např. přítomností posunovacích lišt pro pohyb v textu atd.).

Přehled nejdůležitějších vlastností opět shrneme do tabulky:

Vlastnost Význam
Alignment Komponentu Memo můžeme výhodně využít i pro zadávání jednořádkového vstupu, pokud tento řádek chceme zarovnávat doprava. K tomu právě slouží Alignment.
ScrollBars Umožňuje nastavit nezávisle na sobě oba posuvníky (tedy vodorovné i svislé).
WordWrap Kladná hodnota této vlastnosti (True) znamená, že dojde k zalamování řádků. Pak byste neměli „zapnout“ vodorovné posuvnítko; už z logiky věci vyplývá, že se tyto dvě vlastnosti vzájemně vylučují.
WantTabs, WantReturns Klávesy Tab a Enter mají u většiny komponent jinou funkci. Stisk tabulátoru zpravidla způsobí přechod na další komponentu (její „aktivaci“). Chceme-li ale uživateli umožnit, aby do komponenty Memo mohl zadávat tabelátory a Entery, umožníme mu to těmito vlastnostmi. Podotýkám, že v každém případě může uživatel zmíněné znaky do textu dostat současným stiskem Ctrl (Ctrl+Tab, Ctrl+Enter).
Text Podobná vlastnost jako u Editu; zde znamená práci s celým textem.
Lines Zcela stěžejní vlastnost! Podrobnější popis následuje pod tabulkou.
Nejpodstatnější vlastností komponenty Memo je Lines. Ta umožňuje pracovat s jednotlivými řádky textu. Lines je typu TStrings a jako taková má mnoho užitečných vlastností a metod. Ukážeme si dvě z nejpůvabnějších – LoadFromFile, resp. SaveToFile (tedy načtení textového souboru z disku, resp. uložení textového souboru na disk). Jejich velmi elegantní použití ilustruje následující příklad:

procedure wndHlavni.btnNactiClick(Sender: TObject);
begin
  memoHlavni.Lines.LoadFromFile(`C:\AUTOEXEC.BAT`);
  ShowMessage(`6. řádka souboru AUTOEXEC.BAT je: ` + memoHlavni.Lines[5]);
end;

Číselný vstup – posuvné lišty
Dalším často řešeným problémem je zadávání číselných hodnot z daného intervalu. Lze k tomu použít komponentu Memo, ale velmi pěkným řešením je užití komponent ScrollBar (posuvných lišt).

Nejdůležitější vlastnosti komponenty ScrollBar:

Vlastnost Popis
Kind Určuje orientaci (vodorovná (sbHorizontal) nebo svislá (sbVertical)). Nově umístěná komponenta je na formuláři vždy vodorovná.
Min, Max Určují mezní hodnoty.
Position Stanovuje aktuální pozici jezdce.
SmallChange Jemnost posuvu, tato vlastnost platí pro posuv pomocí koncových šipek ScrollBaru nebo kurzorových kláves.
LargeChange Jemnost posuvu, vlastnost platí pro posuv pomocí kliknutí kamkoliv na pruh ScrollBaru nebo pomocí kláves PageUp, PageDn.

Chcete-li přečíst hodnotu, kterou uživatel nastavil na ScrollBaru, provedete to nejlépe ošetřením události OnChange.

Pokud chcete mít posuvné lišty skutečně elegantní, je vhodné doplnit je popiskou (štítkem, komponenta Label), která bude vždy popisovat aktuální pozici jezdce (aktuální hodnotu).

Poznámka: v tomto příkladu bylo použito volání Windows API funkce RGB, jejíž syntaxe je velmi jednoduchá:

RGB(red, green, blue);

kde red, green a blue jsou barevné složky klasického RGB modelu v rozsahu 0 – 255 (tedy hodnota typu byte). Tyto hodnoty tedy (0 a 255) byly nastaveny jako minimální a maximální ve vlastnostech Min a Max všech posuvných lišt. Události OnChange všech tří lišt proto mají zhruba tento tvar:

procedure TwndBarvy.scrCervenaChange(Sender: TObject);
begin
  memoUkazka.Color := RGB(scrCervena.Position,
                          scrZelena.Position,
                          scrModra.Position);

  lblCervenaHodnota.Caption := IntToStr(scrCervena.Position);
end;

Poznámka:

V předchozím textu byly uvedeny dvě funkce, o nichž možná zatím nic nevíme: StrToInt a IntToStr. Jde o konverzní funkce; první z nich převádí řetězec na číslo a druhá naopak (samozřejmě lze na číslo převést pouze řetězec obsahující číslo, řetězec „Nazdar“ bude převeden jen těžko). Syntaxe je následující:

function IntToStr(Value: Integer): string;
function StrToInt(const S: string): Integer;

Pokud řetězec S neobsahuje platný číselný údaj, vyvolá StrToInt výjimku EconvertError. O výjimkách budeme (psát) mluvit později, proto napíšu jen způsob použití:

try
  Hodnota := StrToInt(Retezec);
except
  on EConvertError do ....
end;

Číselné vstupy – kombinace editačního pole a myši
Na závěr si ukážeme komponentu, která se také používá k zadávání číselných údajů. Jde o komponentu SpinEdit z palety Samples.

Tato komponenta v sobě částečně integruje vlastnosti normálního editačního pole a posuvných lišt. Nebudu v tabulce popisovat všechny její důležité vlastnosti, omezím se na konstatování, že místo vlastnosti Text (kterou má Edit) disponuje vlastností Value a místo vlastností Min, Max, SmallChange (které má ScrollBar) má vlastnosti MinValue, MaxValue a Increment.

Do SpinEditu je možné zadávat údaje jak pomocí klávesnice (jako do Editu), tak klikáním na šipky vpravo.

Zadávat lze i seznamem
Vstup údajů je možný také pomocí seznamů (ListBox, ComboBox, atd.). Protože se ale tyto seznamy často používají také k výpisům, necháme si jejich popis na příští díl seriálu. Kromě nich se podíváme i na další komponenty umožňující výstup a výpis informací, tedy druhý směr komunikace uživatel « aplikace.

    Určitě si přečtěte

    Články odjinud