Sign in Join
Wiki copytemporary

TAChart Tutorial: Dual y axis, Legend: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11## Introduction
22
33![](/wiki-images/4/47/TAChart_DualYAxis11.png)
44
55When different quantities are plotted into the same chart it happens quite often that they cover a largely different range. In an "ordinary" chart, the series with the large values dominates over the series with the small values which is compressed to a flat line. The chart would be much more meaningful if different axes could be used, one for the large values, and one for the small values.
66
77When you worked through the tutorial on [[TAChart Tutorial: Userdefined ChartSource|user-defined chartsource]] you will have come across such a case. In that tutorial we created a diagram of the world population as a function of age. There was also an option to draw the ratio of male-to-female population. This latter number is considerably smaller than the population count. So, if both data were combined in the same chart, the population ratio would shrink to a horizontal line.
88
99This is the background for today's project. We will go to the population data again and draw population count and the male-to-female ratio into the same chart.
1010
1111You will learn how to
1212
1313- create a chart with **two y axes**
1414- work with **auto-scale axis transforms**
1515- use **user-defined axis labels**
1616- **tweak the legend** using less-known properties.
1717
1818This is our "disclaimer", as usual: You should be familiar with Lazarus and FPC, and you should have a basic understanding of the [[TAChart]] library (go through [[TAChart Tutorial: Getting started|Getting started]] tutorial if you don't). In this particular tutorial, it would also be helpful if you had studied the tutorial on [[TAChart Tutorial: Userdefined ChartSource|user-defined chartsource]] first.
1919
2020## Data
2121
2222As already mentioned we will be using the same data as in the [[TAChart Tutorial: Userdefined ChartSource|user-defined chartsource]] tutorial. The primary data file is called "`population.txt`" and originates from [www.census.gov](http://www.census.gov/population/international/data/worldpop/tool_population.php). Copy this file from the [[TAChart Tutorial: Userdefined ChartSource|user-defined chartsource tutorial]] to your project output directory, or follow the instructions [[TAChart Tutorial: Userdefined ChartSource|there]] on how to download the file. [[Unit]] population.pas reads this file and stores the data in an [[Array|array]] of `TPopulationRecord`s:
2323
2424``` pascal
2525type
2626 TPopulationRecord = record
2727 Age: Integer;
2828 Total: Double;
2929 Male: Double;
3030 Female: Double;
3131 Ratio: Double;
3232 end;
3333
3434 TPopulationArray = array of TPopulationRecord;
3535```
3636
3737Now, we have all that we need to start a new project. Add the unit population.pas to the form's [[Uses|uses]] list. Add a [[Variable|variable]] `PopulationData` of type `TPopulationArray` to the [[Private|private]] section of the form. And read the data file by calling the [[Procedure|procedure]] `LoadPopulationData` (in the population unit) from the form's `OnCreate` event handler:
3838
3939``` pascal
4040uses
4141 ..., population, ...;
4242
4343type
4444 TForm1 = class(TForm)
4545 // ...
4646 private
4747 PopulationData : TPopulationArray;
4848 // ...
4949 end;
5050
5151const
5252 POPULATION_FILE = 'population.txt';
5353
5454procedure TForm1.FormCreate(Sender: TObject);
5555begin
5656 LoadPopulationData(POPULATION_FILE, PopulationData);
5757end;
5858```
5959
6060## Preparation of the chart
6161
6262Now we can begin charting...
6363
6464Add a **[[TChart]]** component to the form:
6565
6666- Align it as `alClient`.
6767- Set its `BackColor` to `clWhite`.
6868
6969Add **three line series** to the chart:
7070
7171- To be more descriptive, change their names to `LineSeries_male`, `LineSeries_female`, and `LineSeries_ratio`.
7272- Set the `SeriesColor` of the *male* series to a "boyish" `clSkyBlue`, that of the *female* series to a "girlish" `$00FF80FF`, and leave the color of the *ratio* series at black.
7373
7474Where do the series get their data from? Our data is stored in the `PopulationData` array, so it would be best to take advantage of a [[TAChart documentation|user-defined chart source]]. To be exact: we need three chart sources, one for each series.
7575
7676Therefore, let's add three `TUserDefinedChartSource`s to the form:
7777
7878- Rename them as `ChartSource_male`, `ChartSource_female`, and `ChartSource_ratio`.
7979- Assign each chartsource to the [[Property|property]] `Source` of the corresponding line series.
8080- Write the following event handler and assign it to the `OnGetDataItem` event of each user-defined chart source:
8181
8282``` pascal
8383procedure TForm1.ChartSourceGetChartDataItem(
8484 ASource: TUserDefinedChartSource; AIndex: Integer; var AItem: TChartDataItem);
8585begin
8686 AItem.X := PopulationData[AIndex].Age;
8787 if ASource = ChartSource_male then
8888 AItem.Y := PopulationData[AIndex].Male / 1e6
8989 else if ASource = ChartSource_female then
9090 AItem.Y := PopulationData[AIndex].Female / 1e6
9191 else
9292 AItem.Y := PopulationData[AIndex].Ratio / 100;
9393end;
9494```
9595
9696Here we tell the chart source where it finds the data: the x value is taken from the `Age` field of the `TPopulationRecord`, and, depending on the chart source, the y value is taken from the `Male`, `Female` or `Ratio` fields of the `TPopulationRecord`. We divide the population count by 1 million to get rid of many zeros. So, don't forget: the population axis will be labelled in terms of millions; we will come back to that later. We also divide the male-to-female ratio, as read from the file, by 100 since we just want fractions, no percentages.
9797
9898Time to compile? No, not yet. We have to tell the chart sources the number of data points. This information is known after reading the data file. Therefore, we set the property `PointNumber` of each chart source to the length of the data array in the form's `OnCreate` event handler:
9999
100100``` pascal
101101procedure TForm1.FormCreate(Sender: TObject);
102102begin
103103 LoadPopulationData(POPULATION_FILE, PopulationData);
104104 ChartSource_male.PointsNumber := Length(PopulationData);
105105 ChartSource_female.PointsNumber := Length(PopulationData);
106106 ChartSource_ratio.PointsNumber := Length(PopulationData);
107107end;
108108```
109109
110110At this stage, your form and object tree should look like this:
111111
112112![](/wiki-images/1/18/TAChart_DualYAxis1.png) ![](/wiki-images/1/1b/TAChart_DualYAxis2.png)
113113
114114and when you compile and run you should see something like this:
115115
116116![](/wiki-images/b/bb/TAChart_DualYAxis3.png)
117117
118118As mentioned in the introduction, the male-to-female ratio -- that is the black line -- is very flat and shows almost no structure. This is due to the different magnitudes: the population numbers go almost up to 70 while the ratio is around 1.
119119
120120## Setting up a second y axis
121121
122122We need a second y axis.
123123
124124For this, go to the object tree, right-click on `AxisList` (right underneath the `Chart1`) and add a new axis to the chart. Now we have three axes:
125125
126126![](/wiki-images/c/c4/TAChart_DualYAxis4.png)
127127
128128Select the axis \#2 which is still a left axis at this time, and in the object inspector, set its `Alignment` to `calRight`.
129129
130130This is maybe a good place to mention that having two y axes is not the limit. In fact, you can add any number of axes to the `AxisList` of the chart, and you can use them as vertical axes at the left or right, or as horizontal axes at the top or the bottom of the chart. Have a look at the parameter [[TAChart documentation|Group]] of the axis: axes with the same group parameter are drawn into the same rectangle, axes with different group values are drawn side-by-side.
131131
132132How does the chart know which series belongs to which axis? For this purpose, each series has properties `AxisIndexX` and `AxisIndexY`. Since the *male* and *female* series will be plotted on the left axis set their `AxisIndexY` to 0 -- a look at the object tree tells you that this is the index of the left axis. The *ratio* series belongs to the right axis, its `AxisIndexY` must be set to 2. We could also set the axis indices for the bottom axis, but this is not absolutely necessary here.
133133
134134When we compile the result is a bit disappointing: no change - the black *ratio* series is still very small.
135135
136136![](/wiki-images/b/b4/TAChart_DualYAxis5.png)
137137
138138## Setting up auto-scale axis transformations
139139
140140The reason why the second y axis is not scaled independently of the first one is that we ignore so far internal coordinate systems of the chart. TAChart employs three coordinate systems in the transition from "real world" data to the pixels on the screen:
141141
142142- [[TAChart documentation|axis coordinates]] are the coordinates in which the data come in, or, in other words, which are labeled along the axes. In our project, these are e.g. the population numbers (after division by 1 million), i.e. numbers between about 0 and 70.
143143- [[TAChart documentation|graph coordinates]] are obtained after applying transformations. There is another tutorial where we apply a logarithmic transform, in this case the graph coordinates would be the logarithms of the input data.
144144- [[TAChart documentation|image coordinates]] belong to the pixels on the screen calculated from the graph coordinates.
145145
146146So, there are two mapping operations involved when calculating the coordinates of a data point on the screen:
147147
1481481. axis coordinates to screen coordinates by means of a user-specified `TChartAxisTransform`
1491492. screen coordinates to image coordinates by means of simple linear mapping
150150
151151If the axis transformation from axis to graph coordinates is not specified a simple 1:1 mapping is used instead.
152152
153153Oh -- we did not use any axis transform. Therefore, our *population* and *ratio* data still "live" in a common world of coordinates. To separate both data sets we need to apply a transformation to each data set which maps it to the same interval of graph coordinates, say, from 0 to 1.
154154
155155For this purpose, TAChart has [[TAChart documentation|TAutoScaleAxisTransform]]. It is not directly accessible from the component palette, but is a child of the [[TAChart documentation|TChartAxisTransformations]] component.
156156
157157So, let's drop two `TChartAxisTransformations` on the form, one for the left axis, one for the right axis. Rename them to `LeftAxisTransformations` and `RightAxisTransformations`, respectively. Double-click on each of these components and select *"auto scale"* from the *axis transformations editor*. In the object tree, you will see a child of each `ChartAxisTransformations` component. Rename these children to `LeftAxisAutoscaleTransform` and `RightAxisAutoscaleTransform`, respectively. Now assign the `LeftAxisTransformations` and `RightAxisTransformations` components to the property `Transformations` of the left and the right axis.
158158
159159This is the object tree after these operations:
160160
161161![](/wiki-images/e/ea/TAChart_DualYAxis6.png)
162162
163163Look at the properties `MaxValue` and `MinValue` of the `AutoScaleAxisTransforms`. For both transformations we have `MaxValue = 1` and `MinValue = 0`. This means that each axis is mapped to the range between 0 and 1, in other words, both data sets are compressed or expanded to fill the chart area completely.
164164
165165When we compile we see that this exactly happens.
166166
167167![](/wiki-images/1/1f/TAChart_DualYAxis7.png)
168168
169169We could also, for example, set the `RightAxisAutoscaleTransform`'s `MinValue=1` and `MaxValue=2`. Then the *population* data would still be mapped on the axis from 0 to 1, and the *male-to-female ratio* data would be mapped to the range from 1 to 2, i.e. we would have a paned chart where the lower half would be occupied by the *population* series and the upper half by the *ratio* series. But maybe that's stuff for another tutorial. We leave the `MinValue` and `MaxValue` properties at their defaults.
170170
171171## Cleaning up
172172
173173The most difficult part is done now. What is left is to clean up the chart.
174174
175175- Remove the **grids** from the axes (you should know how to do that...). The left and right axis grids, in particular, are very confusing.
176176- Add **axis titles**:
177177 - *"Population"* on the left axis
178178 - *"Male-to-female ratio"* on the right axis
179179 - *"Age (Years)"* on the bottom axis.
180180 - Set the axes' `LabelFont.Style` to `fsBold`.
181181- You will notice that the title of the right axis is not rotated. The `Title.LabelFont` of the axis has a property `Orientation` - this is the rotation angle in tenth's of degrees. Set it to 900 to get the same orientation as the title of the left axis.
182182- Force the right axis to **start at zero** by setting its `Range.UseMin` to true. This is sufficient since the `Range.XMin` is at 0 by default. In this way, you could also select other ranges for the axes.
183183
184184### Modifying the axis tick mark texts
185185
186186Now we should take care of the fact that we had divided that input data by 1E6, our plot somehow should show this. We could change the title of the left axis to *"Population (in millions)"*. But let's go a different way now: we could append an "M" to the labels, as a shortcut to the correct order of magnitude ("M" = "Mega" = "million"). There are two ways to achieve this: In the first one, we use the property `Marks.Format` of the axis where the "M" can be added ('%0:.9g M'). Or in the second way, we can take advantage of the event `OnMarkToText` that TChartAxis has; this event allows to modify the text displayed at the axis' tick marks in any way. In this case, assign the following procedure to this event of the left axis:
187187
188188``` pascal
189189procedure TForm1.Chart1AxisList0MarkToText(var AText: String; AMark: Double);
190190begin
191191 AText := Format('%s M', [AText]);
192192end;
193193```
194194
195195![](/wiki-images/c/cf/TAChart_DualYAxis8.png)
196196
197197### Setting up the legend
198198
199199We are almost finished now. What is left is a [[TAChart documentation|legend]] -- so far, it is not possible for the user to distinguish which curve belongs to which data. So, go to `Chart1.Legend` and set `Visible` to `true`. You see only short pieces of the curves in the legend, no texts. This is because we did not assign a `Title` to each series. Do this now, use the titles *"male"*, *"female"*, and *"ratio"* for the corresponding series.
200200
201201![](/wiki-images/c/cb/TAChart_DualYAxis9.png)
202202
203203Since we have two axes, it would be fine to have a header *"left"* above the entries for the *male* and *female* series in the legend, and a header *"right"* above the entry for the *ratio* series. This effect can be achieved by **grouping**. Each series has a property `Legend` which can be used to control the appearance of the series in the legend. The property `GroupIndex` allows to group a set of series together underneath a common header. So, set the `Legend.GroupIndex` of the *male* and *female* series to zero, and set that of the *ratio* series to 1. Now we go the `Chart.Legend` and define the titles of the group headers by entering text in the property `Chart.Legend.GroupTitles`, line by line for each `GroupIndex`. And maybe you can also go to `Chart.Legend.GroupFont` and set the font to bold.
204204
205205A final improvement: what about if the legend were underneath the chart such that the items belonging to the left axis were on the left, and those belonging to the right axis were on the right?
206206
207207At first, we set the legend's `Alignment` to `laBottomCenter`. Then we change the `ColumnCount` to 2. We are lucky -- the five legend entries fit into the two-column legend just as we want it. Otherwise we might have to play with the legend [[TAChart documentation|Order]] or the filling sequence of the multicolumn legend (property `ItemFillOrder`), or might have to introduce ([[TAChart documentation|dummy items]]).
208208
209209TAChart's legend is very flexible -- please have a look at the [[TAChart documentation|documentation]] to learn more about it.
210210
211211At the end, we add the text *"World population"* as the chart's title, and we should mention our reference for the data in the footer -- we don't want to steal data...
212212
213213Finished!
214214
215215![](/wiki-images/4/47/TAChart_DualYAxis11.png)
216216
217217## Source code
218218
219219### Project file
220220
221221For Lazarus v2.1+ you can find the [[Source code|source code]] of this tutorial project in folder **components/tachart/tutorials/population2** of your Lazarus installation. For older versions, copy and paste the following code to the corresponding files.
222222
223223``` pascal
224224program project1;
225225
226226{$mode objfpc}{$H+}
227227
228228uses
229229 {$IFDEF UNIX}{$IFDEF UseCThreads}
230230 cthreads,
231231 {$ENDIF}{$ENDIF}
232232 Interfaces, // this includes the LCL widgetset
233233 Forms, Unit1, tachartlazaruspkg
234234 { you can add units after this };
235235
236236{$R *.res}
237237
238238begin
239239 RequireDerivedFormResource := True;
240240 Application.Initialize;
241241 Application.CreateForm(TForm1, Form1);
242242 Application.Run;
243243end.
244244```
245245
246246### Unit1.pas
247247
248248``` pascal
249249unit Unit1;
250250
251251{$mode objfpc}{$H+}
252252
253253interface
254254
255255uses
256256 Classes, SysUtils, FileUtil, TAGraph, TASeries, TASources, Forms, Controls,
257257 Graphics, Dialogs, population, TACustomSource, TATransformations, TAChartAxisUtils;
258258
259259type
260260
261261 { TForm1 }
262262
263263 TForm1 = class(TForm)
264264 Chart1: TChart;
265265 LeftAxisTransformations: TChartAxisTransformations;
266266 LeftAxisAutoScaleTransform: TAutoScaleAxisTransform;
267267 RightAxisTransformations: TChartAxisTransformations;
268268 LineSeries_male: TLineSeries;
269269 LineSeries_female: TLineSeries;
270270 LineSeries_ratio: TLineSeries;
271271 ChartSource_male: TUserDefinedChartSource;
272272 ChartSource_female: TUserDefinedChartSource;
273273 ChartSource_ratio: TUserDefinedChartSource;
274274 RightAxisAutoScaleTransform: TAutoScaleAxisTransform;
275275 procedure Chart1AxisList0MarkToText(var AText: String; AMark: Double);
276276 procedure ChartSourceGetChartDataItem(
277277 ASource: TUserDefinedChartSource; AIndex: Integer;
278278 var AItem: TChartDataItem);
279279 procedure FormCreate(Sender: TObject);
280280 private
281281 { private declarations }
282282 PopulationData: TPopulationArray;
283283 public
284284 { public declarations }
285285 end;
286286
287287var
288288 Form1: TForm1;
289289
290290implementation
291291
292292{$R *.lfm}
293293
294294const
295295 POPULATION_FILE = 'population.txt';
296296
297297{ TForm1 }
298298
299299procedure TForm1.ChartSourceGetChartDataItem(
300300 ASource: TUserDefinedChartSource; AIndex: Integer; var AItem: TChartDataItem);
301301begin
302302 AItem.X := PopulationData[AIndex].Age;
303303 if ASource = ChartSource_male then
304304 AItem.Y := PopulationData[AIndex].Male / 1e6
305305 else if ASource = ChartSource_female then
306306 AItem.Y := PopulationData[AIndex].Female / 1e6
307307 else
308308 AItem.Y := PopulationData[AIndex].Ratio / 100;
309309end;
310310
311311procedure TForm1.Chart1AxisList0MarkToText(var AText: String; AMark: Double);
312312begin
313313 AText := Format('%s M', [AText]);
314314end;
315315
316316procedure TForm1.FormCreate(Sender: TObject);
317317begin
318318 LoadPopulationData(POPULATION_FILE, PopulationData);
319319 ChartSource_male.PointsNumber := Length(PopulationData);
320320 ChartSource_female.PointsNumber := Length(PopulationData);
321321 ChartSource_ratio.PointsNumber := Length(PopulationData);
322322end;
323323
324324end.
325325```
326326
327327### Unit1.lfm
328328
329-``` pascal
329+``` lfm
330330object Form1: TForm1
331331 Left = 365
332332 Height = 409
333333 Top = 169
334334 Width = 470
335335 Caption = 'Form1'
336336 ClientHeight = 409
337337 ClientWidth = 470
338338 OnCreate = FormCreate
339339 LCLVersion = '1.1'
340340 object Chart1: TChart
341341 Left = 0
342342 Height = 409
343343 Top = 0
344344 Width = 470
345345 AxisList = <
346346 item
347347 Grid.Visible = False
348348 Minors = <>
349349 Title.LabelFont.Orientation = 900
350350 Title.LabelFont.Style = [fsBold]
351351 Title.Visible = True
352352 Title.Caption = 'Population'
353353 Transformations = LeftAxisTransformations
354354 OnMarkToText = Chart1AxisList0MarkToText
355355 end
356356 item
357357 Grid.Visible = False
358358 Alignment = calBottom
359359 Minors = <>
360360 Title.LabelFont.Style = [fsBold]
361361 Title.Visible = True
362362 Title.Caption = 'Age (Years)'
363363 end
364364 item
365365 Grid.Visible = False
366366 Alignment = calRight
367367 Minors = <>
368368 Range.UseMin = True
369369 Title.LabelFont.Orientation = 900
370370 Title.LabelFont.Style = [fsBold]
371371 Title.Visible = True
372372 Title.Caption = 'Male-to-female ratio'
373373 Transformations = RightAxisTransformations
374374 end>
375375 BackColor = clWhite
376376 Foot.Alignment = taLeftJustify
377377 Foot.Brush.Color = clBtnFace
378378 Foot.Font.Color = clBlue
379379 Foot.Text.Strings = (
380380 'Source:'
381381 'http://www.census.gov/population/international/data/worldpop/tool_population.php'
382382 )
383383 Foot.Visible = True
384384 Legend.Alignment = laBottomCenter
385385 Legend.ColumnCount = 2
386386 Legend.GroupFont.Style = [fsBold]
387387 Legend.GroupTitles.Strings = (
388388 'left:'
389389 'right:'
390390 )
391391 Legend.Visible = True
392392 Title.Brush.Color = clBtnFace
393393 Title.Font.Color = clBlue
394394 Title.Font.Style = [fsBold]
395395 Title.Text.Strings = (
396396 'World population'
397397 )
398398 Title.Visible = True
399399 Align = alClient
400400 ParentColor = False
401401 object LineSeries_male: TLineSeries
402402 Legend.GroupIndex = 0
403403 Title = 'male'
404404 AxisIndexY = 0
405405 LinePen.Color = clSkyBlue
406406 Source = ChartSource_male
407407 end
408408 object LineSeries_female: TLineSeries
409409 Legend.GroupIndex = 0
410410 Title = 'female'
411411 AxisIndexY = 0
412412 LinePen.Color = 16744703
413413 Source = ChartSource_female
414414 end
415415 object LineSeries_ratio: TLineSeries
416416 Legend.GroupIndex = 1
417417 Title = 'ratio'
418418 AxisIndexY = 2
419419 Source = ChartSource_ratio
420420 end
421421 end
422422 object ChartSource_male: TUserDefinedChartSource
423423 OnGetChartDataItem = ChartSourceGetChartDataItem
424424 left = 130
425425 top = 25
426426 end
427427 object ChartSource_female: TUserDefinedChartSource
428428 OnGetChartDataItem = ChartSourceGetChartDataItem
429429 left = 130
430430 top = 79
431431 end
432432 object ChartSource_ratio: TUserDefinedChartSource
433433 OnGetChartDataItem = ChartSourceGetChartDataItem
434434 left = 130
435435 top = 137
436436 end
437437 object LeftAxisTransformations: TChartAxisTransformations
438438 left = 267
439439 top = 25
440440 object LeftAxisAutoScaleTransform: TAutoScaleAxisTransform
441441 end
442442 end
443443 object RightAxisTransformations: TChartAxisTransformations
444444 left = 267
445445 top = 136
446446 object RightAxisAutoScaleTransform: TAutoScaleAxisTransform
447447 end
448448 end
449449end
450450```
451451
452452### population.pas
453453
454454``` pascal
455455unit population;
456456
457457{$mode objfpc}{$H+}
458458
459459interface
460460
461461uses
462462 Classes, SysUtils;
463463
464464type
465465 TPopulationRecord = record
466466 Age: Integer;
467467 Total: Double;
468468 Male: Double;
469469 Female: Double;
470470 Ratio: Double;
471471 end;
472472 TPopulationArray = array of TPopulationRecord;
473473
474474procedure LoadPopulationData(const AFileName: String; var AData: TPopulationArray);
475475
476476implementation
477477
478478procedure LoadPopulationData(const AFileName: String; var AData: TPopulationArray);
479479
480480 function StripThousandSep(const s: String): String;
481481 // Removes the thousand separators from the string
482482 // Otherwise StrToFloat would fail.
483483 var
484484 i: Integer;
485485 begin
486486 Result := s;
487487 for i:=Length(Result) downto 1 do
488488 if Result[i] = ',' then
489489 Delete(Result, i, 1);
490490 end;
491491
492492var
493493 List1, List2: TStringList;
494494 i, j, n: Integer;
495495 s: String;
496496 ds: char;
497497begin
498498 ds := FormatSettings.DecimalSeparator;
499499 List1 := TStringList.Create;
500500 try
501501 List1.LoadFromFile(AFileName);
502502 n := List1.Count;
503503 SetLength(AData, n-2);
504504 FormatSettings.DecimalSeparator := '.';
505505 List2 := TStringList.Create;
506506 try
507507 List2.Delimiter := #9;
508508 List2.StrictDelimiter := true;
509509 j := 0;
510510 for i:=2 to n-1 do begin
511511 List2.DelimitedText := List1[i];
512512 s := List1[i];
513513 with AData[j] do begin
514514 if i < n-1 then
515515 Age := StrToInt(trim(List2[0]))
516516 else
517517 Age := 100; // the last line is "100 +"
518518 Total := StrToFloat(StripThousandSep(trim(List2[1])));
519519 Male := StrToFloat(StripThousandSep(trim(List2[2])));
520520 Female := StrToFloat(StripThousandSep(trim(List2[3])));
521521 Ratio := StrToFloat(trim(List2[4]));
522522 end;
523523 inc(j);
524524 end;
525525 finally
526526 List2.Free;
527527 end;
528528 finally
529529 FormatSettings.DecimalSeparator := ds;
530530 List1.Free;
531531 end;
532532end;
533533
534534end.
535535```
536536
537537---
538538*From the Free Pascal wiki, page "TAChart Tutorial: Dual y axis, Legend", as it stood on 2019-11-16 after a change by Djzepi, 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).*

Back to history