Sign in Join
Wiki copytemporary

TAChart Tutorial: Userdefined ChartSource/fi

English · Suomi

Johdanto

Tässä opetusohjelmassa käytämme TAChart-komponentteja piirtämään ikäjakauman maailman väestöstä. Näin tehdessään, opit

Kuten tavallista, oletamme, että olet perehtynyt Lazarukseen ja Object Pascal kieleen ja on joitakin perustietoja TAChart paketista, jotka voit käymällä läpi Aloittajan opetusohjelman.

Esivalmistelu

Data

Saitti [www.census.gov](http://www.census.gov) sisältää runsaasti demografista tietoa. Tämä linkki vie suoraan tietoihin, jotka halutaan piirtää: maailman väestö iän ja sukupuolen mukaan.

Mene sivulle, valitse ja klikkaa "submit" (lähetä). Ikkunan alaosassa, näet taulukon sarakkeet "Age", "Both Sexes Population", "Male population", "Female population" ja "Sex ratio". Valitse hiirellä taulukon tiedot (mukaan lukien otsikko) ja kopioi ne leikepöydälle. Liitä tiedot tekstieditoriin ja tallenna nimellä "population.txt".

Avaa Lazarus ja luo uusi projekti. Ensin meidän täytyy ladata tietoja, jotta ne voidaan piirtää.

Määritellään tietue TPopulationRecord ja taulukko TPopulationArray joka pitää väestön tiedot taulukossa:

pascal
type
  TPopulationRecord = record
    Age: Integer;
    Total: Double;
    Male: Double;
    Female: Double;
    Ratio: Double;
  end;

  TPopulationArray = array of TPopulationRecord;

Seuraavaksi luetaan tiedosto. Tutustu käännösyksikköön (unit) population.pas lähdekoodiin. Löydät sieltä:

pascal
procedure LoadPopulationData(const AFilename:string; var Data:TPopulationArray)

mikä tekee tiedoston lukemisen ja tallentaa datan TPopulationArray taulukkoon.

Sisällytä käännösyksikkö (unit) population uses -lauseen luetteloon jotta päästään käsiksi kyseiseen aliohjelmaan. Lisää muuttuja PopulationData joka on tyypiltään TPopulationArray lomakkeen luokan private osioon. Nyt meillä on kaikki valmiina jotta voidaan kutsua LoadPopulationData lomakkeen OnCreate tapahtumasta. Tämä lukee tiedoston tiedot taulukkoon PopulationData.

pascal
uses
  ..., population;

type
  TForm1 = class(TForm)
  // ...
  private
    PopulationData : TPopulationArray;
  // ...
  end;

const
  POPULATION_FILE = 'population.txt';

procedure TForm1.FormCreate(Sender: TObject);
begin
  LoadPopulationData(POPULATION_FILE, PopulationData); 
end;

Käännä tarkistaaksesi ilmeiset virheet pois. Voit saada ajonaikaisen virheen (runtime error), että tiedosto ei löydy. Tämä johtuu siitä, että tiedosto oletetaan sijaitsevan samassa kansiossa kuin ajettava tiedosto (ns. exe-tiedosto). Voit korjata tämän, joko muuttamalla vakio POPULATION_FILE osoittamaan oikeaan tallennuspaikkaan, tai kopioida "population.txt" on exe-kansioon. Nyt ohjelma pitäisi toimia hyvin, näet vaikka näet vain tyhjän lomakkeen.

Yhdistelmäruutu ComboBox ryhmän valitsimena

Tiedosto sisältää eri ryhmiä: koko, miespuolinen ja naispuolinen väestö sekä mies-nais suhde. Miksi se ei näytä näitä tietoja meidän kaaviossa?

Tehdään tämä pohjaksi

Valmistellaan TChart

Nyt on esivalmistelut tehty. On aika valmistella kaaviota...

Luodaan aluekuvaaja

Tällä kertaa näytetään tiedot alue kuvaajan avulla. Tämän tyyppinen kuvaaja yhdistää datapisteet, kuten viivakuvaaja, mutta myös täyttää alueen tietyllä värillä. On hyvin yleistä näyttää demografiset tiedot, kuten väestöpyramidi , tällä tavalla.

Joten, lisää TAreaSeries (aluekuvaaja) kaavioon. Kuten tavallista, et näe sitä tällä hetkellä, koska se ei ole vielä mitään tietoa. Mutta voimme muokata sen joitakin ominaisuuksia:

Myöhemmin, kun kuvaajalla on dataa, voit tutustua näihin ja muihin ominaisuuksiin paremmin ja nähdä mitä ne tekevät.

Käyttäjän määrittämää dataa kaavioon

Miten saadaan oma data aluekuvaajalle?

Ensimmäinen ajatus ehkä on käydä läpi PopulationData taulukko ja kopioida data kuvaajalle käyttäen AddXY kutsua, näin:

pascal
var
  i: Integer;
begin
  for i:=0 to High(PopulationData) do
    Chart1AreaSeries1.AddXY(PopulationData[i].Age, PopulationData[i].Total);
end;

Haittana tässä lähestymistavassa on, että dataa olisi muistissa kahdesti: PopulationData taulukossa ja kuvaajassa - ei kovin hyvä ratkaisu: se tuhlaisi muistia ja lisäksi olisi sovitettava aina myös toisen varaston dataa kun dataa lisätään, poistetaan tai muokataan.

Tämän välttämiseksi lisäämme TUserDefinedChartSource lomakkeelle, se on kolmas tai neljäs kuvake vasemmalta Chart komponenttipaletilla. Tämä kaavio lähde on tehty liitännäksi kaikenlaisten tietojen varastoinnista, tämä lähde itsessään ei tallenna mitään tietoja. Täytyy vain kirjoittaa tapahtuman käsittelijä OnGetChartDataItem jotta voidaan siirtää tietoja. Tätä tarkoitusta varten, tapahtuma ottaa var parametrin AItem joka on tyyppiä TChartDataItem , joka on määritelty seuraavasti (elementtejä joita ei tarvita tässä on jätetty pois):

pascal
type
  TChartDataItem = object
    X, Y: Double;
    // ...
  end;

Kentät X ja Y ovat datapisteen koordinaatit.

Nyt kirjoitetaan tapahtuman käsittelijä OnGetChartDataItem:

pascal
procedure TForm1.UserDefinedChartSource1GetChartDataItem(
  ASource: TUserDefinedChartSource; AIndex: Integer; var AItem: TChartDataItem);
begin
  AItem.X := PopulationData[AIndex].Age;
  case Combobox1.ItemIndex of
    0: AItem.Y := PopulationData[AIndex].Total;
    1: AItem.Y := PopulationData[AIndex].Male;
    2: AItem.Y := PopulationData[AIndex].Female;
    3: AItem.Y := PopulationData[AIndex].Ratio;
  end;
end;

AIndex on indeksi tietokohtaan joka kysytään. Koska sekä kaavion lähde ja PopulationData-taulukko alkaa indeksistä 0 niin vain luetaan data taulukkoa tällä indeksillä ja vastaavaan elementin data kopioidaan AItem:n . Huomaa, että poimimme y-koordinaatin sen mukaan, mitä on valittu yhdistelmäruutuun (ComboBox). Tällä tavoin UserDefinedChartSource voi tarjota monenlaisia ​​tietoja.

On vielä joitakin tärkeitä asioita tekemättä:

Hienosäätö

Käännä projekti tässä vaiheessa. Ei aivan huono, joitakin puutteita, vaikka.

pascal
procedure TForm1.ComboBox1Select(Sender: TObject);
begin
  Chart1.Invalidate;
end;

Kun käännetään ja ohjelman ajossa valitaan toinen ComboBox kohde niin kuvaaja hienosti päivittyy valittuun luokkaan. Mutta akseli on jäätynyt, se ei muutu automaattisesti. Onko tämä virhe? Ei. Muutama rivi aikaisemmin, sanottiin, että kun kaavio, jossa on UserDefinedChartSource ei toimi odotetulla tavalla on olemassa mahdollisuus, että olet ehkä unohtanut kutsua UserDefinedChartSource.Reset. Siinä se on: korvataan Invalidate Reset-kutsulla . Ja voimme korjata myös toisen puutteen: kuvateksti y-akselin pitäisi muuttua, kun toinen luokka on valittu. Tämä onnistuu kopioimalla yhdistelmäruudusta (ComboBox) valittu teksti kaavion LeftAxis.Title.Caption. Lisätään tämä rivi yhdistelmäruudun (ComboBox) OnSelect tapahtumakäsittelijään:

pascal
procedure TForm1.ComboBox1Select(Sender: TObject);
begin
  Chart1.LeftAxis.Title.Caption := Combobox1.Items[Combobox1.ItemIndex];
  UserDefinedChartSource1.Reset;
end;

Nyt dataluokkien vaihto yhdistelmäruudulla (ComboBox) toimii hyvin.

Huomaa vasen alakulma: Koska y arvot eivät sisällä nollaa y-akselilla niin sen takia automaattinen akselin skaalaus ei ala nollasta. Mutta olisi mukavaa, jos siellä oli nolla. Tätä varten meidän täytyy osittain poistaa automaattinen akselin skaalaus. Chart.Extent määrittelee minimi ja maksimi x- ja y-arvot suorakulmiota varten . Kun asetamme Chart.Extent.UseYMin arvoon true, y-akselin minimiksi on otettu arvo Chart.Extent.YMin ja tämä on nolla oletuksena. Näin saadaan nolla takaisin.

Mutta on toinenkin pieni ongelma nyt: alin ruudukkoviiva piirretään x-akselin päälle. Ensinnäkin, mustat viivat, joita ehkä pidetään akseleina eivät ole akseleita, mutta kuuluvat kehyksen ympärille, ne piirretään kuvaaja alueella oletuksena. Voit testata tämän asettamalla Chart1.Frame.Visible väliaikaisesti arvoon false . Akseleilla on ominaisuus AxisPen jossa "true" kytkee akselin viivan päälle AxisPen.Visible = true. Sen jälkeen kun tekee tämän, x-akseli on edelleen katkoviivalla. Tämä johtuu piirustussekvenssistä. Oletuksena y-akseli, yhdessä sen ruudukko, piirretään x-akselin jälkeen. Jos halutaan saada niin että x-akselin piirretään ensin niin mene objektipuuhun ja vedä x-akseli y-akselin edelle.

Kun tämän jälkeen käännät ja ajat kaavion niin se on täydellinen.

Lähdekoodi

Project file

pascal
program project1;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Interfaces, // this includes the LCL widgetset
  Forms, Unit1, tachartlazaruspkg, population
  { you can add units after this };

{$R *.res}

begin
  RequireDerivedFormResource := True;
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

population.pas

pascal
unit population;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils;

type
  TPopulationRecord = record
    Age: Integer;
    Total: Double;
    Male: Double;
    Female: Double;
    Ratio: Double;
  end;

type
  TPopulationArray = array of TPopulationRecord;

procedure LoadPopulationData(const AFileName: String; var Data: TPopulationArray);

implementation

procedure LoadPopulationData(const AFileName: String; var Data: TPopulationArray);

  function StripThousandSep(const s: String): String;
  var
    i: Integer;
  begin
    Result := s;
    for i:=Length(Result) downto 1 do
      if Result[i] = ',' then
        Delete(Result, i, 1);
  end;

var
  List1, List2: TStringList;
  i, j, n: Integer;
  s: String;
  ds: char;
begin
  ds := FormatSettings.DecimalSeparator;
  List1 := TStringList.Create;
  try
    List1.LoadFromFile(AFileName);
    n := List1.Count;
    SetLength(Data, n-2);
    FormatSettings.DecimalSeparator := '.';
    List2 := TStringList.Create;
    try
      List2.Delimiter := #9;
      List2.StrictDelimiter := true;
      j := 0;
      for i:=2 to n-1 do begin
        List2.DelimitedText := List1[i];
        s := List1[i];
        with Data[j] do begin
          if i < n-1 then
            Age := StrToInt(trim(List2[0]))
          else
            Age := 100;
          Total := StrToFloat(StripThousandSep(trim(List2[1])));
          Male := StrToFloat(StripThousandSep(trim(List2[2])));
          Female := StrToFloat(StripThousandSep(trim(List2[3])));
          Ratio := StrToFloat(trim(List2[4]));
        end;
        inc(j);
      end;
    finally
      List2.Free;
    end;
  finally
    FormatSettings.DecimalSeparator := ds;
    List1.Free;
  end;
end;

end.

Unit1.pas

pascal
unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, TAGraph, TASources, TASeries, Forms, Controls,
  Graphics, Dialogs, ExtCtrls, StdCtrls, population, TACustomSource;

type

  { TForm1 }

  TForm1 = class(TForm)
    Chart1: TChart;
    Chart1AreaSeries1: TAreaSeries;
    ComboBox1: TComboBox;
    Panel1: TPanel;
    UserDefinedChartSource1: TUserDefinedChartSource;
    procedure ComboBox1Select(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure UserDefinedChartSource1GetChartDataItem(
      ASource: TUserDefinedChartSource; AIndex: Integer;
      var AItem: TChartDataItem);
  private
    { private declarations }
    PopulationData: TPopulationArray;
  public
    { public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

const
  POPULATION_FILE = 'population.txt';

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
  LoadPopulationData(POPULATION_FILE, PopulationData);
  UserDefinedChartSource1.PointsNumber := Length(PopulationData);
  Chart1.LeftAxis.Title.Caption := Combobox1.Items[Combobox1.ItemIndex];
end;

procedure TForm1.ComboBox1Select(Sender: TObject);
begin
  Chart1.LeftAxis.Title.Caption := Combobox1.Items[Combobox1.ItemIndex];
  UserDefinedChartSource1.Reset;
end;

procedure TForm1.UserDefinedChartSource1GetChartDataItem(
  ASource: TUserDefinedChartSource; AIndex: Integer; var AItem: TChartDataItem);
begin
  AItem.X := PopulationData[AIndex].Age;
  case Combobox1.ItemIndex of
    0: AItem.Y := PopulationData[AIndex].Total;
    1: AItem.Y := PopulationData[AIndex].Male;
    2: AItem.Y := PopulationData[AIndex].Female;
    3: AItem.Y := PopulationData[AIndex].Ratio;
  end;
end;

end.

Unit1.lfm

lfm
object Form1: TForm1
  Left = 342
  Height = 360
  Top = 128
  Width = 480
  Caption = 'World population'
  ClientHeight = 360
  ClientWidth = 480
  OnCreate = FormCreate
  Position = poScreenCenter
  LCLVersion = '1.1'
  object Panel1: TPanel
    Left = 0
    Height = 36
    Top = 0
    Width = 480
    Align = alTop
    ClientHeight = 36
    ClientWidth = 480
    TabOrder = 0
    object ComboBox1: TComboBox
      Left = 12
      Height = 23
      Top = 5
      Width = 196
      ItemHeight = 15
      ItemIndex = 0
      Items.Strings = (
        'Total population'
        'Male population'
        'Female population'
        'Ratio male/female (%)'
      )
      OnSelect = ComboBox1Select
      Style = csDropDownList
      TabOrder = 0
      Text = 'Total population'
    end
  end
  object Chart1: TChart
    Left = 0
    Height = 324
    Top = 36
    Width = 480
    AxisList = <    
      item
        Grid.Visible = False
        Alignment = calRight
        AxisPen.Visible = True
        Marks.Visible = False
        Minors = <>
      end    
      item
        Grid.Color = clSilver
        Alignment = calBottom
        AxisPen.Visible = True
        Minors = <>
        Title.LabelFont.Style = [fsBold]
        Title.Visible = True
        Title.Caption = 'Age'
      end    
      item
        Grid.Color = clSilver
        AxisPen.Visible = True
        Marks.Format = '%.0n'
        Marks.Style = smsCustom
        Minors = <>
        Title.LabelFont.Orientation = 900
        Title.LabelFont.Style = [fsBold]
        Title.Visible = True
      end>
    BackColor = clWhite
    Extent.UseYMin = True
    Foot.Alignment = taLeftJustify
    Foot.Brush.Color = clBtnFace
    Foot.Font.Color = clBlue
    Foot.Text.Strings = (
      'Source:'
      'http://www.census.gov/population/international/data/worldpop/tool_population.php'
    )
    Foot.Visible = True
    Margins.Left = 0
    Margins.Right = 0
    Margins.Bottom = 0
    Title.Brush.Color = clBtnFace
    Title.Font.Color = clBlue
    Title.Font.Style = [fsBold]
    Title.Text.Strings = (
      'World population'
    )
    Title.Visible = True
    Align = alClient
    ParentColor = False
    object Chart1AreaSeries1: TAreaSeries
      AreaBrush.Color = clSkyBlue
      AreaLinesPen.Style = psClear
      Source = UserDefinedChartSource1
    end
  end
  object UserDefinedChartSource1: TUserDefinedChartSource
    OnGetChartDataItem = UserDefinedChartSource1GetChartDataItem
    left = 552
    top = 152
  end
end

From the Free Pascal wiki, page "TAChart Tutorial: Userdefined ChartSource/fi", as it stood on 2020-02-29 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's Licensing page. If you have concerns about this material, message the admin.

Revision 2, last changed by WikiArchive .

Talk about this page

Nobody has opened a bench for this page yet. Sign in to open one.