[Lazarus, TAChart] Wykres paskowy (słupkowy)

Źródłem oryginalnym jest wiki Lazarusa, który znajdziesz pod tym adresem - http://wiki.freepascal.org/TAChart_Tutorial:_BarSeries

Celem będzie taki wykres:



Biblioteka TAChart umożliwia stworzenie różnych wariacji wykresów paskowych, przykładowo:

  • seria pasków obok siebie,
  • seria pasków ułożonych na sobie,
  • seria pasków ułożonych na sobie i znormalizowanych do 100%,
  • seria pasków ułożona pionowo,
  • seria pasków ułożona poziomo.

Zaczynamy ćwiczenia praktyczne.

1. Utwórz nowy projekt okienkowy i zapisz go.
2. Dodaj z Palety komponentów i zakładki Chart komponent TChart przez postawienie go na formie Form1. Rozmiary Form1 ustaw tym razem samodzielnie wpisując odpowiednie wartości lub za pomocą myszki.
3. Ustaw właściwość Align dla wykresu Chart1 na alClient (Chart1.Align := alClient;).

W przykładzie użyjemy nowego komponentu o nazwie TRandomChartSeries, który zasymuluje nam dane dla wykresu w sposób losowy. Może wydawać się to dziwne, że autorzy biblioteki wymyślili taki komponent, ale jest to bardzo praktyczne. Pozwala nam to na przygotowanie wykresu od strony wizualnej, a gdy już jesteśmy zadowoleni z efektu wystarczy do wykresu dołączyć "prawdziwe" źródło danych.

4. Dodaj z Palety komponentów-Chart-TRandomChartSource, trzy takie obiekty. U mnie jest to trzecia ikona licząc od lewej strony (z symboliczną niebieską kostką do gry) .

5. Zmień ich nazwę odpowiednio na RedChartSource, BlueChartSource, YellowChartSource. Powinieneś otrzymać coś w tym stylu:


6. Teraz skonfigurujemy nasze obiekty TRandomChartSource, czyli parametry naszego źródła danych, aby zakres losowanych parametrów mieścił się w zakresie przez nas określonych.
Zakładamy, że chcemy mieć cztery paski na serię - czyli 4 czerwone, 4 niebieskie i 4 żółte.
Ustawiamy zaczynając od RedChartSource właściwość PointsNumber na 4 [RedChartSource.PointsNumber := 4;]. Dla BlueChartSource i YellowChartSource dokładnie tak samo jak wyżej, czyli PointsNumber na wartość 4.
Teraz minimalną wartość dla osi X, czyli w IO wybieramy RedChartSource i zakładce Właściwości wybieramy właściwość XMin i ustawiamy na 1. To samo robimy dla BlueChartSource i YellowChartSource. [RedChartSource.XMin := 1; BlueChartSource := 1; YellowChartSource := 1;]
Teraz podobne czynności wykonamy aby ustawić wartość maksymalną dla osi X. W IO znów wybieramy RedChartSource i w zakładce Właściwości odnajdujemy właściwość XMax i ustawiamy na 4. To samo powtarzamy dla BlueChartSource i YellowChartSource. [RedChartSource.XMax := 4; BlueChartSource := 4; YellowChartSource := 4;]
Podobne czynności musi wykonać dla wartości osi Y, czyli na początek minimalną wartość dla osi Y. I znów w IO wybieramy obiekt RedChartSource, przechodzimy do zakładki Właściwości i wybieramy właściwość YMin, którą ustawiamy na 0. Dokładnie to samo robimy dla BlueChartSource i YellowChartSource. [RedChartSource := 0; BlueChartSource := 0; YellowChartSource := 0;].
Teraz maksymalną wartość osi Y, czyli w IO wybieramy RedChartSource, następnie w zakładce Właściwości, YMax i wpisujemy 100. Dalej, a jak, to samo dla BlueChartSource i YellowChartSource. [RedChartSource := 100; BlueChartSource := 100; YellowChartSource := 100;].

7. Teraz dodamy do naszego wykresu serie danych następnie podepniemy do nich utworzone w poprzednim punkcie źródła danych i na końcu pokolorujemy paski.
Klikamy dwa razy na wykresie Chart1 aby pojawiło się okienko Edit series a w nim wybieramy Add z górnego paska i następnie z rozwijanej listy wybieramy typ wykresu - Bar series. Powtarzamy czynności aż będziemy mieli trzy sztuki dla każdego koloru - Chart1BarSeries1, Chart1BarSeries2, Chart1BarSeries3.


