12. Myš a časovač


Posledná zmena: 29.10.2002

Banner Text 29.10.2002

    čo sme sa doteraz naučili

    • do grafickej plochy zatiaľ vieme kresliť pomocou algoritmov

    čo sa budeme dnes učiť

    • ako môžeme používateľovi umožniť kresliť do grafickej plochy pomocou myši

Myš

  • pri práci s Pixels v grafickej ploche sme trochu experimentovali aj s myšou: pri pohyboch myšou nad grafickou plochou Image2 sme zobrazovali príslušné bodky obrázka - využili sme udalosť onMouseMove, ktorá do obslužnej procedúry Image2MouseMove posiela aj súradnice myši v parametroch X a Y
  • naučíme sa, ako sa pracuje s myšou - grafická plocha má 3 rôzne udalosti:
    • onMouseDown - zatlačili sme nad plochou niektoré tlačidlo myši
    • onMouseMove - myš sa hýbe nad plochou
    • onMouseUp - pustili sme tlačidlo myši
  • začneme najjednoduchším prípadom:  vo formulári máme grafickú plochu Image1 a priradíme jej udalosť onMouseMove (dvojklikneme na riadok s onMouseMove v záložke Events pre Image1 v Inšpektore objektov):

pohyb myši kreslí bodky:

procedure TForm1.Image1MouseMove(Sender:TObject; Shift:TShiftState; X,Y:Integer);
begin
  Image1.Canvas.Pixels[X,Y]:=clBlack;
end;
  • pohybom myši na plochou sa na príslušných miestach zobrazujú bodky
  • už sme si možno zvykli, že veľmi často, keď robíme s grafikou a táto bliká, treba do FormCreate pridať riadok:

aby grafická plocha neblikala:

procedure TForm1.FormCreate(Sender: TObject);
begin
  DoubleBuffered:=true;
end;
  • kreslením bodiek sa veľmi ťažko niečo naozaj nakreslí, preto sa môžeme pokúsiť namiesto bodky (Pixels) kresliť čiary (LineTo):

pohyb myši spája čiary:

procedure TForm1.Image1MouseMove(Sender:TObject; Shift:TShiftState; X,Y:Integer);
begin
  Image1.Canvas.LineTo(X,Y);
end;
  • teraz sa naozaj spájajú čiary podľa pohybu myši, lenže čiary sa kreslia aj keď sa tlačidlo myši nezatlačí
  • využijeme parameter Shift: je typu TShiftState a pre tento platí
      type TShiftState = set of (ssShift,ssAlt,ssCtrl,ssLeft,ssRight,ssMiddle,ssDouble);
  • v tomto parametri sa môžeme dozvedieť či sú zatlačené tlačidlá na myši alebo niektoré špeciálne klávesy na klávesnici
  • využijeme to na to, že čiary sa kresliť len, ak je zatlačené ľavé tlačidlo myši

kreslí sa len pri zatlačenom tlačidle myši:

procedure TForm1.Image1MouseMove(Sender:TObject; Shift:TShiftState; X,Y:Integer);
begin
  if Shift=[ssLeft] then
    Image1.Canvas.LineTo(X,Y);
end;
  • ešte stále to nie je správne riešenie - po zatlačení tlačidla by sa grafické pero malo posunúť do štartového bodu kreslenia, ale bez spojenia s predchádzajúcim bodom - využijeme udalosť onMouseDown, v ktorej sa nastavíme do štartového bodu pomocou MoveTo

pridáme začiatok kreslenej čiary:

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  if Shift=[ssLeft] then
    Image1.Canvas.MoveTo(X,Y);
end;
  • toto riešenie môže niekedy robiť problémy (vyskúšajte počas kreslenia zatlačiť kláves Ctrl), preto môžeme použiť pomocnú logickú premennú dole, ktorá označuje, či práve kreslíme (či sme začali kresliť) a ďalej sa už nepozerá na parameter Shift

použitie pomocnej premennej

var
  dole:boolean = false;

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
 				     Shift: TShiftState; X, Y: Integer);
begin
  dole:=Shift=[ssLeft];    // alebo dole:=ssLeft in Shift;
  if dole then Image1.Canvas.MoveTo(X,Y);
