TAChart Tutorial: Userdefined ChartSource/fi: what changed
Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.
| Old | New | Line | |
|---|---|---|---|
| 1 | 1 | ## Johdanto | |
| 2 | 2 | ||
| 3 | 3 |  | |
| 4 | 4 | ||
| 5 | 5 | Tässä opetusohjelmassa käytämme TAChart-komponentteja piirtämään ikäjakauman maailman väestöstä. Näin tehdessään, opit | |
| 6 | 6 | ||
| 7 | 7 | - miten työskennellä [[TAChart documentation|aluekuvaajan]] kanssa | |
| 8 | 8 | - ja ennen kaikkea, miten soveltaa [[TAChart documentation|käyttäjän määrittämää dataa kaaviossa]] | |
| 9 | 9 | ||
| 10 | 10 | Kuten tavallista, oletamme, että olet perehtynyt Lazarukseen ja Object Pascal kieleen ja on joitakin perustietoja TAChart paketista, jotka voit käymällä läpi [[TAChart Tutorial: Getting started/fi|Aloittajan opetusohjelman]]. | |
| 11 | 11 | ||
| 12 | 12 | ## Esivalmistelu | |
| 13 | 13 | ||
| 14 | 14 | ### Data | |
| 15 | 15 | ||
| 16 | 16 | Saitti [www.census.gov](http://www.census.gov) sisältää runsaasti demografista tietoa. Tämä [linkki](http://www.census.gov/population/international/data/worldpop/tool_population.php) vie suoraan tietoihin, jotka halutaan piirtää: maailman väestö iän ja sukupuolen mukaan. | |
| 17 | 17 | ||
| 18 | 18 | 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". | |
| 19 | 19 | ||
| 20 | 20 | Avaa Lazarus ja luo uusi projekti. Ensin meidän täytyy ladata tietoja, jotta ne voidaan piirtää. | |
| 21 | 21 | ||
| 22 | 22 | Määritellään tietue `TPopulationRecord` ja taulukko `TPopulationArray` joka pitää väestön tiedot taulukossa: | |
| 23 | 23 | ||
| 24 | 24 | ``` pascal | |
| 25 | 25 | type | |
| 26 | 26 | TPopulationRecord = record | |
| 27 | 27 | Age: Integer; | |
| 28 | 28 | Total: Double; | |
| 29 | 29 | Male: Double; | |
| 30 | 30 | Female: Double; | |
| 31 | 31 | Ratio: Double; | |
| 32 | 32 | end; | |
| 33 | 33 | ||
| 34 | 34 | TPopulationArray = array of TPopulationRecord; | |
| 35 | 35 | ``` | |
| 36 | 36 | ||
| 37 | 37 | Seuraavaksi luetaan tiedosto. Tutustu käännösyksikköön (unit) `population.pas` [[TAChart Tutorial: Userdefined ChartSource|lähdekoodiin]]. Löydät sieltä: | |
| 38 | 38 | ||
| 39 | 39 | ``` pascal | |
| 40 | 40 | procedure LoadPopulationData(const AFilename:string; var Data:TPopulationArray) | |
| 41 | 41 | ``` | |
| 42 | 42 | ||
| 43 | 43 | mikä tekee tiedoston lukemisen ja tallentaa datan `TPopulationArray` taulukkoon. | |
| 44 | 44 | ||
| 45 | 45 | 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`. | |
| 46 | 46 | ||
| 47 | 47 | ``` pascal | |
| 48 | 48 | uses | |
| 49 | 49 | ..., population; | |
| 50 | 50 | ||
| 51 | 51 | type | |
| 52 | 52 | TForm1 = class(TForm) | |
| 53 | 53 | // ... | |
| 54 | 54 | private | |
| 55 | 55 | PopulationData : TPopulationArray; | |
| 56 | 56 | // ... | |
| 57 | 57 | end; | |
| 58 | 58 | ||
| 59 | 59 | const | |
| 60 | 60 | POPULATION_FILE = 'population.txt'; | |
| 61 | 61 | ||
| 62 | 62 | procedure TForm1.FormCreate(Sender: TObject); | |
| 63 | 63 | begin | |
| 64 | 64 | LoadPopulationData(POPULATION_FILE, PopulationData); | |
| 65 | 65 | end; | |
| 66 | 66 | ``` | |
| 67 | 67 | ||
| 68 | 68 | 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. | |
| 69 | 69 | ||
| 70 | 70 | ### Yhdistelmäruutu ComboBox ryhmän valitsimena | |
| 71 | 71 | ||
| 72 | 72 | 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? | |
| 73 | 73 | ||
| 74 | 74 | Tehdään tämä pohjaksi | |
| 75 | 75 | ||
| 76 | 76 | - Lisätään paneeli (TPanel-komponentti) lomakkeelle. Asetetaan sen align arvoon `alTop` ja poistetaan siinä oleva teksti. | |
| 77 | 77 | - Lisätään ComboBox tähän paneelin. Asetetaan sen `Style` arvoon `csDropdownList` , lisätään tekstit "Kokonaisväkiluku", "Miesväestö", "Naisväestö" ja "Suhde mies / nainen (%)" `Items` omaisuuteen sekä asetetaan `ItemIndex` arvoon 0. | |
| 78 | 78 | ||
| 79 | 79 | ### Valmistellaan TChart | |
| 80 | 80 | ||
| 81 | 81 | Nyt on esivalmistelut tehty. On aika valmistella kaaviota... | |
| 82 | 82 | ||
| 83 | 83 | - Lisää `TChart` komponentti lomakkeelle, aseta sen align arvoon `alClient`. | |
| 84 | 84 | - Aseta kaavion `BackColor` arvoon `clWhite` ja kummankin akselin `Grid.Color` arvoon `clSilver`. | |
| 85 | 85 | - Laita kummankin akselin otsikot näkyviin asettamalla `LeftAxis.Visible` ja `BottomAxis.Visible` arvoon `true`. | |
| 86 | 86 | - Kirjoita bottom akselin `Caption` ominaisuuteen joko "Age" tai "Ikä" ja left akselille "Total population" (tai jätä tyhjäksi - myöhemmin se korvataan kyseisen suorituksen mukaisen valinnan mukaan mikä on ComboBox:ssa valittuna ) | |
| 87 | 87 | - Kirjoita teksti "World population" tai "Maailman väestö" kaavion otsikoksi. | |
| 88 | 88 | - Laita näiden fontin tyylit arvoon `fsBold`. | |
| 89 | 89 | - Ehkä olisi hyvä idea näyttää viittaus mistä tiedot ovat peräisin. [[TAChart documentation|Footer]] - eli kaavion ominaisuus `Foot` voidaan käyttää tähän. Huomaa, että omaisuuteen Foot.Text (samoin kuin Title.Text ) voidaan merkkijonojen muokkaimen avulla kirjoittaa moni rivisiä otsikoita. | |
| 90 | 90 | ||
| 91 | 91 |  | |
| 92 | 92 | ||
| 93 | 93 | ## Luodaan aluekuvaaja | |
| 94 | 94 | ||
| 95 | 95 | Tällä kertaa näytetään tiedot [alue kuvaajan](https://en.wikipedia.org/wiki/Area_chart) 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](https://en.wikipedia.org/wiki/Population_pyramid) , tällä tavalla. | |
| 96 | 96 | ||
| 97 | 97 | Joten, lisää [[TAChart documentation|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: | |
| 98 | 98 | ||
| 99 | 99 | - Aseta sen `SeriesColor` arvoon `clSkyBlue`. Tämä on aluekuvaajan täyttöväri. | |
| 100 | 100 | - Piilota pystysuorat viivat asettamalla `AreaLinesPen` :n `Style` arvoon `psClear`. | |
| 101 | 101 | - Jos haluat, voit myös vaihtaa `ConnectType` arvoon `ctStepXY` tai `ctStepYX` saadaksesi askel efektin. | |
| 102 | 102 | ||
| 103 | 103 | Myöhemmin, kun kuvaajalla on dataa, voit tutustua näihin ja muihin ominaisuuksiin paremmin ja nähdä mitä ne tekevät. | |
| 104 | 104 | ||
| 105 | 105 | ## Käyttäjän määrittämää dataa kaavioon | |
| 106 | 106 | ||
| 107 | 107 | Miten saadaan oma data aluekuvaajalle? | |
| 108 | 108 | ||
| 109 | 109 | Ensimmäinen ajatus ehkä on käydä läpi `PopulationData` taulukko ja kopioida data kuvaajalle käyttäen `AddXY` kutsua, näin: | |
| 110 | 110 | ||
| 111 | 111 | ``` pascal | |
| 112 | 112 | var | |
| 113 | 113 | i: Integer; | |
| 114 | 114 | begin | |
| 115 | 115 | for i:=0 to High(PopulationData) do | |
| 116 | 116 | Chart1AreaSeries1.AddXY(PopulationData[i].Age, PopulationData[i].Total); | |
| 117 | 117 | end; | |
| 118 | 118 | ``` | |
| 119 | 119 | ||
| 120 | 120 | 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. | |
| 121 | 121 | ||
| 122 | 122 |  | |
| 123 | 123 | ||
| 124 | 124 | Tämän välttämiseksi lisäämme [[TAChart documentation|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): | |
| 125 | 125 | ||
| 126 | 126 | ``` pascal | |
| 127 | 127 | type | |
| 128 | 128 | TChartDataItem = object | |
| 129 | 129 | X, Y: Double; | |
| 130 | 130 | // ... | |
| 131 | 131 | end; | |
| 132 | 132 | ``` | |
| 133 | 133 | ||
| 134 | 134 | Kentät `X` ja `Y` ovat datapisteen koordinaatit. | |
| 135 | 135 | ||
| 136 | 136 | Nyt kirjoitetaan tapahtuman käsittelijä `OnGetChartDataItem`: | |
| 137 | 137 | ||
| 138 | 138 | ``` pascal | |
| 139 | 139 | procedure TForm1.UserDefinedChartSource1GetChartDataItem( | |
| 140 | 140 | ASource: TUserDefinedChartSource; AIndex: Integer; var AItem: TChartDataItem); | |
| 141 | 141 | begin | |
| 142 | 142 | AItem.X := PopulationData[AIndex].Age; | |
| 143 | 143 | case Combobox1.ItemIndex of | |
| 144 | 144 | 0: AItem.Y := PopulationData[AIndex].Total; | |
| 145 | 145 | 1: AItem.Y := PopulationData[AIndex].Male; | |
| 146 | 146 | 2: AItem.Y := PopulationData[AIndex].Female; | |
| 147 | 147 | 3: AItem.Y := PopulationData[AIndex].Ratio; | |
| 148 | 148 | end; | |
| 149 | 149 | end; | |
| 150 | 150 | ``` | |
| 151 | 151 | ||
| 152 | 152 | `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. | |
| 153 | 153 | ||
| 154 | 154 | On vielä joitakin tärkeitä asioita tekemättä: | |
| 155 | 155 | ||
| 156 | 156 | - Asettaa että kuvaaja käyttää UserDefinedChartSource:a (sisäänrakennetun lähteen sijaan). Tätä varten laitetaan kuvaajan ominaisuus `Source` osoittamaan `UserDefinedChartSource1` . | |
| 157 | 157 | - Kertoa UserDefinedChartSource:lle kuinka monta datapistettä ulkoisten tietojen taulukko sisältää. Datamäärä syötetään UserDefinedChartSource:n ominaisuuteen `PointsNumber` . Datamäärää tunnetaan sen jälkeen kun procedure `LoadPopulationData`on päättynyt. Niinpä lisätään rivi, joka asettaa `PointsNumber` oikein `FormCreate` metodiin sen jälkeen kun aliohjelmaa `LoadPopulationData` on kutsuttu. | |
| 158 | 158 | - Koska UserDefinedChartSource ei tiedä mitään rakennetta ulkoisista tiedoista niin täytyy ehdottomasti ilmoittaa aina, kun tiedot ovat käytettävissä tai on muutettu. Teemme tämän kutsumalla `UserDefinedChartSource1.Reset`:ä sopivasta paikasta. Joskus olet onnekas että joku muu tapa saattanut tehdä tämän jo. Mutta pitää muistaa, että aina kun kaavio, jossa on UserDefinedChartSource ja se ei käyttäydy odotetulla tavalla on olemassa mahdollisuus, että olet ehkä unohtanut kutsua `Reset`. Testaa, mitä tapahtuu jos "unohtaa" `Reset` kutsun. | |
| 159 | 159 | ||
| 160 | 160 | ## Hienosäätö | |
| 161 | 161 | ||
| 162 | 162 |  | |
| 163 | 163 | ||
| 164 | 164 | Käännä projekti tässä vaiheessa. Ei aivan huono, joitakin puutteita, vaikka. | |
| 165 | 165 | ||
| 166 | 166 | - Saattaa olla että y-akselin merkinnnät ovat outoja. Miksi he hypätä 80000000, 1 ja sitten 12? Tämä johtuu virheestä joka oli jossain vanhoissa FPC:n versioissa jossa `Format` toiminto ei toiminut oikein suurilla numeroilla. Jos sinulla tämä virhe, vaihda omaisuutta `LeftAxis.Marks.Format`. Tämä merkkijono välitetään `Format` funktiolle joka muuntaa numerot merkkijonoksi. Format tarkenteella "% .0n" vältetään muunnosvirhe ja lisäksi se lisää mukavan tuhaterottimen merkintöihin mikä tekee niistä paljon luettavaa. Huomaa kuitenkin, että "0" katkaisee desimaalit. Siksi tätä format tarkennetta kannattaa käyttää vain suurilla numeroilla. | |
| 167 | 167 | - On pieni väli y-akselin ja täytetty alueen kuvaajan välissä. Samoin on täytetty alue ulottuu vähän x-akselin alle. Tämä johtuu kaavion `Margins` ominaisuudesta. Tämä ominaisuus on yleensä varsin hyödyllistä luoda tyhjän alueella lähellä piste akselilla, vapaa päällekkäisiä tunnuksista ja merkinnöistä. Mutta nyt se on epäedullinen. Joten asettaa `Chart.Margins.Left` ja `Chart.Margins.Bottom` arvoon 0. | |
| 168 | 168 | - Ah niin yhdistelmäruutu (ComboBox) ei toimi vielä. Sille tarvitsee antaa toiminta sen `OnSelect` tapahtumaan. Mitä sillä valitaan? No, kun toinen ComboBox kohde valitaan niin UserDefinedChartSource raportoi sen y-arvon kuvaajalle - sitähän halutaan. Mutta miten saamme `OnGetChartDataItem` kutsuttua? Vain kaavion uudelleen piirtämistä! Kun kuvaaja piirtyy uudelleen se saa aina kyselyn tiedot kaaviosta lähteestä, joten sillä on päivitetyt tiedot automaattisesti. OK - kutsukaamme `Chart1.Invalidate` : | |
| 169 | 169 | ||
| 170 | 170 | ``` pascal | |
| 171 | 171 | procedure TForm1.ComboBox1Select(Sender: TObject); | |
| 172 | 172 | begin | |
| 173 | 173 | Chart1.Invalidate; | |
| 174 | 174 | end; | |
| 175 | 175 | ``` | |
| 176 | 176 | ||
| 177 | 177 | 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: | |
| 178 | 178 | ||
| 179 | 179 | ``` pascal | |
| 180 | 180 | procedure TForm1.ComboBox1Select(Sender: TObject); | |
| 181 | 181 | begin | |
| 182 | 182 | Chart1.LeftAxis.Title.Caption := Combobox1.Items[Combobox1.ItemIndex]; | |
| 183 | 183 | UserDefinedChartSource1.Reset; | |
| 184 | 184 | end; | |
| 185 | 185 | ``` | |
| 186 | 186 | ||
| 187 | 187 | Nyt dataluokkien vaihto yhdistelmäruudulla (ComboBox) toimii hyvin. | |
| 188 | 188 | ||
| 189 | 189 | 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. | |
| 190 | 190 | ||
| 191 | 191 |  | |
| 192 | 192 | ||
| 193 | 193 | 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. | |
| 194 | 194 | ||
| 195 | 195 | Kun tämän jälkeen käännät ja ajat kaavion niin se on täydellinen. | |
| 196 | 196 | ||
| 197 | 197 |  | |
| 198 | 198 | ||
| 199 | 199 |  | |
| 200 | 200 | ||
| 201 | 201 | ## Lähdekoodi | |
| 202 | 202 | ||
| 203 | 203 | ### Project file | |
| 204 | 204 | ||
| 205 | 205 | ``` pascal | |
| 206 | 206 | program project1; | |
| 207 | 207 | ||
| 208 | 208 | {$mode objfpc}{$H+} | |
| 209 | 209 | ||
| 210 | 210 | uses | |
| 211 | 211 | {$IFDEF UNIX}{$IFDEF UseCThreads} | |
| 212 | 212 | cthreads, | |
| 213 | 213 | {$ENDIF}{$ENDIF} | |
| 214 | 214 | Interfaces, // this includes the LCL widgetset | |
| 215 | 215 | Forms, Unit1, tachartlazaruspkg, population | |
| 216 | 216 | { you can add units after this }; | |
| 217 | 217 | ||
| 218 | 218 | {$R *.res} | |
| 219 | 219 | ||
| 220 | 220 | begin | |
| 221 | 221 | RequireDerivedFormResource := True; | |
| 222 | 222 | Application.Initialize; | |
| 223 | 223 | Application.CreateForm(TForm1, Form1); | |
| 224 | 224 | Application.Run; | |
| 225 | 225 | end. | |
| 226 | 226 | ``` | |
| 227 | 227 | ||
| 228 | 228 | ### population.pas | |
| 229 | 229 | ||
| 230 | 230 | ``` pascal | |
| 231 | 231 | unit population; | |
| 232 | 232 | ||
| 233 | 233 | {$mode objfpc}{$H+} | |
| 234 | 234 | ||
| 235 | 235 | interface | |
| 236 | 236 | ||
| 237 | 237 | uses | |
| 238 | 238 | Classes, SysUtils; | |
| 239 | 239 | ||
| 240 | 240 | type | |
| 241 | 241 | TPopulationRecord = record | |
| 242 | 242 | Age: Integer; | |
| 243 | 243 | Total: Double; | |
| 244 | 244 | Male: Double; | |
| 245 | 245 | Female: Double; | |
| 246 | 246 | Ratio: Double; | |
| 247 | 247 | end; | |
| 248 | 248 | ||
| 249 | 249 | type | |
| 250 | 250 | TPopulationArray = array of TPopulationRecord; | |
| 251 | 251 | ||
| 252 | 252 | procedure LoadPopulationData(const AFileName: String; var Data: TPopulationArray); | |
| 253 | 253 | ||
| 254 | 254 | implementation | |
| 255 | 255 | ||
| 256 | 256 | procedure LoadPopulationData(const AFileName: String; var Data: TPopulationArray); | |
| 257 | 257 | ||
| 258 | 258 | function StripThousandSep(const s: String): String; | |
| 259 | 259 | var | |
| 260 | 260 | i: Integer; | |
| 261 | 261 | begin | |
| 262 | 262 | Result := s; | |
| 263 | 263 | for i:=Length(Result) downto 1 do | |
| 264 | 264 | if Result[i] = ',' then | |
| 265 | 265 | Delete(Result, i, 1); | |
| 266 | 266 | end; | |
| 267 | 267 | ||
| 268 | 268 | var | |
| 269 | 269 | List1, List2: TStringList; | |
| 270 | 270 | i, j, n: Integer; | |
| 271 | 271 | s: String; | |
| 272 | 272 | ds: char; | |
| 273 | 273 | begin | |
| 274 | 274 | ds := FormatSettings.DecimalSeparator; | |
| 275 | 275 | List1 := TStringList.Create; | |
| 276 | 276 | try | |
| 277 | 277 | List1.LoadFromFile(AFileName); | |
| 278 | 278 | n := List1.Count; | |
| 279 | 279 | SetLength(Data, n-2); | |
| 280 | 280 | FormatSettings.DecimalSeparator := '.'; | |
| 281 | 281 | List2 := TStringList.Create; | |
| 282 | 282 | try | |
| 283 | 283 | List2.Delimiter := #9; | |
| 284 | 284 | List2.StrictDelimiter := true; | |
| 285 | 285 | j := 0; | |
| 286 | 286 | for i:=2 to n-1 do begin | |
| 287 | 287 | List2.DelimitedText := List1[i]; | |
| 288 | 288 | s := List1[i]; | |
| 289 | 289 | with Data[j] do begin | |
| 290 | 290 | if i < n-1 then | |
| 291 | 291 | Age := StrToInt(trim(List2[0])) | |
| 292 | 292 | else | |
| 293 | 293 | Age := 100; | |
| 294 | 294 | Total := StrToFloat(StripThousandSep(trim(List2[1]))); | |
| 295 | 295 | Male := StrToFloat(StripThousandSep(trim(List2[2]))); | |
| 296 | 296 | Female := StrToFloat(StripThousandSep(trim(List2[3]))); | |
| 297 | 297 | Ratio := StrToFloat(trim(List2[4])); | |
| 298 | 298 | end; | |
| 299 | 299 | inc(j); | |
| 300 | 300 | end; | |
| 301 | 301 | finally | |
| 302 | 302 | List2.Free; | |
| 303 | 303 | end; | |
| 304 | 304 | finally | |
| 305 | 305 | FormatSettings.DecimalSeparator := ds; | |
| 306 | 306 | List1.Free; | |
| 307 | 307 | end; | |
| 308 | 308 | end; | |
| 309 | 309 | ||
| 310 | 310 | end. | |
| 311 | 311 | ``` | |
| 312 | 312 | ||
| 313 | 313 | ### Unit1.pas | |
| 314 | 314 | ||
| 315 | 315 | ``` pascal | |
| 316 | 316 | unit Unit1; | |
| 317 | 317 | ||
| 318 | 318 | {$mode objfpc}{$H+} | |
| 319 | 319 | ||
| 320 | 320 | interface | |
| 321 | 321 | ||
| 322 | 322 | uses | |
| 323 | 323 | Classes, SysUtils, FileUtil, TAGraph, TASources, TASeries, Forms, Controls, | |
| 324 | 324 | Graphics, Dialogs, ExtCtrls, StdCtrls, population, TACustomSource; | |
| 325 | 325 | ||
| 326 | 326 | type | |
| 327 | 327 | ||
| 328 | 328 | { TForm1 } | |
| 329 | 329 | ||
| 330 | 330 | TForm1 = class(TForm) | |
| 331 | 331 | Chart1: TChart; | |
| 332 | 332 | Chart1AreaSeries1: TAreaSeries; | |
| 333 | 333 | ComboBox1: TComboBox; | |
| 334 | 334 | Panel1: TPanel; | |
| 335 | 335 | UserDefinedChartSource1: TUserDefinedChartSource; | |
| 336 | 336 | procedure ComboBox1Select(Sender: TObject); | |
| 337 | 337 | procedure FormCreate(Sender: TObject); | |
| 338 | 338 | procedure UserDefinedChartSource1GetChartDataItem( | |
| 339 | 339 | ASource: TUserDefinedChartSource; AIndex: Integer; | |
| 340 | 340 | var AItem: TChartDataItem); | |
| 341 | 341 | private | |
| 342 | 342 | { private declarations } | |
| 343 | 343 | PopulationData: TPopulationArray; | |
| 344 | 344 | public | |
| 345 | 345 | { public declarations } | |
| 346 | 346 | end; | |
| 347 | 347 | ||
| 348 | 348 | var | |
| 349 | 349 | Form1: TForm1; | |
| 350 | 350 | ||
| 351 | 351 | implementation | |
| 352 | 352 | ||
| 353 | 353 | {$R *.lfm} | |
| 354 | 354 | ||
| 355 | 355 | const | |
| 356 | 356 | POPULATION_FILE = 'population.txt'; | |
| 357 | 357 | ||
| 358 | 358 | { TForm1 } | |
| 359 | 359 | ||
| 360 | 360 | procedure TForm1.FormCreate(Sender: TObject); | |
| 361 | 361 | begin | |
| 362 | 362 | LoadPopulationData(POPULATION_FILE, PopulationData); | |
| 363 | 363 | UserDefinedChartSource1.PointsNumber := Length(PopulationData); | |
| 364 | 364 | Chart1.LeftAxis.Title.Caption := Combobox1.Items[Combobox1.ItemIndex]; | |
| 365 | 365 | end; | |
| 366 | 366 | ||
| 367 | 367 | procedure TForm1.ComboBox1Select(Sender: TObject); | |
| 368 | 368 | begin | |
| 369 | 369 | Chart1.LeftAxis.Title.Caption := Combobox1.Items[Combobox1.ItemIndex]; | |
| 370 | 370 | UserDefinedChartSource1.Reset; | |
| 371 | 371 | end; | |
| 372 | 372 | ||
| 373 | 373 | procedure TForm1.UserDefinedChartSource1GetChartDataItem( | |
| 374 | 374 | ASource: TUserDefinedChartSource; AIndex: Integer; var AItem: TChartDataItem); | |
| 375 | 375 | begin | |
| 376 | 376 | AItem.X := PopulationData[AIndex].Age; | |
| 377 | 377 | case Combobox1.ItemIndex of | |
| 378 | 378 | 0: AItem.Y := PopulationData[AIndex].Total; | |
| 379 | 379 | 1: AItem.Y := PopulationData[AIndex].Male; | |
| 380 | 380 | 2: AItem.Y := PopulationData[AIndex].Female; | |
| 381 | 381 | 3: AItem.Y := PopulationData[AIndex].Ratio; | |
| 382 | 382 | end; | |
| 383 | 383 | end; | |
| 384 | 384 | ||
| 385 | 385 | end. | |
| 386 | 386 | ``` | |
| 387 | 387 | ||
| 388 | 388 | ### Unit1.lfm | |
| 389 | 389 | ||
| 390 | - | ||
| 390 | + | ``` lfm | |
| 391 | 391 | object Form1: TForm1 | |
| 392 | 392 | Left = 342 | |
| 393 | 393 | Height = 360 | |
| 394 | 394 | Top = 128 | |
| 395 | 395 | Width = 480 | |
| 396 | 396 | Caption = 'World population' | |
| 397 | 397 | ClientHeight = 360 | |
| 398 | 398 | ClientWidth = 480 | |
| 399 | 399 | OnCreate = FormCreate | |
| 400 | 400 | Position = poScreenCenter | |
| 401 | 401 | LCLVersion = '1.1' | |
| 402 | 402 | object Panel1: TPanel | |
| 403 | 403 | Left = 0 | |
| 404 | 404 | Height = 36 | |
| 405 | 405 | Top = 0 | |
| 406 | 406 | Width = 480 | |
| 407 | 407 | Align = alTop | |
| 408 | 408 | ClientHeight = 36 | |
| 409 | 409 | ClientWidth = 480 | |
| 410 | 410 | TabOrder = 0 | |
| 411 | 411 | object ComboBox1: TComboBox | |
| 412 | 412 | Left = 12 | |
| 413 | 413 | Height = 23 | |
| 414 | 414 | Top = 5 | |
| 415 | 415 | Width = 196 | |
| 416 | 416 | ItemHeight = 15 | |
| 417 | 417 | ItemIndex = 0 | |
| 418 | 418 | Items.Strings = ( | |
| 419 | 419 | 'Total population' | |
| 420 | 420 | 'Male population' | |
| 421 | 421 | 'Female population' | |
| 422 | 422 | 'Ratio male/female (%)' | |
| 423 | 423 | ) | |
| 424 | 424 | OnSelect = ComboBox1Select | |
| 425 | 425 | Style = csDropDownList | |
| 426 | 426 | TabOrder = 0 | |
| 427 | 427 | Text = 'Total population' | |
| 428 | 428 | end | |
| 429 | 429 | end | |
| 430 | 430 | object Chart1: TChart | |
| 431 | 431 | Left = 0 | |
| 432 | 432 | Height = 324 | |
| 433 | 433 | Top = 36 | |
| 434 | 434 | Width = 480 | |
| 435 | 435 | AxisList = < | |
| 436 | 436 | item | |
| 437 | 437 | Grid.Visible = False | |
| 438 | 438 | Alignment = calRight | |
| 439 | 439 | AxisPen.Visible = True | |
| 440 | 440 | Marks.Visible = False | |
| 441 | 441 | Minors = <> | |
| 442 | 442 | end | |
| 443 | 443 | item | |
| 444 | 444 | Grid.Color = clSilver | |
| 445 | 445 | Alignment = calBottom | |
| 446 | 446 | AxisPen.Visible = True | |
| 447 | 447 | Minors = <> | |
| 448 | 448 | Title.LabelFont.Style = [fsBold] | |
| 449 | 449 | Title.Visible = True | |
| 450 | 450 | Title.Caption = 'Age' | |
| 451 | 451 | end | |
| 452 | 452 | item | |
| 453 | 453 | Grid.Color = clSilver | |
| 454 | 454 | AxisPen.Visible = True | |
| 455 | 455 | Marks.Format = '%.0n' | |
| 456 | 456 | Marks.Style = smsCustom | |
| 457 | 457 | Minors = <> | |
| 458 | 458 | Title.LabelFont.Orientation = 900 | |
| 459 | 459 | Title.LabelFont.Style = [fsBold] | |
| 460 | 460 | Title.Visible = True | |
| 461 | 461 | end> | |
| 462 | 462 | BackColor = clWhite | |
| 463 | 463 | Extent.UseYMin = True | |
| 464 | 464 | Foot.Alignment = taLeftJustify | |
| 465 | 465 | Foot.Brush.Color = clBtnFace | |
| 466 | 466 | Foot.Font.Color = clBlue | |
| 467 | 467 | Foot.Text.Strings = ( | |
| 468 | 468 | 'Source:' | |
| 469 | 469 | 'http://www.census.gov/population/international/data/worldpop/tool_population.php' | |
| 470 | 470 | ) | |
| 471 | 471 | Foot.Visible = True | |
| 472 | 472 | Margins.Left = 0 | |
| 473 | 473 | Margins.Right = 0 | |
| 474 | 474 | Margins.Bottom = 0 | |
| 475 | 475 | Title.Brush.Color = clBtnFace | |
| 476 | 476 | Title.Font.Color = clBlue | |
| 477 | 477 | Title.Font.Style = [fsBold] | |
| 478 | 478 | Title.Text.Strings = ( | |
| 479 | 479 | 'World population' | |
| 480 | 480 | ) | |
| 481 | 481 | Title.Visible = True | |
| 482 | 482 | Align = alClient | |
| 483 | 483 | ParentColor = False | |
| 484 | 484 | object Chart1AreaSeries1: TAreaSeries | |
| 485 | 485 | AreaBrush.Color = clSkyBlue | |
| 486 | 486 | AreaLinesPen.Style = psClear | |
| 487 | 487 | Source = UserDefinedChartSource1 | |
| 488 | 488 | end | |
| 489 | 489 | end | |
| 490 | 490 | object UserDefinedChartSource1: TUserDefinedChartSource | |
| 491 | 491 | OnGetChartDataItem = UserDefinedChartSource1GetChartDataItem | |
| 492 | 492 | left = 552 | |
| 493 | 493 | top = 152 | |
| 494 | 494 | end | |
| 495 | 495 | end | |
| 496 | 496 | ``` | |
| 497 | 497 | ||
| 498 | 498 | --- | |
| 499 | 499 | *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](/wiki-contact).* |
