12. Práce s grafikou II
  1. Kreslení za běhu aplikace si ukážeme ještě na jedné jednoduché aplikaci. Začneme s vývojem nové aplikace. U formuláře nastavíme vlastnost Color na clBlack. Na formulář dále přidáme komponentu Timer, kde Interval nastavíme na 500. Před deklaraci třídy formuláře do hlavičkového souboru formuláře umístíme tyto deklarace:

  2. const int  MaxPoints = 15;  // Počet kreslených bodů
    struct TRPoint {
      float X, Y;
    };
    a do soukromé části deklarace třídy formuláře přidáme:
    TRPoint     Points[MaxPoints];
    float       Rotation;     // v radiánech
    int         PointCount;
    void __fastcall RotatePoints();
    Metoda RotatePoints bude tvořena příkazy:
    void __fastcall TForm1::RotatePoints()
    {
     // Všechny úhly jsou v radiánech
     const float M_2PI = 2 * M_PI;
     float StepAngle = M_2PI / PointCount;
     Rotation += M_PI / 32;
     if (Rotation > StepAngle) Rotation -= StepAngle;
     int i;
     float j;
     for (i = 0, j = Rotation; i < PointCount; i++, j += StepAngle) {
      Points[i].X = cos(j);
      Points[i].Y = sin(j);
     }
    }
    K formuláři vytvoříme obsluhu události OnCreate s příkazy:
    Canvas->Pen->Color = clTeal;
    Rotation = 0;
    PointCount = MaxPoints;
    RotatePoints();
    Dále vytvoříme obsluhu OnPaint formuláře. Zde budou příkazy:
    int centerX = ClientWidth / 2;
    int centerY = ClientHeight / 2;
    int radius = min(centerY, centerX);
    Canvas->Ellipse(0, 0, radius*2, radius*2);
    int i,j;
    for (i = 0; i < PointCount; i++) {
     for (j = i + 1; j < PointCount; j++) {
      Canvas->MoveTo(radius + floor(Points[i].X * radius),
       radius + floor(Points[i].Y * radius));
      Canvas->LineTo(radius + floor(Points[j].X * radius),
       radius + floor(Points[j].Y * radius));
     }
    }
    Obsluhu OnResize formuláře tvoří příkaz:
    Invalidate();
    Zbývá ještě vytvořit obsluhu OnTimer časovače. Zde budou příkazy:
    RotatePoints();
    Invalidate();
    Tím je naše aplikace hotova. Vyzkoušejte co dělá a snažte se pochopit význam jednotlivých metod.
  3. Dále se seznámíme s používáním komponenty HeaderControl. Začneme s vývojem nové aplikace. Na horní okraj formuláře umístíme komponentu HeaderControl a nastavíme u ní vlastnost Sections na tři sekce s texty Červená, Zelená a Modrá (šířku jednotlivých sekcí nastavíme na 125, minimální šířku na 50, maximální šířku na 250 a zarovnávání na centrování). Na formulář dále vložíme vedle sebe tři komponenty Shape tak, aby zaplnily šířku jednotlivých sloupců hlavičky, vlastnost Shape u nich nastavíme na stEllipse a barvu výplně změníme podle textu v hlavičce. Dále vytvoříme obsluhu události OnSectionTrack ovladače HeaderControl, která bude tvořena příkazy:

  4. Section->Width = Width;
    Shape1->Width = HeaderControl->Sections->Items[0]->Width;
    Shape2->Width = HeaderControl->Sections->Items[1]->Width;
    Shape2->Left = HeaderControl->Sections->Items[1]->Left;
    Shape3->Width = HeaderControl->Sections->Items[2]->Width;
    Shape3->Left = HeaderControl->Sections->Items[2]->Left;
    Tím je aplikace hotova, můžeme ji vyzkoušet. Myší lze přetahovat dělící čáry v hlavičce a tím měnit šířku zobrazené elipsy ve sloupci jehož šířku měníme.
  5. V této kapitole se budeme ještě zabývat vytvářením hodin. Na formulář umístíme komponenty Timer a Panel (panel zvětšíme na celou plochu formuláře a také zvětšíme písmo jeho titulku). Vytvoříme ještě obsluhu události OnTimer časovače. Bude tvořena příkazem:

  6. Panel1->Caption = TimeToStr(Time());
    Funkce Time vrací objekt typu TDateTime, který obsahuje aktuální čas a funkce TimeToStr jej převede na řetězec. Tím jsou nejjednodušší (digitální) hodiny hotovy. Tvar výpisu je určen nastavením národnostních zvyklostí ve Windows.
  7. V další aplikaci se pokusíme vytvořit analogové hodiny. Formulář, který potřebujeme pro ručičkové hodiny je ještě jednodušší. Obsahuje pouze časovač. Většina kódu je v obsluze události OnPaint. Protože potřebujeme vykreslovat tři různé hodinové ručičky, přidáme do formuláře veřejnou metodu, kterou budeme kreslit jednotlivé ručičky. Bude to funkce:

  8. void __fastcall TForm1::KresliRucicku(int XStred, int YStred,
                            int Polomer, int ZpPolomer, double Uhel)
    {
      Canvas->MoveTo(XStred-floor(ZpPolomer*cos(Uhel)),
                     YStred-floor(ZpPolomer*sin(Uhel)));
      Canvas->LineTo(XStred+floor(Polomer*cos(Uhel)),
                     YStred+floor(Polomer*sin(Uhel)));
    }
    Do deklarace formuláře ještě vložíme následující soukromé položky:
    unsigned short int Hodina, Minuta, Sekunda;
    int XStred, YStred, Polomer;
    Obsluha události OnPaint formuláře je tvořena následujícími příkazy:
    double Uhel;
    int I, X, Y, Velikost;
    XStred = ClientWidth / 2;
    YStred = ClientHeight / 2;
    if (XStred > YStred) Polomer = YStred - 10;
    else Polomer = XStred - 10;
    Canvas->Pen->Color = clYellow;
    Canvas->Brush->Color = clYellow;
    Velikost = Polomer / 50 + 1;
    for (I = 0; I < 12; I++){
      Uhel = 2 * M_PI * (I + 9) / 12;
      X = XStred - floor(Polomer * cos(Uhel));
      Y = YStred - floor(Polomer * sin(Uhel));
      Canvas->Ellipse(X-Velikost, Y-Velikost, X+Velikost, Y+Velikost);
    }
    Canvas->Pen->Width = 2;
    Canvas->Pen->Color = clBlue;
    Uhel = 2 * M_PI * (Minuta+45) / 60;
    KresliRucicku(XStred, YStred, Polomer * 90 / 100, 0, Uhel);
    Uhel = 2 * M_PI * (Hodina + 9 + Minuta / 60) / 12;
    KresliRucicku(XStred, YStred, Polomer * 70 / 100, 0, Uhel);
    Canvas->Pen->Width = 1;
    Canvas->Pen->Color = clRed;
    Uhel = 2 * M_PI * (Sekunda + 45) / 60;
    KresliRucicku(XStred, YStred, Polomer, Polomer * 30 / 100, Uhel);
    Zbývá ještě vytvořit několik obsluh událostí. Obsluha OnTimer časovače je tvořena příkazy:
    unsigned short SetinySec;
    DecodeTime(Time(), Hodina, Minuta, Sekunda, SetinySec);
    Refresh();
    Obsluhu OnResize formuláře tvoří příkaz: Refresh(); Jestliže vytvoříme ještě obsluhu události OnCreate formuláře s následujícím příkazem, pak hodiny budou ukazovat správný čas ihned od zobrazení.
    Timer1Timer(this);
    Vytvořte tyto hodiny.
  9. Předchozí program sice funguje, ale k dokonalosti mu ještě něco chybí. Obrázek hodin je nestabilní; příliš bliká. Ve skutečnosti je totiž každou sekundu celý obsah formuláře vymazán a znovu vykreslen. Pokuste se tento program vylepšit tak, že smažete pouze starou sekundovou ručičku a potom ji nakreslete znova na novém místě.
  10. Chceme-li uložit informaci o stavu aplikace, abychom ji mohli při dalším spuštění programu obnovit, můžeme ji uložit do souboru, a to v takovém formátu, jaký uznáme za vhodný. Nicméně Windows zajišťují explicitní podporu pro inicializační soubory (s příponou INI). Každá aplikace může mít svůj vlastní INI soubor a zapisovat do něj řetězce, čísla nebo hodnoty typu bool. Stejné hodnoty je možné také číst. Builder obsahuje třídu TIniFile, s jejíž pomocí můžeme manipulovat s INI soubory. Vytvoříme objekt této třídy, přiřadíme mu soubor a můžeme z něj informace jak číst, tak je do něj zapisovat. Pro vytvoření tohoto objektu je nutné zavolat konstruktor, kterému předáme jako parametr název souboru.

  11. Existují dvě možnosti umístění INI souboru na disku. Jednak je možné uložit jej do adresáře aplikace a konstruktoru předat úplnou cestu k němu. Druhým běžnějším řešením je uložit inicializační soubor do adresáře Windows. V tomto případě zadáváme pouze název souboru.
    INI soubory jsou rozděleny na sekce označené názvem uzavřeným v hranatých závorkách. Každá sekce obsahuje několik prvků tří možných druhů: řetězec, celé číslo nebo logická hodnota. Třída TIniFile obsahuje metody pro čtení každého z těchto typů a jsou zde též metody pro jejich zápis. V metodách určených ke čtení můžeme specifikovat implicitní hodnotu, která bude použita v případě, kdy odpovídající záznam v INI souboru neexistuje. Využití INI souborů si ukážeme v následující aplikaci. Formulář aplikace je prázdný a jsou zde pouze obsluhy událostí OnCreate a OnClose formuláře. Do INI souboru budeme ukládat umístění a velikost našeho formuláře a při dalším spuštění toto umístění a velikost použijeme. Do soukromé části deklarace formuláře umístíme (do hlavičkového souboru formuláře vložíme #include <vcl/inifiles.hpp>):
    TIniFile *IniFile;
    Obsluha OnCreate je tvořena příkazy:
    int Stav;
    IniFile = new TIniFile("Project1.ini");
    Stav = IniFile->ReadInteger("Form1","Status", 0);
    if (Stav != 0) {
      Top = IniFile->ReadInteger("Form1","Top", 0);
      Left = IniFile->ReadInteger("Form1","Left", 0);
      Width = IniFile->ReadInteger("Form1","Width", 0);
      Height = IniFile->ReadInteger("Form1","Height", 0);
      switch (Stav) {
        case 2: WindowState = wsMinimized; break;
        case 3: WindowState = wsMaximized; break;
      }
    }
    a obsluhu OnClose tvoří:
    int Stav;
    if (Application->MessageBox("Uložit aktuální stav formuláře?",
                   "Dotaz", MB_YESNO | MB_ICONQUESTION ) == IDYES) {
      switch (WindowState) {
          case wsNormal:
            IniFile->WriteInteger("Form1","Top", Top);
            IniFile->WriteInteger("Form1","Left", Left);
            IniFile->WriteInteger("Form1","Width", Width);
            IniFile->WriteInteger("Form1","Height", Height);
            Stav = 1;
            break;
          case wsMinimized: Stav = 2; break;
          case wsMaximized: Stav = 3; break;
      }
      if (!Active) Stav = 2;
      IniFile->WriteInteger("Form1","Status", Stav);
    }
    delete IniFile;
    Tím je naše aplikace hotova a můžeme ji vyzkoušet. Prohlédněte si také v nějakém textovém editoru vytvořený INI soubor. Pokuste se pochopit, jak tato aplikace pracuje.
  12. V demonstračních programech firmy Borland je uveden další grafický editor. Tento editor má samostatnou paletu nástrojů. Aplikaci si stáhněte, přeložte a vyzkoušejte jak pracuje. Podívejte se jakým způsobem je vyřešena práce s paletou. Prohlédněte si také, jak jsou vyřešeny ostatní obsluhy událostí.
  13. Další aplikace nás podrobněji seznámí s používáním posuvníku. Aplikaci si opět stáhněte a vyzkoušejte. Podívejte se na obsluhu události OnScroll posuvníku. Můžeme zde zjišťovat, jaká činnost s posuvníkem je prováděna.
12. Práce s grafikou II