Improves FPSpreadsheet examples and improves the Grid component

git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@1229 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
sekelsenmat
2010-05-25 09:11:02 +00:00
parent 3ac6af427e
commit 06ae369ff5
13 changed files with 352 additions and 34 deletions

View File

@ -4,6 +4,7 @@
<Version Value="7"/>
<General>
<Flags>
<AlwaysBuild Value="False"/>
<UseDefaultCompilerOptions Value="True"/>
</Flags>
<SessionStorage Value="InProjectDir"/>
@ -72,6 +73,9 @@
</SyntaxOptions>
</Parsing>
<Linking>
<Debugging>
<GenerateDebugInfo Value="True"/>
</Debugging>
<Options>
<Win32>
<GraphicApplication Value="True"/>
@ -79,6 +83,9 @@
</Options>
</Linking>
<Other>
<CompilerMessages>
<UseMsgFile Value="True"/>
</CompilerMessages>
<CompilerPath Value="$(CompPath)"/>
</Other>
</CompilerOptions>

View File

@ -13,7 +13,6 @@ uses
{$R *.res}
begin
Application.Title:='project1';
Application.Initialize;
Application.CreateForm(TFPSChartForm, FPSChartForm);
Application.Run;

View File

@ -1,16 +1,17 @@
object FPSChartForm: TFPSChartForm
Left = 179
Height = 331
Top = 157
Width = 742
Left = 239
Height = 382
Top = 154
Width = 700
Caption = 'FPSpreadsheet Chart Example'
ClientHeight = 331
ClientWidth = 742
ClientHeight = 382
ClientWidth = 700
OnCreate = FormCreate
LCLVersion = '0.9.29'
object MyChart: TChart
Left = 400
Left = 352
Height = 240
Top = 24
Top = 136
Width = 336
AxisList = <
item
@ -37,26 +38,106 @@ object FPSChartForm: TFPSChartForm
object WorksheetGrid: TsWorksheetGrid
Left = 16
Height = 240
Top = 24
Width = 360
Top = 136
Width = 328
Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine, goRangeSelect, goEditing, goSmoothScroll]
TabOrder = 1
end
object btnCreateGraphic: TButton
Left = 104
Left = 464
Height = 25
Top = 280
Top = 56
Width = 128
Caption = 'Create Graphic'
OnClick = btnCreateGraphicClick
TabOrder = 2
end
object Label1: TLabel
Left = 16
Height = 34
Top = 8
Width = 676
AutoSize = False
Caption = 'Please add data to the grid or load it from a file, then choose the location of the data for the X and Y axises and click on the button "Create Graphic" to generate a chart.'
ParentColor = False
WordWrap = True
end
object editSourceFile: TFileNameEdit
Left = 152
Height = 22
Top = 48
Width = 136
DialogOptions = []
FilterIndex = 0
HideDirectories = False
ButtonWidth = 23
NumGlyphs = 0
MaxLength = 0
TabOrder = 3
end
object Label2: TLabel
Left = 14
Height = 18
Top = 51
Width = 130
Caption = 'Source Spreadsheet:'
ParentColor = False
end
object btnLoadSpreadsheet: TButton
Left = 320
Height = 25
Top = 48
Width = 75
Caption = 'Load'
OnClick = btnLoadSpreadsheetClick
TabOrder = 4
end
object editXAxis: TLabeledEdit
Left = 64
Height = 22
Top = 80
Width = 80
EditLabel.AnchorSideLeft.Control = editXAxis
EditLabel.AnchorSideTop.Control = editXAxis
EditLabel.AnchorSideTop.Side = asrCenter
EditLabel.AnchorSideRight.Control = editXAxis
EditLabel.AnchorSideBottom.Control = editXAxis
EditLabel.Left = 17
EditLabel.Height = 18
EditLabel.Top = 82
EditLabel.Width = 44
EditLabel.Caption = 'X-Axis:'
EditLabel.ParentColor = False
LabelPosition = lpLeft
TabOrder = 5
Text = 'A1:A5'
end
object EditYAxis: TLabeledEdit
Left = 208
Height = 22
Top = 80
Width = 80
EditLabel.AnchorSideLeft.Control = EditYAxis
EditLabel.AnchorSideTop.Control = EditYAxis
EditLabel.AnchorSideTop.Side = asrCenter
EditLabel.AnchorSideRight.Control = EditYAxis
EditLabel.AnchorSideBottom.Control = EditYAxis
EditLabel.Left = 161
EditLabel.Height = 18
EditLabel.Top = 82
EditLabel.Width = 44
EditLabel.Caption = 'Y-Axis:'
EditLabel.ParentColor = False
LabelPosition = lpLeft
TabOrder = 6
Text = 'B1:B5'
end
object FPSChartSource: TsWorksheetChartSource
PointsNumber = 5
YFirstCellCol = 1
XSelectionDirection = fpsVerticalSelection
YSelectionDirection = fpsVerticalSelection
left = 376
top = 264
left = 632
top = 56
end
end

