Program s výběrem z funkcí

Minule jsme si napsali program, který umožňoval kreslení po vymezené ploše, změnu barvy a tlouštky pera, a také načtení a uložení obrázku. Pokud má jeden program poskytovat více funkcí (a nemá mít předem pevně dáno, v jakém pořadí se budou používat), používáme ve Windows zpravidla tzv. hlavní menu (main menu). Do formuláře v Delphi si jej můžeme přidat tím, že vybereme hned první komponentu z lišty Standard (), a umístíme ji kamkoli na plochu. Menu se samozřejmě nezobrazí v tomto místě, ale jako pruh pod titulkem formuláře. Pro jeden formulář používáme nejvýše jedno hlavní menu (pokud si je nechceme za chodu vyměňovat).

Menu vyplňujeme tak, že dvojklikem na ikonku , zobrazující menu na ploše, otevřeme další okno. Doplňováním hodnoty Caption (obsah jednotlivých položek) v Object Inspectoru přidáváme další a další položky. Po editovaném menu se přitom můžeme pohybovat klávesami pro pohyb kursoru - vodorovně vybíráme sloupec, svisle řáádek. Poslední volná položka se při běhu programu nezobrazí. Oddělovací čáru v roletové nabídce vložíme tak, že jako Caption napíšeme samotnou pomlčku.

Pokud potřebujeme, aby některá z položek měla podvolby (byla sama rozbalovací), použijeme kombinaci kláves Ctrl a šipka doprava. Tato položka jde ale myší stejně vybrat, tj. lze jí přiřadit obsluhu.

Někdy se za běhu programu používají klávesové zkratky a pro výběr v menu některé z písmen v dané položce (je zpravidla podtrženo). Tyto dvě věci spolu nesouvisí. Podtržené písmeno pro snazší volbu zadáme tak, že do property Caption před zvolené písmeno přidáme znak & (ampersant). Rychlou volbu kombinací kláves odkudkoli z programu přidáme tak, že v Object Inspectoru u příslušné položky zadáme hodnotu property ShortCut (doporučuji vybrat z nabídky).

Pokud má v řádce menu před některou položkou být vložen obrázek (malý, v řádce nalevo), pak jeho jméno zadáme do property Bitmap. Zobrazí se při běhu programu.

Lokální menu

Hned sousední komponenta je lokální menu (PopupMenu). Je určeno k tomu, aby se vyvolávalo pravým tlačítkem myši a má souviset s objektem, na který bylo toto pravé tlačítko použito. Je tedy rozumné mít více komponent PopupMenu. K jednotlivým objektům (tlačítko, obrázek, memo, celý formulář) se příslušné menu přiřadí tak, že u příslušné komponenty vybereme property PopupMenu a zvolíme jedno z možných (musí tedy být nadefinovány předem).

Zadání: Zkuste si do kreslícího programu z minulé hodiny přidat hlavní menu, které ve sloupci Soubor bude mít položky Uložit a Načíst a ve sloupci Pero položku Barva (vyvolá ColorDialog) a několik různých tlouštek pera, např. 1, 2, 3, 5, 7, 10 bodů, každou v jedné řádce.

********...

Case

Jiný případ nastane, když se podle dané volby program má větvit v úplně jiném místě. Tím se dostáváme do stejné situace, jako byli programátoři před rozšířením Windows a ovládání pomocí myši. V historických dobách se počítače ptaly způsobem "zadejte S pro uložení, N pro nový, nebo X pro návrat, a stiskněte Enter", nebo dokonce chtěly číslo volby. Rozhodnutí, která obsluha se provede, se pak neprovádělo řetězcem podmíněných příkazů (nepřehledné), ale funkcí case.

Tvar funkce case v Pascalu je následující:

case <ordinální výraz> of
  <hodnota> : <příkaz>;
  <hodnota> : <příkaz>;
...
  else <příkaz>;
  end;

