Distance between axis tick and label text, and more questions

TeeChart VCL for Borland/CodeGear/Embarcadero RAD Studio, Delphi and C++ Builder.
MXHW
Newbie
Newbie
Posts: 8
Joined: Wed Sep 18, 2024 12:00 am

Distance between axis tick and label text, and more questions

Post by MXHW » Mon Jan 13, 2025 12:13 pm

Hello!

TeeChart Pro v2024.41.240913 32bit VCL
RAD Studio 12 Version 29.0.53571.9782


(1)
In an project I am using a custom axis for each series. There is a 'huge' distance between the text and the axis (picture: left arrow).
The right side of the picture shows the desired result (I'd faked this by editing the picture).
How can I configure the labels' distance, please?
axeslabel.png
axeslabel.png (9.43 KiB) Viewed 9972 times

(2)
By clicking a series in the chart the grid is 'relocated' to that series. In the picture you can see that the grid lines are drawn ontop of axis/axes and the title(s).
If the grid is assigned to the left-most axis, all axis to the right get painted-over by the grid lines.
How can I make sure that the grid lines are drawn starting at the right-most axis regardles of the series the grid is attached to, please?
grid.png
grid.png (3.99 KiB) Viewed 9972 times

(3)
Also, I'd like to use the ScrollPagerTool to have an overview of data loaded from an file.
Setting up the Chart and the Scrollpager, loading data from file and assigning the value to series (TFastLineSeries) works.
On initialisiation, before data is loaded, the chart gets some basic configurations. Also the scrollpager is created and attached to the chart. The scrollpager is hidden until data is loaded, series were created and 'filled' with the data, and (custom) axes were configured.
After that the Scrollpager is set to be visible and ScrollPagerTool.SetUpScrollPager is called.
I've also tried to 'Invalidate' the main chart, if there is something that needs to be pre-calculated, that the scrollpager relies on.

Scrolling the Scrollpager's colorband and modifying the 'viewport' by changing the colorband's start/endline works, so that the (main) chart mirrors the scrolling etc.
But... the series from the chart that is assigned to the scrollpager is not drawn to the scrollpagers subchart.
The scrollpager is 'empty', its subchart has a bottom axis, but there is no series draw, nor a left axis is drawn.

I've tried a) or b)
  • a) ScrollPagerTool.Series := ViewChart[0];
    b) ScrollPagerTool.AddSeries(ViewChart[0]);

Yeray
Site Admin
Site Admin
Posts: 9639
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Distance between axis tick and label text, and more questions

Post by Yeray » Mon Jan 13, 2025 4:31 pm

Hello,
MXHW wrote:
Mon Jan 13, 2025 12:13 pm
In an project I am using a custom axis for each series. There is a 'huge' distance between the text and the axis (picture: left arrow).
The right side of the picture shows the desired result (I'd faked this by editing the picture).
How can I configure the labels' distance, please?
You can play with the Texts.MarginToAxis property to find what fits your needs. See the example below.
MXHW wrote:
Mon Jan 13, 2025 12:13 pm
By clicking a series in the chart the grid is 'relocated' to that series. In the picture you can see that the grid lines are drawn ontop of axis/axes and the title(s).
If the grid is assigned to the left-most axis, all axis to the right get painted-over by the grid lines.
How can I make sure that the grid lines are drawn starting at the right-most axis regardles of the series the grid is attached to, please?
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.

AxisGrids.png
AxisGrids.png (12.38 KiB) Viewed 9949 times

Code: Select all

uses Chart, Series;

var Chart1: TChart;

procedure TForm1.FormCreate(Sender: TObject);

  procedure AddSeries;
  var i: Integer;
  begin
    with Chart1.AddSeries(TFastLineSeries) do
    begin
      with Chart1.CustomAxes.Add do
      begin
        CustomVertAxis:=Chart1.CustomAxes[Chart1.CustomAxes.Count-1];
        SetMinMax(0, 100);
        LabelsFont.Size:=10;
        LabelsFont.Color:=clLtGray;
        LabelsAngle:=90;
        Texts.MarginToAxis:=-70;
        Axis.Width:=2;
        Axis.Color:=clLtGray;
        Grid.Hide;
      end;

      Add(25+Random*50);
      for i:=1 to 4 do
        Add(YValue[Count-1]+Random*10-5);
    end;
  end;

begin
  Chart1:=TChart.Create(Self);

  with Chart1 do
  begin
    Parent:=Self;
    Align:=alClient;
    Color:=$2f2f2f;
    Gradient.Visible:=False;
    Walls.Hide;
    Legend.Hide;
    View3D:=False;
    MarginLeft:=20;

    Axes.Bottom.LabelsFont.Size:=10;
    Axes.Bottom.LabelsFont.Color:=clLtGray;
    Axes.Bottom.Axis.Width:=2;
    Axes.Bottom.Axis.Color:=clLtGray;
    Axes.Bottom.Grid.Hide;
  end;

  AddSeries;
  AddSeries;

  Chart1.CustomAxes[0].Grid.Show;
  Chart1.CustomAxes[1].PositionPercent:=-10;
end;
MXHW wrote:
Mon Jan 13, 2025 12:13 pm
(3)
Also, I'd like to use the ScrollPagerTool to have an overview of data loaded from an file.
Setting up the Chart and the Scrollpager, loading data from file and assigning the value to series (TFastLineSeries) works.
On initialisiation, before data is loaded, the chart gets some basic configurations. Also the scrollpager is created and attached to the chart. The scrollpager is hidden until data is loaded, series were created and 'filled' with the data, and (custom) axes were configured.
After that the Scrollpager is set to be visible and ScrollPagerTool.SetUpScrollPager is called.
I've also tried to 'Invalidate' the main chart, if there is something that needs to be pre-calculated, that the scrollpager relies on.

Scrolling the Scrollpager's colorband and modifying the 'viewport' by changing the colorband's start/endline works, so that the (main) chart mirrors the scrolling etc.
But... the series from the chart that is assigned to the scrollpager is not drawn to the scrollpagers subchart.
The scrollpager is 'empty', its subchart has a bottom axis, but there is no series draw, nor a left axis is drawn.

I've tried a) or b)

a) ScrollPagerTool.Series := ViewChart[0];
b) ScrollPagerTool.AddSeries(ViewChart[0]);
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.
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

