multiple legend

TeeChart VCL for Borland/CodeGear/Embarcadero RAD Studio, Delphi and C++ Builder.
stsl
Newbie
Newbie
Posts: 45
Joined: Thu Jun 21, 2007 12:00 am

multiple legend

Post by stsl » Wed Feb 18, 2009 9:35 am

Hi,

I would like to have 2 legends for one graph. The first one with the series name "Serie 1", "Series 2" and the second one for "Serie 3", "Series 4".
It seems that it's only possible to have all the series of a graph in a legend when we choose the lsSeries property.
Or, is it possible (and how) to have one legend but a line to separate two sets of series ?

Best regards

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

Post by Yeray » Wed Feb 18, 2009 11:16 am

Hi stsl,

Yes, you could have two legends using a custom legend. Please take a look at the demo at All features/Miscellaneous/Legend/Multiple Legends.

If you prefer to draw a line, you should draw it directly to the canvas:

Code: Select all

procedure TForm1.Chart1AfterDraw(Sender: TObject);
begin
  With Chart1.Legend.ShapeBounds do
  begin
    With Chart1.Canvas do
    begin
      DoHorizLine(Left,Right,Bottom-17);
    end;
  end;
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

stsl
Newbie
Newbie
Posts: 45
Joined: Thu Jun 21, 2007 12:00 am

multiple legend

Post by stsl » Wed Feb 18, 2009 12:16 pm

I know that there is a multiple legend demo. But in your demo, if you use the lsSeries mode, 2 legends with all the series will be painted. In my chart, i have, for example 3 series. I would like one legend with the first serie (serie name) and the other one legend with the 2 last series.
I tried to do that :

Code: Select all

procedure TForm1.FormCreate(Sender: TObject);
begin

  Series1.FillSampleValues(100);
  Chart.Legend.LegendStyle := lsSeries;

  Series1.ShowInLegend := False;
  Series2.ShowInLegend := False;
  Series3.ShowInLegend := False;

end;

procedure TForm1.ChartAfterDraw(Sender: TObject);
begin
  if cbAdditionalLegend.Checked then
  begin
    Chart.Legend.Top := 125;
    
	Series1.ShowInLegend := False;
    
	Series2.ShowInLegend := True;
    Series3.ShowInLegend := True;

    

    Chart.Legend.DrawLegend;
    Chart.Legend.CustomPosition := False;
  end;
end;
It works but when i checked a serie (i use checkbox) the 2 legends are both painted with serie2 and serie3 intead of one legend with serie1 and the otherone with serie1 ans serie2.


[/img]

stsl
Newbie
Newbie
Posts: 45
Joined: Thu Jun 21, 2007 12:00 am

Post by stsl » Wed Feb 18, 2009 12:31 pm

I made a mistake in my sample code. In the form create :

Code: Select all


Series1.ShowInLegend := True;

Series2.ShowInLegend := False; 
Series3.ShowInLegend := False;


stsl
Newbie
Newbie
Posts: 45
Joined: Thu Jun 21, 2007 12:00 am

Post by stsl » Wed Feb 18, 2009 12:37 pm

I found a solution of my problem. I added a OnBeforeDrawChart :

Code: Select all

procedure TForm1.ChartBeforeDrawChart(Sender: TObject);
begin
  Series1.ShowInLegend := True;

  Series2.ShowInLegend := False;
  Series3.ShowInLegend := False;
 
end;


stsl
Newbie
Newbie
Posts: 45
Joined: Thu Jun 21, 2007 12:00 am

Post by stsl » Wed Feb 18, 2009 3:16 pm

Now, i would like to have one legend with series and a second legend with serie groups. I modified my ChartAfterDraw procedure like this :

Code: Select all

procedure TForm1.ChartAfterDraw(Sender: TObject);
begin
  if cbadditionallegend.Checked then
  begin
    Chart.Legend.Top := 125;

    Chart.Legend.CheckBoxes:=True;
    Chart.Legend.LegendStyle := lsSeriesGroups;

    Chart.Legend.DrawLegend;
    Chart.Legend.CustomPosition := False;
  end;
end;

