Umíme to s Delphi: 82. díl – vylepšujeme aplikaci o grafy

Při vytváření většiny aplikací je velmi důležité příjemné a elegantní uživatelské prostředí. Zvláště u aplikací, které jakýmkoliv způsobem zprostředkovávají data, hledáme možnosti grafické prezentace. V Delphi se vyskytuje velmi propracovaná podpora grafů: můžeme ji využít jak v „obyčejných“ aplikacích, tak i v databázových. Práce s grafy je jednoduchá a výsledky překvapivě dobré. Nevěříte?
Databázové aplikace pracují v současnosti se širokým spektrem dat, údajů, informací a hodnot. Při vytváření databázových aplikací si často přejeme prezentovat databázové informace nejen textovou formou, ale také graficky – a tedy elegantně. Součástí Delphi je jednotka DBChart, s jejíž pomocí lze snadno vytvářet hezky vypadající grafické databázové výstupy.

Komponenta Chart – vytváření grafů

Jednotka DBChart obsahuje třídu TDBChart, která zapouzdřuje komponentu DBChart. Ta je potomkem komponenty Chart. Je asi zřejmé, že zatímco DBChart slouží k vytváření grafů v databázových aplikacích (dokáže v podstatě automaticky získat data z datového zdroje a zobrazit je ve formě grafu), komponentu Chart můžeme používat pro grafickou prezentaci jakýchkoliv dat i v aplikacích, které s databází nijak nepracují. Grafy jsme se však v našem seriálu dosud nezabývali (a to ani v souvislostmi s databázemi, ani v jiných souvislostech) a dnešní díl je ideální příležitost učinit nápravu.

Databázovými grafy se budeme zabývat v příštím dílu seriálu, dnes si vysvětlíme základní informace a získáme základní vědomosti o grafech.

Komponenta Chart je základním stavebním kamenem pro vytváření „nedatabázových“ grafů. Komponenta Chart se nalézá v paletě komponent v záložce Additional.

K definici vizuálních charakteristik grafů a k přidání nové datové řady se používá nástroj Chart Editor, který je možné vyvolat např. po klepnutí na graf na formuláři pravým tlačítkem myši a zvolením Edit Chart. Všechny změny lze samozřejmě provádět za běhu aplikace pomocí vlastností a metod komponenty Chart.

Vkládání dat určených k zobrazení (tedy definování údajů datových řad) musí být prováděno za běhu programu. Vše si postupně ukážeme.

Krok první – umístění na formulář a správné nastavení

Abychom mohli v aplikaci zobrazovat grafy, umístíme komponentu Chart na formulář, klepneme na něj pravým tlačítkem a spatříme editor grafů Chart Editor, viz obrázek:

Editor je okamžitě nastaven na záložku Chart, která umožňuje modifikovat vlastnosti grafu. Klepneme na tlačítko Add… ,abychom mohli přidat řadu v grafu. Otevře se další okno, v němž je možné zvolit druh grafu, o který máme zájem. Možnosti jsou nemalé, viz obrázek:

Zvolíme si třeba klasický řádkový graf (Line), necháme zaškrtnuté tlačítko 3D a klepneme na OK. V tomto okamžiku dojde k vytvoření první datové řady, k jejímu naplnění náhodnými daty a k jejímu zobrazení na formuláři. Když si nyní prohlédneme výše zmíněný editor Edit Chart, spatříme kromě hlavních dvou záložek (Chart, Series) také několik dalších záložek:

  • Series: seznam existujících datových řad. V našem případě obsahuje jedinou, právě vytvořenou sérii, která se nazývá Series1. Pomocí tlačítek je možné přejmenovat sérii, smazat ji, klonovat (vytvořit identickou kopii), případně změnit její druh.
  • General: obecná nastavení, která zahrnují možnosti tisku a exportu do obrázku, a dále nastavení týkající se zoomu a scrollování.
  • Axis: nastavení týkající se os.
  • Titles: nastavení titulků a popisků, včetně barvy nebo vzoru pozadí apod.
  • Legend: nastavení legendy a pomocných popisků, včetně fontu, barvy, stínování apod.
  • Panel: nastavení panelu, na němž je zobrazen graf na formuláři (okraje, plasticita, barva apod.)
  • Paging: stránkování
  • Walls: nastavování vzhledu levé, spodní a zadní „stěny“ grafu
  • 3D: nastavování trojrozměrného vzhledu grafu

