2020-09-06 10:47:23 +00:00
|
|
|
unit ChartFrameUnit;
|
|
|
|
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
uses
|
2020-09-19 11:42:15 +00:00
|
|
|
Classes, SysUtils, LCLVersion, Forms, Controls, Graphics, ExtDlgs, ComCtrls,
|
|
|
|
PrintersDlgs, TAGraph, TATypes, TACustomSource, TACustomSeries, TASeries,
|
2020-09-19 14:33:02 +00:00
|
|
|
TATools,
|
|
|
|
Globals, MainDM;
|
2020-09-06 10:47:23 +00:00
|
|
|
|
|
|
|
type
|
|
|
|
TPlotType = (ptLines, ptSymbols, ptLinesAndSymbols, ptHorBars, ptVertBars,
|
|
|
|
ptArea);
|
|
|
|
|
|
|
|
{ TChartFrame }
|
|
|
|
|
|
|
|
TChartFrame = class(TFrame)
|
|
|
|
Chart: TChart;
|
2020-09-19 11:42:15 +00:00
|
|
|
ChartToolBar: TToolBar;
|
2020-09-06 16:03:58 +00:00
|
|
|
ChartToolset: TChartToolset;
|
|
|
|
PanDragTool: TPanDragTool;
|
2020-09-06 10:47:23 +00:00
|
|
|
PrintDialog: TPrintDialog;
|
|
|
|
SavePictureDialog: TSavePictureDialog;
|
2020-09-19 11:42:15 +00:00
|
|
|
tbCopyChart: TToolButton;
|
|
|
|
tbPrintChart: TToolButton;
|
|
|
|
tbSaveChart: TToolButton;
|
2020-09-06 16:03:58 +00:00
|
|
|
ZoomDragTool: TZoomDragTool;
|
2020-09-19 11:42:15 +00:00
|
|
|
procedure tbCopyChartClick(Sender: TObject);
|
|
|
|
procedure tbPrintChartClick(Sender: TObject);
|
|
|
|
procedure tbSaveChartClick(Sender: TObject);
|
2020-09-06 10:47:23 +00:00
|
|
|
|
|
|
|
protected
|
2020-09-07 09:21:26 +00:00
|
|
|
function Constline(xy: Double; ADirection: TLineStyle; AColor: TColor;
|
|
|
|
ALineStyle: TPenStyle; ALegendTitle: String): TConstantLine;
|
2020-09-06 10:47:23 +00:00
|
|
|
|
|
|
|
public
|
2020-09-11 09:54:50 +00:00
|
|
|
constructor Create(AOwner: TComponent); override;
|
2020-09-06 10:47:23 +00:00
|
|
|
procedure Clear;
|
|
|
|
procedure GetXRange(out XMin, XMax: Double; Logical: Boolean = true);
|
|
|
|
procedure GetYRange(out YMin, YMax: Double; Logical: Boolean = true);
|
2020-09-07 09:21:26 +00:00
|
|
|
function HorLine(y: Double; AColor: TColor; ALineStyle: TPenStyle;
|
|
|
|
ALegendTitle: String): TConstantLine;
|
2020-09-06 23:24:17 +00:00
|
|
|
function PlotXY(AType: TPlotType; x, y: DblDyneVec; xLabels: StrDyneVec;
|
|
|
|
yErrorBars: DblDyneVec; LegendTitle: string; AColor: TColor;
|
|
|
|
ASymbol: TSeriesPointerStyle = psCircle): TChartSeries;
|
2020-09-06 10:47:23 +00:00
|
|
|
procedure Print;
|
|
|
|
procedure Save;
|
|
|
|
procedure SetFooter(const ATitle: String);
|
2020-09-06 23:24:17 +00:00
|
|
|
procedure SetTitle(const ATitle: String; Alignment: TAlignment = taCenter);
|
2020-09-06 10:47:23 +00:00
|
|
|
procedure SetXTitle(const ATitle: String);
|
|
|
|
procedure SetYTitle(const ATitle: String);
|
2020-09-19 14:33:02 +00:00
|
|
|
procedure UpdateBtnStates; virtual;
|
2020-09-07 09:21:26 +00:00
|
|
|
function VertLine(x: Double; AColor: TColor; ALineStyle: TPenStyle;
|
|
|
|
ALegendTitle: String): TConstantLine;
|
2020-09-06 10:47:23 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
implementation
|
|
|
|
|
|
|
|
{$R *.lfm}
|
|
|
|
|
|
|
|
uses
|
|
|
|
Math, Printers, OSPrinters,
|
|
|
|
TAChartUtils, TADrawerSVG, TAPrint;
|
|
|
|
|
2020-09-11 09:54:50 +00:00
|
|
|
|
|
|
|
constructor TChartFrame.Create(AOwner: TComponent);
|
|
|
|
begin
|
|
|
|
inherited;
|
|
|
|
{$IF LCL_FullVersion >= 2010000}
|
|
|
|
ZoomDragTool.LimitToExtent := [zdDown];
|
|
|
|
PanDragTool.LimitToExtent := [pdDown];
|
|
|
|
{$IFEND}
|
2020-09-19 14:33:02 +00:00
|
|
|
UpdateBtnStates;
|
2020-09-11 09:54:50 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
|
2020-09-06 10:47:23 +00:00
|
|
|
procedure TChartFrame.Clear;
|
|
|
|
begin
|
|
|
|
Chart.ClearSeries;
|
|
|
|
Chart.Title.Text.Clear;
|
|
|
|
Chart.Foot.Text.Clear;
|
|
|
|
Chart.BottomAxis.Title.Caption := '';
|
|
|
|
Chart.LeftAxis.Title.Caption := '';
|
2020-09-06 16:03:58 +00:00
|
|
|
Chart.Legend.Visible := false;
|
2020-09-19 14:33:02 +00:00
|
|
|
UpdateBtnStates;
|
2020-09-06 10:47:23 +00:00
|
|
|
end;
|
|
|
|
|
2020-09-07 09:21:26 +00:00
|
|
|
function TChartFrame.Constline(xy: Double; ADirection: TLineStyle;
|
|
|
|
AColor: TColor; ALineStyle: TPenStyle; ALegendTitle: String): TConstantLine;
|
2020-09-06 10:47:23 +00:00
|
|
|
begin
|
2020-09-07 09:21:26 +00:00
|
|
|
Result := TConstantLine.Create(self);
|
|
|
|
Result.Position := xy;
|
|
|
|
Result.LineStyle := ADirection;
|
|
|
|
Result.Pen.Color := AColor;
|
|
|
|
Result.Pen.Style := ALineStyle;
|
|
|
|
Result.Title := ALegendTitle;
|
|
|
|
Result.Legend.Visible := ALegendTitle <> '';
|
|
|
|
Chart.AddSeries(Result);
|
2020-09-19 14:33:02 +00:00
|
|
|
UpdateBtnStates;
|
2020-09-06 10:47:23 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TChartFrame.GetXRange(out XMin, XMax: Double; Logical: Boolean = true);
|
|
|
|
var
|
|
|
|
ext: TDoubleRect;
|
|
|
|
begin
|
|
|
|
if Logical then
|
|
|
|
ext := Chart.LogicalExtent
|
|
|
|
else
|
|
|
|
ext := Chart.CurrentExtent;
|
|
|
|
XMin := ext.a.x;
|
|
|
|
XMax := ext.b.x;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TChartFrame.GetYRange(out YMin, YMax: Double; Logical: Boolean = true);
|
|
|
|
var
|
|
|
|
ext: TDoubleRect;
|
|
|
|
begin
|
|
|
|
if Logical then
|
|
|
|
ext := Chart.LogicalExtent
|
|
|
|
else
|
|
|
|
ext := Chart.CurrentExtent;
|
|
|
|
YMin := ext.a.y;
|
|
|
|
YMax := ext.b.y;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
2020-09-07 09:21:26 +00:00
|
|
|
function TChartFrame.HorLine(y: Double; AColor: TColor; ALineStyle: TPenStyle;
|
|
|
|
ALegendTitle: String): TConstantLine;
|
2020-09-06 10:47:23 +00:00
|
|
|
begin
|
2020-09-07 09:21:26 +00:00
|
|
|
Result := ConstLine(y, lsHorizontal, AColor, ALineStyle, ALegendTitle);
|
2020-09-06 10:47:23 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
|
2020-09-07 09:21:26 +00:00
|
|
|
function TChartFrame.VertLine(x: Double; AColor: TColor; ALineStyle: TPenStyle;
|
|
|
|
ALegendTitle: String): TConstantLine;
|
2020-09-06 10:47:23 +00:00
|
|
|
begin
|
2020-09-07 09:21:26 +00:00
|
|
|
Result := ConstLine(x, lsVertical, AColor, ALineStyle, ALegendTitle);
|
2020-09-06 10:47:23 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
|
2020-09-06 23:24:17 +00:00
|
|
|
function TChartFrame.PlotXY(AType: TPlotType; x, y: DblDyneVec; xLabels: StrDyneVec;
|
|
|
|
yErrorBars: DblDyneVec; LegendTitle: string; AColor: TColor;
|
|
|
|
ASymbol: TSeriesPointerStyle = psCircle): TChartSeries;
|
2020-09-06 10:47:23 +00:00
|
|
|
var
|
2020-09-06 23:24:17 +00:00
|
|
|
i, n, ns, ne: Integer;
|
|
|
|
s: String;
|
|
|
|
xval: Double;
|
2020-09-06 10:47:23 +00:00
|
|
|
begin
|
|
|
|
case AType of
|
|
|
|
ptLines, ptSymbols, ptLinesAndSymbols:
|
|
|
|
begin
|
|
|
|
Result := TLineSeries.Create(self);
|
|
|
|
TLineSeries(Result).ShowPoints := AType in [ptSymbols, ptLinesAndSymbols];
|
|
|
|
TLineSeries(Result).ShowLines := AType in [ptLines, ptLinesAndSymbols];
|
|
|
|
TLineSeries(Result).SeriesColor := AColor;
|
|
|
|
if AType in [ptSymbols, ptLinesAndSymbols] then
|
|
|
|
begin
|
|
|
|
TLineSeries(Result).Pointer.Brush.Color := AColor;
|
|
|
|
TLineSeries(Result).Pointer.Style := ASymbol;
|
|
|
|
end;
|
2020-09-06 23:24:17 +00:00
|
|
|
if yErrorBars <> nil then
|
|
|
|
begin
|
|
|
|
TLineSeries(Result).YErrorBars.Visible := true;
|
|
|
|
TLineSeries(Result).ListSource.YCount := 2;
|
|
|
|
TLineSeries(Result).ListSource.YErrorBarData.Kind := ebkChartSource;
|
|
|
|
TLineSeries(Result).ListSource.YErrorBarData.IndexPlus := 1;
|
|
|
|
TLineSeries(Result).ListSource.YErrorBarData.IndexMinus := -1;
|
|
|
|
end;
|
2020-09-06 10:47:23 +00:00
|
|
|
end;
|
|
|
|
ptHorBars, ptVertBars:
|
|
|
|
Result := TBarSeries.Create(self);
|
|
|
|
ptArea:
|
|
|
|
Result := TAreaSeries.Create(self);
|
|
|
|
else
|
|
|
|
raise Exception.Create('Unknown plot type.');
|
|
|
|
end;
|
|
|
|
|
2020-09-06 23:24:17 +00:00
|
|
|
ns := Length(xLabels);
|
|
|
|
ne := Length(yErrorBars);
|
|
|
|
if x = nil then
|
|
|
|
n := Length(y)
|
|
|
|
else
|
|
|
|
n := Min(Length(x), Length(y));
|
|
|
|
for i := 0 to n-1 do begin
|
|
|
|
if x = nil then xval := i+1 else xval := x[i];
|
|
|
|
if i < ns then s := xLabels[i] else s := '';
|
|
|
|
if i < ne then
|
|
|
|
Result.AddXY(xval, y[i], [yErrorBars[i]], s)
|
|
|
|
else
|
|
|
|
Result.AddXY(xval, y[i], s);
|
|
|
|
end;
|
2020-09-06 10:47:23 +00:00
|
|
|
|
|
|
|
Result.Title := LegendTitle;
|
|
|
|
Chart.AddSeries(Result);
|
|
|
|
Chart.Legend.Visible := Chart.SeriesCount > 0;
|
2020-09-19 14:33:02 +00:00
|
|
|
UpdateBtnStates;
|
2020-09-06 10:47:23 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TChartFrame.Print;
|
|
|
|
const
|
|
|
|
MARGIN = 10;
|
|
|
|
var
|
|
|
|
R: TRect;
|
|
|
|
d: Integer;
|
|
|
|
begin
|
|
|
|
if not PrintDialog.Execute then
|
|
|
|
exit;
|
|
|
|
|
|
|
|
Printer.BeginDoc;
|
|
|
|
try
|
|
|
|
R := Rect(0, 0, Printer.PageWidth, Printer.PageHeight div 2);
|
|
|
|
|
|
|
|
d := R.Right - R.Left;
|
|
|
|
R.Left += d div MARGIN;
|
|
|
|
R.Right -= d div MARGIN;
|
|
|
|
|
|
|
|
d := R.Bottom - R.Top;
|
|
|
|
R.Top += d div MARGIN;
|
|
|
|
R.Bottom -= d div MARGIN;
|
|
|
|
|
|
|
|
Chart.Draw(TPrinterDrawer.Create(Printer, true), R);
|
|
|
|
finally
|
|
|
|
Printer.EndDoc;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TChartFrame.Save;
|
|
|
|
var
|
|
|
|
ext: String;
|
|
|
|
begin
|
|
|
|
if SavePictureDialog.Execute then
|
|
|
|
begin
|
|
|
|
ext := Lowercase(ExtractFileExt(SavePictureDialog.FileName));
|
|
|
|
case ext of
|
|
|
|
'.bmp': Chart.SaveToFile(TBitmap, SavePictureDialog.Filename);
|
|
|
|
'.png': Chart.SaveToFile(TPortableNetworkGraphic, SavePictureDialog.FileName);
|
|
|
|
'.jpg', '.jpeg', '.jpe', '.jfif': Chart.SaveToFile(TJpegImage, SavePictureDialog.FileName);
|
|
|
|
'.svg': Chart.SaveToSVGFile(SavePictureDialog.FileName);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TChartFrame.SetFooter(const ATitle: String);
|
|
|
|
begin
|
|
|
|
Chart.Foot.Text.Text := ATitle;
|
|
|
|
Chart.Foot.Visible := ATitle <> '';
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
2020-09-06 23:24:17 +00:00
|
|
|
procedure TChartFrame.SetTitle(const ATitle: String; Alignment: TAlignment = taCenter);
|
2020-09-06 10:47:23 +00:00
|
|
|
begin
|
|
|
|
Chart.Title.Text.Text := ATitle;
|
|
|
|
Chart.Title.Visible := ATitle <> '';
|
2020-09-06 23:24:17 +00:00
|
|
|
Chart.Title.Alignment := Alignment;
|
2020-09-06 10:47:23 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TChartFrame.SetXTitle(const ATitle: String);
|
|
|
|
begin
|
|
|
|
Chart.BottomAxis.Title.Caption := ATitle;
|
|
|
|
Chart.BottomAxis.Title.Visible := ATitle <> '';
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TChartFrame.SetYTitle(const ATitle: String);
|
|
|
|
begin
|
|
|
|
Chart.LeftAxis.Title.Caption := ATitle;
|
|
|
|
Chart.LeftAxis.Title.Visible := ATitle <> '';
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
2020-09-19 14:33:02 +00:00
|
|
|
procedure TChartFrame.UpdateBtnStates;
|
2020-09-19 11:42:15 +00:00
|
|
|
begin
|
|
|
|
tbSaveChart.Enabled := Chart.SeriesCount > 0;
|
|
|
|
tbPrintChart.Enabled := Chart.SeriesCount > 0;
|
|
|
|
tbCopyChart.Enabled := Chart.SeriesCount > 0;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TChartFrame.tbSaveChartClick(Sender: TObject);
|
|
|
|
begin
|
|
|
|
Save;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TChartFrame.tbPrintChartClick(Sender: TObject);
|
|
|
|
begin
|
|
|
|
Print;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
procedure TChartFrame.tbCopyChartClick(Sender: TObject);
|
|
|
|
begin
|
|
|
|
Chart.CopyToClipboardBitmap;
|
|
|
|
end;
|
|
|
|
|
|
|
|
|
2020-09-06 10:47:23 +00:00
|
|
|
end.
|
|
|
|
|