Yeray wrote: ↑Mon Jan 13, 2025 4:31 pm
You can play with the
Texts.MarginToAxis
property to find what fits your needs. See the example below.
Thank you, I'll test that.
Is there a way to read the current distance of the texts to the axis line? The margin would/should be different for higher/lower DPI, font types and font sizes etc.
Yeray wrote: ↑Mon Jan 13, 2025 4:31 pm
I'm not sure to understand what do you mean with "By clicking a series in the chart the grid is 'relocated' to that series".
However, here you have a simple example with two series, each one with its custom vertical axis. When adding them, I hide all their Grid. Then, at the end I only have to make sure to move one to the left and show the grid of the other one.
In my picture "grid.png" you can see that the axes do not use the same Min/Max settings -- the axes are scrollable independently.
In the picture the user moved the left axis a bit down, so that the ticks of the left axis are no more aligned to the ticks of the other axis.
If all axes had a grid, it would be very confusing because of multiple grid lines for e.g. 0.0 ticks at different positions for several axes.
So, there is exactly one grid and this is drawn for the ticks of one series and its corresponding custom axis.
The user can click a series in the chart and a callback 'OnChartClickAxis' shows the grid for that series, and hides the grid for the (all) other series.
If I understand your example correct, the grid is drawn for the right-most axis and 'all' other axes are shifted to the left, so that the grid lines to the right of the right-most axis do not interfere with lines, texts of axes to the left of the right-most axis. (Woah, that's confusing
).
Ok, that would be a work-around to rearrange the axes each time the user clicks a series (move the 'grid axis' to the right). But I'd prefer a solution that keeps the order of the axes (e.g. data of several analog input from a data acquisition system Ai0, Ai1, Ai2,...)
Yeray wrote: ↑Mon Jan 13, 2025 4:31 pm
Could you please arrange a simple example project to show us that part so we can understand what are you exactly doing there?
Thanks in advance.
Sure, I will submit that later.
Called once on application start:
Code: Select all
procedure TFrameViewData.InitPanel;
begin
[...]
ViewChart.Left := 0;
ViewChart.Top := 0;
ViewChart.Width := FrameScrollPanel.Width; // contains a scrollbar
ViewChart.Height := FrameScrollPanel.Height;
ViewChart.Anchors := [akLeft, akTop, akRight, akBottom];
ViewChart.Walls.Show;
ViewChart.Border.Show;
ViewChart.View3DWalls := False;
ViewChart.BevelInner := bvNone;
ViewChart.BevelOuter := bvNone;
ViewChart.View3D := False;
ViewChart.RemoveAllSeries; // remove default "Series1" from chart (sometime tchart crashs on init if there is no default series)
ViewChart.Zoom.Animated := False;
ViewChart.AllowPanning := pmBoth;
ViewChart.Title.Hide;
ViewChart.ClipPoints := True;
ViewChart.Color := BackgroundColor;
{$IFDEF USE_SCROLLPAGER}
ViewChart.MarginTop := 15; // add space between chart and its scrollpager (less tick text overlapping)
ViewChart.MarginBottom := 1; // remove empty space between bottom axis and the control panel by setting a value (no automatic)
{$ENDIF}
ViewChart.Legend.Font.Quality := fqClearType;
ViewChart.Legend.Font.Name := AppSettings.UiFontName;
ViewChart.Legend.Font.SizeFloat := -MulDiv(AppSettings.UiFontSize, CurrentPPI, 72);
ViewChart.Legend.Color := BackgroundColor; // application specific TColor
ViewChart.Legend.Frame.Color := ScaleColor;
// do not show chart's default axes: left, right, top (using 'custom axes' for the left side)
ViewChart.LeftAxis.Visible := False;
ViewChart.RightAxis.Visible := False;
ViewChart.TopAxis.Visible := False;
ViewChart.LeftAxis.Grid.Visible := False; // grid will be attached to custom axis
// x
ViewChart.BottomAxis.Visible := False; // shown after file selection
ViewChart.BottomAxis.Grid.Color := GridColor;
ViewChart.BottomAxis.LabelsOnAxis := True;
ViewChart.BottomAxis.MinorTickCount := 1;
ViewChart.BottomAxis.Title.Text := '';
ViewChart.BottomAxis.Title.Position := tpEnd;
ViewChart.BottomAxis.Title.Alignment := taRightJustify;
ViewChart.BottomAxis.LabelsFont.Name := AppSettings.UiFontName;
ViewChart.BottomAxis.LabelsFont.SizeFloat := -MulDiv(AppSettings.ScaleFontSize, CurrentPPI, 72);
ViewChart.BottomAxis.LabelsFont.Quality := fqClearType;
ViewChart.BottomAxis.LabelsFont.Color := ScaleTextColor;
ViewChart.BottomAxis.Axis.Color := ScaleColor;
ViewChart.BottomAxis.Ticks.Color := ScaleColor;
[...]
case BottomAxisTimeType of
xtMeas:
begin
ViewChart.BottomAxis.AxisValuesFormat := '#,##0.##';
end;
xtDate:
begin
ViewChart.BottomAxis.DateTimeFormat := 'hh:mm:ss,zzz' + #13 + 'ddddd';
end;
end;
CursorTool := TCursorTool.Create(ViewChart);
[...]
CursorTool.OnChange := OnCursorToolChange;
// displays cursor tool's information
RectTool := TRectangleTool.Create(ViewChart);
[...]
RectTool.Shape.Frame.Color := ScaleColor;
// text hint 'select file'
AnnoTool := TAnnotationTool.Create(ViewChart);
[...]
AnnoTool.Shape.Frame.Color := ScaleColor;
{$IFDEF USE_SCROLLPAGER}
// test scrollpager
ScrollPagerTool := TScrollPagerTool.Create(Self);
ScrollPagerTool.ParentChart := ViewChart;
ScrollPagerTool.Visible := False; // hidden until a file is selected
ScrollPagerTool.DivisionRatio := 8.0; // ScrollPager is part of the chart and consumes a bit of its space
ScrollPagerTool.SubChartTChart.BottomAxis.Grid.Hide;
// ScrollPagerTool.SubChartTChart.LeftAxis.Grid.Hide;
ScrollPagerTool.SubChartTChart.Frame.Visible := False;
ScrollPagerTool.SubChartTChart.View3D := False;
ScrollPagerTool.ColorBandTool.StartLine.Active := True;
ScrollPagerTool.ColorBandTool.EndLine.Active := True;
ScrollPagerTool.StartLinePointer.Size := 6; // *dpi?
ScrollPagerTool.EndLinePointer.Size := 6;
ScrollPagerTool.Align := saBefore; // show ScrollPager above (=before) the chart, default: below
ScrollPagerTool.SubChartTChart.BottomAxis.LabelsFont.Color := ScaleColor;
{$ENDIF}
[...]
ChkCursor.Checked := False;
ChkReadOnSlide.Checked := ReadOnSlide;
ChkMeasDate.Checked := (BottomAxisTimeType = xtDate);
end;
Called each time a new file is selected:
Code: Select all
procedure TFrameViewData.InitFile;
var
[...]
begin
[...]
MdfDecodeTime(pMdfMeas.FileStartTime[0], MdfFileStartTime);
// prepare existing chart for new file
ViewChart.RemoveAllSeries; // does not delete series objects!
if NumSignals > 0 then
begin
for var Idx := 0 to NumSignals - 1 do
if Signals[Idx].Serie <> nil then
Signals[Idx].Serie.Delete(Idx);
NumSignals := 0;
end;
ViewChart.BottomAxis.Visible := True;
[... access file, determine #channels...]
for DgIdx := 0 to MdfNumDgScalar - 1 do
begin
[...]
for var CnIdx := 1 to pChanGrp.CNNumber - 1 do // skip virtual time channel
begin
[...]
Signals[SignalIdx].Serie := TFastLineSeries.Create(ViewChart);
Signals[SignalIdx].Serie.Title := PWChar(pChanGrp.Channels[CnIdx].ChanName);
Signals[SignalIdx].Serie.Pen.Width := 1;
Signals[SignalIdx].Serie.BeforeDrawValues := OnBeforeDrawSeries;
Signals[SignalIdx].Serie.AfterDrawValues := OnAfterDrawSeries;
TFastLineSeries(Signals[SignalIdx].Serie).FastPen := True;
TFastLineSeries(Signals[SignalIdx].Serie).DrawAllPoints := False; // do not draw multiple values to the same x-position
TFastLineSeries(Signals[SignalIdx].Serie).ClickTolerance := 2;
case BottomAxisTimeType of
xtMeas:
Signals[SignalIdx].Serie.XValues.DateTime := False;
xtDate:
Signals[SignalIdx].Serie.XValues.DateTime := True;
end;
Inc(SignalIdx);
end;
end;
[...]
if ViewChart.CustomAxes.Count > 0 then
begin
while ViewChart.CustomAxes.Count > 0 do
ViewChart.CustomAxes.Delete(0);
NumScales := 0; // =axis
end;
[...]
for var Idx := 0 to NumScales - 1 do
begin
ViewChart.CustomAxes.Add();
Scales[Idx] := ViewChart.CustomAxes[Idx];
Scales[Idx].Grid.Hide;
Scales[Idx].Grid.Color := GridColor;
Scales[Idx].MinorTickCount := 1;
Scales[Idx].SetMinMax(-10.0, 10.0);
Scales[Idx].Increment := 2.5;
Scales[Idx].Title.Text := '';
Scales[Idx].Title.Position := tpCenter;
Scales[Idx].Title.Alignment := taRightJustify;
Scales[Idx].Title.Font.Name := DmSettings.UiFontName;
Scales[Idx].Title.Font.SizeFloat := -MulDiv(AppSettings.ScaleFontSize, CurrentPPI, 72);
Scales[Idx].PositionPercent := Idx * ScalePosFactor + ScalePosOffset;
Scales[Idx].MinimumOffset := 40;
Scales[Idx].MaximumOffset := 40;
Scales[Idx].LabelsFont.Name := AppSettings.UiFontName;
Scales[Idx].LabelsFont.SizeFloat := -MulDiv(AppSettings.ScaleFontSize, CurrentPPI, 72);
Scales[Idx].LabelsFont.Quality := fqClearType;
Scales[Idx].LabelsAngle := 90;
Scales[Idx].LabelsFont.Color := ScaleTextColor;
// Scales[Idx].LabelStyle:= talAuto;
Scales[Idx].Axis.Color := ScaleColor;
Scales[Idx].Ticks.Color := ScaleColor;
Scales[Idx].OnDrawLabel := OnAxisDrawLabel;
// This changes the distance of channel name (title) to the axis line,
// but *not* the size even if its named 'Label*SIZE*' ( nor does is change the distance of tick texts)
// Scales[idx].LabelsSize := 2;
// link grid to first scale (series/axis)
if Idx = 0 then
begin
Scales[Idx].Grid.Show;
Scales[Idx].Grid.Width := MulDiv(1, CurrentPPI, 72); // 1
end;
Delta := Scales[Idx].Maximum - Scales[Idx].Minimum;
ValueFormat := '#,##0.';
Decimals := 1;
while ((Delta / Scales[Idx].Items.Count) * (Decimals * 10) < 1) and (Decimals < 13) do
begin
Inc(Decimals);
ValueFormat := ValueFormat + '#';
end;
ValueFormat := ValueFormat + '0';
Scales[Idx].AxisValuesFormat := ValueFormat;
ScrollTools[Idx] := TAxisScrollTool.Create(ViewChart);
ScrollTools[Idx].Axis := Scales[Idx];
// attach scale to series
Signals[Idx].Serie.CustomVertAxis := Scales[Idx];
Scales[Idx].Title.Text := Signals[Idx].Serie.Title;
end;
ViewChart.BottomAxis.StartPosition := ScalePosFactor * (NumScales - 1) + ScalePosOffset;
ViewChart.BottomAxis.EndPosition := 100.0;
[...]
AnnoTool.Active := False;
Self.BtnReadDataClick(Self); // read data from file into byte buffer, buffer content converted to values in 'BufToSeries' (channel type single, int, etc)
ViewChart.Invalidate;
{$IFDEF USE_SCROLLPAGER}
ScrollPagerTool.Visible := False;
ScrollPagerTool.SubChartTChart.RemoveAllSeries;
// select series from main chart to be shown as preview in ScrollPager's sub chart
// ScrollPagerTool.Series := ViewChart[0];
ScrollPagerTool.AddSeries(ViewChart[0]);
// ScrollPagerTool.Series.Visible := True;
// ScrollPagerTool.SubChartTChart.LeftAxis.SetMinMax(-10.0, 10.0);
// ScrollPagerTool.SubChartTChart.LeftAxis.Automatic := True;
// set left axis of series of main chart to be used as left axis of series in Scrollpager's sub chart
// ScrollPagerTool.Series.VertAxis := ViewChart[0].VertAxis;
ScrollPagerTool.SubChartTChart.Axes.Left.Assign(Signals[0].Serie.GetVertAxis);
ScrollPagerTool.SubChartTChart.LeftAxis.Visible := True;
ScrollPagerTool.SubChartTChart.BottomAxis.DateTimeFormat := ViewChart.BottomAxis.DateTimeFormat;
ScrollPagerTool.SubChartTChart.BottomAxis.AxisValuesFormat := ViewChart.BottomAxis.AxisValuesFormat;
ScrollPagerTool.SubChartTChart.BottomAxis.Automatic := False;
ScrollPagerTool.SubChartTChart.BottomAxis.AutomaticMinimum := True;
ScrollPagerTool.SubChartTChart.BottomAxis.AutomaticMaximum := True;
// show labels with numeric value of start/end position's Y value of series
ScrollPagerTool.ColorBandTool.StartLine.AnnotationValue := True;
ScrollPagerTool.ColorBandTool.EndLine.AnnotationValue := True;
// determine beginning/ending of Scrollpager's handle
var
XStart, XEnd: Double;
XStart := ViewChart[0].XValue[0]; // first x value of first series in main chart
XEnd := ViewChart[0].XValue[(Signals[0].Serie.Count - 1) div 10]; // last value/record
ScrollPagerTool.ColorBandTool.StartValue := XStart;
ScrollPagerTool.ColorBandTool.EndValue := XEnd;
ScrollPagerTool.SubChartTChart.BottomAxis.Increment := (XEnd - XStart) / 5.0;
ScrollPagerTool.Visible := True;
ScrollPagerTool.SetUpScrollPager;
{$ENDIF}
// ViewChart.Invalidate;
end;
Code: Select all
procedure TFrameViewData.BufToSeries(pChanGrp: PMdfCfgChannelGroup);
[...]
begin
case BottomAxisTimeType of
xtMeas:
begin
var
TimeStamp: Double;
for var SignalIdx := 0 to NumSignals - 1 do
begin
pSerie := @Signals[SignalIdx].Serie;
pSerie.BeginUpdate;
pSerie.Clear;
pValue := @ByteBuffer[Signals[SignalIdx].MdfOffset];
for var SampleIdx := 0 to NumberRecords - 1 do
begin
TimeStamp := (StartRecord + SampleIdx) * pChanGrp.SampleDistance;
pSerie.AddXY(TimeStamp, pValue^);
Inc(pValue, pChanGrp.CNNumber - 1);
end;
pSerie.EndUpdate;
pSerie.ParentChart := ViewChart; // nil: remove from chart
end;
end;
xtDate:
[...]
end;