The two legends are properly drawn but it's now impossible to check or uncheck the series in the first legend and the groups in the second legend.

Where is my mistake ?

Best regards

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

Post by Yeray » Thu Feb 19, 2009 10:32 am

Hi stsl,

Yes, I've seen the problem. It seems that, having multiple series, only the last one is clickable. I''ve added the issue to the wish list to be fixed in further releases (TV52013878).

In the meanwhile, here you have a workaround example that works for me here:

Code: Select all

const CheckBoxesSeparation = 17;
CheckBoxesSize = 12;
FirstItemTop = 56;
CheckBoxesMargin = 4;


implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var i:Integer;
begin
  Chart1.View3D := false;

  for i:=0 to Chart1.SeriesCount-1 do
    Chart1[i].FillSampleValues(25);

  with Chart1.SeriesGroups do
  begin
    Chart1.SeriesList.AddGroup('Group A');
    Items[0].Add(Chart1[0]);
    Items[0].Add(Chart1[1]);

    Chart1.SeriesList.AddGroup('Group B');
    Items[1].Add(Chart1[2]);
    Items[1].Add(Chart1[3]);
  end;

  Chart1.Legend.CheckBoxes:=True;
end;

procedure TForm1.Chart1AfterDraw(Sender: TObject);
begin
  Chart1.Legend.Top := 125;
  Chart1.Legend.LegendStyle := lsSeriesGroups;
  Chart1.Legend.DrawLegend;
end;

procedure TForm1.Chart1BeforeDrawChart(Sender: TObject);
begin
  Chart1.Legend.CustomPosition := False;
  Chart1.Legend.LegendStyle := lsSeries;
end;

procedure TForm1.Chart1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var i: Integer;
begin
  for i:= 0 to Chart1.SeriesCount-1 do
    if ((X > Chart1.Legend.Left + CheckBoxesMargin) and
      (X < Chart1.Legend.Left + CheckBoxesMargin + CheckBoxesSize)) then
      if ((Y > FirstItemTop + (i*CheckBoxesSeparation)) and
        (Y < FirstItemTop + (i*CheckBoxesSeparation) + CheckBoxesSize)) then
        Chart1[i].Active := not Chart1[i].Active;
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

stsl
Newbie
Newbie
Posts: 45
Joined: Thu Jun 21, 2007 12:00 am

Post by stsl » Thu Feb 19, 2009 2:19 pm

Hi,

You said that only the last one is clickable. All my legends are not clickable.
So, your sample code solved the problem for the first legend but not for the second one.
How is it possible to track the mouse position for the second legend ?

Best regards

stsl
Newbie
Newbie
Posts: 45
Joined: Thu Jun 21, 2007 12:00 am

Post by stsl » Thu Feb 19, 2009 2:29 pm

and an other question :

How i can get the real values of the constantes CheckBoxesSeparation,
CheckBoxesSize, FirstItemTop and CheckBoxesMargin ?

Regards

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

Post by Yeray » Thu Feb 19, 2009 4:15 pm

Hi stsl,

1) It's strange, Ive tried with extralegend tool and with more than 2 legends and always the last drawn legend is clickable.

Well, you always will be able to add another "for" to check if the click was on the checkbox. This time you could do as follows:

Code: Select all

const CheckBoxesSeparation = 17;
CheckBoxesSize = 12;
FirstItemTopLegend1 = 56;
FirstItemTopLegend2 = 131;
CheckBoxesMargin = 4;

//...

procedure TForm1.Chart1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var i, j: Integer;
begin
  for i:=0 to Chart1.SeriesCount-1 do
    if ((X > Chart1.Legend.Left + CheckBoxesMargin) and
      (X < Chart1.Legend.Left + CheckBoxesMargin + CheckBoxesSize)) then
      if ((Y > FirstItemTopLegend1 + (i*CheckBoxesSeparation)) and
        (Y < FirstItemTopLegend1 + (i*CheckBoxesSeparation) + CheckBoxesSize)) then
        Chart1[i].Active := not Chart1[i].Active;

  for i:=0 to Chart1.SeriesGroups.Count-1 do
    if ((X > Chart1.Legend.Left + CheckBoxesMargin) and
      (X < Chart1.Legend.Left + CheckBoxesMargin + CheckBoxesSize)) then
      if ((Y > FirstItemTopLegend2 + (i*CheckBoxesSeparation)) and
        (Y < FirstItemTopLegend2 + (i*CheckBoxesSeparation) + CheckBoxesSize)) then
        for j:=0 to Chart1.SeriesGroups[i].Series.Count-1 do
          Chart1.SeriesGroups[i].Series[j].Active := not Chart1.SeriesGroups[i].Series[j].Active;
