lazarus/components/tachart/demo/datapointtools/main.pas

376 lines
11 KiB
ObjectPascal

unit main;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ComCtrls,
ExtCtrls, StdCtrls, Types,
TAGraph, TASeries, TATools, TAStyles, TASources, TAChartUtils, TAMultiSeries,
TARadialSeries, TAFuncSeries, TACustomSeries;
type
{ TMainForm }
TMainForm = class(TForm)
Chart: TChart;
BarSeries: TBarSeries;
AreaSeries: TAreaSeries;
CbCandleStick: TCheckBox;
BoxWhiskerSeries: TBoxAndWhiskerSeries;
BubbleSeries: TBubbleSeries;
BSplineSeries: TBSplineSeries;
PieSeriesDatapointDragTool: TDataPointDragTool;
PieSeries: TPieSeries;
FitSeries: TFitSeries;
FieldSeries: TFieldSeries;
FuncSeries: TFuncSeries;
ConstantLineSeries: TConstantLine;
CubicSplineSeries: TCubicSplineSeries;
ClickTool: TDataPointClickTool;
FieldSeriesDatapointDragTool: TDataPointDragTool;
PolarSeries: TPolarSeries;
OHLCSeries: TOpenHighLowCloseSeries;
ChartStyles: TChartStyles;
CbDragXY: TCheckBox;
DatapointInfo: TLabel;
LblNOTE: TLabel;
LblUSAGE: TLabel;
ListChartSource: TListChartSource;
CrosshairTool: TDataPointCrosshairTool;
HintTool: TDataPointHintTool;
BubbleSeriesDatapointDragTool: TDataPointDragTool;
Panel1: TPanel;
Toolset: TChartToolset;
LineSeries: TLineSeries;
DataPointDragTool: TDataPointDragTool;
TabControl: TTabControl;
procedure CbCandleStickChange(Sender: TObject);
procedure CbDragXYChange(Sender: TObject);
procedure ClickToolPointClick(ATool: TChartTool; {%H-}APoint: TPoint);
procedure CrosshairToolAfterKeyUp(ATool: TChartTool; {%H-}APoint: TPoint);
procedure CrosshairToolAfterMouseUp(ATool: TChartTool; {%H-}APoint: TPoint);
procedure CrosshairToolDraw(ASender: TDataPointDrawTool);
procedure FormCreate(Sender: TObject);
procedure DataPointDragToolDragXY(ASender: TDataPointDragTool;
var AGraphPoint: TDoublePoint);
procedure DataPointDragToolDragY(ASender: TDataPointDragTool;
var AGraphPoint: TDoublePoint);
procedure FuncSeriesCalculate(const AX: Double; out AY: Double);
procedure HintToolHint(ATool: TDataPointHintTool; const {%H-}APoint: TPoint;
var AHint: String);
procedure TabControlChange(Sender: TObject);
private
function GetDatapointInfo(ATool: TDatapointTool): String;
public
end;
var
MainForm: TMainForm;
implementation
{$R *.lfm}
uses
TALegend;
{ TMainForm }
procedure TMainForm.CbCandleStickChange(Sender: TObject);
begin
if CbCandleStick.Checked then
OHLCSeries.Mode := mCandleStick
else
OHLCSeries.Mode := mOHLC;
end;
procedure TMainForm.CbDragXYChange(Sender: TObject);
begin
if CbDragXY.Checked then
begin
DataPointDragTool.OnDrag := @DatapointDragToolDragXY;
BubbleSeriesDataPointDragTool.OnDrag := nil;
end else
begin
DataPointDragTool.OnDrag := @DatapointDragToolDragY;
BubbleSeriesdataPointDragTool.OnDrag := @DatapointDragToolDragY;
end;
end;
procedure TMainForm.ClickToolPointClick(ATool: TChartTool; APoint: TPoint);
begin
DatapointInfo.Caption := '';
ShowMessage(GetDatapointInfo(TDatapointTool(ATool)));
end;
procedure TMainForm.CrosshairToolAfterKeyUp(ATool: TChartTool; APoint: TPoint);
begin
TDatapointCrosshairTool(ATool).Hide;
end;
procedure TMainForm.CrosshairToolAfterMouseUp(ATool: TChartTool; APoint: TPoint);
begin
TDatapointCrosshairTool(ATool).Hide;
end;
procedure TMainForm.CrosshairToolDraw(ASender: TDataPointDrawTool);
begin
DatapointInfo.Caption := GetDatapointInfo(ASender);
end;
// Avoids changing of the x value while dragging a data point
procedure TMainForm.DataPointDragToolDragY(ASender: TDataPointDragTool;
var AGraphPoint: TDoublePoint);
begin
AGraphPoint.X := ASender.Origin.X;
end;
// Modifies the x value if one of the levels of a multi-y-value series is dragged.
// e.g, stacked series, ohlc, box-whisker.
procedure TMainForm.DataPointDragToolDragXY(ASender: TDataPointDragTool;
var AGraphPoint: TDoublePoint);
var
ser: TChartSeries;
begin
if (ASender.YIndex > 0) and (ASender.Series is TChartSeries) then begin
ser := TChartSeries(ASender.Series);
ser.XValue[ASender.PointIndex] := AGraphPoint.X;
end;
end;
procedure TMainForm.FormCreate(Sender: TObject);
const
N = 10;
var
i: Integer;
x, x2, y1, y2, y3,yO, yH, yL, yC: Double;
begin
ListChartSource.YCount := 2;
ListChartSource.Sorted := true;
for i:=0 to N-1 do begin
x := i + (Random - 0.5) * 0.4;
y1 := x + Random*2;
y2 := y1 + Random*2;
ListChartSource.AddXYList(x, [y1, y2]);
end;
ListChartSource.Sorted := false;
OHLCSeries.ListSource.YCount := 4;
OHLCSeries.ListSource.Sorted := true;
for i:=0 to N-1 do begin
x := i + Random;
yL := x + Random;
yH := yL + Random + 5;
yO := yL + Random * 5;
yC := yL + Random * 5;
OHLCSeries.AddXOHLC(x, yO, yH, yL, yC);
end;
OHLCSeries.ListSource.Sorted := false;
BoxWhiskerSeries.ListSource.YCount := 5;
BoxWhiskerSeries.ListSource.Sorted := true;
for i:=0 to N-1 do begin
x := i + (Random-0.5)*0.4;
yL := x * Random;
y1 := yL + Random;
y2 := y1 + Random;
y3 := y2 + Random;
yH := y3 + Random;
BoxWhiskerSeries.AddXY(x, yL, y1, y2, y3, yH);
end;
BoxWhiskerSeries.ListSource.Sorted := true;
BubbleSeries.ListSource.YCount := 3;
for i:=0 to N-1 do begin
x := (Random - 0.5) * 2.0;
y1 := (Random - 0.5) * 2.0;
y2 := Random*0.3;
BubbleSeries.AddXY(x, y1, y2);
end;
FitSeries.Clear;
for i:= 0 to N - 1 do begin
x := Random * 10;
y1 := x + random * 1;
FitSeries.AddXY(x, y1);
end;
FieldSeries.ListSource.XCount := 2;
FieldSeries.ListSource.YCount := 2;
for i:=0 to N-1 do begin
x := Random * 10;
x2 := x + Random;
y1 := Random * 10;
y2 := y1 + Random;
FieldSeries.AddVector(x, y1, x2, y2);
end;
PieSeries.ListSource.Clear;
for i:=0 to 4 do begin
y1 := Random*10;
PieSeries.AddXY(0, y1, 'Item ' + IntToStr(i+1));
end;
PieSeries.Legend.Multiplicity := lmPoint;
PieSeries.Marks.Style := smsLabel;
PieSeries.Exploded := true;
LineSeries.Index := 0;
BarSeries.Index := 1;
AreaSeries.Index := 2;
PolarSeries.Index := 3;
CubicSplineSeries.Index := 4;
BSplineSeries.Index := 5;
FitSeries.Index := 6;
OHLCSeries.Index := 7;
BoxWhiskerSeries.Index := 8;
BubbleSeries.Index := 9;
FieldSeries.Index := 10;
FuncSeries.Index := 11;
ConstantLineSeries.Index := 12;
PieSeries.Index := 13;
TabControlChange(nil);
DatapointInfo.Caption := '';
end;
procedure TMainForm.FuncSeriesCalculate(const AX: Double; out AY: Double);
begin
AY := sin(AX);
end;
function TMainForm.GetDataPointInfo(ATool: TDataPointTool): String;
var
ser: TChartSeries;
begin
Result := '';
if ATool.Series is TConstantLine then
Result := Format('"%s": Position %.2f', [
TConstantLine(ATool.Series).Title, TConstantLine(ATool.Series).Position
])
else
if ATool.Series is TFuncSeries then
Result := Format('"%s": x = %.2f, y = %,2f', [
TFuncSeries(ATool.Series).Title, ATool.NearestGraphPoint.X, ATool.NearestGraphPoint.Y
])
else
if ATool.Series is TChartSeries then begin
ser := TChartSeries(ATool.Series);
if ser <> nil then begin
if (ATool.YIndex = -1) and
((ser is TFitSeries) or (ser is TCubicSplineSeries) or (ser is TBSplineSeries))
then
Result := Format('"%s": x = %.2f, y = %.2f', [
ser.Title,
ATool.NearestGraphPoint.X, ATool.NearestGraphPoint.Y
])
else
if (ATool.PointIndex > -1) and (ser.Source.YCount = 1) then
Result := Format('"%s": Point index %d, x = %.2f, y = %.2f', [
ser.Title,
ATool.PointIndex,
ser.XValue[ATool.PointIndex],
ser.YValue[ATool.PointIndex]
])
else
if (ATool.PointIndex > -1) and (ser.Source.YCount > 1) and (ATool.YIndex > -1) then
Result := Format('"%s": Point index %d, x = %.2f, y = %.2f (y index %d)', [
ser.Title,
ATool.PointIndex,
ser.XValue[ATool.PointIndex],
ser.YValues[ATool.PointIndex, ATool.YIndex],
ATool.YIndex
])
else
if (ATool.PointIndex = -1) and (ser.Source.YCount > 1) and (ATool.YIndex > -1) then
Result := Format('"%s": on segment, x = %.2f, y = %.2f (y index %d)', [
ser.Title,
ser.GraphToAxisX(ATool.NearestGraphPoint.X),
ser.GraphToAxisY(ATool.NearestGraphPoint.Y),
ATool.YIndex
]);
end;
end;
end;
procedure TMainForm.HintToolHint(ATool: TDataPointHintTool;
const APoint: TPoint; var AHint: String);
begin
AHint := GetDatapointInfo(ATool);
DatapointInfo.Caption := '';
end;
procedure TMainForm.TabControlChange(Sender: TObject);
var
ser: TBasicChartSeries;
s: String;
begin
// Show only the series of the corresponding tab
for ser in Chart.Series do
ser.Active := ser.Index = TabControl.TabIndex;
// Avoid tools react on hidden series
s := IntToStr(TabControl.TabIndex);
CrosshairTool.AffectedSeries := s;
HintTool.AffectedSeries := s;
BubbleSeriesDatapointDragTool.Enabled := TabControl.TabIndex = BubbleSeries.Index;
FieldSeriesDatapointDragTool.Enabled := TabControl.TabIndex = FieldSeries.Index;
PieSeriesDatapointDragTool.Enabled := TabControl.TabIndex = PieSeries.Index;
DataPointDragTool.Enabled := not
(BubbleSeriesDatapointDragTool.Enabled or FieldSeriesDatapointDragtool.Enabled);
if BubbleSeriesDatapointDragTool.Enabled then
BubbleSeriesDatapointDragTool.AffectedSeries := s
else
if FieldSeriesDatapointDragTool.Enabled then
FieldSeriesDatapointDragTool.AffectedSeries := s
else
if PieSeriesDatapointDragTool.Enabled then
PieSeriesDatapointDragtool.AffectedSeries := s
else
DatapointDragtool.AffectedSeries := s;
// Treatment of special requirements of particular series
CbCandleStick.Visible := OHLCSeries.Active;
LblNOTE.Visible := LineSeries.Active or BarSeries.Active or AreaSeries.Active;
Chart.LeftAxis.Range.UseMax := ConstantLineSeries.Active;
Chart.LeftAxis.Range.UseMin := ConstantLineSeries.Active;
if PieSeries.Active then
LblUsage.Caption := StringReplace(LblUsage.Caption, 'datapoint', 'pie', [])
else
LblUsage.Caption := StringReplace(LblUsage.Caption, 'pie', 'datapoint', []);
s := '';
if LineSeries.Active or BarSeries.Active or AreaSeries.Active then begin
s := 'These are stacked series, i.e. they share the same x ' +
'value defined by the red series --> When you drag a datapoint of any ' +
'series the x value of the other series will follow.';
if BarSeries.Active then
s := s + LineEnding + 'Grab the bars at their upper ends.';
end else
if FieldSeries.Active then
s := 'Rotate an arrow by grabbing it at its ends.' + LineEnding +
'Shift it by grabbing it in the middle.' + LineEnding +
'The checkbox "Drag both x and y" is not operating here.'
else
if BubbleSeries.Active then
s := 'Grab a bubble at its perimeter to change the radius.' + LineEnding +
'Grab it in the center to change its location.' + LineEnding +
'Overlapping bubbles may be detected erroneously.';
if s <> '' then
LblNOTE.Caption := 'NOTE:' + LineEnding + s;
LblNOTE.Visible := (s <> '');
CbDragXY.Enabled := not PieSeries.Active;
end;
end.