mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-02 13:23:44 +02:00
376 lines
11 KiB
ObjectPascal
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.
|
|
|