end;
2) I've found the constant values with a simple trick. Add the following code to your OnMouseDown or OnMouseMove event:

Code: Select all

caption := 'X: ' + floattostr(X) + '  Y: ' + floattostr(Y);
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

stsl
Newbie
Newbie
Posts: 45
Joined: Thu Jun 21, 2007 12:00 am

Post by stsl » Fri Feb 20, 2009 7:39 am

Hi,

It tried your solution but it didn't work. Could i post (and how) a sample project to you ?

How is it possible to get the position of the legend at runtime (to fix firstItemTopLegend1 and 2) ? The user can customize the position of the legend ar runtime with the editor.

Best regards

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

Post by Yeray » Fri Feb 20, 2009 11:10 am

Hi stsl,

I've made some changes to the example to be mora adaptable to different chart sizes.

Code: Select all

var
  Form1: TForm1;
  CheckBoxesSeparation, CheckBoxesSize, FirstItemTopLegend1, Leged2Top, CheckBoxesLeftMargin, CheckBoxesTopMargin: Integer;

implementation

uses Types;

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var i:Integer;
begin
  Chart1.View3D := false;

  for i:=0 to Chart1.SeriesCount-1 do
    Chart1[i].FillSampleValues(25);

  with Chart1.SeriesGroups do
  begin
    Chart1.SeriesList.AddGroup('Group A');
    Items[0].Add(Chart1[0]);
    Items[0].Add(Chart1[1]);

    Chart1.SeriesList.AddGroup('Group B');
    Items[1].Add(Chart1[2]);
    Items[1].Add(Chart1[3]);
  end;

  Chart1.Legend.CheckBoxes:=True;

  CheckBoxesSeparation := 17;
  CheckBoxesSize := 12;
  CheckBoxesLeftMargin := 4;
  CheckBoxesTopMargin := 5;
  Leged2Top := 125;
end;

procedure TForm1.Chart1AfterDraw(Sender: TObject);
begin
  Chart1.Legend.Top := Leged2Top;
  Chart1.Legend.LegendStyle := lsSeriesGroups;
  Chart1.Legend.DrawLegend;
end;

procedure TForm1.Chart1BeforeDrawChart(Sender: TObject);
begin
  Chart1.Legend.CustomPosition := False;
  Chart1.Legend.LegendStyle := lsSeries;
end;