Všechny uvedené volby se týkají záložky Chart. Druhou základní záložkou je Series. Ta obsahuje parametry závislé ne konkrétním formátu datové řady.

Krok druhý – konfigurace datových řad

V tomto okamžiku je již na formuláři umístěn řádkový graf, který obsahuje jednu řadu naplněnou náhodnými hodnotami, viz obrázek:

Takto ovšem graf vypadá jen v době návrhu. Pokud aplikaci spustíme, záhy zjistíme, že graf žádná data neobsahuje a že jej musíme naplnit.

Nyní si ukážeme, jakým způsobem toto „naplnění daty“ provést. Práce s konkrétními datovými údaji se provádí prostřednictvím programového kódu, v době návrhu to není možné.

V předchozím příkladu jsme do grafu přidali řádkovou sérii (zvolili jsme možnost Line). Předpokládejme, že jsme nezměnili implicitní název této série (Series1). Nyní umístíme na formulář tlačítko (Button1) a ošetříme jeho událost OnClick:

procedure TForm1.Button1Click(Sender: TObject);
begin
  with Series1 do
  begin
    Add( 100, `Pepa` , clGreen ) ;
    Add( 120, `Franta`, clBlue ) ;
    Add(  50, `Standa`, clRed ) ;
    Add( 200, `Evicka`, clWhite ) ;
    Add( 150, `Zdenicka`, clBlack ) ;
  end;
end;

Popis metody Add a dalších dostupných metod si uvedeme níže. Nyní se pro názornost pokusíme celou aplikaci přeložit a spustit. Po spuštění klepneme na tlačítko Button1: funguje!

Než si podrobně popíšeme zmíněné metody a způsoby „nalévání“ dat do grafu, pokusíme se provést změnu grafu z řádkového na výsečový.

Krok třetí – změny vzhledu grafu

Dejme tomu, že jsme se rozhodli změnit styl grafu z řádkového na výsečový. Tato operace bude díky koncepci komponenty Chart nesmírně snadná. Stačí v době návrhu klepnout na graf pravým tlačítkem, zvolit Edit Chart a v záložce Chart a podzáložce Series klepnout na tlačítko Change. Z nabídky zvolíme Pie.

Aplikaci opět spustíme a klepnutím na tlačítko vytvoříme hodnoty v sérii (všimněte si, že jsme nikterak neměnili těch pár řádků zdrojového kódu, které jsme s velkou slávou naprogramovali výše):

Poznámka: vzhledem k tomu (viz níže), že jsme změnou druhu grafu fakticky nahradili komponentu LineSeries komponentou PineSeries, mohli bychom změnit kód tak, že namísto metody Add použijeme metodu AddPie. Metoda AddPie je v některých verzích doporučována, nicméně v nových verzích je přítomna jen z důvodu zpětné kompatibility a interně stejně volá metodu Add, takže je správnější ponechat volání metody Add.

Podrobnější popis

V předchozím textu jsme si uvedli vlastně jakýsi nejjednodušší příklad použití komponenty Chart. Pojďme se podrobněji podívat na to, jakým způsobem graf funguje a především jak pracovat s datovými řadami. Pokud si nyní prohlédnete modul formuláře, v němž je umístěn náš ukázkový graf, zjistíte, že součástí deklarace formuláře se stala zajímavá vlastnost Series1 typu TPieSeries (předpokládejme, že jsme prošli změnou řádkového grafu na výsečový):

type
  TForm1 = class(TForm)
    Chart1: TChart;
    Button1: TButton;
    Series1: TPieSeries;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

Editor grafu přidal do deklarace automaticky položku pro datovou řadu, kterou jsme v jeho rámci přidali. Jedná se v podstatě o komponentu PieSeries, která byla automaticky přidána do naší aplikace. V našem konkrétním případě se jedná o komponentu PieSeries, ale analogické komponenty existují pro většinu základních druhů řad:

