TAChart Runtime FAQ: Difference between revisions

From Free Pascal wiki
Jump to navigationJump to search
m (Fixed syntax highlighting)
Line 1: Line 1:
This wiki is trying to answer frequently asked questions related to usage of TAChart at runtime
This article answers frequently asked questions related to usage of TAChart at runtime.


== Series ==
== Series ==
===How to add a series at runtime?===
===How to add a series at runtime?===
Just create the series, set its properties and call the chart method <code>AddSeries</code>
Just create the series, set its properties and call the chart method <code>AddSeries</code>
<syntaxhighlight>
 
<syntaxhighlight lang=pascal>
uses
uses
   TATypes, TASeries;
   TATypes, TASeries;
Line 27: Line 30:
   // Add new series to the chart
   // Add new series to the chart
   AChart.AddSeries(Result);
   AChart.AddSeries(Result);
end;</syntaxhighlight>
end;
</syntaxhighlight>


=== How to iterate through the series of a chart? ===
=== How to iterate through the series of a chart? ===
The series are accessible via the array-like property <code>Series</code> of the chart. Note that this returns only the most fundamental series type, TBasicChartSeries, and a type-cast may be necessage before being able to access the series properties.
The series are accessible via the array-like property <code>Series</code> of the chart. Note that this returns only the most fundamental series type, TBasicChartSeries, and a type-cast may be necessage before being able to access the series properties.


The following example re-colors all line series in red:
The following example re-colors all line series in red:
<syntaxhighlight>uses
 
<syntaxhighlight lang=pascal>
uses
   TASeries;
   TASeries;
var  
var  
Line 45: Line 52:


If you prefer the more modern iterators you can use the syntax  
If you prefer the more modern iterators you can use the syntax  
<syntaxhighlight>
 
<syntaxhighlight lang=pascal>
uses
uses
   TASeries, TAEnumerators;
   TASeries, TAEnumerators;
Line 53: Line 61:
   for ser in CustomSeries(AChart) do
   for ser in CustomSeries(AChart) do
     TLineSeries(ser).Series.Color := clRed;
     TLineSeries(ser).Series.Color := clRed;
end;</syntaxhighlight>
end;
</syntaxhighlight>


=== How to delete/hide a series at runtime? ===
=== How to delete/hide a series at runtime? ===
* If you don't need the series any more just destroy it: <code>series.Free</code>.
* If you don't need the series any more just destroy it: <code>series.Free</code>.
* If you want to keept it for other usage, for example to insert it into another chart, call <code>Chart.DeleteSeries(series)</code>.
* If you want to keept it for other usage, for example to insert it into another chart, call <code>Chart.DeleteSeries(series)</code>.
* If you want to hide it, but keep it in the chart, set the property <code>Active</code> of the series to <code>false</code>.
* If you want to hide it, but keep it in the chart, set the property <code>Active</code> of the series to <code>false</code>.
[[Category:TAChart]]


== Axes and axis transformations ==
== Axes and axis transformations ==


=== Fixed axis limits ===
=== Fixed axis limits ===
Normally the limits of the chart axes are determined such that the chart covers the available area as much as possible. But sometimes it is better to keep the axis limit frozen, independent of the data range.
Normally the limits of the chart axes are determined such that the chart covers the available area as much as possible. But sometimes it is better to keep the axis limit frozen, independent of the data range.


This code uses the Extent of the chart to define a constant range for the y axis between 0 and 100. The first example manipulates the Extent consecutively, the second one simultaneously by using an intermediate variable. Note that the first method may crash the program in the third line if, for some reason, the new maximum is  smaller than the currently active minimum. Note that the second code freezes also the x axis; you'll have to call <code>Chart1.Extent.UseXMin := false</code> and <code>Chart1.Extent.UseXmax := false</code> to release it again.
This code uses the Extent of the chart to define a constant range for the y axis between 0 and 100. The first example manipulates the Extent consecutively, the second one simultaneously by using an intermediate variable. Note that the first method may crash the program in the third line if, for some reason, the new maximum is  smaller than the currently active minimum. Note that the second code freezes also the x axis; you'll have to call <code>Chart1.Extent.UseXMin := false</code> and <code>Chart1.Extent.UseXmax := false</code> to release it again.
<syntaxhighlight>
 