procedure TForm1.Chart1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var i, j: Integer;
begin
  Chart1.Canvas.Pen.Color := clRed;

  for i:=0 to Chart1.SeriesCount-1 do
  begin
    if ((X > Chart1.Legend.Left + CheckBoxesLeftMargin) and
      (X < Chart1.Legend.Left + CheckBoxesLeftMargin + CheckBoxesSize)) then
      if ((Y > FirstItemTopLegend1 + (i*CheckBoxesSeparation) + CheckBoxesTopMargin) and
        (Y < FirstItemTopLegend1 + (i*CheckBoxesSeparation) + CheckBoxesSize + CheckBoxesTopMargin)) then
        Chart1[i].Active := not Chart1[i].Active;

  //  Draw rectangles to test positions
  //  Chart1.Canvas.Rectangle(Chart1.Legend.Left + CheckBoxesLeftMargin, FirstItemTopLegend1 + (i*CheckBoxesSeparation) + CheckBoxesTopMargin, Chart1.Legend.Left + CheckBoxesLeftMargin + CheckBoxesSize, FirstItemTopLegend1 + (i*CheckBoxesSeparation) + CheckBoxesSize + CheckBoxesTopMargin);
  end;

  for i:=0 to Chart1.SeriesGroups.Count-1 do
  begin
    if ((X > Chart1.Legend.Left + CheckBoxesLeftMargin) and
      (X < Chart1.Legend.Left + CheckBoxesLeftMargin + CheckBoxesSize)) then
      if ((Y > Chart1.Legend.Top + (i*CheckBoxesSeparation) + CheckBoxesTopMargin) and
        (Y < Chart1.Legend.Top + (i*CheckBoxesSeparation) + CheckBoxesSize + CheckBoxesTopMargin)) then
        for j:=0 to Chart1.SeriesGroups[i].Series.Count-1 do
          Chart1.SeriesGroups[i].Series[j].Active := not Chart1.SeriesGroups[i].Series[j].Active;

  //  Draw rectangles to test positions
  //  Chart1.Canvas.Rectangle(Chart1.Legend.Left + CheckBoxesLeftMargin, Chart1.Legend.Top + (i*CheckBoxesSeparation) + CheckBoxesTopMargin, Chart1.Legend.Left + CheckBoxesLeftMargin + CheckBoxesSize, Chart1.Legend.Top + (i*CheckBoxesSeparation) + CheckBoxesSize + CheckBoxesTopMargin);
  end;

//  Show X and Y of the pixel clicked 
//  caption := 'X: ' + floattostr(X) + '  Y: ' + floattostr(Y);
end;

procedure TForm1.Chart1GetLegendRect(Sender: TCustomChart;
  var Rect: TRect);
begin
  if Rect.Top <> Leged2Top then
    FirstItemTopLegend1 := Rect.Top;
end;
Yes, you can either post your files at news://www.steema.net/steema.public.attachments newsgroup or at our upload page.
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

stsl
Newbie
Newbie
Posts: 45
Joined: Thu Jun 21, 2007 12:00 am

Post by stsl » Fri Feb 20, 2009 12:33 pm

Thank you for your sample.
I uploaded a file with your upload page (TChartLegend.zip). In this sample, i can check/unchek the second legend (group) but for the first legend (Series), i have to click at the right of the check box (and not on the check box) to uncheck/check the series. Click on the Gantt values button to add the grouped series and check the additionallegend check box to see the ExtraLegend tool.

In your sample, do you use an ExtraLegend tool or a multiple legend ?
It would be better if i could download your complete sample.

Best regards

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

Post by Yeray » Fri Feb 20, 2009 3:39 pm

Hi stsl,

In my sample I used multiple legends, but with extralegend tool it looks more simple because you can access to legend positions when you want because it's not re-written. So we won't need OnBeforeDraw, OnAfterDraw and OnGetLegendRect events.

In your project, the legend works fine for me all the time. I mean, simply adding an extralegendtool, the normal legend remains clickable. Then we only need to find the checkboxes of the extralegend and see if the click is in its area.

The following code works for me in your project:

Code: Select all

procedure TForm1.ChartMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
  i, j: Integer;
begin
    for i:= 0 to Chart.SeriesGroups.Count - 1 do
    begin
      if (X > ExtraLegend.Legend.Left) and (X < ExtraLegend.Legend.Left + ExtraLegend.Legend.Width) then
      begin
        if (Y > ExtraLegend.Legend.Top + CheckBoxesMargin + (i * CheckBoxesSeparation)) and (Y < ExtraLegend.Legend.Top + CheckBoxesMargin + (i*CheckBoxesSeparation) + CheckBoxesSize) then
        begin
          outputdebugstring('Clic groups good');
          if Chart.SeriesGroups[i].Active = gaYes then
            Chart.SeriesGroups[i].Active := gaNo
          else
            Chart.SeriesGroups[i].Active := gaYes;

          Exit;
        end;
      end;
    end;
end;
PS: I also recommend you to change the value of CheckBoxesMargin to 5.
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

stsl
Newbie
Newbie
Posts: 45
Joined: Thu Jun 21, 2007 12:00 am

Post by stsl » Fri Feb 20, 2009 3:44 pm

Congratulations, it works very well...
Best regards

Post Reply