View File

@ -6,7 +6,8 @@ interface
uses
Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
StdCtrls, Grids, fpspreadsheetchart, fpspreadsheetgrid, TAGraph, TASeries;
StdCtrls, Grids, EditBtn, ExtCtrls, fpspreadsheetchart, fpspreadsheetgrid,
TAGraph, TASeries;
type
@ -14,11 +15,19 @@ type
TFPSChartForm = class(TForm)
btnCreateGraphic: TButton;
btnLoadSpreadsheet: TButton;
editSourceFile: TFileNameEdit;
Label1: TLabel;
Label2: TLabel;
editXAxis: TLabeledEdit;
EditYAxis: TLabeledEdit;
MyChart: TChart;
FPSChartSource: TsWorksheetChartSource;
MyChartLineSeries: TLineSeries;
WorksheetGrid: TsWorksheetGrid;
procedure btnCreateGraphicClick(Sender: TObject);
procedure btnLoadSpreadsheetClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ private declarations }
public
@ -30,14 +39,40 @@ var
implementation
uses
// FPSpreadsheet and supported formats
fpspreadsheet, xlsbiff8, xlsbiff5, xlsbiff2, xlsxooxml, fpsopendocument;
{$R *.lfm}
{ TFPSChartForm }
procedure TFPSChartForm.btnCreateGraphicClick(Sender: TObject);
begin
FPSChartSource.LoadPropertiesFromStrings(editXAxis.Text, editYAxis.Text, '', '', '');
FPSChartSource.LoadFromWorksheetGrid(WorksheetGrid);
end;
procedure TFPSChartForm.btnLoadSpreadsheetClick(Sender: TObject);
var
Format: TsSpreadsheetFormat;
lExt: string;
begin
// First some logic to detect the format from the extension
lExt := ExtractFileExt(editSourceFile.Text);
if lExt = STR_EXCEL_EXTENSION then Format := sfExcel2
else if lExt = STR_OOXML_EXCEL_EXTENSION then Format := sfOOXML
else if lExt = STR_OPENDOCUMENT_CALC_EXTENSION then Format := sfOpenDocument
else raise Exception.Create('Invalid File Extension');
// Now the actual loading
WorksheetGrid.LoadFromSpreadsheetFile(editSourceFile.Text, Format);
end;
procedure TFPSChartForm.FormCreate(Sender: TObject);
begin
editSourceFile.InitialDir := ExtractFilePath(ParamStr(0));
end;
end.

Binary file not shown.

Binary file not shown.

View File

@ -189,7 +189,8 @@ begin
//process each cell of the row
CellNode:=RowNode.FindNode('table:table-cell');
while Assigned(CellNode) do begin
while Assigned(CellNode) do
begin
ParamColsRepeated:=GetAttrValue(CellNode,'table:number-columns-repeated');
if ParamColsRepeated='' then ParamColsRepeated:='1';
@ -200,11 +201,9 @@ begin
for ColsCount:=0 to StrToInt(ParamColsRepeated)-1 do begin
if ParamValueType='string' then
ReadLabel(Row+RowsCount,Col+ColsCount,CellNode)
else
if ParamFormula<>'' then
else if ParamFormula<>'' then
ReadFormula(Row+RowsCount,Col+ColsCount,CellNode)
else
if ParamValueType='float' then
else if ParamValueType='float' then
ReadNumber(Row+RowsCount,Col+ColsCount,CellNode);
end; //for ColsCount
end; //for RowsCount
@ -226,7 +225,8 @@ end;
procedure TsSpreadOpenDocReader.ReadFormula(ARow: Word; ACol : Word; ACellNode : TDOMNode);
begin
// For now just read the number
ReadNumber(ARow, ACol, ACellNode);
end;
procedure TsSpreadOpenDocReader.ReadLabel(ARow: Word; ACol : Word; ACellNode : TDOMNode);
@ -237,14 +237,21 @@ end;
procedure TsSpreadOpenDocReader.ReadNumber(ARow: Word; ACol : Word; ACellNode : TDOMNode);
var
FSettings: TFormatSettings;
Value: String;
Value, Str: String;
lNumber: Double;
begin
FSettings.DecimalSeparator:='.';
Value:=GetAttrValue(ACellNode,'office:value');
if UpperCase(Value)='1.#INF' then begin
if UpperCase(Value)='1.#INF' then
begin
FWorkSheet.WriteNumber(Arow,ACol,1.0/0.0);
end else begin
FWorkSheet.WriteNumber(Arow,ACol,StrToFloat(GetAttrValue(ACellNode,'office:value'),FSettings));
end
else
begin
// Don't merge, or else we can't debug
Str := GetAttrValue(ACellNode,'office:value');
lNumber := StrToFloat(Str,FSettings);
FWorkSheet.WriteNumber(Arow,ACol,lNumber);
end;
end;

