Page 1 of 1

labels for marks

Posted: Fri Oct 20, 2017 5:06 pm
by 16579481
What is the best way to position the labels for point marks so that the string is centered at the same height as the mark and the left end of the string is just a little to the right of the mark? I have been trying to do the computations in pixels and then convert to user units but that seems like too much work for this simple (?) task.

Re: labels for marks

Posted: Mon Oct 23, 2017 6:53 am
by yeray
Hello,

Note there are several LabelStyles you can try. Ie smsLabelValue:

Code: Select all

uses Series;

procedure TForm1.FormCreate(Sender: TObject);
var i : Integer;
const nPoints = 5;
      myLabels : array  [0..nPoints-1] of string = ('Cars', 'Phones', 'Tables', 'Monitors', 'Lamps');
begin
  with Chart1.AddSeries(TPointSeries) as TPointSeries do
  begin
    for i:=0 to nPoints-1 do
      Add(50+random*50, myLabels[i]);

    Marks.Visible:=True;
    Marks.Style:=smsLabelValue;
  end;
end;
If they don't fit your needs and you still want to draw the label next to the mark, you can use TAnnotationTools. Ie:

Code: Select all

uses Series, TeeTools;

var annotations: array of TAnnotationTool;

procedure TForm1.FormCreate(Sender: TObject);
var i : Integer;
const nPoints = 5;
      myLabels : array  [0..nPoints-1] of string = ('Cars', 'Phones', 'Tables', 'Monitors', 'Lamps');
begin
  SetLength(annotations, nPoints);

  with Chart1.AddSeries(TPointSeries) as TPointSeries do
  begin
    for i:=0 to nPoints-1 do
    begin
      annotations[i]:=Chart1.Tools.Add(TAnnotationTool) as TAnnotationTool;
      annotations[i].Text:=myLabels[i];

      Add(50+random*50);
    end;

    Marks.Visible:=True;
    Marks.Style:=smsValue;

    OnGetMarkText:=SeriesGetMarkText;
  end;
end;

procedure TForm1.SeriesGetMarkText(Sender: TChartSeries; ValueIndex: Integer; var MarkText: string);
begin
  if (ValueIndex>=0) and (ValueIndex<Chart1[0].Count) and (Sender.Marks.Positions[ValueIndex] <> nil) then
  begin
    annotations[ValueIndex].Left:=Sender.Marks.Positions[ValueIndex].LeftTop.X+Sender.Marks.Positions[ValueIndex].Width;
    annotations[ValueIndex].Top:=Sender.Marks.Positions[ValueIndex].LeftTop.Y;
  end;
end;

Re: labels for marks

Posted: Thu Oct 26, 2017 3:38 pm
by 16579481
Thanks but this does not yet give me what I want. I have attached a screenshot below.

Two problems. First, It puts the labels to the right of the values that label each mark. I need the labels to be to the right of each point not the labels for the marks.

Second, when I run the program the annotations are initially all on top of each other at the top left of the chart until I move the mouse over one of the points. They then snap to the positions shown in the screenshot. Do I need to set some options for the TChart? My form just has a TChart component dropped onto it and then set to align with the client. No other changes to the form.

My code (copied from your second example) is given below.

Code: Select all

unit testTeeu;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, VclTee.TeeGDIPlus, VclTee.TeEngine,
  Vcl.ExtCtrls, VclTee.TeeProcs, VclTee.Chart, VclTee.Series, VclTee.TeeTools;

type
  TForm1 = class(TForm)
    Chart1: TChart;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  var
    annotations: array of TAnnotationTool;
    procedure SeriesGetMarkText(Sender: TChartSeries; ValueIndex: Integer;
      var MarkText: string);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
  i: Integer;
const
  nPoints = 5;
  myLabels: array [0 .. nPoints - 1] of string = ('Cars', 'Phones', 'Tables',
    'Monitors', 'Lamps');