MXHW
Newbie
Newbie
Posts: 8
Joined: Wed Sep 18, 2024 12:00 am

Re: Distance between axis tick and label text, and more questions

Post by MXHW » Tue Jan 14, 2025 12:34 pm

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;

Yeray
Site Admin
Site Admin
Posts: 9639
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Distance between axis tick and label text, and more questions

Post by Yeray » Tue Jan 14, 2025 2:31 pm

Hello,
MXHW wrote:
Tue Jan 14, 2025 12:34 pm
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,...)
Then you could do something like this:

mstsc_oqJEAggaeV.gif
mstsc_oqJEAggaeV.gif (47.79 KiB) Viewed 9786 times

Code: Select all

procedure TForm1.ChartOnClickAxis(Sender:TCustomChart; Axis:TChartAxis;
  Button:TMouseButton; Shift: TShiftState; X, Y: Integer);
var i: Integer;
begin
  if Axis = Chart1.Axes.Bottom then
     Exit;

  for i:=0 to Chart1.CustomAxes.Count-1 do
    Chart1.CustomAxes[i].Grid.Hide;

  Axis.Grid.Show;
end;

procedure TForm1.ChartMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var i: Integer;
    a: TChartAxis;
    tmpPart: TChartClickedPart;
begin
  Chart1.Cursor:=crDefault;

  for i:=0 to Chart1.CustomAxes.Count-1 do
  begin
    a:=Chart1.CustomAxes[i];

    if a.Horizontal then
       Continue;

    Chart1.CalcClickedPart(TeePoint(x,y),tmpPart);
    if (tmpPart.Part = cpAxis) and  (tmpPart.AAxis <> Chart1.Axes.Bottom) then
       Chart1.Cursor:=crHandPoint;

  end;

  Chart1.CancelMouse:=True;
end;

procedure TForm1.FormCreate(Sender: TObject);

  procedure AddSeries;
  var i: Integer;
      color: TColor;
  begin
    color:=OperaPalette[Chart1.SeriesCount];

    with Chart1.AddSeries(TFastLineSeries) do
    begin
      with Chart1.CustomAxes.Add do
      begin
        CustomVertAxis:=Chart1.CustomAxes[Chart1.CustomAxes.Count-1];
        SetMinMax(0, Chart1.SeriesCount*100+100);
        LabelsFont.Size:=10;
        LabelsFont.Color:=color;
        LabelsAngle:=90;
        Texts.MarginToAxis:=-70;
        Axis.Width:=2;
        Axis.Color:=color;
        Grid.Color:=color;
        Grid.Style:=psDash;
        Grid.Hide;
      end;

      Pen.Width:=2;
      Add((Chart1.SeriesCount-1)*100+25+Random*50);
      for i:=1 to 4 do
        Add(YValue[Count-1]+Random*10-5);
    end;
  end;

