TAChart Tutorial: Dual y axis, Legend/fi
│
English (en) │
suomi (fi) │
Johdanto
Kun erilaisia suureita piirretään samaan kaavioon niin tapahtuu melko usein, että niiden suurusluokat ovat eri alueella. "Tavallisessa" kaaviossa kuvaajia hallitsee suuret arvot pienten arvojen kustannuksella jotka "puristuu" tasaiseksi viivaksi. Kaaviolla olisi paljon enemmän merkitystä, jos erilaisia akseleita voidaan käyttää, yhdellä olisi isoja arvoja ja toisella pieniä arvoja.
Kun olet käynyt läpi opetusohjelman käyttäjän määrittämä chartsource olet törmännyt tällaiseen tapaukseen. Siinä opetusohjelmassa tehtiin kaavio maailman väestöstä iän funktiona. Siinä on myös mahdollisuus nähdä miesten ja naisten määrien välinen suhde. Tämä jälkimmäinen luku on huomattavasti pienempi kuin väestön määrä. Joten, jos molemmat tiedot yhdistettäisiin samaan kaavion niin väestö suhde olisi kutistunut vaakasuoraksi viivaksi
Tämä on taustana tähän projektiin. Katsotaan väestön tiedot uudelleen, piirretään väkiluku ja mies-nais suhde samaan kaavioon.
Opit miten
- luoda kaavion, jossa on kaksi y-akselia
- käyttää automaattista skaalausta akselin asteikko muunnoksessa
- tehdään käyttäjän muokkaamia akselin merkintöjä
- tehdään kuvaajien otsikkopalkki (legend) käyttämällä vähemmän tunnettuja ominaisuuksia.
Kuten tavallista niin sinun tulisi tuntea Lazarus ja FPC sekä pitäisi olla perustiedot TAChart kirjastosta (käy ensin läpi TAChart:n aloitus opetusohjelma ). Tässä nimenomaisessa opetusohjelmassa olisi myös hyödyllistä, jos olisit tutkinut opetusohjelman käyttäjän määrittämiä chartsource ensin.
Data
Kuten jo mainittiin käytämme samoja tietoja kuin käyttäjän määrittämä chartsource opetusohjelmassa. Ensisijainen datatiedosto on nimeltään "population.txt" ja peräisin www.census.gov. Kopioi tämä tiedosto käyttäjän määrittämä chartsource opetusohjelma projektin hakemistosta tai seuraa siellä olevia ohjeita miten ladata tiedosto. Käännösyksikkö population.pas
lukee tämän tiedoston ja tallentaa tiedot tietueiden TPopulationRecord
taulukkoon
type
TPopulationRecord = record
Age: Integer;
Total: Double;
Male: Double;
Female: Double;
Ratio: Double;
end;
TPopulationArray = array of TPopulationRecord;
Nyt on kaikki mitä tarvitaan jotta voidaan aloittaa uuden projektin. Lisää käännösyksikkö population.pas
lomakkeen uses luetteloon. Lisää muuttujan PopulationData
tyyppiä TPopulationArray
esittely lomakkeen private
osaan. Ja datatiedoston lukemisen kutsu LoadPopulationData
(joka on population
unit käännösyksikössä) lomakkeen OnCreate
tapahtumaan:
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;
Kaavion valmistelu
Nyt voidaan aloittaa kaavion valmistelu...
Lisää TChart
komponentti lomakkeelle:
- Aseta sen Align ominaisuus arvoon
alClient
. - Aseta sen
BackColor
arvoonclWhite
.
Lisää kolme viivakuvaajaa kaavioon:
- Vaihda niiden nimet kuvailemmiksi
LineSeries_male
,LineSeries_female
jaLineSeries_ratio
. - Aseta
SeriesColor
väri kuvaajallaLineSeries_male
arvoonclSkyBlue
, kuvaajallaLineSeries_female
arvoon$00FF80FF
ja jätäLineSeries_ratio
kuvaajan väri mustaksi.
Mistä kuvaajat saavat tietonsa? Tiedot on tallennettu PopulationData
taulukkoon, joten se olisi parasta hyödyntää käyttäjän määrittämä kaavio lähde . Tarkasti ottaen: tarvitaan kolme kaavion lähdettä, yksi kutakin kuvaajaa kohti.
Siksi lisää kolme TUserDefinedChartSource
-komponenttia lomakkeelle:
- Nimetään ne kuvaammaksi
ChartSource_male
,ChartSource_female
jaChartSource_ratio
. - Aseta kunkin chartsource ominaisuus
Source
vastaamaan oikeata viivakuvaajaa. - Kirjoitetaan seuraava tapahtumankäsittelijä ja liitetään se kunkin user-defined chart source:n
OnGetDataItem
tapahtumaan:
procedure TForm1.ChartSourceGetChartDataItem(
ASource: TUserDefinedChartSource; AIndex: Integer; var AItem: TChartDataItem);
begin
AItem.X := PopulationData[AIndex].Age;
if ASource = ChartSource_male then
AItem.Y := PopulationData[AIndex].Male / 1e6
else if ASource = ChartSource_female then
AItem.Y := PopulationData[AIndex].Female / 1e6
else
AItem.Y := PopulationData[AIndex].Ratio / 100;
end;
Seuraavaksi kerrotaan kaavion datan lähde, josta ottaa datan: x: n arvo on otettu tietueen TPopulationRecord
kentästä Age
, ja riippuen kaavion datalähteestä, y:n arvo otetaan tietueen TPopulationRecord
kentistä Male
, Female
tai Ratio
.
Väkiluku jaetaan miljoonalla jolloin päästään eroon monista nollista. Joten pitää muistaa kertoa että väestöakselilla luvut ovat miljoonia. Myös mies-naissuhde jaetaan 100:lla koska käytetään vain jakojäännöksiä, ei prosentteja.
Onko jo aika kääntää? Ei, ei vielä. Vielä on kerrottava kaavion lähteissä datapisteiden lukumäärä. Tämä tieto saadaan tiedoston lukemisen jälkeen. Aseteaan ominaisuus PointNumber
kunkin kaavion datalähteen taulukon pituuden mukaiseksi lomakkeen OnCreate
tapahtumakäsittelyssä:
procedure TForm1.FormCreate(Sender: TObject);
begin
LoadPopulationData(POPULATION_FILE, PopulationData);
ChartSource_male.PointsNumber := Length(PopulationData);
ChartSource_female.PointsNumber := Length(PopulationData);
ChartSource_ratio.PointsNumber := Length(PopulationData);
end;
Tässä vaiheessa lomake ja objekti puu pitäisi näyttää tältä:
ja kun tuon kääntää ja ajaa niin tuloksen pitäisi näyttää joltain tämäntapaiselta:
Kuten johdannossa mainittiin, mies-nais suhde - joka on musta viiva - on erittäin tasainen. Tämä johtuu arvojen erisuuruidesta: väestön määrä menee melkein nollasta 70:n, kun suhde on lähes aina noin yksi.
Asetetaan toinen y-akseli
Tarvitaan toinen y-akseli.
Tätä varten siirry objektipuuhun, klikkaa hiiren oikealla AxisList
(oikealla Chart1
:n alla ) ja lisää uusi akseli kaavioon. Nyt meillä on kolme akselia:
Valitse akseli #2, joka on vielä vasen akseli tällä hetkellä ja aseta komponenttimuokkaimessa sen Alignment
arvoon calRight
.
Tämä on ehkä hyvä paikka mainita, että kaksi y-akselia ei ole maksimi. Akseleiden määrää voi lisätä kaavion AxisList
kohtaan. Niitä voidaan käyttää pystyakseleina vasemmalle tai oikealle tai vaaka-akseleina kaavion yläreunassa tai alareunassa. Tutustu akselin parametriin Group
: saman ryhmän akselit piirretään samaan suorakulmion, akselien eri ryhmän arvot on piirretty side-by-side.
Miten kaavio tietää, mikä kuvaaja kuuluu mihinkin akseliin? Tätä tarkoitusta varten kullakin kuvaajalla on ominaisuuksia AxisIndexX
ja AxisIndexY
. Koska miesten ja naisten kuvaajat piirretään vasemman akselin mukaan asetetaan AxisIndexY
arvoon 0 - katsomalla objektipuusta nähdään, että tämä indeksi kuuluu vasemmalle akselille. Suhde kuvaaja kuuluu oikealle akselille, sen AxisIndexY
on oltava 2. Voisimme myös asettaa akselin indeksin ala-akselille, mutta tämä ei ole välttämätöntä tässä.
Kun tämä käännetään niin tulos on hieman pettymys: ei muutosta - musta suhde-kuvaaja on edelleen hyvin tasainen.
Akselin asettaminen automaattisesti muuttuvaan mittakaavaan
Syy siihen, miksi toinen y-akseli ei skaalaudu riippumattomasti ensimmäiseen on se, että ohittaa sisäisten koordinaattijärjestelmän. TAChart käyttää kolmea koordinaattijärjestelmää siirrettäessä "reaalimaailman" dataa pikseleihin ruudulle:
- akselin koordinaatit ovat koordinaatit, jossa tiedot tulevat tai toisin sanoen, ne jotka on merkitty akselille. Esimerkiksi tässä projektissa nämä ovat väestön määrä (miljoonalla jaon jälkeen), eli numerot välillä 0 - 70.
- kaavio koordinaatit saadaan sovittamisen jälkeen tehdyistä muutoksista. On olemassa toinen opetusohjelma, jossa käytetään logaritminen muunnosta, tässä tapauksessa kuvaajan koordinaatit olisivat lähtötietoja logaritmille.
- kuva koordinaatit kuuluvat pikseliä ruudulla lasketaan kuvaajan koordinaatteja.
Niin, on olemassa kaksi muunnostoimintoa mukana laskettaessa koordinaattien datapisteet näytölle:
- akselin koordinaatit näytön koordinaatteihin käyttäjän määrittelemän
TChartAxisTransform
avulla - näytön koordinaatit kuvan koordinaateiksi yksinkertaisella lineaarikuvauksen avulla
Jos akselin siirtymistä akselista kaavion koordinaatteihin ei ole määritelty niin käytetään yksinkertaista 1: 1 muunnosta.
Ai niin - käytössä ei ole mitään akselin muunnosta. Siksi väestö ja mies/nais-suhde tiedot ovat edelleen yhteisen maailman koordinaateissa. Jos halutaan erottaa kaksi tietokokonaisuutta meidän soveltaa muunnosta jokaiselle tietokokonaisuudelle, joka kuvaa sitä samaa aluetta kuvaajan koordinaattien vaikkapa 0-1.
Tätä tarkoitusta varten TAChart on TAutoScaleAxisTransform
. Siihen ei ole suoraa pääsyä komponenttipaletilta, mutta sen lapsi löytyy: TChartAxisTransformations
-komponentti.
Joten, vedä ja pudota kaksi TChartAxisTransformations
komponenttia lomakkeelle, yksi vasemmalle akselille, yksi oikealle akselille. Nimeä ne LeftAxisTransformations
ja RightAxisTransformations
, vastaavasti. Kaksoisnapsauta kutakin näistä komponenteista ja valitse "Automaattinen skaalaus" ("auto scale") Muokkaa akselimuunnoksia (axis transformations)-editorista. Objektipuusta, näet kunkin ChartAxisTransformations
komponentin lapsen. Nimeä nämä lapset LeftAxisAutoscaleTransform
ja RightAxisAutoscaleTransform
, vastaavasti. Aseta LeftAxisTransformations
ja RightAxisTransformations
vastaavasti akselien Left (vasemman) ja Right (oikea) ominaisuuteen Transformations
.
Tältä näyttää objektipuu näiden käsittelyiden jälkeen:
Katso AutoScaleAxisTransforms
:n ominaisuudet MaxValue
ja MinValue
. Molemmilla muunnokset ovat samat MaxValue = 1
ja MinValue = 0
. Tämä tarkoittaa, että kukin akseli on sijoitettu välille 0 ja 1, toisin sanoen, molempia aineistoja puristetaan tai laajennetaan täyttämään kaavion alue kokonaan.
Kun käännetään ja ajetaan ohjelma niin silloin nähdään mitä tarkalleen tapahtuu.
Voisimme myös esimerkiksi asettaa RightAxisAutoscaleTransform
ominaisuudet MinValue=1
ja MaxValue=2
. Sitten väestötiedot olisi edelleen kartoitettu akselille 0-1, ja mies-nais suhde data olisi kartoitettu alueelle 1-2, eli meillä olisi paned kaavio, jossa alempi puolisko näyttäisi väestön kuvaajat ja ylempi puoli suhde kuvaajan. Mutta se kuuluu toiseen opetusohjelmaan. Jätämme MinValue
ja MaxValue
ominaisuudet oletusarvoihin.
Hieno viilaus
Vaikein osa on tehty nyt. Jäljellä on vielä kaavion "heino viilaus".
- Poista akseliruudukko (tämä on jo aikaisemmin opetettu). Vasemman ja oikeamman akselin viivat tekevät siitä hyvin sekavan
- Lisää akseleiden otsikot:
- "Väestö" tai "Population" vasemmalle akselille
- "Mies/Nais suhde" tai "Male-to-female ratio" oikealle akselille
- "Ikä (vuosina)" tai "Age (Years)" ala akselille.
- Aseta akselien
LabelFont.Style
arvoonfsBold
.
- Huomaa, että oikean akselin otsikkoa ei kierretty. Akselin
Title.LabelFont
on ominaisuusOrientation
- tämä kierton kulma ilmoitetaan kymmenesosa asteina. Aseta se arvoon 900 niin saadaan sama suunta kuin vasemman akselin otsikolla. - Pakota oikea akseli aloittamaan nollasta asettamalla sen Range.
Range.UseMin
arvoontrue
. Tämä on riittävä, koskaRange.XMin
on 0 oletuksena. Tällä tavalla voit myös valita muita alueita akselille.
Muokataan akselin väliviivojen tekstiä
Nyt pitäisi huolehtia siitä, että tulodata on jaettu 1E6 (eli miljoonalla), jotenkin tämä pitäisi osoittaa.
Olisi mahdollista muuttaa vasemman akselin otsikkoa "Väestö (miljoonaa)". Mutta mennään eri tavalla nyt: liitetään "M" osoittamaan oikeaan suuruusluokkaa ( "M" = "Mega" = "miljoona") asteikkomerkkeihin. On kaksi tapaa saavuttaa näin: ensimmäinen, on käyttää akselin ominaisuutta Marks.Format
, jossa "M" voidaan lisätä (% 0: .9g M '). Tai toisella tavoin voimme hyödyntää TChartAxis:n tapahtuman OnMarkToText
; Tämä tapahtuma mahdollistaa muuttaa asteikkomerkkien tekstiä joka näytetään akselilla. Tässä tapauksessa tehdään seuraavanlainen aliohjelma vasemman akselin tapahtumaan :
procedure TForm1.Chart1AxisList0MarkToText(var AText: String; AMark: Double);
begin
AText := Format('%s M', [AText]);
end;
Asetetaan kuvaajien otsikkopalkki (legend)
Opetusohjelma on melkein valmis nyt. Mitä on jäljellä on kuvaajien otsikkopalkki (legend) - toistaiseksi ei kerrota käyttäjälle mikä käyrä kuuluu mihinkin tietoihin. Joten, mennään Chart1.Legend
ominaisuuteen ja asetetaan Visible
arvoon true
. Nyt nähdään vain lyhyitä viivoja kuvaajien otsikkopalkkissa (legend), ei tekstejä. Tämä johtuu siitä, ettei ole määritelty Title
:ä jokaiselle kuvaajalle. Tehdään tämä nyt, kirjoitetaan otsikot "miehet" ("male"), "naiset" ("female") ja "suhde"("ratio") vastaaviin kuvaajiin.
Koska on kaksi akselia, olisi hienoa saada otsikko "vasen" ("left" )miesten ja naisten kuuvaajien yläpuolelle kuvaajien otsikkopalkkiin ja otsikko "oikea" ("right") suhteen kuvaajan yläpuolella. Tämä vaikutus voidaan saavuttaa käyttämällä ryhmiä. Jokaisella kuvaajalla on ominaisuus Legend
, jota voidaan käyttää ohjaamaan paikkaa kuvaajien otsikkopalkkiin . Ominaisuus GroupIndex
mahdollistaa kuvaajien ryhmien saamisen yhteisen otsikon alle. Joten, aseta Legend.GroupIndex
miesten ja naisten kuvaajat nollaan (0) ja aseta suhteen kuvaaja arvoon yksi (1). Sitten mennään Chart.Legend
ja määrittellään ryhmän otsikot kirjoittamalla tekstiä omaisuutta Chart.Legend.GroupTitles
ominaisuuteen. Kukin rivi vastaa aina kutakin GroupIndex
arvoa. Voidaan myös mennä Chart.Legend.GroupFont
ominaisuuteen ja asettaa fontin lihavoiduksi.
Vielä parannettavaa: entäpä jos kuvaajien otsikkopalkki olisi kaavion alla niin, että kuvaajat jotka kuuluvat vasemmalle akselille olisivat vasemmalla ja ne jotka kuuluvat oikealle akselille olisivat oikealla?
Aluksi asetamme kuvaajien otsikkopalkin Alignment
arvoon laBottomCenter
. Sitten muutamme ColumnCount
arvoon 2. Olemme onnekkaita - kaikki kuvaajien otsikkopalkin merkinnät mahtuvat menevät aivan kuten haluamme. Muuten täytyisi muokata niiden Order
ominaisuutta tai muuttaa omaisuutta ItemFillOrder
tai ehkä ottaa käyttöön (tyhjiä kohteita).
TAChart's kuvaajien otsikkopalkki (legend) on erittäin joustava -- Ole hyvä ja katso sen dokumentaatio saadaksesi lisätietoa siitä.
Lopuksi, lisätään teksti "World population" kaavion otsikkoon ja referenssi tiedot alatunnisteeseen.
Lähdekoodi
Project file
program project1;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
Interfaces, // this includes the LCL widgetset
Forms, Unit1, tachartlazaruspkg
{ you can add units after this };
{$R *.res}
begin
RequireDerivedFormResource := True;
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
Unit1.pas
unit Unit1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, TAGraph, TASeries, TASources, Forms, Controls,
Graphics, Dialogs, population, TACustomSource, TATransformations, TAChartAxisUtils;
type
{ TForm1 }
TForm1 = class(TForm)
Chart1: TChart;
LeftAxisTransformations: TChartAxisTransformations;
LeftAxisAutoScaleTransform: TAutoScaleAxisTransform;
RightAxisTransformations: TChartAxisTransformations;
LineSeries_male: TLineSeries;
LineSeries_female: TLineSeries;
LineSeries_ratio: TLineSeries;
ChartSource_male: TUserDefinedChartSource;
ChartSource_female: TUserDefinedChartSource;
ChartSource_ratio: TUserDefinedChartSource;
RightAxisAutoScaleTransform: TAutoScaleAxisTransform;
procedure Chart1AxisList0MarkToText(var AText: String; AMark: Double);
procedure ChartSourceGetChartDataItem(
ASource: TUserDefinedChartSource; AIndex: Integer;
var AItem: TChartDataItem);
procedure FormCreate(Sender: TObject);
private
{ private declarations }
PopulationData: TPopulationArray;
public
{ public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.lfm}
const
POPULATION_FILE = 'population.txt';
{ TForm1 }
procedure TForm1.ChartSourceGetChartDataItem(
ASource: TUserDefinedChartSource; AIndex: Integer; var AItem: TChartDataItem);
begin
AItem.X := PopulationData[AIndex].Age;
if ASource = ChartSource_male then
AItem.Y := PopulationData[AIndex].Male / 1e6
else if ASource = ChartSource_female then
AItem.Y := PopulationData[AIndex].Female / 1e6
else
AItem.Y := PopulationData[AIndex].Ratio / 100;
end;
procedure TForm1.Chart1AxisList0MarkToText(var AText: String; AMark: Double);
begin
AText := Format('%s M', [AText]);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
LoadPopulationData(POPULATION_FILE, PopulationData);
ChartSource_male.PointsNumber := Length(PopulationData);
ChartSource_female.PointsNumber := Length(PopulationData);
ChartSource_ratio.PointsNumber := Length(PopulationData);
end;
end.
Unit1.lfm
object Form1: TForm1
Left = 365
Height = 409
Top = 169
Width = 470
Caption = 'Form1'
ClientHeight = 409
ClientWidth = 470
OnCreate = FormCreate
LCLVersion = '1.1'
object Chart1: TChart
Left = 0
Height = 409
Top = 0
Width = 470
AxisList = <
item
Grid.Visible = False
Minors = <>
Title.LabelFont.Orientation = 900
Title.LabelFont.Style = [fsBold]
Title.Visible = True
Title.Caption = 'Population'
Transformations = LeftAxisTransformations
OnMarkToText = Chart1AxisList0MarkToText
end
item
Grid.Visible = False
Alignment = calBottom
Minors = <>
Title.LabelFont.Style = [fsBold]
Title.Visible = True
Title.Caption = 'Age (Years)'
end
item
Grid.Visible = False
Alignment = calRight
Minors = <>
Range.UseMin = True
Title.LabelFont.Orientation = 900
Title.LabelFont.Style = [fsBold]
Title.Visible = True
Title.Caption = 'Male-to-female ratio'
Transformations = RightAxisTransformations
end>
BackColor = clWhite
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
Legend.Alignment = laBottomCenter
Legend.ColumnCount = 2
Legend.GroupFont.Style = [fsBold]
Legend.GroupTitles.Strings = (
'left:'
'right:'
)
Legend.Visible = True
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 LineSeries_male: TLineSeries
Legend.GroupIndex = 0
Title = 'male'
AxisIndexY = 0
LinePen.Color = clSkyBlue
Source = ChartSource_male
end
object LineSeries_female: TLineSeries
Legend.GroupIndex = 0
Title = 'female'
AxisIndexY = 0
LinePen.Color = 16744703
Source = ChartSource_female
end
object LineSeries_ratio: TLineSeries
Legend.GroupIndex = 1
Title = 'ratio'
AxisIndexY = 2
Source = ChartSource_ratio
end
end
object ChartSource_male: TUserDefinedChartSource
OnGetChartDataItem = ChartSourceGetChartDataItem
left = 130
top = 25
end
object ChartSource_female: TUserDefinedChartSource
OnGetChartDataItem = ChartSourceGetChartDataItem
left = 130
top = 79
end
object ChartSource_ratio: TUserDefinedChartSource
OnGetChartDataItem = ChartSourceGetChartDataItem
left = 130
top = 137
end
object LeftAxisTransformations: TChartAxisTransformations
left = 267
top = 25
object LeftAxisAutoScaleTransform: TAutoScaleAxisTransform
end
end
object RightAxisTransformations: TChartAxisTransformations
left = 267
top = 136
object RightAxisAutoScaleTransform: TAutoScaleAxisTransform
end
end
end
population.pas
unit population;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils;
type
TPopulationRecord = record
Age: Integer;
Total: Double;
Male: Double;
Female: Double;
Ratio: Double;
end;
TPopulationArray = array of TPopulationRecord;
procedure LoadPopulationData(const AFileName: String; var AData: TPopulationArray);
implementation
procedure LoadPopulationData(const AFileName: String; var AData: TPopulationArray);
function StripThousandSep(const s: String): String;
// Removes the thousand separators from the string
// Otherwise StrToFloat would fail.
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(AData, 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 AData[j] do begin
if i < n-1 then
Age := StrToInt(trim(List2[0]))
else
Age := 100; // the last line is "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.