<syntaxhighlight lang=pascal>
Chart1.Extent.YMax := 100;
Chart1.Extent.YMax := 100;
Chart1.Extent.YMin := 0;
Chart1.Extent.YMin := 0;
Chart1.Extent.UseYMax := true;
Chart1.Extent.UseYMax := true;
Chart1.Extent.UseYMin := true;
Chart1.Extent.UseYMin := true;
</syntaxhighlight>


or:
or:


<syntaxhighlight lang=pascal>
var
var
   ex: TDoubleRect;
   ex: TDoubleRect;
Line 84: Line 96:
   Chart1.Extent.FixTo(ex);
   Chart1.Extent.FixTo(ex);
end;
end;
</syntaxhighlight>
</syntaxhighlight>


=== How to add an axis at runtime? ===
=== How to add an axis at runtime? ===
A chart can contain several axes which are stored in its <code>AxisList</code>. Since <code>AxisList</code> inherits from <code>TCollection</code> you can use its <code>Add</code> method to create a new axis:
A chart can contain several axes which are stored in its <code>AxisList</code>. Since <code>AxisList</code> inherits from <code>TCollection</code> you can use its <code>Add</code> method to create a new axis:
<syntaxhighlight>
 
<syntaxhighlight lang=pascal>
uses
uses
   TAChartAxis, TAChartUtils;
   TAChartAxis, TAChartUtils;
Line 109: Line 122:
   // Show axis line
   // Show axis line
   Result.AxisPen.Visible := true;   
   Result.AxisPen.Visible := true;   
end;</syntaxhighlight>
end;
</syntaxhighlight>
 
If you want to assign the new axis to AxisIndexX or AxisIndexY of a series you can use the property <code>Index</code> of the newly created axis.
If you want to assign the new axis to AxisIndexX or AxisIndexY of a series you can use the property <code>Index</code> of the newly created axis.


=== How to add an axis transformation? ===
=== How to add an axis transformation? ===
TChartAxisTransformations is a container for several TAxisTransform instances. It must be assigned to the <code>Transformations</code> property of an axis in order to be operative. It is created at runtime like any other component:
TChartAxisTransformations is a container for several TAxisTransform instances. It must be assigned to the <code>Transformations</code> property of an axis in order to be operative. It is created at runtime like any other component:
<syntaxhighlight>
 
<syntaxhighlight lang=pascal>
uses
uses
   TATransformations;
   TATransformations;
Line 126: Line 143:
However, a bare TChartAxisTransformations component is useless. It must contain at least one TAxisTransform object. In the following code, we will add a TAutoScaleAxisTransform which is needed for paned charts. Please note that, although the TAxisTransform objects are collected in the TChartAxisTransformations property <code>List</code> which derives from TFPList, new objects must not be added by calling <code>List.Add</code>. Instead, assign the <code>Transformations</code> property of the new TAxisTransform to the TChartAxisTransformations component:
However, a bare TChartAxisTransformations component is useless. It must contain at least one TAxisTransform object. In the following code, we will add a TAutoScaleAxisTransform which is needed for paned charts. Please note that, although the TAxisTransform objects are collected in the TChartAxisTransformations property <code>List</code> which derives from TFPList, new objects must not be added by calling <code>List.Add</code>. Instead, assign the <code>Transformations</code> property of the new TAxisTransform to the TChartAxisTransformations component:


<syntaxhighlight>
<syntaxhighlight lang=pascal>
uses
uses
   TATransformations;
   TATransformations;
Line 148: Line 165:


=== Logarithmic axis ===
=== Logarithmic axis ===
Data varying over several orders of magnitude are usually plotted on a logarithmic axis. For achieving a logarithmic scale a ChartTransformations component containing a TLogarithmAxisTransform must be assigned to the corresponding axis. Some optimization must be done with the axis marks in order to get "nice" labels. Don't forget to set the index of the log axis to the <code>AxisIndexX</code> or <code>AxisIndexY</code> property of each series.
Data varying over several orders of magnitude are usually plotted on a logarithmic axis. For achieving a logarithmic scale a ChartTransformations component containing a TLogarithmAxisTransform must be assigned to the corresponding axis. Some optimization must be done with the axis marks in order to get "nice" labels. Don't forget to set the index of the log axis to the <code>AxisIndexX</code> or <code>AxisIndexY</code> property of each series.