begin
  Chart1:=TChart.Create(Self);

  with Chart1 do
  begin
    Parent:=Self;
    Align:=alClient;
    Color:=$2f2f2f;
    Gradient.Visible:=False;
    Walls.Hide;
    Legend.Hide;
    View3D:=False;
    MarginLeft:=15;

    Axes.Bottom.LabelsFont.Size:=10;
    Axes.Bottom.LabelsFont.Color:=clLtGray;
    Axes.Bottom.Axis.Width:=2;
    Axes.Bottom.Axis.Color:=clLtGray;
    Axes.Bottom.Grid.Hide;
  end;

  AddSeries;
  AddSeries;

  Chart1.CustomAxes[0].Grid.Show;
  Chart1.CustomAxes[1].PositionPercent:=-10;

  Chart1.OnClickAxis:=ChartOnClickAxis;
  Chart1.OnMouseMove:=ChartMouseMove;
end;
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

MXHW
Newbie
Newbie
Posts: 8
Joined: Wed Sep 18, 2024 12:00 am

Re: Distance between axis tick and label text, and more questions

Post by MXHW » Tue Jan 14, 2025 3:19 pm

Ja, that's what I want, changing the grid (show/hide for axes) works already.
The problem: In my chart the horizontal lines of a grid are painted starting directly from the clicked axis.
In your video/example: the 'orange' horizontal grid lines start at '0.0' position of the bottom axis.
In my chart the 'orange' horizontal grid lines start at the position of the orange axis.
That confuses me...

MXHW
Newbie
Newbie
Posts: 8
Joined: Wed Sep 18, 2024 12:00 am

Re: Distance between axis tick and label text, and more questions

Post by MXHW » Wed Jan 15, 2025 9:28 am

MXHW wrote:
Tue Jan 14, 2025 12:34 pm
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.
Unfortunately, 'MarginsToAxis' does not have any effect -- the texts keep their distance (first picture in my first posting).

Code: Select all

for var Idx := 0 to NumScales - 1 do
   begin
   	ViewChart.CustomAxes.Add();
	Scales[Idx] := ViewChart.CustomAxes[Idx];
	// Scales[Idx] := ViewChart.CustomAxes.Add();
   
 	Scales[Idx].Texts.MarginToAxis := -170; // 100; -70;
	//ViewChart.CustomAxes[Idx].Texts.MarginToAxis := -70;
[...]

Yeray
Site Admin
Site Admin
Posts: 9639
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Distance between axis tick and label text, and more questions

Post by Yeray » Thu Jan 16, 2025 2:38 pm

Hello,

I've added a scrollbar to test different Texts.MarginToAxis values and it seems to work fine for me here:
mstsc_49oVHJTEMM.gif
mstsc_49oVHJTEMM.gif (190.45 KiB) Viewed 9327 times

Code: Select all

procedure TForm1.FormCreate(Sender: TObject);
//...
  SBLabelsMargin.Min:=-100;
  SBLabelsMargin.Max:=400;
  SBLabelsMargin.Position:=-Chart1.CustomAxes[0].Texts.MarginToAxis;
  LLabelsMargin.Caption:=IntToStr(Chart1.CustomAxes[0].Texts.MarginToAxis);
end;

procedure TForm1.SBLabelsMarginChange(Sender: TObject);
var i: Integer;
begin
  for i:=0 to Chart1.CustomAxes.Count-1 do
      Chart1.CustomAxes[i].Texts.MarginToAxis:=-SBLabelsMargin.Position;

  LLabelsMargin.Caption:=IntToStr(Chart1.CustomAxes[0].Texts.MarginToAxis);
end;
I've tested the same project at 175% and it also seems to work fine for me here.

If you still find problem with it, please arrange a simple example project we can run as-is to reproduce the problem here.

Regarding the axes grids overlapping other axes, note the axes are drawn in the order of the CustomAxes array, so axes being added later will overlap axes being added previously.
Also note you can play with the Chart1.Axes.Behind property to modify if the axes should be drawn before or after the series.
Sure, I will submit that later.

Called once on application start:
Sorry but that code has several unknown variables. Please arrange a simple example we can run as-is to reproduce the problem here.

Thanks in advance.
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

MXHW
Newbie
Newbie
Posts: 8
Joined: Wed Sep 18, 2024 12:00 am

