fpspreadsheet: Add basic infrastructure for chart support.

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@6742 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
wp_xxyyzz
2018-12-01 23:02:22 +00:00
parent 1423ff2b2b
commit d597266e0d
5 changed files with 569 additions and 3 deletions

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="11"/>
<PathDelim Value="\"/>
<General>
<Flags>
<MainUnitHasCreateFormStatements Value="False"/>
<MainUnitHasTitleStatement Value="False"/>
<MainUnitHasScaledStatement Value="False"/>
</Flags>
<SessionStorage Value="InProjectDir"/>
<MainUnit Value="0"/>
<Title Value="chartdemo"/>
<UseAppBundle Value="False"/>
<ResourceType Value="res"/>
</General>
<BuildModes Count="1">
<Item1 Name="Default" Default="True"/>
</BuildModes>
<PublishOptions>
<Version Value="2"/>
<UseFileFilters Value="True"/>
</PublishOptions>
<RunParams>
<FormatVersion Value="2"/>
<Modes Count="0"/>
</RunParams>
<RequiredPackages Count="1">
<Item1>
<PackageName Value="laz_fpspreadsheet"/>
</Item1>
</RequiredPackages>
<Units Count="1">
<Unit0>
<Filename Value="chartdemo.lpr"/>
<IsPartOfProject Value="True"/>
</Unit0>
</Units>
</ProjectOptions>
<CompilerOptions>
<Version Value="11"/>
<PathDelim Value="\"/>
<Target>
<Filename Value="chartdemo"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir)"/>
<UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
</CompilerOptions>
<Debugging>
<Exceptions Count="3">
<Item1>
<Name Value="EAbort"/>
</Item1>
<Item2>
<Name Value="ECodetoolError"/>
</Item2>
<Item3>
<Name Value="EFOpenError"/>
</Item3>
</Exceptions>
</Debugging>
</CONFIG>

View File