View File

@ -74,7 +74,14 @@ type
TCellContentType = (cctEmpty, cctFormula, cctRPNFormula, cctNumber, cctUTF8String);
{@@ Cell structure for TsWorksheet }
{@@ Cell structure for TsWorksheet
Never suppose that all *Value fields are valid,
only one of the ContentTypes is valid. For other fields
use TWorksheet.ReadAsUTF8Text and similar methods
@see TWorksheet.ReadAsUTF8Text
}
TCell = record
Col: Byte; // zero-based
@ -211,6 +218,8 @@ procedure RegisterSpreadFormat(
AWriterClass: TsSpreadWriterClass;
AFormat: TsSpreadsheetFormat);
implementation
uses

View File

@ -19,12 +19,10 @@ uses
// FPSpreadsheet Visual
fpspreadsheetgrid,
// FPSpreadsheet
fpspreadsheet;
fpspreadsheet, fpsutils;
type
TsSelectionDirection = (fpsVerticalSelection, fpsHorizontalSelection);
{@@ Chart data source designed to work together with TChart from Lazarus
to display the data.
@ -60,6 +58,7 @@ type
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure LoadFromWorksheetGrid(const AValue: TsWorksheetGrid);
procedure LoadPropertiesFromStrings(AXInterval, AYInterval, AXTitle, AYTitle, ATitle: string);
public
published
// property WorksheetGrid: TsWorksheetGrid read FWorksheetGrid write SetWorksheetGrid;
@ -205,4 +204,16 @@ begin
Notify;
end;
procedure TsWorksheetChartSource.LoadPropertiesFromStrings(AXInterval,
AYInterval, AXTitle, AYTitle, ATitle: string);
var
lXCount, lYCount: Integer;
begin
ParseIntervalString(AXInterval, FXFirstCellRow, FXFirstCellCol, lXCount, FXSelectionDirection);
ParseIntervalString(AYInterval, FYFirstCellRow, FYFirstCellCol, lYCount, FYSelectionDirection);
if lXCount <> lYCount then raise Exception.Create(
'TsWorksheetChartSource.LoadPropertiesFromStrings: Interval sizes don''t match');
FPointsNumber := lXCount;
end;
end.

View File

@ -31,6 +31,7 @@ type
{ methods }
constructor Create(AOwner: TComponent); override;
procedure LoadFromWorksheet(AWorksheet: TsWorksheet);
procedure LoadFromSpreadsheetFile(AFileName: string; AFormat: TsSpreadsheetFormat; AWorksheetIndex: Integer = 0);
procedure SaveToWorksheet(AWorksheet: TsWorksheet);
property DisplayFixedColRow: Boolean read FDisplayFixedColRow write SetDisplayFixedColRow;
end;
@ -210,7 +211,7 @@ begin
begin
lCol := lCell^.Col;
lRow := lCell^.Row;
lStr := lCell^.UTF8StringValue;
lStr := FWorksheet.ReadAsUTF8Text(lRow, lCol);
if DisplayFixedColRow then
SetCells(lCol + 1, lRow + 1, lStr)
@ -221,6 +222,20 @@ begin
end;
end;
procedure TsCustomWorksheetGrid.LoadFromSpreadsheetFile(AFileName: string;
AFormat: TsSpreadsheetFormat; AWorksheetIndex: Integer);
var
lWorkbook: TsWorkbook;
begin
lWorkbook := TsWorkbook.Create;
try
lWorkbook.ReadFromFile(AFileName, AFormat);
LoadFromWorksheet(lWorkbook.GetWorksheetByIndex(AWorksheetIndex));
finally
lWorkbook.Free;
end;
end;
procedure TsCustomWorksheetGrid.SaveToWorksheet(AWorksheet: TsWorksheet);
var
x, y: Integer;

View File

@ -1,3 +1,6 @@
{
Utility functions from FPSpreadsheet
}
unit fpsutils;
{$mode objfpc}{$H+}
@ -5,8 +8,13 @@ unit fpsutils;
interface
uses
Classes, SysUtils;
Classes, SysUtils, StrUtils;
// Exported types
type
TsSelectionDirection = (fpsVerticalSelection, fpsHorizontalSelection);
// Endianess helper functions
function WordToLE(AValue: Word): Word;
function DWordToLE(AValue: Cardinal): Cardinal;
function IntegerToLE(AValue: Integer): Integer;
@ -16,6 +24,17 @@ function WordLEtoN(AValue: Word): Word;
function DWordLEtoN(AValue: Cardinal): Cardinal;
function WideStringLEToN(const AValue: WideString): WideString;
// Other routines
function ParseIntervalString(const AStr: string;
var AFirstCellRow, AFirstCellCol, ACount: Integer;
var ADirection: TsSelectionDirection): Boolean;
function ParseCellString(const AStr: string;
var ACellRow, ACellCol: Integer): Boolean;
function ParseCellRowString(const AStr: string;
var AResult: Integer): Boolean;
function ParseCellColString(const AStr: string;
var AResult: Integer): Boolean;
implementation
{
@ -114,5 +133,136 @@ begin
{$ENDIF}
end;
{@@
Parses strings like A5:A10 into an selection interval information
}
function ParseIntervalString(const AStr: string;
var AFirstCellRow, AFirstCellCol, ACount: Integer;
var ADirection: TsSelectionDirection): Boolean;
var
Cells: TStringList;
LastCellRow, LastCellCol: Integer;
begin
Result := True;
// First get the cells
Cells := TStringList.Create;
ExtractStrings([':'],[], PChar(AStr), Cells);
// Then parse each of them
Result := ParseCellString(Cells[0], AFirstCellRow, AFirstCellCol);
if not Result then Exit;
Result := ParseCellString(Cells[1], LastCellRow, LastCellCol);
if not Result then Exit;
if AFirstCellRow = LastCellRow then
begin
ADirection := fpsHorizontalSelection;
ACount := LastCellCol - AFirstCellCol + 1;
end
else if AFirstCellCol = LastCellCol then
begin
ADirection := fpsVerticalSelection;
ACount := LastCellRow - AFirstCellRow + 1;
end
else Exit(False);
end;
{@@
Parses a cell string, like 'A1' into zero-based column and row numbers
The parser is a simple state machine, with the following states:
0 - Reading Column part 1 (necesserely needs a letter)
1 - Reading Column part 2, but could be the first number as well
2 - Reading Row
}
function ParseCellString(const AStr: string; var ACellRow, ACellCol: Integer): Boolean;
var
i: Integer;
state: Integer;
Col, Row: string;
lChar: Char;
const
cLetters = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'W', 'X', 'Y', 'Z'];
cDigits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
begin
// Starting state
Result := True;
state := 0;
Col := '';
Row := '';
// Separates the string into a row and a col
for i := 0 to Length(AStr) - 1 do
begin
lChar := AStr[i + 1];
case state of
0:
begin
if lChar in cLetters then
begin
Col := lChar;
state := 1;
end
else Exit(False);
end;
1:
begin
if lChar in cLetters then Col := Col + lChar
else if lChar in cDigits then
begin
Row := lChar;
state := 2;
end
else Exit(False);
end;
2:
begin
if lChar in cDigits then Row := Row + lChar
else Exit(False);
end;
end;
end;
// Now parses each separetely
ParseCellRowString(Row, ACellRow);
ParseCellColString(Col, ACellCol);
end;
function ParseCellRowString(const AStr: string; var AResult: Integer): Boolean;
begin
try
AResult := StrToInt(AStr) - 1;
except
Result := False;
end;
Result := True;
end;
function ParseCellColString(const AStr: string; var AResult: Integer): Boolean;
const
INT_NUM_LETTERS = 26;
begin
Result := False;
AResult := 0;
if Length(AStr) = 1 then AResult := Ord(AStr[1]) - Ord('A')
else if Length(AStr) = 2 then
begin
AResult := (Ord(AStr[1]) - Ord('A') + 1) * INT_NUM_LETTERS
+ Ord(AStr[2]) - Ord('A');
end
else Exit(False);
Result := True;
end;
end.

View File

@ -1250,9 +1250,11 @@ begin
try
// Only one stream is necessary for any number of worksheets
OLEDocument.Stream := MemStream;
OLEStorage.ReadOLEFile(AFileName, OLEDocument);
// Check if the operation succeded
if MemStream.Size = 0 then raise Exception.Create('FPSpreadsheet: Reading the OLE document failed');
// Rewind the stream and read from it
MemStream.Position := 0;
ReadFromStream(MemStream, AData);

View File

@ -1234,9 +1234,11 @@ begin
try
// Only one stream is necessary for any number of worksheets
OLEDocument.Stream := MemStream;
OLEStorage.ReadOLEFile(AFileName, OLEDocument,'Workbook');
// Check if the operation succeded
if MemStream.Size = 0 then raise Exception.Create('FPSpreadsheet: Reading the OLE document failed');
// Rewind the stream and read from it
MemStream.Position := 0;
ReadFromStream(MemStream, AData);