Re: Distance between axis tick and label text, and more questions

Post by MXHW » Fri Jan 17, 2025 1:00 pm

Attached is an (ugly) but stand-alone working example of the 'ViewChart' (TChart) that is normally embedded in a big application with lots of dependencies.

Please click the button 'Default' (bottom-left corner) to add random data to the chart.
You can scroll the axes, and click series in the chart. Other features not tested in this example.
(I know the code is a mess, still evaluating/learning the TChart software.)

In 'OnChartClickSeries' I am adding a constant to the current margin. Each time a series is clicked in the chart it should increase the distance between the text and the axis.
The value of the property reflects the changed value, but the graphics do not.
Observing the i variable shows, that the value was remembered each time a series was clicked. The actual distance painted in the chart is always the same, it does not increase.

Code: Select all

procedure TFrameViewData.OnChartClickSeries(Sender: TCustomChart; ASerie: TChartSeries; ValueIndex: Integer; Button: TMouseButton; Shift: TShiftState;
   X, Y: Integer);

	[...]
   // test
   var
      i: Integer;
   i := ViewChart.CustomAxes[0].Texts.MarginToAxis; // first time=10, next time=30, next time=50, etc
   ViewChart.CustomAxes[0].Texts.MarginToAxis := ViewChart.CustomAxes[0].Texts.MarginToAxis + 20;
   i := ViewChart.CustomAxes[0].Texts.MarginToAxis; // 10+20=30 , 30+20=50 , ...
   Inc(i); // breakpoint
   ViewChart.Update; // invalidate // repaint
end;
Also you can see that the lines of the grid always start at the left most axis.


Thank you for your patience with me.... ;)
Attachments
tchart_trouble.zip
(26.26 KiB) Downloaded 286 times

Yeray
Site Admin
Site Admin
Posts: 9639
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Distance between axis tick and label text, and more questions

Post by Yeray » Mon Jan 20, 2025 2:38 pm

Hello,

I see the axis labels to be moving to the left every time I click a series:
mstsc_8jfJENhWOp.gif
mstsc_8jfJENhWOp.gif (95.84 KiB) Viewed 8100 times
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

Yeray
Site Admin
Site Admin
Posts: 9639
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Distance between axis tick and label text, and more questions

Post by Yeray » Mon Jan 20, 2025 2:41 pm

Hello,

Ah, I see there must be a difference between TeeChart v2024.41 and TeeChart v2024.42 where MarginToAxis got fixed.
Probably #2740 affected MarginToAxis.
Can you update to TeeChart v2024.42?
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

MXHW
Newbie
Newbie
Posts: 8
Joined: Wed Sep 18, 2024 12:00 am

Re: Distance between axis tick and label text, and more questions

Post by MXHW » Wed Jan 22, 2025 7:39 am

Ok, update to ..42.. fixes text distance issue, thank you very much -- that was problem 1/3, now to problem 2 and 3. ;)

2) Grid
The horizontal grid lines are drawn starting at the left most axis, not at the 0.0 position of the bottom axis. I do not know how to fix this!
In the example project, clicking a series in the chart 'relocates' the grid to the axis of the clicked series. To see that, you need to scroll one of the axis a bit, so that e.g. the 0.0 ticks are no more on the same height.
Clicking a series changes the grid corresponding to the axis ticks of the clicked series.

3) Scrollpager
To enable the example's ScrollPager tool:
a) Uncomment the directive {$define USE_SCROLLPAGER} at the top of SetupViewData.pas
b) In TFrameViewData.InitPanel make the Scrollpager visible: //ScrollPagerTool.Visible := False; // hidden until a file is selected

The Scrollpager in the example does not display the graph of series. In a very (very) simple example with a chart, a series and a Scrollpager the scrollpager's subchart displays the data of the series of its main chart.
But in my project it does not -- I do not understand the reason for that!

There's a record 'TSignal' that acts as an intermediate layer between TChart series and channels of a file. Files can have dozens to hundreds of channels, but the TChart should display only a very limited count. For each channel of the file there is a is a record element, if the channel should be displayed in the chart, the records 'Series' member is non-nil.

Assigning a series that's displayed in the chart to the scrollpager works (I think), because moving/dragging the colorband of the scrollpager works, but the scrollpager does not paint the signal/series graph.

Yeray
Site Admin
Site Admin
Posts: 9639
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Distance between axis tick and label text, and more questions

Post by Yeray » Thu Jan 23, 2025 1:21 pm