TAreaSeries
TBarSeries
TCircledSeries
TCustomBarSeries
TCustomSeries
TFastLineSeries
THorizBarSeries
TLineSeries
TPieSeries
TPointSeries
TSeriesPointer

Popis jednotlivých komponent (druhů řad) se mírně liší, ale základní filozofie práce je shodná. Následující popis se týká PieSeries.

Objekt třídy PieSeries může být do aplikace přidán automaticky (jak jsme se toho stali svědky výše), nebo jej můžeme přidat ručně. V tom případě je nutné nastavit vlastnost ParentChart, která označuje „rodičovský“ graf, v jehož rámci má být série zobrazena.

PieSeries má několik zajímavých vlastností, s jejichž pomocí lze nastavovat vzhled a chování „koláče“. Toto nastavování však lze mnohem pohodlněji nastavovat pomocí vizuálního editoru Edit Chart, proto se jimi nebudeme podrobně zabývat.

PieSeries má však metody, které slouží k přidávání hodnot do grafu. Na tyto metody se nyní podívejme.

Nejdůležitější metodou je Add, která slouží k přidání hodnoty:

function Add(Const AValue:Double; Const ALabel:String; AColor:TColor):Longint; virtual;

Poznámka: raději nepoužívejte metodu AddPie; možná ji naleznete, nicméně pouze z důvodu zpětné kompatibility; výrobce upozorňuje, že v dalších verzí nebude přítomna. Čistší je proto použití přímo metody Add, což je metoda třídy TChartSeries (společného předka tříd pro série v grafu).

Prvním parametrem je hodnota (y-ové) souřadnice, která má být vynesena do grafu. Druhý parametr pak označuje popisek daného bodu, třetí pak barvu. Druhý a třetí parametr nemusí být uveden – jsou nepovinné.

Metoda Add může být použita k přidání nové série pro jakýkoliv typ grafu. Při přidávání pomocí Add zadáváme pouze y-ovou souřadnici, x-ová bude dopočítána automaticky. Zajímá-li nás tato hodnota x-ové souřadnice, můžeme ji snadno získat – metoda Add tuto souřadnici vrací jako svou návratovou hodnotu.

Existuje ještě mnoho dalších zajímavých metod třídy TchartSeries. Jako příklad lze uvést např. AddNull, která vloží nulové (prázdné) místo do grafu nebo Clear, která vymaže všechny hodnoty ze série apod.

Ukázka použití grafu

Nyní společně vytvoříme ukázkovou aplikaci, z níž snad bude dobře patrné, jak se s grafem v praxi pracuje. Cílem aplikace bude grafické zobrazení poměru druhů souborů na disku. Aplikace po svém spuštění projde disk (přesněji řečeno – projde adresář, který bude zadán v parametru při volání funkce pro vyhledávání a všechny jeho podadresáře) a zjistí, kolikrát se na něm vyskytují soubory s jednotlivými příponami. Výsledky nakonec zobrazí ve formě grafu, přičemž aby nebylo v grafu nepřehledné množství položek, budou všechny přípony s výskytem menším než 3% sloučeny do jediné položky označené „ostatní“.

Postup tvorby aplikace je opět velmi jednoduchý a je popsán v následujících bodech:

1. Vytvořte novou aplikaci. Na formulář umístěte pouze jedno tlačítko (Button1) a jeden graf (Chart1).

2. Klepneme na graf pravým tlačítkem a v editoru vytvoříme novou sérii typu Pie.

3. Vytvoříme proceduru pro vyhledávání souborů na disku. Procedura Vyhledej prohledá pevný disk a pro každý nalezený soubor provede následující operaci:

  • pokud se přípona nalezeného souboru dosud nevyskytuje v seznamu přípon, přidá ji tam a zároveň přidá do seznamu četností novou položku s hodnotou „1“.
  • pokud se přípona nalezeného souboru již nalézá v seznamu přípon, zvyš hodnotu na odpovídající pozici v seznamu četností o jedna.

Na konci (po prohledání celého disku) tedy budeme mít dva seznamy. V prvním z nich budou (unikátní) přípony souborů, ve druhém budou na stejných pozicích jejich četnosti. Ne úplně elegantní, ale účinné.