The following code shows how an axis can be toggled between logarithmic and linear scale:
The following code shows how an axis can be toggled between logarithmic and linear scale:
<syntaxhighlight>
 
<syntaxhighlight lang=pascal>
uses
uses
   TAChartAxis, TAChartTransformations, TACustomSource;
   TAChartAxis, TAChartTransformations, TACustomSource;
Line 197: Line 216:


In order to toggle between log and linear scale it must be known whether the axis currently is logarithmic or linear:
In order to toggle between log and linear scale it must be known whether the axis currently is logarithmic or linear:
<syntaxhighlight>
 
<syntaxhighlight lang=pascal>
function IsLogAxis(AChart: TChart; AxisIndex: Integer): Boolean;
function IsLogAxis(AChart: TChart; AxisIndex: Integer): Boolean;
var
var
Line 215: Line 235:
end;
end;
</syntaxhighlight>
</syntaxhighlight>
In total, these routines could be called in a button's OnClick event handler to switch the left axis (index 0) between log and linear:
In total, these routines could be called in a button's OnClick event handler to switch the left axis (index 0) between log and linear:
<syntaxhighlight>
 
<syntaxhighlight lang=pascal>
procedure TForm1.Button1Click(Sender: TObject);
procedure TForm1.Button1Click(Sender: TObject);
begin
begin
   LogAxis(Chart1, 0, not IsLogAxis(Chart1, 0));
   LogAxis(Chart1, 0, not IsLogAxis(Chart1, 0));
end; </syntaxhighlight>
end;  
</syntaxhighlight>


== Miscellaneous ==
== Miscellaneous ==


=== What can I do to avoid compiler error ''"Identifier not found 'TDoublePoint'"''? ===
=== What can I do to avoid compiler error ''"Identifier not found 'TDoublePoint'"''? ===
<code>TDoublePoint</code> is declared in unit <code>TAChartUtils</code>, add it to the <code>uses</code> clause of the unit. It also contains the declaration of   
<code>TDoublePoint</code> is declared in unit <code>TAChartUtils</code>, add it to the <code>uses</code> clause of the unit. It also contains the declaration of   
* <code>TDoubleRect</code> which is needed for chart extents.
* <code>TDoubleRect</code> which is needed for chart extents.
Line 232: Line 256:
* <code>TSeriesPointerStyle</code> for the series pointer styles, e.g. <code>psCircle, psRectangle</code> --> in unit <code>TATypes</code>
* <code>TSeriesPointerStyle</code> for the series pointer styles, e.g. <code>psCircle, psRectangle</code> --> in unit <code>TATypes</code>
* geometrical functions (creation of <code>TDoublePoint</code> and <code>TDoubleRect</code>, overloaded operators for <code>TPoint</code> and <code>TDoublePoints</code>) are in unit <code>TAGeometry</code>
* geometrical functions (creation of <code>TDoublePoint</code> and <code>TDoubleRect</code>, overloaded operators for <code>TPoint</code> and <code>TDoublePoints</code>) are in unit <code>TAGeometry</code>
[[Category:TAChart]]
[[Category:Lazarus]]
[[Category:Components]]
[[Category:Graphics]]

Revision as of 12:50, 28 February 2020

This article answers frequently asked questions related to usage of TAChart at runtime.

Series

How to add a series at runtime?

Just create the series, set its properties and call the chart method AddSeries

uses
  TATypes, TASeries;

function AddLineSeries(AChart: TChart; ATitle: String): TChartSeries;
begin
  Result := TLineSeries.Create(AChart.Owner);
  with TLineSeries(Result) do
  begin
    // Series title for the legend
    SeriesTitle := ATitle;
    // Show data point markers (red fill color, black border)
    ShowPoints := true;  
    Pointer.Brush.Color := clRed;
    Pointer.Pen.Color := clBlack;
    Pointer.Style := psCircle;
    // Show red line segments connecting the data points
    ShowLines := true;   
    LinePen.Style := psSolid;
    SeriesColor := clRed;
  end;
  // Add new series to the chart
  AChart.AddSeries(Result);