Hello,
MXHW wrote:
Wed Jan 22, 2025 7:39 am
2) Grid
The horizontal grid lines are drawn starting at the left most axis, not at the 0.0 position of the bottom axis. I do not know how to fix this!
In the example project, clicking a series in the chart 'relocates' the grid to the axis of the clicked series. To see that, you need to scroll one of the axis a bit, so that e.g. the 0.0 ticks are no more on the same height.
Clicking a series changes the grid corresponding to the axis ticks of the clicked series.
I see you are moving the Bottom axis StartPosition at around line 912.

Code: Select all

ViewChart.BottomAxis.StartPosition := ScalePosFactor * (NumScales - 1) + ScalePosOffset;
That calculation gives 5 so the bottom axis starts at 5% of the chart rectangle.
You have your custom vertical axes at positions 1% and 5%.
That's why the bottom axis starts exactly aligned with one of your vertical axes.

This is a project with +2k lines of code. I understand it can take time to prepare a simple example project to show a concrete behaviour, but extracting part of a big project so "it shows the issue" (note I had to remove some components I don't have here to compile it here) isn't optimal.
MXHW wrote:
Wed Jan 22, 2025 7:39 am
3) Scrollpager
To enable the example's ScrollPager tool:
a) Uncomment the directive {$define USE_SCROLLPAGER} at the top of SetupViewData.pas
b) In TFrameViewData.InitPanel make the Scrollpager visible: //ScrollPagerTool.Visible := False; // hidden until a file is selected

The Scrollpager in the example does not display the graph of series. In a very (very) simple example with a chart, a series and a Scrollpager the scrollpager's subchart displays the data of the series of its main chart.
But in my project it does not -- I do not understand the reason for that!

There's a record 'TSignal' that acts as an intermediate layer between TChart series and channels of a file. Files can have dozens to hundreds of channels, but the TChart should display only a very limited count. For each channel of the file there is a is a record element, if the channel should be displayed in the chart, the records 'Series' member is non-nil.

Assigning a series that's displayed in the chart to the scrollpager works (I think), because moving/dragging the colorband of the scrollpager works, but the scrollpager does not paint the signal/series graph.
I see you are assigning the vertical axis of the Signals[0] to the subchart at InitFile. Instead of that, just make sure the VertAxis of the series in the scrollpager is set to aLeftAxis:

Code: Select all

   // 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[0].VertAxis:=aLeftAxis;  // <--- Try this
   ScrollPagerTool.SubChartTChart.LeftAxis.Visible := True;
One tip that can help you to debug complex charts is to add a button showing the editor at runtime:

Code: Select all

uses VCLTee.EditChar, VCLTee.TeeEditPro;

procedure TFrameViewData.Button1Click(Sender: TObject);
begin
  EditChart(Self, ViewChart);
end;
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

MXHW
Newbie
Newbie
Posts: 8
Joined: Wed Sep 18, 2024 12:00 am

Re: Distance between axis tick and label text, and more questions

Post by MXHW » Fri Jan 24, 2025 3:04 pm

Yeray wrote:
Thu Jan 23, 2025 1:21 pm
I see you are moving the Bottom axis StartPosition at around line 912.

Code: Select all

ViewChart.BottomAxis.StartPosition := ScalePosFactor * (NumScales - 1) + ScalePosOffset;
That calculation gives 5 so the bottom axis starts at 5% of the chart rectangle.
You have your custom vertical axes at positions 1% and 5%.
That's why the bottom axis starts exactly aligned with one of your vertical axes.