end;

procedure TForm1.Image1MouseMove(Sender:TObject; Shift:TShiftState; X,Y:Integer);
begin
  if dole then Image1.Canvas.LineTo(X,Y);
end;

procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  dole:=false;
end;
  • v situácii, keď nepotrebujeme pri zatlačení tlačidla myši (onMouseDown) nič nastavovať, stačí použiť len onMouseMove

kreslenie machúľ:

procedure TForm1.Image1MouseMove(Sender:TObject; Shift:TShiftState; X,Y:Integer);
var
  r:integer;
begin
  if Shift=[ssLeft] then
    with Image1.Canvas do begin
      r:=random(5)+10;
      Pen.Color:=random(16777216);      // RGB(0,0,random(156)+100);
      Brush.Color:=Pen.Color;
      Ellipse(X-r,Y-r,X+r,Y+r);
    end;
end;
  • v procedúre pre udalosť onMouseMove môžeme na nejaké miesto vo formulári vypisovať momentálnu pozíciu myši:
  • niekam do formulára umiestnime nový komponent Label - text vo formulári - keď budeme meniť jeho stavovú premennú (property) Caption, bude sa meniť jeho zobrazenie

kontrolné výpisy počas pohybu myši nad grafickou plochou:

procedure TForm1.Image1MouseMove(Sender:TObject; Shift:TShiftState; X,Y:Integer);
begin
  Label1.Caption:=IntToStr(X)+','+IntToStr(Y);
  ...
end;

Skicár - kresliaca aplikácia

ďalej ukážeme postupnú konštrukciu jednoduchej kresliacej aplikácie:

  • do formulára vložíme grafickú plochu (Image1), pod ňu textový informačný riadok (Label1), nad plochu tlačidlo s textom zmaž (Button1)

prvá verzia programu:

var
  dole:boolean = false;

procedure TForm1.FormCreate(Sender: TObject);
begin
  DoubleBuffered:=true;
  Button1.Click;   // zmaže sa grafická plocha
  Label1.Caption:='';
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  with Image1, Canvas do begin
    Brush.Color:=clWhite;
    Brush.Style:=bsSolid;
    FillRect(ClientRect);
  end;
end;

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  dole:=ssLeft in Shift;
  if dole then Image1.Canvas.MoveTo(X,Y);
end;

procedure TForm1.Image1MouseMove(Sender:TObject; Shift:TShiftState; X,Y:Integer);
begin
  Label1.Caption:=IntToStr(X)+' '+IntToStr(Y);
  if dole then Image1.Canvas.LineTo(X,Y);
end;

procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  dole:=false;
end;

