Merged revision(s) 65376 #4160d7c7ef, 65379 #becaac9c5d, 65384-65385 #9d7566217a-#9d7566217a, 65389 #9f896e3f00, 65391 #7bf4f7da41, 65393-65394 #5ef5288afe-#5ef5288afe, 65420-65423 #b75cbf6965-#b75cbf6965, 65425 #2bf2902c91, 65427 #f549172f40, 65431 #51044e806b, 65440 #0ec2f041e7, 65453 #4553a2eafb from trunk:

TAChart: Update xml help.
........
TAChart: Decode system colors in text elements.
........
TAChart: Add method GetAxisRange to determine the range of an axis covered by all series assigned to it.
........
TAChart: Use real axis limits in ChartEditor demo.
........
TAChart: Rename TChart.GetAxisRange to .GetAllSeriesAxisLimits (more precise).
........
TAChart/ChartEditor demo: Check that axis maximum is always greater than the minimum.
........
TAChart/ChartEditor demo: Fix bug in handling of axis.Inverted.
........
TAChart: Update xml doc.
........
TAChart/ChartEditor demo: Fix update of axis grid visibility.
........
TAChart/ChartEditor demo: Fix axis lines not being updated.
........
TAChart: Always draw axis arrows with solid pen.
........
TAChart: Fix usage of axistransformations in ChartLiveView having ExtentY = lveAuto.
........
TAChart: Fix initialization error in TChartLiveView when ExtentY is lveAuto.
........
TAChart: Fix axis scaling of TChartLiveView with coincident axis limits and ExtentY = lveAuto.
........
TAChart/ChartEditor demo: add forgotten lfm file.
........
TAChart/ChartLiveView: Fix autoscaling when multiple axes are used. Add new ExtentY mode, lveMultiAxisRange
........
TAChart/TChartLiveView: Add some comments. Add xml documentation.
........

git-svn-id: branches/fixes_2_2@65460 -
This commit is contained in:
maxim 2021-07-15 21:13:02 +00:00
parent 3ed9efb552
commit 2c461bda95
17 changed files with 383 additions and 76 deletions

2
.gitattributes vendored
View File

@ -5404,6 +5404,7 @@ components/tachart/fpdoc/descr.lst svneol=native#text/plain
components/tachart/fpdoc/input.lst svneol=native#text/plain
components/tachart/fpdoc/tachartaxis.xml svneol=native#text/plain
components/tachart/fpdoc/tachartaxisutils.xml svneol=native#text/xml
components/tachart/fpdoc/tachartliveview.xml svneol=native#text/xml
components/tachart/fpdoc/tachartutils.xml svneol=native#text/plain
components/tachart/fpdoc/tacustomseries.xml svneol=native#text/plain
components/tachart/fpdoc/tacustomsource.xml svneol=native#text/xml
@ -5415,6 +5416,7 @@ components/tachart/fpdoc/talegend.xml svneol=native#text/plain
components/tachart/fpdoc/tamath.xml svneol=native#text/xml
components/tachart/fpdoc/taseries.xml svneol=native#text/plain
components/tachart/fpdoc/tasources.xml svneol=native#text/plain
components/tachart/fpdoc/tatools.xml svneol=native#text/xml
components/tachart/fpdoc/tatypes.xml svneol=native#text/plain
components/tachart/fpvectorial/tachartfpvectorial.lpk svneol=native#text/plain
components/tachart/fpvectorial/tachartfpvectorial.pas svneol=native#text/pascal

View File

@ -11,7 +11,7 @@ object ChartAxisEditor: TChartAxisEditor
OnCreate = FormCreate
OnShow = FormShow
Position = poScreenCenter
LCLVersion = '2.1.0.0'
LCLVersion = '2.3.0.0'
object ButtonPanel: TButtonPanel
Left = 6
Height = 34

View File