This is a project with +2k lines of code. I understand it can take time to prepare a simple example project to show a concrete behaviour, but extracting part of a big project so "it shows the issue" (note I had to remove some components I don't have here to compile it here) isn't optimal.
Thank you for your patience.
Perhaps it is easier to describe what I want to achieve instead of trying to repair my constructs.

In another project I draw it all by myself with direct2d, the series' data, axes and their texts and minor/major ticks, cursors, titles etc.
The main concept is that the area in which the signals (aka series) are displayed only takes up the space left by the dynamic number of axes. So, if a file has four channels, each signal in the chart has one axis. The four axes are either to the left side or the right side (or mixed eg. 2 left, 2 right) of the painting area.
The "signal area" takes the rest: Its rectangle's left and right side is the height of the custom axes (Y), the rectangle's top/bottom is the width of the time scale (X, 'bottom axis').

So, inherently, the first data point of a signal (series) is always painted some pixels (borders, padding, margins etc) to the right of the right most axis (if all axes are left), because the Minimum of 'my' X axis starts on the left side of the rectangle of the "signal area".
So are the horizontal lines of a grid.

I wanted to mimic that concept by moving the custom axes: first signal's (series) custom axis is the left most with some margin (ScalePosOffset = <some value>) and the "ScalePosFactor" set to zero.
The next signals' (series) axes' positions were calculated with formula : Factor * X + Offset.

Now, I thought, that I can tell TChart to draw the series data, grid etc aligned with the 0.0 of the X scale (bottom axis) by setting its 'StartPosition' to the position of the right most axis.
But, unfortunately and obviously, this does not seem to be the right way... I would be grateful for suggestions.

Yeray
Site Admin
Site Admin
Posts: 9639
Joined: Tue Dec 05, 2006 12:00 am
Location: Girona, Catalonia
Contact:

Re: Distance between axis tick and label text, and more questions

Post by Yeray » Mon Jan 27, 2025 8:41 am

Hello,
MXHW wrote:
Fri Jan 24, 2025 3:04 pm
The main concept is that the area in which the signals (aka series) are displayed only takes up the space left by the dynamic number of axes. So, if a file has four channels, each signal in the chart has one axis. The four axes are either to the left side or the right side (or mixed eg. 2 left, 2 right) of the painting area.
The "signal area" takes the rest: Its rectangle's left and right side is the height of the custom axes (Y), the rectangle's top/bottom is the width of the time scale (X, 'bottom axis').

So, inherently, the first data point of a signal (series) is always painted some pixels (borders, padding, margins etc) to the right of the right most axis (if all axes are left), because the Minimum of 'my' X axis starts on the left side of the rectangle of the "signal area".
So are the horizontal lines of a grid.
In the example above, all the series and grids are draw at the right of the right most axis. So as I understand, it fits that description.
However, you replied that wasn't the desired behaviour. I'm getting confused.
I'd suggest to just take "paint" and manually draw how you want it to look like and we'll prepare a simple example.
Best Regards,
ImageYeray Alonso
Development & Support
Steema Software
Av. Montilivi 33, 17003 Girona, Catalonia (SP)
Image Image Image Image Image Image Please read our Bug Fixing Policy

MXHW
Newbie
Newbie
Posts: 8
Joined: Wed Sep 18, 2024 12:00 am

Re: Distance between axis tick and label text, and more questions

Post by MXHW » Mon Jan 27, 2025 9:17 am

Yeray wrote:
Thu Jan 23, 2025 1:21 pm
I see you are assigning the vertical axis of the Signals[0] to the subchart at InitFile. Instead of that, just make sure the VertAxis of the series in the scrollpager is set to aLeftAxis:

Code: Select all

   // 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[0].VertAxis:=aLeftAxis;  // <--- Try this
   ScrollPagerTool.SubChartTChart.LeftAxis.Visible := True;
That works, thank you! But I do not understand why it works.

If the main chart has a series with a custom axis (Y values), I want the Scrollpager's subchart to use that custom axis (or at least its settings), too.

Create/Add custom axis for main chart.
Configure some properties.
Link the axis to the series.
[...]
Scales[Idx] := ViewChart.CustomAxes.Add();
Scales[Idx].PositionPercent := Idx * ScalePosFactor + ScalePosOffset;
Scales[Idx].Grid.Hide;
Scales[Idx].SetMinMax(-10.0, 10.0);
Scales[Idx].Increment := 2.5;
Signals[Idx].Serie.CustomVertAxis := ViewChart.CustomAxes[Idx];
[...]


Add series of main chart to Scrollpager tool.
Make the series of the scrollpager tool use the same vertical axis the series from the main chart uses.

ScrollPagerTool.AddSeries(ViewChart[0]);

(a) ScrollPagerTool.Series.VertAxis := ViewChart[0].VertAxis; // does not work
(b) ScrollPagerTool.SubChartTChart[0].VertAxis := aLeftAxis; // works, why?

Why does (b) work?
The series of the main chart uses a custom axis, but in the subchart the series is configured to use the 'leftaxis'...?!


Also I am confused about the 'series' member of a Scrollpager tool:

ScrollPagerTool.Series.SomeProperty ... ;
ScrollPagerTool.SubChartTChart[0].SomeProperty ... ;
ScrollPagerTool.SubChartTChart[1].SomeProperty ... ;


Using "ScrollPagerTool.SubChartTChart[X]" one can access a series in the subchart, but what series is accessed when "ScrollPagerTool.Series" is used? The first one? All series?

Post Reply