begin
  SetLength(annotations, nPoints);

  with Chart1.AddSeries(TPointSeries) as TPointSeries do
  begin
    for i := 0 to nPoints - 1 do
    begin
      annotations[i] := Chart1.Tools.Add(TAnnotationTool) as TAnnotationTool;
      annotations[i].Text := myLabels[i];

      Add(50 + random * 50);
    end;

    Marks.Visible := True;
    Marks.Style := smsValue;

    OnGetMarkText := SeriesGetMarkText;
  end;
end;

procedure TForm1.SeriesGetMarkText(Sender: TChartSeries; ValueIndex: Integer;
  var MarkText: string);
begin
  if (ValueIndex >= 0) and (ValueIndex < Chart1[0].Count) and
    (Sender.Marks.Positions[ValueIndex] <> nil) then
  begin
    annotations[ValueIndex].Left := Sender.Marks.Positions[ValueIndex].LeftTop.X
      + Sender.Marks.Positions[ValueIndex].Width;
    annotations[ValueIndex].Top := Sender.Marks.Positions[ValueIndex].LeftTop.Y;
  end;
end;

end.
Thanks for your help.

Re: labels for marks

Posted: Fri Oct 27, 2017 7:26 am
by yeray
Hello,
JimR wrote:First, It puts the labels to the right of the values that label each mark. I need the labels to be to the right of each point not the labels for the marks.
I'm not sure to understand if you want to also show the default marks or not. If you don't want to show the default marks, you can just modify their text and position without needing to use TAnnotationTools.
An image could help to clarify the goal.
JimR wrote:Second, when I run the program the annotations are initially all on top of each other at the top left of the chart until I move the mouse over one of the points. They then snap to the positions shown in the screenshot. Do I need to set some options for the TChart? My form just has a TChart component dropped onto it and then set to align with the client. No other changes to the form.
It also fails to correctly reposition the annotations when you zoom and unzoom. this happens because the TAnnotationTools are drawn earlier than the series in the drawing center; so the annotation positions that are calculated in the SeriesGetMarkText will apply at the next repaint. To solve it, you can force a chart repaint when these events happen. Ie:

Code: Select all

procedure TForm1.Chart1UndoZoom(Sender: TObject);
begin
  Chart1.Draw;
end;

procedure TForm1.Chart1Zoom(Sender: TObject);
begin
  Chart1.Draw;
end;

procedure TForm1.FormCreate(Sender: TObject);
//...

  Chart1.Draw;
end;

Re: labels for marks

Posted: Wed Nov 01, 2017 4:53 pm
by 16579481
Right, I don't want to display the marks - I just want to show their labels at the ends of some arrow series.

The default seems to be to plot them centered above where the mark would be. I need them centered vertically but plotted to the right of where the mark would be (though for some plots I would like them to the left and right justified up to where the mark would be). For example: Xaaaaaa or aaaaaX (where X is the mark and aaaaa is some text for the label).

I could not find position constants for left, right, center, etc. for the labels.

The annotation option seems more complicated as I believe I would need to calculate positions in pixels and redraw after any zoom etc. (Is that correct?)

Thanks

Re: labels for marks

Posted: Thu Nov 02, 2017 11:11 am
by yeray
Hello,
JimR wrote:Right, I don't want to display the marks - I just want to show their labels at the ends of some arrow series.

The default seems to be to plot them centered above where the mark would be. I need them centered vertically but plotted to the right of where the mark would be (though for some plots I would like them to the left and right justified up to where the mark would be). For example: Xaaaaaa or aaaaaX (where X is the mark and aaaaa is some text for the label).

I could not find position constants for left, right, center, etc. for the labels.

The annotation option seems more complicated as I believe I would need to calculate positions in pixels and redraw after any zoom etc. (Is that correct?)
You can modify the Marks positions by accessing the Marks.Positions array. You can find an example of how to modify these positions here.
Note you have to recalculate the positions after every zoom/scroll, exactly the same issue as with the TAnnotationTools option.

If you still find problems with it, please try to show a screenshot/mockup showing the expected result.