22. Různé aplikace II
  1. Další komponentou, kterou se budeme zabývat je komponenta TChart (na Paletě komponent najdeme další dvě verze této komponenty TDBChart a TQRChart; tyto verze se používají obdobně, ale zatím je nebudeme používat). Komponenta opět umožňuje vytvářet grafy. Po umístění této komponenty na formulář můžeme zahájit vytváření grafu. V místní nabídce komponenty TChart zvolíme Edit Chart. Je zobrazen Editor grafu. Nyní již můžeme editovat graf, definovat a zaplňovat jeho datové série.

  2. Editor grafu vypadá takto:

    Stránka Chart (z vnějších stránek) Editoru grafu obsahuje definující informace pro graf. Jsou různé možnosti definování obecných a specifických parametrů grafu. Některé parametry nemůžeme zadávat dokud nejsou definované série dat v grafu. Např. se pokuste modifikovat parametr Title a uvidíte, že se ihned změní v grafu. Ne ale všechny změny vidíme v grafu ihned.
    Nejprve se tedy budeme zabývat přidáváním sérií. Stiskneme tlačítko Add na stránce Series (na vnější stránce Chart). Je zobrazena galerie typů grafů. Vybereme jeden pro přidání do našeho grafu (později jej budeme moci změnit, pokud budeme požadovat jiný způsob zobrazení dat) a stiskneme OK. Typ série je automaticky přidán k našemu grafu. V Editoru grafu vidíme přidanou novou konfiguraci pro novou sérii.
    Po přidání série do grafu jsou také přidány některé náhodné hodnoty, které jsou zobrazeny v grafu (jsou viditelné pouze při návrhu). Pokud nyní přeložíme naši aplikaci, pak uvidíme prázdný graf.
    Je tedy zapotřebí přidat data k sérii. Tato činnost se liší pro TDBChart a nebudeme se jí zatím zabývat. Předpokládejme, že na formuláři máme komponentu TChart s přidanou sérií. Nyní se pokusíme zaplnit sérii programově. Budeme předpokládat, že máme sérii Pie (s implicitním jménem Series1). Na formulář umístíme tlačítko a vytvoříme obsluhu jeho stisknutí s kódem:
    Series1->Add(40, "Pencil", clRed);
    Series1->Add(60, "Paper", clBlue);
    Series1->Add(30, "Ribbon", clGreen);
    Popis metody Add a dalších potřebných metod a vlastností je dostupný v nápovědě. Naši aplikaci se pokusíme spustit a po stisknutí tlačítka uvidíme zobrazení grafu, kód tedy pracuje.
    Během návrhu jsou všechny vlastnosti grafu a sérií dostupné v Inspektoru objektů nebo v Editoru grafu. Editace sérii nejsnadněji provedeme v Editoru grafu na stránce Series. Pokuste se provést některé změny a uvidíte, že se provedené změny automaticky projevují v grafu. Při návrhu není dostupný náš kód a používají se tedy náhodná data. Abychom zjistili, jak se naše změny projeví za běhu, musíme aplikaci spustit.
    Mnoho věcí můžeme vyřešit vizuálně v Editoru nebo můžeme některé parametry modifikovat v Inspektoru objektů. Můžeme je také řešit kódem.
    Pokuste se modifikovat aplikaci ze 4. a 5. zadání předchozí kapitoly tak, aby se místo komponenty ChartFX používala komponenta TChart. Více se touto komponentou nebudeme zabývat.

  3. Pro zadávání datumů a časů lze použít komponentu TDateTimePicker. Vlastnost DateMode určuje režim zobrazení ovladače a vlastnost Kind určuje zda ovladač bude požíván pro datum nebo čas. Umístěte tuto komponentu na formulář a vyzkoušejte si její použití.
  4. Komponenta TAnimate slouží k přehrávání klipů AVI. Pracuje s nekomprimovanými soubory AVI nebo klipy komprimované kódováním RLE. Tato komponenta nepodporuje zvuky u klipů. Následují některé vlastnosti této komponenty:
  5. Pokuste se tuto komponentu použít v některé aplikaci.
  6. Komponenta TSplitter rozděluje klientskou oblast formuláře na části, které mohou měnit velikost. Tuto komponentu přidáváme na formulář mezi dva zarovnané ovladače. Spliter je umístěn mezi ovladačem který je zarovnán s jedním okrajem formuláře a ovladačem, který zaplňuje zbytek klientské oblasti. Použití si ukážeme v následující aplikaci.

  7. Začneme vývoj nové aplikace. Na formulář umístíme Panel, který zarovnáme se spodním okrajem formuláře. Dále na formulář přidáme komponentu TSplitter u které musíme také nastavit vlastnost Align na alBottom. Na formulář přidáme komponentu TDirectoryListBox, u které vlastnost Align nastavíme na alLeft, další komponentu TSplitter (vlastnost Align je zde již nastavena na alLeft). Zbývající plochu formuláře zaplníme komponentou TFileListBox (Align nastavíme na alClient). Nyní již aplikaci můžeme vyzkoušet. Spustíme ji a pokusíme se měnit velikosti jednotlivých oblastí formuláře.

  8. Další zajímavou oblastí C++ Builderu je podpora souborových proudů. Knihovna VCL obsahuje abstraktní třídu TStream a její tři potomky: TFileStream, THandleStream a TMemoryStream. Pro nahrávání dat ze souboru a ukládání do souboru můžeme použít dva souborově orientované proudy. THandleStream se používá v případě, kdy již máme madlo souboru. TFileStream použijeme, když máme jen jméno souboru. Třetí proudovou třídou je TMemoryStream, která pracuje s pamětí a nikoli se skutečným souborem. Tato třída však obsahuje speciální metody pro kopírování svého obsahu do nebo z jiného proudu, který může být souborovým proudem. Proudy mohou nahradit tradiční soubory. Jejich velkou výhodou je např. to, že můžeme pracovat s paměťovými proudy, a potom je uložit do souboru. Tímto způsobem lze zvýšit rychlost programu intenzivně pracujícího se soubory.

  9. Zvláště důležitou vlastností proudů je jejich schopnost přenášet komponenty. Všechny třídy knihovny VCL jsou potomci TPersistent, speciální třídy umožňující ukládání objektů do proudů a obsahují metody pro ukládání a nahrávání všech vlastností a veřejných položek. Proto mohou všichni potomci třídy TComponent ukládat sami sebe do proudu nebo mohou být nahráním z proudu automaticky vytvořeni. Program k tomu může využívat metod proudu WriteComponent a ReadComponent. Proudy v zásadě nevědí nic o čtení nebo zápisu komponent. Metody tříd TStream jednoduše používají dvě jiné třídy: TReader a TWriter, obě potomky třídy TFiler. Objekty TReader a TWriter používají proud, ke kterému se vztahují a jsou schopné mu přidělit speciální značky pro provedení kontroly datového formátu. Objekt TWriter může uložit značku komponenty, potom uložit komponentu, všechny její vlastnosti a všechny komponenty, které obsahuje. Obsahuje metodu WriteRootComponent, která uloží komponentu předanou jako parametr a také všechny komponenty, které obsahuje. Podobně třída TReader obsahuje metodu ReadRootComponent, která je schopná vytvořit nové objekty s využitím informace o třídách uložených v proudu. To je možné pod jednou podmínkou: název komponenty musí být aplikací registrován (funkcí RegisterClasses).
    Vraťme se ale ke konkrétní aplikaci. Začneme vývojem nové aplikace. Formulář této aplikace je jednoduchý. Na horní okraj formuláře vložíme komponentu Panel (zrušíme jeho titulek) a na ní vložíme tři voliče s texty Label, Edit a Button (první z nich nastavíme). Při kliknutí myší na formuláři, vytvoříme komponentu určenou vybraným voličem. Před deklaraci typu formuláře vložíme deklaraci výčtového typu
    enum Typ {Label, Edit, ButtonX};
    popisující vybraný volič a do soukromé části deklarace formuláře umístíme:
    int Citac;
    Typ Odkaz;
    Vytvořte obsluhy kliknutí na voličích tak, aby v položce Odkaz byla uložena informace o vybraném voliči. Dále vytvoříme obsluhu OnCreate formuláře s těmito příkazy:
    Odkaz = Label;
    Citac = 0;
    Po vytvoření obsluhy OnMouseDown formuláře již můžeme aplikaci vyzkoušet. Tuto obsluhu tvoří příkazy:
    TControl *Objekt;
    AnsiString Nazev;
    switch (Odkaz) {
      case Label: Objekt = new TLabel(this); break;
      case Edit: Objekt = new TEdit(this); break;
      case ButtonX: Objekt = new TButton(this); break;
    }
    Objekt->Parent = this;
    Objekt->Left = X;
    Objekt->Top = Y;
    Citac++;
    Nazev = String(Objekt->ClassName()) + IntToStr(Citac);
    Nazev.Delete(1, 1);
    Objekt->Name = Nazev;
    Objekt->Visible = true;
    Klikáním myši na formuláři vytváříme komponenty určené vybraným voličem. Vyzkoušejte.
  10. Této aplikaci dále umožníme ukládání vložených komponent do souboru a jejich opětovnému nahrání ze souboru. K aplikaci přidáme nabídku Soubor s volbami: Zrušit, Otevřít, Uložit jako a Konec. Přidáme také komponenty OpenDialog a SaveDialog (kde nastavíme vhodné vlastnosti; u našich souborů budeme používat příponu CMP). Tyto komponenty musíme vložit na již použitou komponentu Panel. Obsluhu volby Zrušit tvoří příkazy (zrušíme všechny komponenty mimo komponent vložených na panel):

  11. for (int I = ControlCount-1; I >= 0; I--)
      if (String(Controls[I]->ClassName()) != "TPanel") Controls[I]->Free();
    Citac = 0;
    Komponenty ukládané do proudu musíme registrovat a tedy do obsluhy OnCreate formuláře přidáme příkazy:
    TComponentClass classes[3] =
                 {__classid(TEdit), __classid(TLabel), __classid(TButton)};
    RegisterClasses(classes, 2);
    Vytvoříme ještě obsluhu volby Uložit jako. Tvoří ji příkazy:
    if (SaveDialog1->Execute()){
      TFileStream *S;
      S = new TFileStream(SaveDialog1->FileName, fmOpenWrite | fmCreate);
      for (int I = 0; I < ControlCount; I++)
        if (String(Controls[I]->ClassName()) != "TPanel")
          S->WriteComponent(Controls[I]);
      delete S;
    }
    Obsluhu volby Otevřít tvoří:
    if (OpenDialog1->Execute()) {
      TFileStream *S;
      TComponent *Novy;
      Zruit1Click(this);
      S = new TFileStream(OpenDialog1->FileName, fmOpenRead);
      while (S->Position < S->Size){
        Novy = S->ReadComponent(NULL);
        InsertControl(dynamic_cast<TControl *> (Novy));
        Citac++;
      }
      delete S;
    }
    Zbývající obsluhy vytvořte sami. Aplikaci vyzkoušejte.
  12. Ještě se seznámíme s klíčovými slovy C++ přidanými do C++ Builderu pro podporu VCL.

  13. Operátor __classid je používán překladačem pro generování ukazatele na vtable (tabulka virtuálních metod) pro specifikovanou třídu. Tento operátor byl také použit v předchozím příkladě. Syntaxe:
    __classid(classname)
    Např. __classid používáme při registraci editoru vlastnosti, komponenty nebo třídy a s metodou InheritsFrom třídy TObject.
    Klíčové slovo __closure je používáno k deklarování speciálního typu ukazatele na metodu. Na rozdíl od normálního ukazatele na funkci, tento ukazatel obsahuje i ukazatel na objekt. Ve standardním C++ můžeme přiřadit instanci odvozené třídy ukazateli na základní třídu, ale nemůžeme přiřadit metodu odvozené třídy ukazateli na metodu základní třídy. Ukazuje to následující příklad:
    class base
    {
     public:
     void func(int x);
    };
    class derived: public base
    {
     public:
     void new_func(int i);
    };
    void (base::*bptr)(int);
    bptr = &derived::new_func; // nedovoleno
    Jazykové rozšíření __closure umožňuje toto v C++ Builderu provést. Closure přiřazuje ukazatel na metodu k ukazateli na instanci třídy. Ukazatel na instanci třídy je použit jako ukazatel this, když voláme přiřazenou metodu. Deklarace Closure je stejná jako deklarace ukazatele na funkci, ale před identifikátorem funkce je uvedeno klíčové slovo __closure. Např.
    struct MyObject
    {
      double MemFunc(int);
    };
    double func1(MyObject *obj)
    {
      double ( __closure *myClosure )(int);
      // Inicializace Closure.
      myClosure = obj -> MemFunc;
      // Použití Closure k volání metody.
      return myClosure(1);
    }
    V C++ Builderu jsou Closure používány s událostmi.
    Klíčové slovo __property deklaruje v deklaraci třídy vlastnost. Vlastnosti mohou být deklarovány pouze ve třídách. Pro pole vlastností, index pole může být libovolného typu. Syntaxe:
    <property declaration> ::=
      __property <type> <id> [ <prop dim list> ]="{" <prop attrib list> "}"
      <prop dim list> ::= "[" <type> [ <id> ] "]" [ <prop dim list> ]
      <prop attrib list> ::= <prop attrib> [ , <prop attrib list> ]
      <prop attrib> ::=     read = <data/function id>
      <prop attrib> ::=     write = <data/function id>
      <prop attrib> ::=     stored = <data/function id>
      <prop attrib> ::=     stored = <boolean constant>
      <prop attrib> ::=     default = <constant>
      <prop attrib> ::=     nodefault
      <prop attrib> ::=     index = <const int expression>
    Vlastnosti mají několik služeb, čímž se odlišují od datových složek. Vlastnosti mohou mít: Klíčové slovo __published specifikuje, že vlastnosti v této sekci jsou zobrazeny v Inspektoru objektů. Sekci __published mohou mít pouze třídy odvozené od TObject. Pravidla viditelnosti pro zveřejňované složky jsou stejné jako pro veřejné složky. Jediný rozdíl mezi zveřejňovanými a veřejnými složkami je ten, že u zveřejňovaných složek jsou generovány informace RTTI. To umožňuje aplikacím dynamické dotazy na složky, metody a vlastnosti.
  14. Několik parametrů klíčového slova __declspec poskytuje jazykovou podporu pro VCL. Následuje popis těchto parametrů.

  15. Parametr delphiclass je použit pro deklarování tříd odvozených od TObject. Tyto třídy jsou vytvořeny tak, aby byly kompatibilní s VCL.
    Parametr delphireturn je použit pro deklarování tříd, které vytváříme v C++ Builderu pro podporu zabudovaných datových typů a jazykových konstrukcí Object Pascalu, které nejsou přirozené v C++. To zahrnuje Currency, AnsiString, Variant, TDateTime a Set. Parametr delphireturn označuje třídy C++ pro VCL kompatibilní zpracování volání funkcí (parametry a návratové hodnoty). Tento modifikátor je zapotřebí, když předáváme strukturu funkci hodnotou mezi Object Pascalem a C++.
    Parametr dynamic je používán pro deklarování dynamických funkcí. Dynamické funkce se podobají virtuálním, ale jsou jinak uložené.
    Parametr hidebase chrání sémantiku programu Object Pascalu při předávání virtuálních a překrytých funkcí C++ Builderu. V Object Pascalu, virtuální funkce v základní třídě mohou být zobrazeny v odvozené třídě jako funkce se stejným jménem, ale mohou být chápány jako kompletně nové funkce bez explicitního vztahu k předchozí funkci. Překladač používá makro HIDESBASE, k specifikaci, že tyto typy deklarací funkcí jsou kompletně samostatné. Např. jestliže základní třída T1 deklaruje virtuální bezparametrickou funkci func a odvozená třída T2 deklaruje funkci se stejným jménem a signaturou, pak DCC32 vytváří hlavičkový soubor s následujícím prototypem:
    virtual void T1::func(void);
    HIDESBASE void T2::func(void);
    Bez HIDESBASE, sémantika C++ indikuje, že virtuální funkce T1::func bude přepsána T2::func.
    Parametr package indikuje, že kód definice třídy bude přeložen do balíčku. Parametr pascalimplementation indikuje, že kód definice třídy bude implementován v Object Pascalu.
22. Různé aplikace II