Paleta farieb

  • druhým krokom bude pridanie farebnej palety, aby sme mohli zvoliť farbu kreslenia:
    • nad grafickú plochu najprv položíme menšiu grafickú plochu Image2, ktorá bude obsahovať nejaký obrázok palety (ak použijete bitmapu paleta.bmp, zvoľte veľkosť Image2 225x33) - umiestnite ju napr. úplne vpravo
    • pred Image2 položte malú plôšku Image3, napr. veľkosti 25x25 - sem sa bude vykresľovať aktuálna zvolená farba - pri štarte programu čierna
  • kliknutím do Image2 sa zvolí farba kresliaceho pera: nastaví sa farba pera pre Image1 a tiež sa zafarbí Image3:
  • spracovanie vybranej farby pera:

    procedure TForm1.Image2MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    begin
      Image1.Canvas.Pen.Color:=Image2.Canvas.Pixels[X,Y];
    
      with Image3, Canvas do begin
        Brush.Color:=Image1.Canvas.Pen.Color;
        FillRect(ClientRect);
      end;
    end;
    • asi by bolo dobré, keby bolo Image3 zafarbené už pri štarte programu: do FormCreate pridáme jeho zafarbenie

    inicializácia zobrazenia aktuálnej farby:

    procedure TForm1.FormCreate(Sender: TObject);
    begin
      ...
      with Image3, Canvas do begin
        Brush.Color:=Image1.Canvas.Pen.Color;
        FillRect(ClientRect);
      end;
    end;

    Posúvač - posuvná lišta

    • na nastavenie hrúbky pera môžeme použiť posúvač, napr. komponent TrackBar (môžete použiť aj ScrollBar):
      • TrackBar1 položíme naľavo od Image3 a trochu prispôsobíme jeho veľkosť voľnej ploche vo formulári
      • ešte musíme zmeniť dve stavové premenné (property) Min a Max, ktoré vyjadrujú minimálnu a maximálnu dosiahnuteľnú hodnotu pre posúvač - nastavíme Min na 1 a Max napr. na 20
      • pre tento komponent využijeme udalosť onChange, ktorá je zavolaná vždy, keď používateľ posúva bežca na lište - podľa momentálnej pozície bežca budeme nastavovať hrúbku pera grafickej plochy
    • onChange procedúra sa automaticky pripraví dvojklikom na komponent vo formulári

    spracovanie nastavovania hrúbky pera:

    procedure TForm1.TrackBar1Change(Sender: TObject);
    begin
      Image1.Canvas.Pen.Width:=TrackBar1.Position;
    end;

    Tlačidlá nástrojov kreslenia

    • každý aj najjednoduchší kresliaci program umožňuje kresliť rôznymi nástrojmi - napr. nielen od ruky perom, ale aj úsečky, obdĺžniky, elipsy ale aj vypĺňať farbou nejaké oblasti
    • na to zadefinujeme 5 malých tlačidiel - komponentov SpeedButton - tieto majú na rozdiel od klasického Button viac vylepšení:
      • keď na ne klikneme, môžu ostať zatlačené (vyjadruje zvolený nástroj)
      • okrem textu môžu obsahovať obrázok (môžete poexperimentovať s Glyph)
      • môžeme ich zoskupiť do skupiny, v ktorej iba jedno tlačidlo bude zatlačené (zatlačenie iného spôsobí automatické vyskočenie predchádzajúceho) - do stavovej premennej (property) GroupIndex všetkým nastavíme rovnaké číslo, napr. 1
    • na tlačidlá napíšeme texty (je vhodné zvoliť nejaký malý font): pero, úsečka, obdĺžnik, elipsa, vyplň
    • prvému tlačidlu (s textom pero) nastavíme stavovú premennú Down na true - tlačidlo bude zatlačené
    • zadefinujeme globálnu premennú rezim, ktorá bude obsahovať momentálne zvolený nástroj - táto premenná sa bude nastavovať v udalosti onClick pre všetky SpeedButton1, SpeedButton2, ...

    napr.

    var
      rezim:(pero,usecka,obdlznik,elipsa,vypln) = pero;
    
    procedure TForm1.SpeedButton1Click(Sender: TObject);
    begin
      rezim:=pero;
    end;
    
    procedure TForm1.SpeedButton2Click(Sender: TObject);
    begin
      rezim:=usecka;
    end;
    
    procedure TForm1.SpeedButton3Click(Sender: TObject);
    begin
      rezim:=obdlznik;
    end;
    
    ...
    • asi by bolo krajšie, keby sme nemuseli vytvárať pre každé tlačidlo zvlášť procedúru (v inej aplikácii by ich mohlo byť oveľa viac) - ale napísali jednu univerzálnu - napr. SpeedButton1Click by mohla byť spoločná pre všetky
    • potom, ako ju zadefinujeme, ju nastavíme všetkým ostatným tlačidlám nástrojov: v záložke udalosti (Events) inšpektora objektov pre SpeedButton2 sa nastavíme na riadok s udalosťou onClick a z ponuky vyberieme SpeedButton1Click - toto urobíme pre všetky tlačidlá nástrojov (ak sme stihli vytvoriť SpeedButton2Click, ... - zrušíme ich tak, že vyprázdnime telo procedúr a po uložení - Save unitu sa korektne zrušia celé procedúry)
    • SpeedButton1Click teraz na základe parametra Sender zistí, ktorý komponent ju zavolal a na základe toho nastaví premennú rezim

    univerzálna SpeedButton1Click procedúra:

    procedure TForm1.SpeedButton1Click(Sender: TObject);
    begin
      if Sender = SpeedButton1 then
        rezim:=pero
      else if Sender = SpeedButton2 then
        rezim:=usecka
      else if Sender = SpeedButton3 then
        rezim:=obdlznik
      else if Sender = SpeedButton4 then
        rezim:=elipsa
      else if Sender = SpeedButton5 then
        rezim:=vypln;
    end;
    • skôr ako sa pustíme do upravovania procedúr Image1MouseDownImage1MouseMove, aby zvládali nové nástroje, musíme vysvetliť ako budú pracovať:
      • úsečka, obdĺžnik a elipsa budú pracovať na veľmi podobnom princípe - pri štarte nástroja (teda zatlačení myšou) sa zapamätá momentálny stav grafickej plochy v pomocnej bitmape bmp (globálna premenná typu TBitmap) a pre každom ťahaní nástroja, napr. úsečky, sa najprv grafická plocha vráti do tohoto pôvodného stavu a potom sa nakreslí nová úsečka - musíme si pri tom pamätať počiatočný bod kreslenia, napr. v globálnych premenných x0 a y0
    • pomocná premenná bmp sa musí inicializovať v FormCreate
    • režim vypĺňania oblasti je ešte jednoduchší - celý sa zrealizuje pri zatlačení tlačidla myši, t.j. v Image1MouseDown (mohol by byť v Image1MouseUp - premyslite si, čo by sa tým zmenilo) - na vypĺňanie oblasti použijeme metódu FloodFill

    spracovanie všetkých režimov:

    var
      dole:boolean = false;
      rezim:(pero,usecka,obdlznik,elipsa,vypln) = pero;
      bmp:TBitmap;
      x0,y0:integer;
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      DoubleBuffered:=true;
      Button1.Click;   // zmaže sa grafická plocha
      Label1.Caption:='';
      with Image3, Canvas do begin
        Brush.Color:=Image1.Canvas.Pen.Color;
        FillRect(ClientRect);
      end;
      bmp:=TBitmap.Create;
    end;
    
    procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    begin
      dole:=ssLeft in Shift;
      if dole then
        with Image1.Canvas do
          case rezim of
            pero:
              MoveTo(X,Y);
            usecka,obdlznik,elipsa:
              begin
                bmp.Assign(Image1.Picture);
                x0:=X; y0:=Y;
              end;
            vypln:
              begin
                Brush.Color:=Pen.Color;
                FloodFill(X,Y,Pixels[X,Y],fsSurface);
              end;
          end;
    end;
    
    procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState;
      X,Y: Integer);
    begin
      Label1.Caption:=IntToStr(X)+' '+IntToStr(Y);
      if dole then
        with Image1.Canvas do
          case rezim of
            pero:
              LineTo(X,Y);
            usecka:
              begin
                Draw(0,0,bmp);
                MoveTo(x0,y0); LineTo(X,Y);
              end;
            obdlznik:
              begin
                Draw(0,0,bmp);
                Brush.Color:=Pen.Color;
                Brush.Style:=bsClear;
                Rectangle(x0,y0,X,Y);
              end;
            elipsa:
              begin
                Draw(0,0,bmp);
                Brush.Color:=Pen.Color;
                Brush.Style:=bsClear;
                Ellipse(x0,y0,X,Y);
              end;
          end;
    end;

    Zaškrtávacie políčko

    • ešte urobíme posledné vylepšenie: umožníme používateľovi programu aby si mohol zvoliť, či obdĺžnik, resp. elipsa budú pri vykreslení vyplnené farbou (zatiaľ sme ich vyrobili "deravé" - nastavili sme Brush.Style:=bsClear;)
    • do formulára vložíme ešte jeden komponent - zaškrtávacie políčko CheckBox - nastavíme mu popis (Caption) napr. na plný a opravíme v metóde Image1MouseMove kreslenie obdĺžnika a elipsy

    použitie CheckBoxu:

    procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState;
      X,Y: Integer);
    begin
     ...
            obdlznik:
              begin
                Draw(0,0,bmp);
                Brush.Color:=Pen.Color;
                if CheckBox1.Checked then
                  Brush.Style:=bsSolid
                else
                  Brush.Style:=bsClear;
                Rectangle(x0,y0,X,Y);
              end;
            elipsa:
              begin
                Draw(0,0,bmp);
                Brush.Color:=Pen.Color;
                if CheckBox1.Checked then
                  Brush.Style:=bsSolid
                else
                  Brush.Style:=bsClear;
                Ellipse(x0,y0,X,Y);
              end;
          end;
    end;
    • a projekt je hotový - rozloženie komponentov môže byť takéto:

    Zhrnutie

    • stavové premenné komponentov (vidíme ich v inšpektore objektov) sú podobné položkám záznamu (record)
    • pri ich zmene v inšpektore objektov sa automaticky prejaví aj ich vizuálna zmena vo formulári (napr. šírka, text, bublinkový help a pod.)
    • v Delphi sa takýto typ stavových premenných nazýva Vlastnosť, t.j. Property
    • niektoré sú pre nás užitočné už počas tvorby formulára (Caption, Name, Hint, Min, Max, ...), iné využijeme hlavne za behu programu (niektoré fungujú len za behu, a preto v inšpektore objektov nie sú)
    • meno komponentu (Name) treba zmeniť hneď ako sa položí do formulára (ak ho chceme meniť), inak niektoré situácie Delphi nemusia správne uhádnuť a opraviť túto zmenu korektne
    • niektoré užitočné vlastnosti (property) nám známych komponentov:
      • formulár TForm
        • Caption - reťazec - titulok okna
        • ClientWidth, ClientHeight - veľkosť vnútra okna (nie obvodu okna, pre ten je veľkosť Width a Height)
        • Left, Top - x-ová a y-ová súradnica okna na obrazovke - môžeme celé okno posúvať
        • Visible - či je okno viditeľné
        • WindowState - okno môžeme zminimalizovať, zmaximalizovať
        • Canvas (funguje len počas behu programu) - šedá plocha - môžeme do nej kresliť (v udalosti OnPaint)
      • tlačidlo TButton
        • Caption - text na tlačidle
        • Font - rovnako ako v grafickej ploche
        • Height, Width - veľkosť
        • Left, Top - poloha vo formulári
        • Visible - môžeme ho skryť
        • Hint, ShowHint - string a boolean - bublinkový help a či sa má zobrazovať
        • Enabled - môžeme ho zablokovať, false znamená, že tlačidlo bude zašedené
    • vlastnosti Width, Height, Left, Top, Visible, Hint, ShowHint, Enabled - fungujú skoro rovnako pre skoro všetky typy komponentov
      • grafická plocha TImage
        • Canvas (funguje len počas behu programu) - sprístupnenie kreslenia do grafickej plochy
        • ClientRect (funguje len počas behu programu) - hodnota typu TRect, t.j. obdĺžnik, ktorý popisuje veľkosť plochy
      • malé tlačidlo TSpeedButton - veľmi podobné obyčajnému tlačidlu, ale tieto tlačidlá môžu tvoriť skupinu (vzájomne sa vylučujú - maximálne jedno z nich je zatlačené), môže mať aj obrázok
        • Caption - text na tlačidle (môže byť spolu s obrázkom)
        • GroupIndex - poradové číslo skupiny tlačidiel, ktoré sa navzájom vylučujú
        • Down - či je zatlačené
        • Glyph - obrázok
      • posuvná lišta TTrackBar
        • Min, Max - minimálna a maximálna hodnota
        • Position - momentálna hodnota
        • Orientation - (trHorizontal,trVertical)
      • zaškrtávacie políčko TCheckBox
        • Caption - text za políčkom
        • Font - písmo pre text
        • Checked - či je zaškrtnuté
      • text TLabel
        • Caption - ak reťazec obsahuje #13#10, bude viacriadkový
        • Font - písmo pre text
      • textová plocha TMemo
        • Color - farba podkladu
        • Font - písmo
        • Lines - obsah textovej plochy - pole reťazcov
        • ReadOnly - či má používateľ zakázané meniť obsah
      • editovací riadok TEdit - je veľmi podobný TMemo
        • Color - farba podkladu
        • Font - písmo
        • Text - momentálny obsah riadka
        • ReadOnly - či má používateľ zakázané meniť obsah
        • PasswordChar - ak obsahuje napr. '*', tak sa namiesto zadávaných znakov budú vypisovať hviezdičky - používa sa pri zadávaní hesla
    • udalosti (Events) = záložka v inšpektore objektov = môžeme definovať správanie pri špecifických situáciách
    • inšpektor nám pomôže správne zadeklarovať takú procedúru, ktorú vyvolá Windows, keď vznikne príslušná udalosť (napr. klikne sa, zmení sa, hýbe sa a pod.)
    • tieto procedúry definujeme tak, že v záložke Events (v riadku) pri danej udalosti dvojklikneme v pravom stĺpci (alebo si zvolíme procedúru z už existujúcej ponuky procedúr)
    • nedopisujte manuálne žiadne nové procedúry na spracovávanie udalostí - vždy použite inšpektor objektov
    • ak potrebujete nejakú procedúru obsluhujúcu udalosť zrušiť, najlepšie to urobíte tak, že vyčistíte obsah procedúry - necháte v nej len počiatočný begin a koncový end a pri nasledujúcom uložení na disk (napr. Ctrl+S) ju Delphi automaticky vyhodí

    Myš a korytnačky

    • korytnačka je grafické pero, ktoré dokáže nejaké veci navyše: jednoducho "vie natočiť súradnicovú sústavu", v grafickej ploche ich môžeme definovať ľubovoľný počet, ...
    • prvý príklad ilustruje použitie korytnačky ako obyčajného grafického pera

    kreslenie korytnačkou:

    var
      k:TKor;
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      k:=TKor.Create; k.PH;
    end;
    
    procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    begin
      k.PD;
    end;
    
    procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    begin
      k.ZmenXY(X,Y);
    end;
    
    procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    begin
      k.PH;
    end;
    • v tomto programe je korytnačka "prilepená" k myši - pri každom pohybe myši (onMouseMove) sa pohne na jej miesto - korytnačka má ale stále pero hore a kreslí len, keď príde udalosť onMouseDown - keď stlačíme nejaké tlačidlo myši
    • do onMouseMove môžeme pridať aj iné akcie, napr. korytnačka sa najprv otočí smerom k novej pozícii (použili sme funkciu Smerom(x,y) - vypočíta absolútny uhol k zadanému bodu v rovine) a až potom sa na ňu presunie - pritom môže niečo korytnačie kresliť

    pri pohybe korytnačka kreslí úsečky:

    procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    begin
      k.Smer:=k.Smerom(X,Y);
      k.PresunXY(X,Y);
      k.Dopredu(100);
      k.Dopredu(-100);
    end;

    môžeme pridať zmaz:

    procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    begin
      zmaz;
      k.Smer:=k.Smerom(X,Y);
      k.PresunXY(X,Y);
      k.Dopredu(100);
      k.Dopredu(-100);
    end;

    alebo

    procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    var
      i:integer;
    begin
      k.Smer:=k.Smerom(X,Y);
      k.PresunXY(X,Y);
      for i:=1 to 5 do begin
        k.Dopredu(10); k.Vpravo(144);
      end;
    end;

    alebo farebné paličky:

    procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    begin
      k.Smer:=k.Smerom(X,Y)-90;
      k.PresunXY(X,Y);
      k.HP:=5; k.FP:=random(256*256*256);
      k.Dopredu(30);
      k.HP:=15; k.Dopredu(0); k.HP:=5;
      k.Dopredu(-60); k.Dopredu(30);
    end;

    alebo rovnostranný trojuholník:

    procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Integer);
    begin
      zmaz;
      k.Smer:=k.Smerom(X,Y);
      k.PresunXY(X,Y);
      k.HP:=5; k.FP:=clGreen;
      k.Vpravo(90); k.Dopredu(8); k.Vlavo(105); k.Dopredu(30.9);
      k.Vlavo(150); k.Dopredu(30.9); k.Vlavo(105); k.Dopredu(8);
    end;
    • ďalší príklad ukáže generovanie korytnačiek pri klikaní myšou: kliknutie na voľné miesto vygeneruje novú korytnačku, kliknutie na už existujúcu korytnačku ju mierne otočí

    klikanie na korytnačky:

    var
      k:array[1..100] of TKor;
      n:integer = 0;     // aktuálny počet korytnačiek
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      zmaz;   // aby sa grafická plocha zobrazila už pri štarte
    end;
    
    procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    var
      i:integer;
    begin
      i:=1; while (i<=n) and not k[i].Blizko(X,Y) do inc(i);
      if i<=n then
        with k[i] do begin
          Vpravo(15); Dopredu(50); Dopredu(-50);
        end
      else begin
        inc(n); k[n]:=TKor.Create(X,Y);
        with k[n] do begin
          HP:=5; FP:=random(256*256*256);
          Dopredu(50); Dopredu(-50);
        end;
      end;
    end;
    • použili sme korytnačiu preddefinovanú funkciu (metódu) Blizko, ktorá pre daný bod v rovine odpovie, či je korytnačka od neho blízko

    NDÚ

    • nové korytnačky by mohli vznikať na kliknutie so zatlačeným pravým tlačidlom myši, na ľavé tlačidlo môže korytnačka urobiť nejakú akciu - nakresliť nejaký obrázok, alebo sa prilepí na myš a do nasledujúceho kliknutia putuje s myšou

    Časovač

    • predchádzajúcu úlohu, v ktorej sme klikaním vytvárali nové korytnačky, resp. ich otáčali trochu pozmeníme: kliknutie na korytnačku naštartuje kreslenie kružnice - a to pomaly - použime známu procedúru cakaj

    kliknutie naštartuje kružnicu:

    procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    var
      i,j:integer;
    begin
      i:=1; while (i<=n) and not k[i].Blizko(X,Y) do inc(i);
      if i>n then begin
        inc(n); k[n]:=TKor.Create(X,Y);
        with k[n] do begin HP:=5; FP:=random(256*256*256); Dopredu(0); end;
      end
      else
        with k[i] do
          for j:=1 to 36 do begin
            Dopredu(10); Vpravo(10);
            cakaj(50);       // sleep(50); Image1.Repaint;
          end;
    end;
    •  vzniká zaujímavý efekt: kliknutie na korytnačku ju rozbehne - počas tohoto klikania môžeme ďalej klikať do plochy, na iné korytnačky - asi ľahko odhalíte, že každé nové kliknutie na korytnačku, zastaví predchádzajúci pohyb a naštartuje nový pohyb - po skončení tohoto nového pohybu sa dokončí aj ten pozastavený...
    • ak namiesto cakaj použijeme systémový podprogram sleep (zakomentovaná časť), tak sa tento efekt stratí: po rozbehnutí korytnačky program nereaguje, až kým korytnačka nedobehne celú kružnicu
      • celé je to spôsobené tým, že v procedúre cakaj je ukryté Application.ProcessMessages - to znamená, že počas čakania 50 milisekúnd dovolíme Windows reagovať na iné udalosti, napr. aj na kliknutie myšou a teda umožníme znovu naštartovať novú kružnicu (po jej skončení sa pokračuje v prerušenej akcii)
      • procedúra sleep nemá v sebe zabudované takéto mechanizmy a preto je veľmi nebezpečná
    • ukážeme iný spôsob, ako sa rieši problém so spomaľovaním akcií: použijeme nový komponent časovač - Timer (z palety komponentov System) - položíme ho niekam do plochy - je to nevizuálny komponent a teda po spustení aplikácie nebude zobrazený
    • časovač si môžeme predstaviť ako hodiny, ktoré s nejakou frekvenciou "tikajú" - pri každom tiknutí môžu vykonať nejakú akciu
    • frekvenciu "tikania" nastavíme v stavovej premennej Interval, pozastavenie / naštartovanie časovača môžeme urobiť logickou stavovou premennou Enabled - true znamená, že hodiny bežia, false znamená, že sme ich pozastavili
    • časovač má jedinú udalosť onTimer, ktorá sa naštartuje po uplynutí "tikacieho" intervalu, t.j. zodpovedá jej procedúra Timer1Timer
    • v nasledujúcom príklade predpokladáme časovač so sekundovým tikaním, t.j. Interval je 1000; korytnačka bude kresliť úsečku dĺžky 100 a každú sekundu sa otočí o 6 stupňov

    sekundová ručička:

    var
      k:TKor;
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      k:=TKor.Create; k.HP:=5;
    end;
    
    procedure TForm1.Timer1Timer(Sender: TObject);
    begin
      zmaz;
      k.Vpravo(6);
      k.Dopredu(100);
      k.Dopredu(-100);
    end;
    • príklad s korytnačkami, ktoré sa po kliknutí rozbehnú po kružnici preprogramujeme pomocou časovača: pre každú korytnačku si budeme pamätať v poli kolko, koľko krokov po kružnici ešte musí prebehnúť

    korytnačky krúžia po svojich kružniciach:

    var
      k:array[1..100] of TKor;
      kolko:array[1..100] of integer;
      n:integer = 0;
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      zmaz;
    end;
    
    procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    var
      i:integer;
    begin
      i:=1; while (i<=n) and not k[i].Blizko(X,Y) do inc(i);
      if i>n then begin
        inc(n); k[n]:=TKor.Create(X,Y);
        with k[n] do begin HP:=8; Dopredu(0); HP:=3; end;
        kolko[n]:=0;
      end
      else begin
        k[i].FP:=random(256*256*256); kolko[i]:=36;
      end;
    end;
    
    procedure TForm1.Timer1Timer(Sender: TObject);
    var
      i:integer;
    begin
      for i:=1 to n do
        if kolko[i]>0 then
          with k[i] do begin
            Dopredu(10); Vpravo(10);
            dec(kolko[i]);
          end;
    end;
    • časovaču Timer1 sme nastavili Interval na 50 milisekúnd
    • na záver ukážeme príklad práce s časovačom a bitmapami: na nejakom pozadí, napr. na fotografii tiger.bmp sa bude pomaly pohybovať nejaký obrázok, napr. opica.bmp - táto sa bude odrážať od bočných stien grafickej plochy ako lopta od stien miestnosti (v súbore opicatiger.zip sú obidve bitmapy)
    • okrem grafickej plochy (Image1) umiestnime časovač Timer1 s premennou Intreval nastavenou na 100

    jednoduchá animácia:

    var
      pozadie,obr:TBitmap;
      x,y,dx,dy,x2,y2:integer;
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      DoubleBuffered:=true;
      pozadie:=TBitmap.Create;
      pozadie.LoadFromFile('tiger.bmp');
      obr:=TBitmap.Create;
      obr.LoadFromFile('opica.bmp');
      obr.TransparentColor:=clWhite;
      obr.Transparent:=true;
      x:=100; y:=50; dx:=3; dy:=4;
      x2:=obr.Width div 2; y2:=obr.Height div 2;
    end;
    
    procedure TForm1.Timer1Timer(Sender: TObject);
    begin
      Image1.Canvas.Draw(0,0,pozadie);
      // Image1.Canvas.StretchDraw(Image1.ClientRect,pozadie);
      inc(x,dx); if (x<x2)or(x>=Image1.Width-x2) then dx:=-dx;
      inc(y,dy); if (y<y2)or(y>=Image1.Height-y2) then dy:=-dy;
      Image1.Canvas.Draw(x-x2,y-y2,obr);
    end;
    • namiesto Draw pri kreslení pozadia môžeme použiť aj iné prostriedky, napr. vykachličkovanie alebo StretchDraw, ale tie môžu výrazne spomaliť priebeh animácie

    NDÚ

    • umiestnite do plochy väčší počet korytnačiek - napr. pravidelne ich rozmiestnite na priamke alebo kružnici a nechajte sa ich hýbať podľa nejakých pravidiel, napr. rôznou rýchlosťou po kružnici, alebo kmitať hore - dolu, tiež s rôznou rýchlosťou - napr. prvá nech má rýchlosť 1, druhá 1.1, tretia 1.2, ...


    © 2002 AB, KVI
    blaho@fmph.uniba.sk