Od razu zmienimy ich nazwy, czyli wykonaj następujący schemat działania: w IO wybieramy Chart1BarSeries1 i przechodzimy niżej do zakładki Właściwości i odnajdujemy właściwość Name, którą zmieniamy na RedBarSeries. To samo powtórz dla Chart1BarSeries2 - zmień na BlueChartSeries i ChartBarSeries3 - zmień na YellowChartSeries.
Teraz ustawimy kolory pasków. W IO wybierz RedBarSeries przejdź do zakładki Właściwości i w niej wybierz SeriesColor. Ustaw odpowiednio przez wybranie zdefiniowanego koloru dla: RedBarSeries - clRed, BlueBarSeries - clBlue i YellowBarSeries - clYellow.
Czas podłączyć dane. I znów podobny schemat działania, czyli w IO wybieramy RedBarSeries, przechodzimy do zakładki Właściwości i odszukujemy właściwość Source i z rozwijanej listy wybieramy - RedChartSource. To samo robimy dla BlueBarSeries - BlueChartSource i YellowBarSeries - YellowChartSource.
W celu sprawdzenia uruchom projekt (F9). Powinieneś widzieć coś podobnego do obrazka poniżej.


8. Teraz zajmiemy się legendą. Zaczniemy od jej wyświetlenia. Wybierz w IO nasz wykres, czyli w drzewie obiektów klikamy na Chart1 i przechodzimy do zakładki Właściwości i szukamy w drzewie grupy właściwości Legend, którą rozwijamy. Na samym dole znajdziemy właściwość Visible, którą zmieniamy na True (domyślnie jest zawsze False) w efekcie czego pojawi się na po prawej stronie legenda. [Chart1.Legend.Visible := True;].
Taka ciekawostka - każdą składową legendy można kontrolować - w IO wybieramy np. RedBarSeries, przechodzimy do zakładki Właściwości i odnajdujemy grupę właściwości Legend - rozwijamy ją i na samym dole znajduje się właściwość Visible dzięki której możemy możemy włączać i wyłączać zmieniając odpowiednio z True na False. [RedBarSeries.Legend.Visible :=  Flase; ].
Opisy w legendzie. Opisy kontrolujemy za pomocą właściwości Title. W IO wybierz RedBarSeries i przejdź do zakładki Właściwości i odszukaj Title i wpisz tekst Red. To samo powtórz dla BlueBarSeries i YellowBarSeries, oczywiście odpowiednio nazywając poszczególne serie. [RedBarSeries.Title := "Red"; BlueBarSeries.Title := "Blue"; YellowBarSeries := "Yellow";]
Sprawę legendy mamy załatwioną - powinieneś teraz mieć coś takiego.


9. Jak widzisz niektóre słupki wzajemnie się zasłaniają. Zajmiemy się tym teraz, czyli rozsuniemy je trochę. Obiekt typu TBarSeries oferuje do tego dwie właściwości: BarWidthPercent, BarOffsetPercent. Obie właściwości są wyrażone w procentach i jak się domyślasz na podstawie ich nazw, jedna odpowiada z szerokość a druga za odległość paska.
Jeśli paski mają być niejednakowej szerokości to zainteresuj się właściwością BarWidthStyle := bwPercentMin.
Przyjmujemy koncepcję, że w "grupie" mamy 3 paski (zawsze czerwony, niebieski i żółty) + 1 przerwa i cała ten obszar to 100%. Dzielimy więc 100% / 4 = 25% i z tego, dlatego będzie to wyglądało tak:

RedBarSeries.BarOffsetPercent := -25;
RedBarSeries.BarWidthPercent := 25;
BlueBarSeries.BarOffsetPercent := 0;
BlueBarSeries.BarWidthPercent := 25;
YellowBarSeries.BarOffsetPercent := 25;
YellowBarSeries.BarWidthPercent := 25;


Jak odczytać powyższy kod? Powinno to być oczywiste, ale gwoli formalności wyjaśnię. Patrz na pierwszą linijkę - i po kolei mamy obiekt RedBarSeries, po kropce jest jego właściwość BarOffsePercent i przypisujemy jej za pomocą znak przypisania := wartość -25. Taki kod można "przeklikać" w następujący sposób.  W IO w drzewie obiektów wybierz RedBarSeries następnie przejdź do zakładki Właściwości i odszukaj właściwość BarOffsetPercent i wpisz do niej wartość -25. Chyba jest to proste. Wykonaj czynności dla reszty kodu a zobaczysz, że zmiany w projekcie zachodzą w "design-time". Obserwując je zrozumiesz jak działają. Aktualnie powinieneś mieć coś takiego na ekranie.


Jesteśmy w połowie drogi :-)
10. Osie - zajmiemy się teraz dostosowaniem osi. Użyjemy na osi X etykiet tekstowych.
Do tej pory mamy na osi X tylko etykiety numeryczne (z wartościami liczbowymi) i w dodatku jeszcze pomiędzy grupami pasków. Chcemy zdefiniować etykiety sami i tak aby były umieszczone dokładnie pod grupą pasków.
Najlepszym sposobem na osiągnięcie tego celu będzie dodanie kolejnego CharSource w postaci TListChartSource (druga ikona w palecie komponentów Chart).
Dodajemy więc komponent TListChartSource do naszej formy i zmieniamy właściwość Name na LabelsChartSource. Komponent ten ma wbudowany edytor danych, aby go wywołać w IO wybierz LabelsChartSource i następnie jego właściwość DataPoints i kliknij przycisk z wielokropkiem.


Pojawi się Edytor punktów, który wypełnij jak niżej na obrazku.