end;

How to iterate through the series of a chart?

The series are accessible via the array-like property Series of the chart. Note that this returns only the most fundamental series type, TBasicChartSeries, and a type-cast may be necessage before being able to access the series properties.

The following example re-colors all line series in red:

uses
  TASeries;
var 
  i: Integer;
begin
  for i := 0 to AChart.SeriesCount-1 do
    if AChart.Series[i] is TLineSeries then
      TLineSeries(AChart.Series[i]).SeriesColor := clRed;
end;

If you prefer the more modern iterators you can use the syntax

uses
  TASeries, TAEnumerators;
var
  ser: TCustomSeries;
begin
  for ser in CustomSeries(AChart) do
    TLineSeries(ser).Series.Color := clRed;
end;

How to delete/hide a series at runtime?

  • If you don't need the series any more just destroy it: series.Free.
  • If you want to keept it for other usage, for example to insert it into another chart, call Chart.DeleteSeries(series).
  • If you want to hide it, but keep it in the chart, set the property Active of the series to false.

Axes and axis transformations

Fixed axis limits

Normally the limits of the chart axes are determined such that the chart covers the available area as much as possible. But sometimes it is better to keep the axis limit frozen, independent of the data range.

This code uses the Extent of the chart to define a constant range for the y axis between 0 and 100. The first example manipulates the Extent consecutively, the second one simultaneously by using an intermediate variable. Note that the first method may crash the program in the third line if, for some reason, the new maximum is smaller than the currently active minimum. Note that the second code freezes also the x axis; you'll have to call Chart1.Extent.UseXMin := false and Chart1.Extent.UseXmax := false to release it again.

Chart1.Extent.YMax := 100;
Chart1.Extent.YMin := 0;
Chart1.Extent.UseYMax := true;
Chart1.Extent.UseYMin := true;

or:

var
  ex: TDoubleRect;
begin
  ex := Chart1.GetFullExtent;
  ex.b.y := 100;
  ex.a.y := 0;
  Chart1.Extent.FixTo(ex);
end;

How to add an axis at runtime?

A chart can contain several axes which are stored in its AxisList. Since AxisList inherits from TCollection you can use its Add method to create a new axis:

uses
  TAChartAxis, TAChartUtils;

function AddAxis(AParentChart: TChart; ATitle: String; AAlignment: TChartAxisAlignment): TChartAxis;
begin
  // Create a new axis
  Result := AParentChart.AxisList.Add;
  // Axis orientation: calLeft, calTop, calRight or calBottom
  Result.Alignment := AAlignment;   
  // Axis title
  Result.Title.Caption := ATitle;
  Result.Title.Visible := true;
  // Rotate title of a vertical axis
  case AAlignment of
    calLeft  : Result.Title.LabelFont.Orientation := +900;
    calRight : Result.Title.LabelFont.Orientation := -900;
  end;
  // Show axis line
  Result.AxisPen.Visible := true;  
end;

If you want to assign the new axis to AxisIndexX or AxisIndexY of a series you can use the property Index of the newly created axis.

How to add an axis transformation?

TChartAxisTransformations is a container for several TAxisTransform instances. It must be assigned to the Transformations property of an axis in order to be operative. It is created at runtime like any other component:

uses
  TATransformations;

procedure TForm1.CreateAxisTransformationOfAxis(AChart: TChart; AxisIndex: Integer);
begin
  AChart.AxisList[AxisIndex].Transformations := TChartAxisTransformations.Create(Self);
end;

However, a bare TChartAxisTransformations component is useless. It must contain at least one TAxisTransform object. In the following code, we will add a TAutoScaleAxisTransform which is needed for paned charts. Please note that, although the TAxisTransform objects are collected in the TChartAxisTransformations property List which derives from TFPList, new objects must not be added by calling List.Add. Instead, assign the Transformations property of the new TAxisTransform to the TChartAxisTransformations component:

uses
  TATransformations;

procedure TForm1.CreateAutoScaleAxisTransformOfAxis(AChart: TChart; AxisIndex: Integer);
var
  T: TChartAxisTransformations;
  transf: TAxisTransform;