<ordinální výraz> je cokoli, co nabývá konečného počtu hodnot, zpravidla celočíselná, nebo znaková proměnná, ale bývá to i výraz s takovým výsledkem. Pokud tento výraz nabyde <hodnoty>, uvedené v některém z řádků, provede se <příkaz>, a pokračuje se za příkazem end; (dále se netestuje). Pokud žádný z řádků nevyhovuje, provede se <příkaz>, uvedený za příkazem else (tato část může být - a zpravidla bývá - vynechána).

Je-li třeba provést více příkazů pro daný případ, použijí se programové závorky begin - end; , a příkazy se uvedou mezi ně.

 

****************** ukázky: 1,2,3 : ****; 4..9 : ****; atd.

....

....

Příklad: Program má umožňovat malovat nejen volnou čáru ("od ruky"), ale i polygony (každé kliknutí jeden bod). Za tím účelem si nadeklarujeme globální proměnnou mode, která bude měnit hodnotu podle toho, co se právě provádí.

Vlastní činnost budou provádět procedury, vyvolané událostmi "někdo pohnul s myší" (OnMouseMove) a "tlačítko myši bylo právě stisknuto" (=je stále stisknuto, OnMouseDown, doslova "pohybuje se dolů"). Vlastní obslužný program si můžete okopírovat z následujícího textu, ale samotné procedury musíte vygenerovat dvojklikem do příslušného políčka v Object Inspectoru na kartě (se záložkou-) Events.

Kreslení polygonu (řady navazujících úseček) může pracovat například následujícím způsobem. V menu zvolíme kreslení polygonu. Pak najedeme myší na začátek první čáry. Jednou klikneme, tím se nám označí výchozí bod. Nyní přesuneme myš na další bod. Pro usnadnění je dobré, když se nám bude průběžně zobrazovat, jak bude výsledná čára asi vypadat.

Abychom mohli vzhled namalované čáry naznačit, a pak jej mohli bez problémů vzít zpět, musíme kreslit módem pmXor. Funkce Xor pro jednotlivý obrazový bod pracuje následovně: každý bit barvy pera se xoruje s bitem pozadí. V prvním řádku je zamýšlené pozadí, v druhém barva pera:

původní 0 1 1 0 1 0 1 0 0 1
pero 0 0 0 0 1 1 1 1 1 1
výsledek 0 1 1 0 0 1 0 1 1 0

Při smazání prostě postup zopakujeme. Vychází se z toho, že pro sčítání platí asociativní zákon, a funkce xor pro dvě stejná čísla dává nulu:

po první úpravě 0 1 1 0 0 1 0 1 1 0
pero 0 0 0 0 1 1 1 1 1 1
nový výsledek 0 1 1 0 1 0 1 0 0 1

Opakovaným voláním funkce tedy grafický objekt opět smažeme. V praxi se ovšem snažíme zvolit takovou barvu, aby byla co nejlépe čitelná, v našem případě představovala co nejvíce změn. Binárně samé jedničky přitom representují bílou barvu; té je nejblíže předvolená barva clWhite. Původní nastavenou barvu je potřeba na chvilku někam schovat; uděláme si na ní proměnnou color. Barva má rozsah 24 bitů, v Delphi od verse 3 se tedy vejde i do proměnné typu integer (dříve se používal longint). Proměnná musí zachovávat hodnotu, i když procedury opouštíme a vracíme se do nich, takže musí být vytvořena jako globální (například ji můžeme připsat v místě deklarace "Form1:TForm1").

Aby bylo možné začít malovat čáru od jiného bodu, než z okraje, je třeba odlišit výběr počátečního bodu od označování dalších (v jejich průběhu se již "táhnou" čáry). Musíme tedy zvolit jiný mode pro tuto situaci a pro průběh. V následující proceduře je pro kreslení "od ruky" zvolen mode 1, pro samotný průběh polygonu 2, pro jeho začátek 3 (obslouženo jen pro událost OnMouseDown, v události OnMouseMove se zatím nedělá nic).