Po tabelce poruszamy się klawiszami strzałek. W kolumnie Color nic nie wpisujemy. W kolumnę Y również moglibyśmy nic nie wpisywać, ale będziemy chcieli obrócić wykres do poziomu więc nam się to przyda. Kolumna Text zawiera opisy (etykiety) pod nasze grupy pasków. Jak uzupełniłeś tabelkę to kliknij przycisk OK aby zakończyć edycję.
Aby aktywować nasze opisy wybieramy w IO 1 - Button, czyli naszą umowną oś x i teraz zakładkę Właściwości w której rozwijamy "grupę" Marks i wybieramy w niej Source. Ustawiamy dla niej z rozwijanej listy nasz utworzony przed chwilą komponent LabelsChartSource. [Chart1.AxisList.Axes[1].Marks.Source := LabelChartSource;]. Jak zauważyłeś już w "design-time" pojawiły się na osi poziomej etykiety z kolumny X.
Teraz dalej w "grupie" Marks wybieramy właściwość Style i z listy wybieramy smsLabel - zobacz na nasz wykres, etykiety zostały odpowiednio zastąpione z kolumny Text. Po tym kroku powinieneś widzieć coś takiego.


11. Dopasowanie linii siatki i znaczników pomiędzy grupami pasków.
Wykres byłby czytelniejszy, gdyby znaczniki na osi x (1 - Bottom) nie znajdowały się na środku każdej grupy pasków lecz pomiędzy nimi. Zmienimy to.
Wyłącz siatkę dla osi 1 - Bottom, czyli w IO wybieramy 1 - Bottom, przechodzimy do Właściwości,  odnajdujemy Grid i właściwość Visible, którą ustawiamy na False. [Chart1.AxisList.Axes[1].Grid.Visible := False;]. Śledź zmiany w formie naszego programu, bo widać je w "design-time" - nie ma potrzeby kompilacji programu, co przyspiesza pracę.
Teraz wyłączymy (ukryjemy) znaczniki na osi 1 - Bottom. W IO wybieramy 1 - Bottom, przechodzimy do TickLenght i wpisujemy wartość 0 [Chart1.AxisList.Axes[1].TickLenght := 0;] i jeśli masz inaczej to TickInnerLenght ustaw na 0 [Chart1.AxisList.Axes[1].TickInnerLenght : = 0;].
Jak utworzyć linie siatki pomiędzy grupami pasków? Można to zrobić tworząc dodatkową oś (minor) do osi głównej (major) 1 - Bottom. Będzie to inny "zestaw" znaczników i linii siatki niż ten na głównej osi.
Wybierz w IO1 - Bottom i jej właściwość Minors - kliknij w wielokropek aby otworzyło się okno Editing Chart1.AxisList[1].Minors. W tym oknie na górnym pasku kliknij Add - na liście pojawi się zapis 0 - M. Wybierz go jeśli nie jest wybrany, aby był podświetlony, spowoduje to, że w IO w zakładce Właściwości pojawią się właściwości (he, he, he - propertisy). Ustaw je następująco: Visible := True; TickLenght := 4; i Intervals.Count := 1; jeśli jeszcze nie widzisz siatki to sprawdź, czy Grid.Visible := True;. Cały czas podglądaj wykres w "design-time" w formie. Zamknij okno edycji.
Jeśli pod paskami irytuje cię luka to ustaw Chart1.Margins.Bottom := 0; - będzie ładniej.
Teraz powinno to wyglądać coś koło tego.


I właściwie na tym etapie można już zakończyć, jednak czasami istnieje konieczność poziomego ułożenia pasków np. gdy opisy na osi X są długie.
12. Teraz więc zajmiemy się obróceniem. Możemy łatwo to osiągnąć modyfikując obiekty TBarSeries (patrz IO), czyli nasze RedBarSeries, BlueBarSeries, YellowBarSeries, a konkretnie chodzi o właściwość AxisIndexX i AxisIndexY. Właściwości te wskazują, która oś odpowiada za współrzędne X i Y. Domyślnie mają wartość -1 None co oznacza "ignoruj transformację osi". Ustaw to wg. następującego kodu:

RedBarSeries.AxisIndexX := 0;
RedBarSeries.AxisIndexY := 1;
BlueBarSeries.AxisIndexX := 0;
BlueBarSeries.AxisIndexY := 1;
YellowBarSeries.AxisIndexX := 0;
YellowBarSeries.AxisIndexY := 1;


Jak zauważyłeś, jeszcze trzeba zamienić opisy osi.

Chart1.LeftAxis.Marks.Source := LabelsChartSource;
Chart1.LeftAxis.Marks.Style := smsLabel;

Chart1.LeftAxis.Marks.Format := %2:s;
Chart1.BottomAxis.Marks.Source := none;
Chart1.BottomAxis.Marks.Style := smsLabel;

Chart1.BottomAxis.Marks.Format := %0:.9s;

Teraz powinno to tak wyglądać:


Zostało jeszcze dostosować siatkę, ustawić odstęp wewnętrzny dla osi X i Y - opisałem to już wyżej (#11), lub w poprzednim wpisie.

Komentarze