V závěru dnešního dílu si uvedeme zdrojový kód celé aplikace a za týden si jej trochu rozebereme, protože se v něm vyskytuje několik zajímavých fragmentů.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, TeEngine, Series, ExtCtrls, TeeProcs, Chart;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Chart1: TChart;
    Series1: TPieSeries;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    List1, List2: TStringList;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure Vyhledej(CoAKde:string);
var
  Nalezeno: TSearchRec;
  Cesta: String;
  Soubor: String;
  Pom: Integer;

begin
  Soubor := ExtractFileName(CoAKde);
  Cesta := ExtractFilePath(CoAKde);

  if Cesta[Length(Cesta)] <> `\` then Cesta := Cesta + `\`;
  if FindFirst(CoAKde, faAnyFile, Nalezeno) = 0 then begin
    repeat
      if (ExtractFileExt(Nalezeno.Name) <> ``) and (ExtractFileExt(Nalezeno.Name) <> `.`) then
        if Form1.List1.IndexOf(ExtractFileExt(Nalezeno.Name)) = -1 then
        begin  // jeste v seznamu neni
          Form1.List1.Add(ExtractFileExt(Nalezeno.Name));
          Form1.List2.Add(`1`);
        end
        else
        begin
          Pom := StrToInt(Form1.List2.Strings: Invalid JSON primitive: Form1.List1.IndexOf.-->);
          Inc(Pom);
          Form1.List2.Strings: Invalid JSON primitive: Form1.List1.IndexOf.--> := IntToStr(Pom);
        end;

    until FindNext(Nalezeno) <> 0;
  end;

  // nyni najdeme podadresar aktualniho adresare v zavolame rekurzivne Vyhledej
  if FindFirst(Cesta + `*.*`, faAnyFile, Nalezeno) = 0 then begin
    repeat
      // pokud jsme nasli adresar a nejedna se o adresar nebo `..`, zavolame Vyhledej
      if ((Nalezeno.Attr and faDirectory) = faDirectory) and (Nalezeno.Name[1] <> `.`) then begin
        Application.ProcessMessages;
        Vyhledej(Cesta + Nalezeno.Name + `\` + Soubor);
      end;
    until FindNext(Nalezeno) <> 0;
    // uvolnime Nalezeno z pameti
    FindClose(Nalezeno);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  I: Integer;

begin
  Form1.List1 := TStringList.Create;
  Form1.List2 := TStringList.Create;

  Button1.Caption := `Start`;
  Button1.Enabled := False;

  Caption := `Moment, vyhledavam...`;
  Vyhledej(`c:\dokumenty\*.*`);
  Caption := `Struktura souboru na disku`;
  Button1.Enabled := True;

  Form1.Series1.OtherSlice.Style := poBelowPercent;
  Form1.Series1.OtherSlice.Value := 3;
  Form1.Series1.OtherSlice.Text := `Ostatni`;

  for I := 0 to List1.Count-1 do
    Form1.Series1.Add(StrToInt(List2.Strings[I]), List1.Strings[I]);

  List1.Destroy;
  List2.Destroy;
end;

end.

Možný vzhled aplikace si můžete prohlédnout na následujícím obrázku:

Na závěr

V dnešním dílu seriálu jsme si ukázali, jak zapracovat do aplikace grafický výstup (graf). Z předchozího textu jste zřejmě poznali, že možnosti grafů v Delphi jsou velmi široké a vysvětlení všech podrobností by vydalo na několik dílů seriálu. Věřím však, že i z dnešního jemného úvodu jste o grafech získali základní představu a že nyní jste sami schopni vytvářet aplikace s grafickým výstupem. Pokud dospějete k názoru, že byste se o grafech rádi dozvěděli více, napište do diskuse nebo na můj email a v budoucnu se ke grafům v našem seriálu vrátíme.

Za týden se ke grafům vrátíme, ale tentokráte v přímé souvislosti s databázemi. Ukážeme si totiž, jak používat komponentu DBChart pro tvorbu grafických výstupů z databází.

Diskuze (8) Další článek: ČR má nejmenší podíl nelegálního softwaru v regionu

Témata článku: , , , , , , , , , , , , , , , , , , , , , , , , ,