@ -179,8 +179,17 @@ begin
end;
procedure TChartAxisEditor.OKButtonClick(Sender: TObject);
var
msg: String;
C: TWinControl;
begin
FOKClicked := true;
if not FAxisFrame.Validate(msg, C) then
begin
C.SetFocus;
MessageDlg(msg, mtError, [mbOK], 0);
ModalResult := mrNone;
end else
FOKClicked := true;
end;
procedure TChartAxisEditor.Prepare(Axis: TChartAxis;

View File

@ -40,23 +40,24 @@ object ChartAxisFrame: TChartAxisFrame
Height = 446
Top = 25
Width = 646
ActivePage = pgLabels
ActivePage = pgGrid
Align = alClient
TabIndex = 1
TabIndex = 2
TabOrder = 1
OnChanging = PageControlChanging
object pgTitle: TTabSheet
Caption = 'Title'
ClientHeight = 378
ClientHeight = 418
ClientWidth = 638
object TitleMemoPanel: TPanel
Left = 8
Height = 151
Height = 191
Top = 8
Width = 622
Align = alClient
BorderSpacing.Around = 8
BevelOuter = bvNone
ClientHeight = 151
ClientHeight = 191
ClientWidth = 622
TabOrder = 0
object lblTitle: TLabel
@ -68,7 +69,6 @@ object ChartAxisFrame: TChartAxisFrame
Width = 21
BorderSpacing.Top = 2
Caption = 'Text'
ParentColor = False
end
object mmoTitle: TMemo
AnchorSideLeft.Control = lblTitle
@ -79,7 +79,7 @@ object ChartAxisFrame: TChartAxisFrame
AnchorSideBottom.Control = TitleMemoPanel
AnchorSideBottom.Side = asrBottom
Left = 0
Height = 130
Height = 170
Top = 21
Width = 622
Anchors = [akTop, akLeft, akRight, akBottom]
@ -109,7 +109,7 @@ object ChartAxisFrame: TChartAxisFrame
object TitleParamsPanel: TPanel
Left = 8
Height = 203
Top = 167
Top = 207
Width = 622
Align = alBottom
BorderSpacing.Around = 8
@ -199,7 +199,6 @@ object ChartAxisFrame: TChartAxisFrame
Width = 45
Caption = 'Distance'
FocusControl = seTitleDistance
ParentColor = False
end
object seTitleDistance: TSpinEdit
AnchorSideLeft.Control = lblTitleDistance
@ -248,7 +247,6 @@ object ChartAxisFrame: TChartAxisFrame
BorderSpacing.Left = 16
BorderSpacing.Top = 6
Caption = 'Automatic'
ParentColor = False
end
object cbAutoMax: TCheckBox
AnchorSideLeft.Control = lblAutomatic
@ -427,7 +425,6 @@ object ChartAxisFrame: TChartAxisFrame
Top = 31
Width = 38
Caption = 'Format'
ParentColor = False
end
object seLabelDistance: TSpinEdit
AnchorSideLeft.Control = edLabelFormat
@ -453,7 +450,6 @@ object ChartAxisFrame: TChartAxisFrame
Width = 45
Caption = 'Distance'
FocusControl = seLabelDistance
ParentColor = False
end
end
object Bevel2: TBevel
@ -524,7 +520,6 @@ object ChartAxisFrame: TChartAxisFrame
Width = 67
BorderSpacing.Left = 16
Caption = 'Outer length'
ParentColor = False
end
object lblTickInnerLength: TLabel
AnchorSideLeft.Control = lblTickLength
@ -535,7 +530,6 @@ object ChartAxisFrame: TChartAxisFrame
Top = 37
Width = 64
Caption = 'Inner length'
ParentColor = False
end
object cbTickColor: TColorButton
AnchorSideTop.Side = asrCenter
@ -555,8 +549,8 @@ object ChartAxisFrame: TChartAxisFrame
end
object pgGrid: TTabSheet
Caption = 'Grid'
ClientHeight = 378
ClientWidth = 513
ClientHeight = 418
ClientWidth = 638
object gbGrid: TGroupBox
AnchorSideLeft.Control = pgGrid
AnchorSideTop.Control = pgGrid
@ -584,14 +578,15 @@ object ChartAxisFrame: TChartAxisFrame
BorderSpacing.Right = 16
BorderSpacing.Bottom = 8
Caption = 'Visible'
OnChange = cbGridVisibleChange
TabOrder = 0
end
end
end
object pgLine: TTabSheet
Caption = 'Line'
ClientHeight = 378
ClientWidth = 513
ClientHeight = 418
ClientWidth = 638
object gbFrame: TGroupBox
AnchorSideLeft.Control = pgLine
AnchorSideTop.Control = pgLine
@ -599,19 +594,19 @@ object ChartAxisFrame: TChartAxisFrame
Left = 8
Height = 119
Top = 8
Width = 275
Width = 400
Anchors = [akTop, akLeft, akRight]
BorderSpacing.Left = 8
BorderSpacing.Top = 8
Caption = 'gbFrame'
ClientHeight = 99
ClientWidth = 271
ClientWidth = 396
TabOrder = 0
object cbFrameVisible: TCheckBox
Left = 16
Height = 19
Top = 8
Width = 239
Width = 364
Align = alTop
BorderSpacing.Left = 16
BorderSpacing.Top = 8
@ -631,18 +626,18 @@ object ChartAxisFrame: TChartAxisFrame
Left = 8
Height = 137
Top = 143
Width = 275
Width = 400
Anchors = [akTop, akLeft, akRight]
BorderSpacing.Top = 16
Caption = 'gbAxisLine'
ClientHeight = 117
ClientWidth = 271
ClientWidth = 396
TabOrder = 1
object cbAxisLineVisible: TCheckBox
Left = 16
Height = 19
Top = 8
Width = 239
Width = 364
Align = alTop
BorderSpacing.Left = 16
BorderSpacing.Top = 8
@ -659,7 +654,7 @@ object ChartAxisFrame: TChartAxisFrame
AnchorSideTop.Control = gbFrame
AnchorSideRight.Control = pgLine
AnchorSideRight.Side = asrBottom
Left = 307
Left = 432
Height = 156
Top = 8
Width = 200
@ -693,7 +688,6 @@ object ChartAxisFrame: TChartAxisFrame
Top = 39
Width = 61
Caption = 'Base length'
ParentColor = False
end
object lblArrowLength: TLabel
AnchorSideLeft.Control = cbArrowVisible
@ -704,7 +698,6 @@ object ChartAxisFrame: TChartAxisFrame
Top = 70
Width = 37
Caption = 'Length'
ParentColor = False
end
object lblArrowWidth: TLabel
AnchorSideLeft.Control = cbArrowVisible
@ -715,7 +708,6 @@ object ChartAxisFrame: TChartAxisFrame
Top = 101
Width = 32
Caption = 'Width'
ParentColor = False
end
object seArrowBaseLength: TSpinEdit
AnchorSideLeft.Control = lblArrowBaseLength

View File

@ -76,6 +76,7 @@ type
procedure cbAutoMinChange(Sender: TObject);
procedure cbAxisLineVisibleChange(Sender: TObject);
procedure cbFrameVisibleChange(Sender: TObject);
procedure cbGridVisibleChange(Sender: TObject);
procedure cbInvertedChange(Sender: TObject);
procedure cbLabelsVisibleChange(Sender: TObject);
procedure cbShowChange(Sender: TObject);
@ -83,6 +84,7 @@ type
procedure cbTitleVisibleChange(Sender: TObject);
procedure edLabelFormatEditingDone(Sender: TObject);
procedure mmoTitleChange(Sender: TObject);
procedure PageControlChanging(Sender: TObject; var AllowChange: Boolean);
procedure rgTitleAlignmentClick(Sender: TObject);
procedure seArrowBaseLengthChange(Sender: TObject);
procedure seArrowLengthChange(Sender: TObject);
@ -95,6 +97,7 @@ type
procedure seTitleDistanceChange(Sender: TObject);
private
FAxis: TChartAxis;
FAxisMin, FAxisMax: Double;
FTitleFontFrame: TChartFontFrame;
FTitleShapeBrushPenMarginsFrame: TChartShapeBrushPenMarginsFrame;
FLabelFontFrame: TChartFontFrame;
@ -119,9 +122,12 @@ type
procedure CalculatePreferredSize(var PreferredWidth, PreferredHeight: integer;
{%H-}WithThemeSpace: Boolean); override;
function GetChart: TChart;
function GetRealAxisMax: Double;
function GetRealAxisMin: Double;
public
constructor Create(AOwner: TComponent); override;
procedure Prepare(Axis: TChartAxis);
function Validate(out AMsg: String; out AControl: TWinControl): Boolean;
property Page: TChartAxisEditorPage read GetPage write SetPage;
end;
@ -266,7 +272,7 @@ end;
procedure TChartAxisFrame.cbAxisLineVisibleChange(Sender: TObject);
begin
FAxis.Grid.Visible := cbGridVisible.Checked;
FAxis.AxisPen.Visible := cbAxisLineVisible.Checked;
end;
procedure TChartAxisFrame.cbFrameVisibleChange(Sender: TObject);
@ -274,9 +280,14 @@ begin
GetChart.Frame.Visible := cbFrameVisible.Checked;
end;
procedure TChartAxisFrame.cbGridVisibleChange(Sender: TObject);
begin
FAxis.Grid.Visible := cbGridVisible.Checked;
end;
procedure TChartAxisFrame.cbInvertedChange(Sender: TObject);
begin
FAxis.Inverted := not FAxis.Inverted;
FAxis.Inverted := cbInverted.Checked;
end;
procedure TChartAxisFrame.cbLabelsVisibleChange(Sender: TObject);
@ -336,6 +347,22 @@ begin
Result := TChartAxisEditorPage(PageControl.ActivePageIndex);
end;
function TChartAxisFrame.GetRealAxisMax: Double;
begin
if cbAutoMax.Checked then
Result := FAxisMax
else
Result := seMaximum.Value;
end;
function TChartAxisFrame.GetRealAxisMin: Double;
begin
if cbAutoMin.Checked then
Result := FAxisMin
else
Result := seMinimum.Value;
end;
procedure TChartAxisFrame.LabelChangedHandler(Sender: TObject);
begin
GetChart.Invalidate;
@ -356,6 +383,20 @@ begin
FAxis.Title.Caption := mmoTitle.Lines.Text;
end;
procedure TChartAxisFrame.PageControlChanging(Sender: TObject;
var AllowChange: Boolean);
var
msg: String;
C: TWinControl;
begin
if not Validate(msg, C) then
begin
C.SetFocus;
MessageDlg(msg, mtError, [mbOK], 0);
AllowChange := false;
end;
end;
procedure TChartAxisFrame.Prepare(Axis: TChartAxis);
begin
FAxis := Axis;
@ -379,8 +420,9 @@ begin
end;
// Page "Labels"
seMaximum.Value := Axis.Range.Max;
seMinimum.Value := Axis.Range.Min;
GetChart.GetAllSeriesAxisLimits(Axis, FAxisMin, FAxisMax);
seMaximum.Value := IfThen(Axis.Range.UseMax, Axis.Range.Max, FAxisMax);
seMinimum.Value := IfThen(Axis.Range.UseMin, Axis.Range.Min, FAxisMin);
cbAutoMax.Checked := not Axis.Range.UseMax;
cbAutoMin.Checked := not Axis.Range.UseMin;
cbInverted.Checked := Axis.Inverted;
@ -491,6 +533,23 @@ begin
FAxis.Title.Shape := AShape;
end;
function TChartAxisFrame.Validate(out AMsg: String; out AControl: TWinControl): Boolean;
begin
Result := false;
if GetRealAxisMin >= GetRealAxisMax then
begin
AMsg := 'The axis minimum must be smaller than the axis maximum.';
if seMaximum.Visible then
AControl := seMaximum
else if seMinimum.Visible then
AControl := seMinimum
else
AControl := cbAutoMax;
exit;
end;
Result := true;
end;
end.

View File

@ -10,7 +10,7 @@ object ChartEditorForm: TChartEditorForm
OnCloseQuery = FormCloseQuery
OnCreate = FormCreate
OnDestroy = FormDestroy
LCLVersion = '2.1.0.0'
LCLVersion = '2.3.0.0'
object ButtonPanel: TButtonPanel
Left = 6
Height = 34
@ -41,6 +41,7 @@ object ChartEditorForm: TChartEditorForm
Constraints.MinWidth = 120
Images = ChartImagesDM.ChartImages
TabOrder = 1
OnChanging = TreeChanging
OnDeletion = TreeDeletion
OnSelectionChanged = TreeSelectionChanged
end
@ -110,7 +111,6 @@ object ChartEditorForm: TChartEditorForm
Font.Color = clWindow
Font.Height = -16
Font.Style = [fsBold]
ParentColor = False
ParentFont = False
end
object Image1: TImage

View File

@ -29,6 +29,8 @@ type
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure OKButtonClick(Sender: TObject);
procedure TreeChanging(Sender: TObject; Node: TTreeNode;
var AllowChange: Boolean);
procedure TreeDeletion(Sender: TObject; Node: TTreeNode);
procedure TreeSelectionChanged(Sender: TObject);
private
@ -57,6 +59,7 @@ type
procedure SaveChartToStream;
procedure SelectNode(ANode: TTreeNode);
procedure RestoreChartFromStream;
function Validate(ANode: TTreeNode; out AMsg: String; out AControl: TWinControl): boolean;
public
procedure SelectAxis(AxisIndex: Integer; APage: TChartAxisEditorPage);
procedure SelectFooter;
@ -210,8 +213,17 @@ begin
end;
procedure TChartEditorForm.ApplyButtonClick(Sender: TObject);
var
msg: String;
C: TWinControl;
begin
RestoreChartFromStream;
if not Validate(Tree.Selected, msg, C) then
begin
C.SetFocus;
MessageDlg(msg, mtError, [mbOK], 0);
ModalResult := mrNone;
end else
RestoreChartFromStream;
end;
procedure TChartEditorForm.FormActivate(Sender: TObject);
@ -338,8 +350,31 @@ begin
end;
procedure TChartEditorForm.OKButtonClick(Sender: TObject);
var
msg: String;
C: TWinControl;
begin
FOKClicked := true;
if not Validate(Tree.selected, msg, C) then
begin
C.SetFocus;
MessageDlg(msg, mtError, [mbOK], 0);
ModalResult := mrNone;
end else
FOKClicked := true;
end;
procedure TChartEditorForm.TreeChanging(Sender: TObject; Node: TTreeNode;
var AllowChange: Boolean);
var
msg: String;
C: TWinControl;
begin
if not Validate(Tree.Selected, msg, C) then
begin
C.SetFocus;
MessageDlg(msg, mtError, [mbOk], 0);
AllowChange := false;
end;
end;
procedure TChartEditorForm.PopulateAxes(AChart: TChart);
@ -559,5 +594,19 @@ begin
ChartImagesDM.ChartImages.GetBitmap(Tree.Selected.ImageIndex, Image1.Picture.Bitmap);
end;
function TChartEditorForm.Validate(ANode: TTreeNode; out AMsg: String;
out AControl: TWinControl): Boolean;
begin
if ANode = nil then
exit(true);
if TObject(ANode.Data) is TChartAxisFrame then
begin
Result := TChartAxisFrame(ANode.Data).Validate(AMsg, AControl);
if not Result then exit;
end;
Result := true;
end;
end.

View File

@ -198,6 +198,7 @@
</CodeGeneration>
<Linking>
<Debugging>
<DebugInfoType Value="dsDwarf2Set"/>
<TrashVariables Value="True"/>
</Debugging>
<Options>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<fpdoc-descriptions><package name="TAChartLazarusPkg"><module name="TAChartLiveView"><element name="TChartLiveView"><short><var>TChartLiveView</var> is a component optimized for displaying a long array of incoming data in a viewport with only the most recent data while older data are flowing to the left out of the vewport.</short><descr>The viewport is updated whenever the full extent of the associated chart changes. However, when property <var>Active</var> is set to <var>false</var> the viewport is fixed -- this is a setting which allows the user to review older data, or change the extent while still new data are coming at the same time.
</descr>
</element><element name="TChartLiveView.Chart"><short>Chart on which the LiveView operates</short>
</element><element name="TChartLiveView.ViewportSize"><short>Width of the visible viewport on the x axis, in graph units</short><descr>If set to 0, the width of the current <var>LogicalExtent</var> is used instead.
</descr>
</element><element name="TChartLiveView.Active"><short>Allows to turn the scrolling controlled by the component ON and OFF.</short>
</element><element name="TChartLiveView.ExtentY"><short>Mode determining how the vertical y axis is displayed in the scrolling viewport</short>
<descr><p><var>lveAuto</var>: adjusts the height to the visible data range automatically. This works also when the chart contains several y axes.
</p><p><var>lveFull</var>: freezes the height to the full extent of the chart.</p><p><var>lveLogical</var>: freezes the height to the logical extent of the chart.</p><p><var>lveMultiAxisRange</var> tries to use a predefined <var>Range</var> of the y axes in case of a multi-axis chart. Use the <var>Range.Min</var>, <var>Range.Max</var>, <var>Range.UseMin</var> and <var>Range.UseMax</var> properties to set up the axis range. Note that the range is extended automatically when a series attached to this axis contains out-of-range y values. When no range is specified (i.e., <var>axis.Range.UseMin=false</var> and <var>axis.Range.UseMax=false</var>) the behaviour is similar to <var>lveAuto</var>.</p>
</descr>
</element><element name="TChartLiveView.Create"><short>Constructor of the <var>TChartLiveView</var>
</short>
</element><element name="TChartLiveView.Destroy"><short>Destructor of the <var>TChartLiveView</var>
</short>
</element>
</module>
</package>
</fpdoc-descriptions>

View File

@ -7,7 +7,7 @@
</element>
<element name="TChartGetMarkEvent"/>
<element name="TChartSeries.Count">
<short>Return number of points in the series.</short>
<short>Returns the number of points in the series.</short>
</element>
<element name="TChartSeries.Add">
<short>Add new point to the right of the series.</short>
@ -32,7 +32,7 @@
<element name="TChartSeries.ListSource">
<errors><link id="TASources.EEditableSourceRequired">EEditableSourceRequired</link>
</errors>
<short>Verify that the data source is editable and return it.</short>
<short>Verifies that the data source is editable and returns it (or <var>nil</var> if the source is not a <var>TListChartSource</var>)</short>
</element>
<element name="TChartSeries.Clear">
<short>Remove all points from the series.</short>
@ -40,12 +40,10 @@
</descr>
</element>
<element name="TChartSeries.Delete">
<errors>
<link id="TASources.EEditableSourceRequired">EEditableSourceRequired</link>
<errors><link id="TASources.EEditableSourceRequired">EEditableSourceRequired</link>
</errors>
<short>Remove point by index.</short>
<descr>
<p>Requires <link id="TChartSeries.ListSource">editable data source</link>.</p>
<short>Removes a data point by index.</short>
<descr><p>Requires <link id="TChartSeries.ListSource">editable data source</link>.</p>
</descr>
</element>
<element name="TChartSeries.SetXValue"/>
@ -55,6 +53,8 @@
</element>
<element name="TChartSeries.Marks">
<short>Mark parameters.</short>
<descr>Marks are the text labels drawn for each data point.
</descr>
</element>
<element name="TChartSeries.OnGetMark">
<short>Called before the drawing of each mark.</short>
@ -81,7 +81,7 @@ from a <link id="TACustomSource.TCustomChartSource"><var>chart source</var>
<short>Return the label for the point number <var>AIndex</var> formatted as per <var>Marks</var> property</short>
</element>
<element name="TChartSeries.Extent">
<short>Return the bounding rectangle for all points in the serie.</short>
<short>Returns the bounding rectangle for all points in the serie.</short>
<seealso><link id="TASources.TCustomChartSource.Extent">TCustomChartSource.Extent</link>
</seealso>
</element>
@ -192,6 +192,20 @@ per data points than specified by <link id="TChartSeriesGetXYCountNeeded"><var>G
By default, i.e. <var>DepthBrightnessDelta=0</var>, the series sides have the same color as the series itself.
</descr>
</element>
<element name="TChartSeries.FindYRange"><short>Finds the y maximum and minimum of the series data having an x between AXMin and AXMax</short>
</element><element name="TChartSeries.IsEmpty"><short>Returns true when the series contains no data.</short>
</element><element name="TChartSeries.LastValueIndex"><short>Returns the index of the last data point.</short>
</element><element name="TChartSeries.MaxXValue"><short>Returns the largest x value</short>
</element><element name="TChartSeries.MinXValue"><short>Returns the smallest x value</short>
</element><element name="TChartSeries.MaxYValue"><short>Returns the largest y value</short>
</element><element name="TChartSeries.MinYValue"><short>Returns the smallest y value</short>
</element><element name="TChartSeries.XValue"><short>Returns the x value of the data point with the given index</short>
</element><element name="TChartSeries.YValue"><short>Returns the y value of the data point with the given index</short>
</element><element name="TChartSeries.XValues"><short>If a source provides data points with multiple x values, the x value with the index <var>AXIndex</var> is returned for the data point at index <var>AIndex</var>
</short>
</element><element name="TChartSeries.YValues"><short>If a source provides data points with multiple y values, the y value with the index <var>AYIndex</var> is returned for the data point at index <var>AIndex</var>
</short>
</element>
</module>
</package>
</fpdoc-descriptions>

View File

@ -268,10 +268,7 @@ For example, <link id="TASeries.TPieSeries">pie series</link> displays a separat
</element>
<element name="TChart.YImageToGraph">
<short>Converts the Y coordinate of a point from image units to graph units.</short>
<seealso>
<link id="TChart.YGraphToImage"/>
<link id="TChart.XImageToGraph"/>
<link id="TChart.ImageToGraph"/>
<seealso><link id="TChart.YGraphToImage"/><link id="TChart.XImageToGraph"/><link id="TChart.ImageToGraph"/>
</seealso>
</element>
<element name="TChart.CurrentExtent">
@ -316,7 +313,9 @@ chart margin and the space reserved for series marks.
</element>
<element name="Register" skip="1"/>
<element name="RegisterSeriesClass" skip="1"/>
<element name="TChart.EraseBackground" skip="1"/>
<element name="TChart.EraseBackground" skip="1"><short>This overridden background painting method does nothing because the background will be painted over anyway.</short><descr>The inherited behaviour is ignored.
</descr>
</element>
<element name="TChart.GetChildren" skip="1"/>
<element name="TChart.Paint" skip="1"/>
<element name="TChart.SetChildOrder" skip="1"/>
@ -326,10 +325,10 @@ chart margin and the space reserved for series marks.
</element>
<element name="TChart.PaintOnCanvas.Draw"/>
<element name="TChart.Draw">
<short>Draws the chart usung the given drawing back-end</short>
<short>Draws the chart using the given drawing back-end</short>
<descr>This method draws the chart by means of the given drawing back-end,
<var>Drawer</var>. Unlike
<var>PaintOnCanvas</var> the chart can be drawn on non-canvas devices, such as a svg file.
<var>PaintOnCanvas</var> the chart can be drawn on non-canvas devices, such as a svg file or OpenGL context.
</descr>
<seealso><link id=""/>
</seealso>
@ -659,6 +658,15 @@ as a replacement.
</descr>
</element><element name="TChart.Proportional"><short><var>Proportional</var>, when set to <var>true</var>, applies the same scaling factor to the x and y axis.</short>
</element>
<element name="TChart.ScalingValid"><short>Indicates whether the scaling parameters for the conversion between graph and image coordinates have valid values.</short>
</element><element name="TChart.ActiveToolIndex"><short>Returns the index of the currently active chart tool in the toolset attached to the chart.</short>
</element>
<element name="TChart.MouseDown"><short>The inherited handler for MouseDown events is overridden to dispatch the event to the ChartToolset assigned to the chart.</short>
</element><element name="TChart.MouseMove"><short>The inherited handler for MouseMove events is overridden to dispatch the event to the ChartToolset assigned to the chart.</short>
</element><element name="TChart.MouseUp"><short>The inherited handler for MouseUp events is overridden to dispatch the event to the ChartToolset assigned to the chart.</short>
</element>
<element name="TChart.GetAllSeriesAxisLimits"><short>Determines the data range covered by all series assigned to the specified axis. Minimum and maximum are returned in axis units.</short>
</element>
</module>
</package>
</fpdoc-descriptions>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<fpdoc-descriptions><package name="TAChartLazarusPkg"><module name="TATools"><element name="TZoomDragTool"><short>Tool for zooming while dragging the mouse over the area to be magnified</short>
</element><element name="TZoomDragTool.EscapeCancels"/><element name="TChartTool.EscapeCancels"><short>Cancels the current operation of the chart tool by pressing the ESC key</short><descr>When the property <var>EscapeCancels</var> is true the current operation of the currently active chart tool, e.g. dragging the mouse for zooming, can be aborted by pressing the ESC key.
</descr>
</element>
</module>
</package>
</fpdoc-descriptions>

View File

@ -1,3 +1,24 @@
{
/***************************************************************************
TAChartLiveView.pas
-------------------
TChartLiveView is a component optimized for displaying a long array of incoming
data in a viewport with only the most recent data while older data are flowing
to the left out of the viewport.
It was created based on the following forum discussions:
- https://forum.lazarus.freepascal.org/index.php/topic,15037.html
- https://forum.lazarus.freepascal.org/index.php/topic,50759.0.html
- https://forum.lazarus.freepascal.org/index.php/topic,55266.html
See the file COPYING.modifiedLGPL.txt, included in this distribution,
for details about the license.
*****************************************************************************
Author: Werner Pamler
}
unit TAChartLiveView;
{$mode objfpc}{$H+}
@ -8,21 +29,33 @@ uses
Classes, SysUtils, TAGraph, TAChartUtils;
type
TChartLiveViewExtentY = (lveAuto, lveFull, lveLogical);
TChartLiveViewExtentY = (lveAuto, lveFull, lveLogical, lveMultiAxisRange);
{ TChartLiveView }
TChartLiveView = class(TComponent)
private
type
TLVAxisRange = record
Min, Max: double;
UseMin, UseMax: Boolean;
end;
private
FActive: Boolean;
FChart: TChart;
FExtentY: TChartLiveViewExtentY;
FListener: TListener;
FViewportSize: Double;
FAxisRanges: Array of TLVAxisRange;
procedure FullExtentChanged(Sender: TObject);
procedure SetActive(const AValue: Boolean);
procedure SetChart(const AValue: TChart);
procedure SetExtentY(const AValue: TChartLiveViewExtentY);
procedure SetViewportSize(const AValue: Double);
procedure UpdateViewport;
protected
procedure RestoreAxisRanges;
procedure StoreAxisRanges;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
@ -39,7 +72,7 @@ procedure Register;
implementation
uses
Math, TACustomSeries;
Math, TAChartAxis, TAChartAxisUtils, TACustomSeries;
constructor TChartLiveView.Create(AOwner: TComponent);
begin
@ -53,6 +86,8 @@ begin
inherited;
end;
{ A new data point has been added to the chart so that the full extent changes.
As a consequence the viewport of the live view must be updated. }
procedure TChartLiveView.FullExtentChanged(Sender: TObject);
begin
if (not FActive) or (FChart = nil) then
@ -60,14 +95,46 @@ begin
UpdateViewport;
end;
procedure TChartLiveView.RestoreAxisRanges;
var
i: Integer;
ax: TChartAxis;
begin
if FChart = nil then
exit;
for i := 0 to FChart.AxisList.Count-1 do
begin
ax := FChart.AxisList[i];
ax.Range.Max := FAxisRanges[i].Max;
ax.Range.Min := FAxisRanges[i].Min;
ax.Range.UseMax := FAxisRanges[i].UseMax;
ax.Range.UseMin := FAxisRanges[i].UseMin;
end;
end;
{ Activates the live view mode. Because the Range of the y axes can be changed
their current Range is stored before activating, and restored after
deactivating the mode. }
procedure TChartLiveView.SetActive(const AValue: Boolean);
begin
if FActive = AValue then exit;
FActive := AValue;
if FChart <> nil then
begin
if FActive then
StoreAxisRanges
else
RestoreAxisRanges;
end;
FullExtentChanged(nil);
end;
{ Attaches the chart on which the liveview operates. Installs a "listener"
object so that the liveview can be notified of a change in the chart's full
extent when a new data point has been added (method FullExtentChanged). }
procedure TChartLiveView.SetChart(const AValue: TChart);
begin
if FChart = AValue then exit;
@ -77,6 +144,7 @@ begin
FChart := AValue;
if FChart <> nil then
FChart.FullExtentBroadcaster.Subscribe(FListener);
StoreAxisRanges;
FullExtentChanged(Self);
end;
@ -84,30 +152,54 @@ procedure TChartLiveview.SetExtentY(const AValue: TChartLiveViewExtentY);
begin
if FExtentY = AValue then exit;
FExtentY := AValue;
RestoreAxisRanges;
FullExtentChanged(nil);
end;
procedure TChartLiveView.SetViewportSize(const AValue: Double);
begin
if FViewportSize = AValue then exit;
FViewportSize := AValue;
FullExtentChanged(nil);
end;
procedure TChartLiveView.StoreAxisRanges;
var
i: Integer;
ax: TChartAxis;
begin
SetLength(FAxisRanges, FChart.AxisList.Count);
for i := 0 to FChart.AxisList.Count-1 do
begin
ax := FChart.AxisList[i];
FAxisRanges[i].Max := ax.Range.Max;
FAxisRanges[i].Min := ax.Range.Min;
FAxisRanges[i].UseMax := ax.Range.UseMax;
FAxisRanges[i].UseMin := ax.Range.UseMin;
end;
end;
{ "Workhorse" method of the component. It calculates the logical extent and
the axis ranges needed to display only the recent data values in the
given viewport. }
procedure TChartLiveView.UpdateViewport;
var
fext, lext: TDoubleRect;
fext, lext: TDoubleRect; // "full extent", "logical extent" variables
w: double;
i: Integer;
i, j: Integer;
ymin, ymax: Double;
ygmin, ygmax: Double;
ser: TChartSeries;
ax: TChartAxis;
begin
if not FChart.ScalingValid then
exit;
if Length(FAxisRanges) = 0 then
StoreAxisRanges;
fext := FChart.GetFullExtent();
lext := FChart.LogicalExtent;
w := lext.b.x - lext.a.x;
if FViewportSize = 0 then
w := lext.b.x - lext.a.x
else
@ -119,19 +211,6 @@ begin
lext.b.x := lext.a.x + w;
end;
case FExtentY of
lveAuto:
begin
ymin := Infinity;
ymax := -Infinity;
for i := 0 to FChart.SeriesCount-1 do
if FChart.Series[i] is TChartSeries then
TChartSeries(FChart.Series[i]).FindYRange(lext.a.x, lext.b.x, ymin, ymax);
if (ymin <> Infinity) and (ymax <> -Infinity) then
begin
lext.a.y := ymin;
lext.b.y := ymax;
end;
end;
lveFull:
begin
lext.a.y := fext.a.y;
@ -139,6 +218,57 @@ begin
end;
lveLogical:
;
lveAuto,
lveMultiAxisRange:
begin
lext.a.y := Infinity;
lext.b.y := -Infinity;
for i := 0 to FChart.AxisList.Count-1 do
begin
ax := FChart.AxisList[i];
// we only support scrolling along x, i.e. ax must be a y axis.
if not (ax.Alignment in [calLeft, calRight]) then
Continue;
ymin := Infinity;
ymax := -Infinity;
for j := 0 to FChart.SeriesCount-1 do
if FChart.Series[j] is TChartSeries then
begin
ser := TChartSeries(FChart.Series[j]);
if (not ser.Active) or (ser.GetAxisY <> ax) or ser.IsRotated then
continue;
ser.FindYRange(lext.a.x, lext.b.x, ymin, ymax);
end;
if (ymin = Infinity) then
begin
ymin := -1;
ymax := +1;
end else
if (ymin = ymax) then
begin
if ymin = 0 then
begin
ymin := -1;
ymax := +1;
end else
begin
ymin := abs(ymin) * 0.9;
ymax := abs(ymax) * 1.1;
end;
end;
ax.Range.Min := ymin;
ax.Range.Max := ymax;
if (FExtentY = lveMultiAxisRange) then
begin
if FAxisRanges[i].UseMin then ax.Range.Min := FAxisRanges[i].Min;
if FAxisRanges[i].UseMax then ax.Range.Max := FAxisRanges[i].Max;
end;
ax.Range.UseMin := true;
ax.Range.UseMax := true;
lext.a.y := Min(lext.a.y, ax.GetTransform.AxisToGraph(ymin));
lext.b.y := Max(lext.b.y, ax.GetTransform.AxisToGraph(ymax));
end; // for i
end;
end;
FChart.LogicalExtent := lext;
end;

View File

@ -441,13 +441,17 @@ function TCustomChartSeries.GetAxisBounds(AAxis: TChartAxis;
out AMin, AMax: Double): Boolean;
var
ex: TDoubleRect;
axIndexX, axIndexY: Integer;
begin
if (AAxis.Index = AxisIndexX) or (AAxis.Index = AxisIndexY) then begin
axIndexX := GetAxisX.Index;
axIndexY := GetAxisY.Index;
if (AAxis.Index = axIndexX) or (AAxis.Index = axIndexY) then begin
ex := EmptyExtent;
GetBounds(ex);
with ex do begin
UpdateBoundsByAxisRange(FChart.AxisList, AxisIndexX, a.X, b.X);
UpdateBoundsByAxisRange(FChart.AxisList, AxisIndexY, a.Y, b.Y);
UpdateBoundsByAxisRange(FChart.AxisList, axIndexX, a.X, b.X);
UpdateBoundsByAxisRange(FChart.AxisList, axIndexY, a.Y, b.Y);
if IsRotated then begin
Exchange(a.X, a.Y);
Exchange(b.X, b.Y);

View File

@ -352,6 +352,7 @@ type
procedure Draw(ADrawer: IChartDrawer; const ARect: TRect);
procedure DrawLegendOn(ACanvas: TCanvas; var ARect: TRect);
procedure EnableRedrawing;
procedure GetAllSeriesAxisLimits(AAxis: TChartAxis; out AMin, AMax: Double);
function GetFullExtent: TDoubleRect;
function GetLegendItems(AIncludeHidden: Boolean = false): TChartLegendItems;
procedure Notify(ACommand: Integer; AParam1, AParam2: Pointer; var AData); override;
@ -1140,6 +1141,15 @@ begin
AClass := nil;
end;
procedure TChart.GetAllSeriesAxisLimits(AAxis: TChartAxis; out AMin, AMax: Double);
var
interval: TDoubleInterval;
begin
interval := GetAxisBounds(AAxis);
AMin := interval.FStart;
AMax := interval.FEnd;
end;
function TChart.GetAxisBounds(AAxis: TChartAxis): TDoubleInterval;
var
s: TBasicChartSeries;

View File

@ -365,7 +365,8 @@ begin
ADrawer.ClippingStop;
DrawLink(ADrawer, ADataPoint, ALabelCenter);
ADrawer.Brush := GetLabelBrush;
with GetLabelBrush do
ADrawer.SetBrushParams(Style, ColorToRGB(Color));
if IsMarginRequired then begin
if GetFrame.Visible then
ADrawer.Pen := GetFrame

View File

@ -751,6 +751,7 @@ begin
diag := -ADrawer.Scale(Round(Sqrt(Sqr(Length) + Sqr(Width))));
pt1 := AEndPos + RotatePointX(diag, AAngle - da)*sgn;
pt2 := AEndPos + RotatePointX(diag, AAngle + da)*sgn;
ADrawer.SetPenParams(psSolid, FPColorToTColor(APen.FPColor), APen.Width);
if BaseLength > 0 then begin
ptBase := AEndPos + RotatePointX(-ADrawer.Scale(BaseLength), AAngle)*sgn;
ADrawer.SetBrushParams(bsSolid, FPColorToChartColor(APen.FPColor));