@ -0,0 +1,44 @@
program chartdemo;
uses
fpspreadsheet, fpsutils, fpstypes, fpschart;
var
wb: TsWorkbook;
sh: TsWorksheet;
chart: TsChart;
ser: TsChartSeries;
idx: Integer;
begin
wb := TsWorkbook.Create;
try
ws := wb.AddWorksheet('Test');
// x values
ws.WriteNumber(0, 0, 1.0);
ws.WriteNumber(1, 0, 2.1);
ws.WriteNumber(2, 0, 2.9);
ws.WriteNumber(3, 0, 4.15);
ws.WriteNumber(4, 0, 5.05);
// y values
ws.WriteNumber(0, 1, 10.0);
ws.WriteNumber(1, 1, 12.0);
ws.WriteNumber(2, 1, 9.0);
ws.WriteNumber(3, 1, 7.5);
ws.WriteNumber(4, 1, 11.2);
idx := ws.WriteChart(0, 0, 12, 9);
chart := ws.GetChart(idx);
ser := TsLineSeries.Create(chart);
ser.XRange := Range(0, 0, 4, 0);
ser.YRange := Range(0, 1, 4, 1);
ser.Title := 'Scatter series';
ser.ShowSymbols := true;
ser.ShowLines := true;
chart.AddSeries(
finally
wb.Free;
end;
end.

View File

@ -34,7 +34,7 @@
This package is all you need if you don't want graphical components (like grids and charts)."/> This package is all you need if you don't want graphical components (like grids and charts)."/>
<License Value="LGPL with static linking exception. This is the same license as is used in the LCL (Lazarus Component Library)."/> <License Value="LGPL with static linking exception. This is the same license as is used in the LCL (Lazarus Component Library)."/>
<Version Major="1" Minor="11"/> <Version Major="1" Minor="11"/>
<Files Count="46"> <Files Count="47">
<Item1> <Item1>
<Filename Value="source\fps.inc"/> <Filename Value="source\fps.inc"/>
<Type Value="Include"/> <Type Value="Include"/>
@ -258,6 +258,10 @@ This package is all you need if you don't want graphical components (like grids
<Filename Value="source\common\fpscrypto.pas"/> <Filename Value="source\common\fpscrypto.pas"/>
<UnitName Value="fpsCrypto"/> <UnitName Value="fpsCrypto"/>
</Item46> </Item46>
<Item47>
<Filename Value="source\common\fpschart.pas"/>
<UnitName Value="fpschart"/>
</Item47>
</Files> </Files>
<i18n> <i18n>
<EnableI18N Value="True"/> <EnableI18N Value="True"/>
@ -278,7 +282,6 @@ This package is all you need if you don't want graphical components (like grids
</UsageOptions> </UsageOptions>
<PublishOptions> <PublishOptions>
<Version Value="2"/> <Version Value="2"/>
<IgnoreBinaries Value="False"/>
</PublishOptions> </PublishOptions>
<CustomOptions Items="ExternHelp" Version="2"> <CustomOptions Items="ExternHelp" Version="2">
<_ExternHelp Items="Count"/> <_ExternHelp Items="Count"/>

View File

@ -0,0 +1,426 @@
unit fpschart;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Contnrs, fpsTypes, fpsUtils;
type
TsChart = class;
TsChartFill = record
Style: TsFillStyle;
FgColor: TsColor;
BgColor: TsColor;
end;
TsChartPenStyle = (cpsSolid, cpsDashed, cpsDotted, cpsDashDot, cpsDashDotDot, cpsClear);
TsChartLine = record
Style: TsChartPenStyle;
Width: Double; // mm
Color: TsColor;
end;
TsChartObj = class
private
FOwner: TsChartObj;
FVisible: Boolean;
public
constructor Create(AOwner: TsChartObj);
property Visible: Boolean read FVisible write FVisible;
end;
TsChartFillObj = class(TsChartObj)
private
FBackground: TsChartFill;
FBorder: TsChartLine;
public
constructor Create(AOwner: TsChartObj);
property Background: TsChartFill read FBackground write FBackground;
property Border: TsChartLine read FBorder write FBorder;
end;
TsChartText = class(TsChartObj)
private
FCaption: String;
FShowCaption: Boolean;
FFont: TsFont;
public
constructor Create(AOwner: TsChartObj);
property Caption: String read FCaption write FCaption;
property Font: TsFont read FFont write FFont;
property ShowCaption: Boolean read FShowCaption write FShowCaption;
end;
TsChartAxis = class(TsChartText)
private
FAutomaticMax: Boolean;
FAutomaticMin: Boolean;
FAutomaticMajorInterval: Boolean;
FAutomaticMinorSteps: Boolean;
FAxisLine: TsChartLine;
FGridLines: TsChartLine;
FInverted: Boolean;
FLabelFont: TsFont;
FLogarithmic: Boolean;
FMajorInterval: Double;
FMajorTickLines: TsChartLine;
FMax: Double;
FMin: Double;
FMinorSteps: Double;
FMinorTickLines: TsChartLine;
FShowGrid: Boolean;
FShowLabels: Boolean;
public
constructor Create(AOwner: TsChartObj);
property AutomaticMax: Boolean read FAutomaticMax write FAutomaticMax;
property AutomaticMin: Boolean read FAutomaticMin write FAutomaticMin;
property AutomaticMajorInterval: Boolean read FAutomaticMajorInterval write FAutomaticMajorInterval;
property AutomaticMinorSteps: Boolean read FAutomaticMinorSteps write FAutomaticMinorSteps;
property AxisLine: TsChartLine read FAxisLine write FAxisLine;
property GridLines: TsChartLine read FGridLines write FGridLines;
property Inverted: Boolean read FInverted write FInverted;
property LabelFont: TsFont read FLabelFont write FLabelFont;
property Logarithmic: Boolean read FLogarithmic write FLogarithmic;
property MajorInterval: Double read FMajorInterval write FMajorInterval;
property MajorTickLines: TsChartLine read FMajorTickLines write FMajorTickLines;
property Max: Double read FMax write FMax;
property Min: Double read FMin write FMin;
property MinorSteps: Double read FMinorSteps write FMinorSteps;
property MinorTickLines: TsChartLine read FMinorTickLines write FMinorTickLines;
property ShowGrid: Boolean read FShowGrid write FShowGrid;
property ShowLabels: Boolean read FShowLabels write FShowLabels;
end;
TsChartLegend = class(TsChartFillObj)
end;
TsChartAxisLink = (alPrimary, alSecondary);
TsChartSeries = class(TsChartObj)
private
FXRange: TsCellRange; // cell range containing the x data
FYRange: TsCellRange;
FLabelsRange: TsCellRange;
FXIndex: array of Integer; // index of data point's x value within XRange
FYIndex: array of Integer;
FLabelsIndex: array of Integer;
FYAxis: TsChartAxisLink;
FTitle: String;
function GetCount: Integer;
public
constructor Create(AChart: TsChart);
property Count: Integer read GetCount;
property LabelsRange: TsCellRange read FLabelsRange;
property Title: String read FTitle;
property XRange: TsCellRange read FXRange write FXRange;
property YRange: TsCellRange read FYRange write FYRange;
property YAxis: TsChartAxisLink read FYAxis write FYAxis;
end;
TsChartSeriesSymbol = (cssRect, cssDiamond, cssTriangle, cssTriangleDown,
cssCircle, cssStar);
TsLineSeries = class(TsChartSeries)
private
FLineStyle: TsChartLine;
FShowLines: Boolean;
FShowSymbols: Boolean;
FSymbol: TsChartSeriesSymbol;
FSymbolFill: TsChartFill;
FSymbolBorder: TsChartLine;
FSymbolHeight: Double; // in mm
FSymbolWidth: Double; // in mm
public
constructor Create(AChart: TsChart);
property LineStyle: TsChartLine read FLineStyle write FLineStyle;
property ShowLines: Boolean read FShowLines write FShowLines;
property ShowSymbols: Boolean read FShowSymbols write FShowSymbols;
property Symbol: TsChartSeriesSymbol read FSymbol write FSymbol;
property SymbolBorder: TsChartLine read FSymbolBorder write FSymbolBorder;
property SymbolFill: TsChartFill read FSymbolFill write FSymbolFill;
property SymbolHeight: double read FSymbolHeight write FSymbolHeight;
property SymbolWidth: double read FSymbolWidth write FSymbolWidth;
end;
TsChartSeriesList = class(TFPList)
private
function GetItem(AIndex: Integer): TsChartSeries;
procedure SetItem(AIndex: Integer; AValue: TsChartSeries);
public
property Items[AIndex: Integer]: TsChartSeries read GetItem write SetItem; default;
end;
TsChart = class(TsChartFillObj)
private
FSheetIndex: Integer;
FRow, FCol: Cardinal;
FOffsetX, FOffsetY: Double;
FWidth, FHeight: Double; // Width, Height of the chart, in mm.
FPlotArea: TsChartFillObj;
FXAxis: TsChartAxis;
FYAxis: TsChartAxis;
FY2Axis: TsChartAxis;
FTitle: TsChartText;
FSubTitle: TsChartText;
FLegend: TsChartLegend;
FSeriesList: TsChartSeriesList;
public
constructor Create;
destructor Destroy; override;
function AddSeries(ASeries: TsChartSeries): Integer;
{ Index of worksheet sheet which contains the chart. }
property SheetIndex: Integer read FSheetIndex write FSheetIndex;
{ Row index of the cell in which the chart has its top/left corner (anchor) }
property Row: Cardinal read FRow write FRow;
{ Column index of the cell in which the chart has its top/left corner (anchor) }
property Col: Cardinal read FCol write FCol;
{ Offset of the left chart edge relative to the anchor cell, in mm }
property OffsetX: double read FOffsetX write FOffsetX;
{ Offset of the top chart edge relative to the anchor cell, in mm }
property OffsetY: double read FOffsetY write FOffsetY;
{ Width of the chart, in mm }
property Width: double read FWidth write FHeight;
{ Height of the chart, in mm }
property Height: double read FHeight write FHeight;
{ Attributes of the plot area (rectangle enclosed by axes) }
property PlotArea: TsChartFillObj read FPlotArea write FPlotArea;
{ Attributes of the chart's title }
property Title: TsChartText read FTitle write FTitle;
{ Attributes of the chart's subtitle }
property Subtitle: TsChartText read FSubtitle write FSubTitle;
{ Attributs of the chart's legend }
property Legend: TsChartLegend read FLegend write FLegend;
{ Attributes of the plot's primary x axis (bottom) }
property XAxis: TsChartAxis read FXAxis write FXAxis;
{ Attributes of the plot's primary y axis (left) }
property YAxis: TsChartAxis read FYAxis write FYAxis;
{ Attributes of the plot's secondary y axis (right) }
property Y2Axis: TsChartAxis read FY2Axis write FY2Axis;
{ Attributes of the series }
property Series: TsChartSeriesList read FSeriesList write FSeriesList;
end;
TsChartList = class(TObjectList)
private
function GetItem(AIndex: Integer): TsChart;
procedure SetItem(AIndex: Integer; AValue: TsChart);
public
property Items[AIndex: Integer]: TsChart read GetItem write SetItem; default;
end;
implementation
const
DEFAULT_LINE_WIDTH = 0.75; // pts
{ TsChartObj }
constructor TsChartObj.Create(AOwner: TsChartObj);
begin
inherited Create;
FOwner := AOwner;
FVisible := true;
end;
{ TsChartFillObj }
constructor TsChartFillObj.Create(AOwner: TsChartObj);
begin
inherited Create(AOwner);
FBackground.Style := fsSolidFill;
FBackground.BgColor := scWhite;
FBackground.FgColor := scWhite;
FBorder.Style := cpsSolid;
FBorder.Width := PtsToMM(DEFAULT_LINE_WIDTH);
FBorder.Color := scBlack;
end;
{ TsChartText }
constructor TsChartText.Create(AOwner: TsChartObj);
begin
inherited Create(AOwner);
FShowCaption := true;
FFont.FontName := ''; // replace by workbook's default font
FFont.Size := 0; // replace by workbook's default font size
FFont.Style := [];
FFont.Color := scBlack;
end;
{ TsChartAxis }
constructor TsChartAxis.Create(AOwner: TsChartObj);
begin
inherited Create(AOwner);
FAutomaticMajorInterval := true;
FAutomaticMinorSteps := true;
FLabelFont.FontName := ''; // replace by workbook's default font
FLabelFont.Size := 0; // Replace by workbook's default font size
FLabelFont.Style := [];
FLabelFont.Color := scBlack;
FShowLabels := true;
FAxisLine.Color := scBlack;
FAxisLine.Style := cpsSolid;
FAxisLine.Width := PtsToMM(DEFAULT_LINE_WIDTH);
FMajorTickLines.Color := scBlack;
FMajorTickLines.Style := cpsSolid;
FMajorTickLines.Width := PtsToMM(DEFAULT_LINE_WIDTH);
FMinorTickLines.Color := scBlack;
FMinorTickLines.Style := cpsSolid;
FMinorTickLines.Width := PtsToMM(DEFAULT_LINE_WIDTH);
FGridLines.Color := scSilver;
FGridLines.Style := cpsDotted;
FGridLines.Width := PtsToMM(DEFAULT_LINE_WIDTH);
end;
{ TsChartSeries }
constructor TsChartSeries.Create(AChart: TsChart);
begin
inherited Create(AChart);
AChart.AddSeries(self);
FTitle := 'Series ' + IntToStr(AChart.Series.Count);
end;
function TsChartSeries.GetCount: Integer;
begin
Result := Length(FYIndex);
end;
{ TsChartSeriesList }
function TsChartSeriesList.GetItem(AIndex: Integer): TsChartSeries;
begin
Result := TsChartSeries(inherited Items[AIndex]);
end;
procedure TsChartSeriesList.SetItem(AIndex: Integer; AValue: TsChartSeries);
begin
inherited Items[AIndex] := AValue;
end;
{ TsLineSeries }
constructor TsLineSeries.Create(AChart: TsChart);
begin
inherited Create(AChart);
FLineStyle.Color := scBlack;
FLineStyle.Style := cpsSolid;
FLineStyle.Width := PtsToMM(DEFAULT_LINE_WIDTH);
FSymbolBorder.Color := scBlack;
FSymbolBorder.Style := cpsSolid;
FSymbolBorder.Width := PtsToMM(DEFAULT_LINE_WIDTH);
FSymbolFill.FgColor := scWhite;
FSymbolFill.BgColor := scWhite;
FSymbolFill.Style := fsSolidFill;
FSymbolWidth := 2.5;
FSymbolHeight := 2.5;
end;
{ TsChart }
constructor TsChart.Create;
begin
inherited Create(nil);
FSheetIndex := 0;
FRow := 0;
FCol := 0;
FOffsetX := 0.0;
FOffsetY := 0.0;
FWidth := 12;
FHeight := 9;
FTitle := TsChartText.Create(self);
FTitle.Font.Size := 14;
FSubTitle := TsChartText.Create(self);
FSubTitle.Font.Size := 12;
FLegend := TsChartLegend.Create(self);
FXAxis := TsChartAxis.Create(self);
FXAxis.Caption := 'x axis';
FXAxis.LabelFont.Size := 9;
FXAxis.Font.Size := 10;
FXAxis.Font.Style := [fssBold];
FYAxis := TsChartAxis.Create(self);
FYAxis.Caption := 'y axis';
FYAxis.LabelFont.Size := 9;
FYAxis.Font.Size := 10;
FYAxis.Font.Style := [fssBold];
FY2Axis := TsChartAxis.Create(self);
FY2Axis.Caption := 'Secondary y axis';
FY2Axis.LabelFont.Size := 9;
FY2Axis.Font.Size := 10;
FY2Axis.Font.Style := [fssBold];
FYAxis.Visible := false;
FSeriesList := TsChartSeriesList.Create;
end;
destructor TsChart.Destroy;
begin
FSeriesList.Free;
FXAxis.Free;
FYAxis.Free;
FY2Axis.Free;
FLegend.Free;
FTitle.Free;
FSubtitle.Free;
inherited;
end;
function TsChart.AddSeries(ASeries: TsChartSeries): Integer;
begin
Result := FSeriesList.Add(ASeries);
end;
{ TsChartList }
function TsChartList.GetItem(AIndex: Integer): TsChart;
begin
Result := TsChart(inherited Items[AIndex]);
end;
procedure TsChartlist.SetItem(AIndex: Integer; AValue: TsChart);
begin
inherited Items[AIndex] := AValue;
end;
end.

View File

@ -23,7 +23,8 @@ uses
clocale, clocale,
{$endif}{$endif}{$endif} {$endif}{$endif}{$endif}
Classes, SysUtils, fpimage, avglvltree, lconvencoding, Classes, SysUtils, fpimage, avglvltree, lconvencoding,
fpsTypes, fpsExprParser, fpsClasses, fpsNumFormat, fpsPageLayout, fpsImages; fpsTypes, fpsExprParser, fpsClasses, fpsNumFormat, fpsPageLayout,
fpsImages, fpsChart;
type type
{ Forward declarations } { Forward declarations }
@ -69,6 +70,7 @@ type
FHyperlinks: TsHyperlinks; FHyperlinks: TsHyperlinks;
FFormulas: TsFormulas; FFormulas: TsFormulas;
FImages: TFPList; FImages: TFPList;
FCharts: TsChartList;
FRows, FCols: TIndexedAVLTree; // This lists contain only rows or cols with styles different from default FRows, FCols: TIndexedAVLTree; // This lists contain only rows or cols with styles different from default
FActiveCellRow: Cardinal; FActiveCellRow: Cardinal;
FActiveCellCol: Cardinal; FActiveCellCol: Cardinal;
@ -558,6 +560,10 @@ type
procedure AddHyperlinkToImage(AImageIndex: Integer; ATarget: String; procedure AddHyperlinkToImage(AImageIndex: Integer; ATarget: String;
AToolTip: String = ''); AToolTip: String = '');
{ Charts }
function WriteChart(ARow, ACol: Cardinal; AWidth, AHeight: Double;
AOffsetX: Double = 0.0; AOffsetY: Double = 0.0): Integer;
{ Protection } { Protection }
procedure Protect(AEnable: Boolean); procedure Protect(AEnable: Boolean);
@ -1137,6 +1143,7 @@ begin
FHyperlinks := TsHyperlinks.Create; FHyperlinks := TsHyperlinks.Create;
FFormulas := TsFormulas.Create; FFormulas := TsFormulas.Create;
FImages := TFPList.Create; FImages := TFPList.Create;
FCharts := TsChartList.Create;
FPageLayout := TsPageLayout.Create(self); FPageLayout := TsPageLayout.Create(self);
@ -1179,6 +1186,7 @@ begin
FHyperlinks.Free; FHyperlinks.Free;
FFormulas.Free; FFormulas.Free;
FImages.Free; FImages.Free;
FCharts.Free;
inherited Destroy; inherited Destroy;
end; end;
@ -3732,6 +3740,26 @@ begin
DeleteFormula(ACell); DeleteFormula(ACell);
end; end;
{@@ ----------------------------------------------------------------------------
Creates a chart object with its top/left corner in the specified row/colum and
having the specified width. Inserts the chart in the FCharts list of the
worksheet and returns its index.
-------------------------------------------------------------------------------}
function TsWorksheet.WriteChart(ARow, ACol: Cardinal; AWidth, AHeight: Double;
AOffsetX: Double = 0.0; AOffsetY: Double = 0.0): Integer;
var
chart: TsChart;
begin
chart := TsChart.Create;
chart.Row := ARow;
chart.Col := ACol;
chart.OffsetX := AOffsetX;
chart.OffsetY := AOffsetY;
chart.Width := AWidth;
chart.Height := AHeight;
Result := FCharts.Add(chart);
end;
{@@ ---------------------------------------------------------------------------- {@@ ----------------------------------------------------------------------------
Returns the parameters of the image stored in the internal image list at Returns the parameters of the image stored in the internal image list at
the specified index. the specified index.