Dalším závažným problémem je ukončení čáry. Aby nemusela každá jiná funkce obsahovat "odznačení" poslední čáry, malované funkcí xor, je třeba čáru nějak ukončit v rámci mode 2. Jednou z možností je polygon ukončit, pokud je zadán dvakrát bezprostředně za sebou stejný bod (čára nulové délky). Toto ošetření je nutno doplnit k obsluze události OnMouseDown.

Začněme deklarací proměnných. Přidáme například následující řádky do kapitoly var:

var
  Form1: TForm1;
  mode, x0,y0, x1,y1 : integer;
  color : longint;

implementation

Dvojklikem na příslušné políčko v Object Inspectoru (komponenta Image1, karta Events) vygenerujeme příslušnou proceduru, a doplníme černě vepsané řádky:

procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
  case mode of
    0 : {nic - nečinný program};
    1 : if ssLeft in Shift then Image1.Canvas.LineTo(X,Y)
                           else Image1.Canvas.MoveTo(X,Y);
        {1 = malování souvislé čáry "od ruky"}
    2 : begin  {průběh zadávání polygonu; nejprve se nastaví barva a pero pro xor}
          Image1.Canvas.Pen.Mode := pmXor;
          color := Image1.Canvas.Pen.Color; {stará hodnota barvy se zapamatuje}
          Image1.Canvas.Pen.Color := clWhite; {tahle bude nejvíc vidět}
          Image1.Canvas.MoveTo(x0,y0);  {znovu se vykreslí minulá čára = smazání}
          Image1.Canvas.Lineto(x1,y1);
          x1:=x;                          {zadání nové polohy}
          y1:=y;
          Image1.Canvas.MoveTo(x0,y0);  {nakreslení v nové poloze}
          Image1.Canvas.Lineto(x1,y1);
          Image1.Canvas.Pen.Mode := pmCopy;  {vrácení nastavení hodnot pera}
          Image1.Canvas.Pen.Color := color;
        end;
  end {of case};
end;

Druhá procedura se uplatní pro zadávání lomené čáry (úseček, polygonu), ale musí umět i první a poslední bod:

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  case mode of
    2 : begin
          Image1.Canvas.Pen.Mode := pmXor;    {smazat provizorní čáru - viz výše}
          color := Image1.Canvas.Pen.Color;
          Image1.Canvas.Pen.Color := clWhite;
          Image1.Canvas.MoveTo(x0,y0);
          Image1.Canvas.Lineto(x1,y1);
          Image1.Canvas.Pen.Mode := pmCopy;    {nastavení definitivní barvy}
          Image1.Canvas.Pen.Color := color;
          Image1.Canvas.MoveTo(x0,y0);         {namalování definitivní čáry}
          Image1.Canvas.Lineto(X,Y);
          {test, zda není zadán dvakrát stejný bod - pokud ano, konec => mode:=0}
          {Poznámka. Právě proběhlé vykreslení čáry nulové délky není na závadu.}
          if (X=x0) and (Y=y0) then mode := 0;
          x0:=X;                               {zadání nového výchozího bodu}
          y0:=Y;
          x1:=X;
          y1:=Y;
        end;
    3 : begin  {začátek lomené čáry}
          x0:=X;     {zadání prvního výchozího bodu}
          y0:=Y;
          x1:=X;
          y1:=Y;
          mode := 2; {změna funkce na průběh lomené čáry}
        end;   {poznámka - první čára nulové délky se nemusí kreslit}
  end {of case};
end;

Zbývá jen doplnit vhodné hlavní menu, umožňující zadat hodnotu mode jako 1 pro volnou kresbu a 3 pro malování lomené čáry. Budete-li mít čas, zkuste si doplnit například kreslení kružnice pomocí procedury Ellipse.

--- doplnit FontDialog a TextOut(X,Y,form2.edit1.text);