begin
  // Create the TChartAxisTransformations component (or use the one dropped in the form at designtime)
  T := TChartAxisTransformations.Create(self);
  // and assign it to the Transformations property of the corresponding axis
  AChart.AxisList[AxisIndex].Transformations := T;

  // Create the AutoScaleAxisTransform object
  transf := TAutoscaleAxisTransform.Create(T);
  // and add it to the TChartAxisTransformations component
  transf.Transformations := T;
end;

Logarithmic axis

Data varying over several orders of magnitude are usually plotted on a logarithmic axis. For achieving a logarithmic scale a ChartTransformations component containing a TLogarithmAxisTransform must be assigned to the corresponding axis. Some optimization must be done with the axis marks in order to get "nice" labels. Don't forget to set the index of the log axis to the AxisIndexX or AxisIndexY property of each series.

The following code shows how an axis can be toggled between logarithmic and linear scale:

uses
  TAChartAxis, TAChartTransformations, TACustomSource;

procedure LogAxis(AChart: TChart; AxisIndex: Integer; Enable: Boolean);
var
  axis: TChartAxis;
  transf: TLogarithmAxisTransform;
begin
  // Determine the axis to be processed
  axis := AChart.AxisList[AxisIndex];

  // If an AxisTransformations component has not yet been assigned to the axis, we must create one
  if axis.Transformations = nil then
    axis.Transformations := TChartAxisTransformations.Create(AChart.Owner);

  if axis.Transformations.List.Count = 0 then
  begin
    // Create the log transform...
    transf := TLogarithmAxisTransform.Create(Axis.Transformations);
    transf.Base := 10;
    transf.Transformations := Axis.Transformations;
  end else
    transf := (TAxisTransform(axis.Transformations.List[0]) as TLogarithmAxisTransform);

  // Enable the transformation for a log scale, or disable it for a linear scale
  transf.Enabled := Enable;

  // Find "nice" axis labels
  if Enable then begin
    axis.Intervals.Options := axis.Intervals.Options + [aipGraphCoords];
    // Depending on the size of the chart and the data extent you may have to
    // play with the following numbers to get "nice" log labels.
    axis.Intervals.MaxLength := 200;
    axis.Intervals.Tolerance := 100;
  end else begin
    // Adapt these numbers, too. These are the defaults of a linear scale.
    // Often the Intervals.MaxLength has to be increased to avoid overlapping labels.
    axis.Intervals.Options := axis.Intervals.Options - [aipGraphCoords];
    axis.Intervals.MaxLength := 50;
    axis.Intervals.Tolerance := 1;
  end;
end;

In order to toggle between log and linear scale it must be known whether the axis currently is logarithmic or linear:

function IsLogAxis(AChart: TChart; AxisIndex: Integer): Boolean;
var
  axis: TChartAxis;
  transf: TAxisTransform;
begin
  Result := false;
  if Assigned(AChart) and (AxisIndex < AChart.AxisList.Count) then
  begin
    axis := AChart.AxisList[AxisIndex];
    if Assigned(axis.Transformations) and (axis.Transformations.List.Count > 0) then
    begin
      transf := TAxisTransform(axis.Transformations.List[0]);
      Result := transf.Enabled and (transf is TLogarithmAxisTransform);
    end;
  end;
end;

In total, these routines could be called in a button's OnClick event handler to switch the left axis (index 0) between log and linear:

procedure TForm1.Button1Click(Sender: TObject);
begin
  LogAxis(Chart1, 0, not IsLogAxis(Chart1, 0));
end;

Miscellaneous

What can I do to avoid compiler error "Identifier not found 'TDoublePoint'"?

TDoublePoint is declared in unit TAChartUtils, add it to the uses clause of the unit. It also contains the declaration of

  • TDoubleRect which is needed for chart extents.

Other declarations, often not found when working with run-time code, are

  • TSeriesMarksStyle for the Style of axis and series Marks --> in unit TAChartUtils
  • TSeriesPointerStyle for the series pointer styles, e.g. psCircle, psRectangle --> in unit TATypes
  • geometrical functions (creation of TDoublePoint and TDoubleRect, overloaded operators for TPoint and TDoublePoints) are in unit TAGeometry