Page 1 of 2

How to read chart width & height from a non visible char

Posted: Thu Mar 13, 2008 1:07 pm
by 9349911
Hi !

I try to get the chart width & height from a chart I load from a template. This is my code:

Code: Select all

          _t2 := TChart.Create(Self);
          _t2.Parent := NIL;
          Chart_Container.ChartMemStream.Position := 0;  // Stream auf Anfang positionieren
          LoadChartFromStream(TCustomChart(_t2), TStream(Chart_Container.ChartMemStream));
          ChartStreamWidth  := _t2.ChartRect.Bottom - _t2.ChartRect.Top;
          ChartStreamHeight := _t2.ChartRect.right  - _t2.chartrect.left;

          if _t2 <> NIL then _t2.Free;
The problem is the nonvisible chart because of parent = NIL. I get 0 as result for height und width.

If I use _t2.Parent := TestTab (TestTab is a Pagecontrol Panel) it works as I aspected.

But if I set the paranet <> NIL the chart will be painted completely?!

Is there a way to get the values with parent = NIL ?

Posted: Thu Mar 13, 2008 3:36 pm
by narcis
Hi Dominik,

This should work fine because when exporting a TChart to a template file or stream, its width and height are exported as well. Code below works fine for me here.

Code: Select all

uses TeeStore, TeeEditPro;

procedure TForm1.Button1Click(Sender: TObject);
var tmpChart: TChart;
    stream: TMemoryStream;
begin
  stream:=TMemoryStream.Create;
  tmpChart:=TChart.Create(self);

  try
    SaveChartToStream(Chart1,stream);
    stream.Position:=0;
    LoadChartFromStream(TCustomChart(tmpChart),stream);
  finally
    stream.Free;
  end;

  Caption:=IntToStr(tmpChart.Width) + ', ' + IntToStr(tmpChart.Height);
end;
Even the chart has no parent you could try calling its Draw method so that Width and Height properties are computed.

Hope this helps!

Posted: Thu Mar 13, 2008 7:07 pm
by 9349911
Hi Narcis,

well I can read width and height. That is no problem.
But I need only the width / height of the chart area.

Width and Height includes the axis. And that´s not what I need.

So I need something like this:
ChartStreamWidth := _t2.ChartRect.Bottom - _t2.ChartRect.Top;
ChartStreamHeight := _t2.ChartRect.right - _t2.chartrect.left;

I will test if I get useful value if I call the draw methode before. At the moment the chartrect is 0,0,0,0.

Posted: Fri Mar 14, 2008 10:32 am
by narcis
Hi Dominik,

Ok, you need to set a parent for the chart to be able to draw it and obtain valid values. This works fine for me here:

Code: Select all

uses TeeStore, TeeEditPro;

procedure TForm1.Button1Click(Sender: TObject);
var tmpChart: TChart;
    stream: TMemoryStream;
    RectWidth, RectHeight: Integer;
begin
  stream:=TMemoryStream.Create;
  tmpChart:=TChart.Create(self);

  try
    SaveChartToStream(Chart1,stream);
    stream.Position:=0;
    LoadChartFromStream(TCustomChart(tmpChart),stream);
  finally
    stream.Free;
  end;

  tmpChart.Parent:=self;
  tmpChart.Draw;
  tmpChart.Parent:=nil;

  RectWidth:=tmpChart.ChartRect.Right-tmpChart.ChartRect.Left;
  RectHeight:=tmpChart.ChartRect.Bottom-tmpChart.ChartRect.Top;
  Caption:=IntToStr(RectWidth) + ', ' + IntToStr(RectHeight);
end;

Posted: Tue Mar 18, 2008 6:46 am
by 9349911
Hi Narcis,

I tried your solution but this works not really good.

What I need is the original ChartRect size. The problem in your code is the following:
You save a Chart to a stream. Then resize the Form and do this:

Code: Select all

          Chart_Container.ChartMemStream.Position := 0;  // Stream auf Anfang positionieren
          LoadChartFromStream(TCustomChart(_t2), TStream(Chart_Container.ChartMemStream));
          _t2.Parent := Self;
          _t2.Draw;
          _t2.Parent := nil;
The first thing what happens is a resize of _t2. And if I read the ChartRect values in the next step I don´t get the correct values.

I want to use these values for calculate the correct positions for callouts. You know that the position is saved in pixel values. And if you load a chart from a stream and your form size has changed the callouts will be placed wrong.

My idea was to read the saved ChartRect information and do some calculations like this:

Code: Select all

Left  := Trunc(Left  * (Width  / ChartStreamWidth));
Width = actual ChartRect Width
ChartStreamWidth = saved ChartRect Width

This would result in correct callout positions. But with your code I won´t get the correct ChartStreamWidth value and in the end I have the callouts at the wrong position, too.

Any further idea?

Posted: Tue Mar 18, 2008 10:31 am
by narcis
Hi Dominik,
I want to use these values for calculate the correct positions for callouts. You know that the position is saved in pixel values. And if you load a chart from a stream and your form size has changed the callouts will be placed wrong.


Chart's dimensions are independent to form's dimensions, moreover if it's a chart with its Parent set to nil.

Could you please send us a simple example project we can run "as-is" and let us know the exact steps we should follow to reproduce your problem here?

Thanks in advance.

Posted: Thu Mar 20, 2008 11:03 am
by 9349911
Hi Narcis,

I have done a small example and uploaded it to your server:
Received CallOutPos After Load.zip Content Type application/x-zip-compressed Length 21703

Just run it and click the Button from 1 to 4.

You will see that the callout is at the wrong position. That´s because the position is stored in pixel.

The simple question is ...
How can I set the callout to the correct position after loading the TEE file?

Posted: Thu Mar 20, 2008 12:24 pm
by narcis
Hi Dominik,

Thanks for the example project. I've just sent it back to you modified doing what I already suggested you:

1. Using relative position for the rectangle tool.
2. Refreshing its position after loading the template file.

Posted: Thu Mar 20, 2008 12:56 pm
by 9349911
Hi Narcis,

thx for the example but that won´t fix my problem.

You do this:

Code: Select all

  x := Chart1[0].CalcXPos(6);
  y := Chart1[0].CalcYPos(5);
  Annotacion.Callout.XPosition := x;
  Annotacion.Callout.YPosition := y;
You set the position always to the same position.

But I have a saved Chart which includes Rectangles with callouts. And I don´t know the positions before.

So I have to read the Chart File (TEE), get the Rectangles and set the positions of the callouts.

I have no idea how this should work with your code. The problem is the following (i think):
You have to calculate the x / y position with CalcXPos but you only have the pixel value from the Chart file. And if you resize your form / chart you won´t get the same position.

In facts ... This is what I´m doing in my application:

Code: Select all

  Annotacion.Shape.Text             := 'This is text :-)';

  Annotacion.Shape.Shadow.HorizSize := 0;
  Annotacion.Shape.Shadow.VertSize  := 0;
  Annotacion.Shape.Transparency     := 75;
  Annotacion.Shape.AutoSize         := True;
  Annotacion.Shape.Cursor           := crHandPoint;
  Annotacion.Callout.Visible        := True;
  Annotacion.Callout.Arrow.Visible  := True;
  Annotacion.Callout.Arrow.Color    := clGreen;
  Annotacion.Callout.Brush.Color    := clBlack;
  Annotacion.Callout.HorizSize      := 3;
  Annotacion.Callout.Style          := psLeftTriangle;
  Annotacion.Callout.VertSize       := 3;
  Annotacion.Callout.Visible        := True;
  Annotacion.Callout.Arrow.Visible  := True;
  Annotacion.Shape.Left := 10;
  Annotacion.Shape.Top  := 20;
  Annotacion.Callout.XPosition := 100;
  Annotacion.Callout.YPosition := 150;
And in that case your SetAnnotationPosition won´t fix my problem.

Posted: Thu Mar 20, 2008 3:14 pm
by narcis
Hi Dominik,

That's why I added the other alternative commented in the code. You could also add the OnResize event and do something like this:

Code: Select all

procedure TForm16.Chart1Resize(Sender: TObject);
begin
  if Chart1.Tools.Count>0 then
    SetAnnotationPosition(Chart1.Tools.Items[0] as TRectangleTool);
end;

procedure TForm16.SetAnnotationPosition(Annotacion: TRectangleTool);
begin
  Chart1.Draw;

  Annotacion.Shape.Left := Chart1.ChartRect.Left  + (150 - Chart1.Left);
  Annotacion.Shape.Top :=  Chart1.ChartRect.Top + (100 - Chart1.Top);
  Annotacion.Callout.XPosition := Chart1.ChartRect.Left  + (250 - Chart1.Left);
  Annotacion.Callout.YPosition := Chart1.ChartRect.Top + (180 - Chart1.Top);
end;

Posted: Wed Mar 26, 2008 11:14 am
by 9349911
Hi Narcis,

Code: Select all

 Annotacion.Shape.Left := Chart1.ChartRect.Left  + (150 - Chart1.Left); 
  Annotacion.Shape.Top :=  Chart1.ChartRect.Top + (100 - Chart1.Top); 
  Annotacion.Callout.XPosition := Chart1.ChartRect.Left  + (250 - Chart1.Left); 
  Annotacion.Callout.YPosition := Chart1.ChartRect.Top + (180 - Chart1.Top); 
Sure, this will work. But you won´t read the positions from a stream (or a Tee file). You use fixed positions and that won´t solve my problem.

Keep in mind what I need to do ...
Lets say we have an annotation and a callout. We save the chart to a file. Clear the chart and resize the form / chart. If you load the chart back from the file the callout position is wrong because of resizing the chart. And that is the problem I´m trying to fix.

So I need something like this:

Code: Select all

Annotacion.Callout.XPosition := SavedPositionX * (NewChartWidth / OldChartWidth);
SavedPositionX is the X Pixel Position from the TEE file (or Stream)
NewChartWidth = actual Chart width (chartrect.right - ChartRect.Left)
OldChartWidth = Chart width from the saved chart within the TEE file
The Chart Dimensions are stored in the TEE file:
object TChart
Left = 8
Top = 39
Width = 400
Height = 239
As far as I can see your solution on ly uses fixed positions.

So what can I do to solve my problem ?

Posted: Wed Mar 26, 2008 11:22 am
by narcis
Hi Dominik,

Thanks for the information.
So I need something like this:
Code:
Annotacion.Callout.XPosition := SavedPositionX * (NewChartWidth / OldChartWidth);

SavedPositionX is the X Pixel Position from the TEE file (or Stream)
NewChartWidth = actual Chart width (chartrect.right - ChartRect.Left)
OldChartWidth = Chart width from the saved chart within the TEE file
The Chart Dimensions are stored in the TEE file:
Quote:
object TChart
Left = 8
Top = 39
Width = 400
Height = 239
Ok, I think you have everything necessary then. SavedPositionX will be annotation's position after loading the .tee file, you already have NewChartWidth and OldChartWidth is saved in the .tee file as well.

Posted: Wed Mar 26, 2008 11:28 am
by 9349911
Hi Narcis,
and OldChartWidth is saved in the .tee file as well
But there is one problem ... As soon as I create a chart Object to load the Tee temporarily the Width value is set to a new one.

So could you please give me an example (you have my small demo) which shows loading a Tee file and setting the correct callout position?

I have tried many ways now, but can´t get anything working :(

Posted: Wed Mar 26, 2008 1:01 pm
by narcis
Hi Dominik,

Yes, and I already saved that in a variable in the example I sent you some days ago.

You can do this:

Code: Select all

procedure TForm16.SetAnnotationPosition(Annotacion: TRectangleTool);
var w,h: Double;
begin
  Chart1.Draw;

  Annotacion.Shape.Left := Chart1.ChartRect.Left  + (150 - Chart1.Left);
  Annotacion.Shape.Top :=  Chart1.ChartRect.Top + (100 - Chart1.Top);

  if tmpWidth=0 then
    Annotacion.Callout.XPosition := Chart1.ChartRect.Left  + (250 - Chart1.Left)
  else
  begin
    w := Chart1.Width / tmpWidth;
    Annotacion.Callout.XPosition := Round(Annotacion.Callout.XPosition * w);
  end;

  if tmpHeight=0 then
    Annotacion.Callout.YPosition := Chart1.ChartRect.Top + (180 - Chart1.Top)
  else
  begin
    h:= Chart1.Height / tmpHeight;
    Annotacion.Callout.YPosition := Round(Annotacion.Callout.YPosition * h);
  end;
end;
I've sent your project modified with this code.

Posted: Thu Mar 27, 2008 7:09 am
by 9349911
Hi Narcis,

your solution only works if you don´t close the application.

And in fact this is the most important case.
Let me show you a common usecase :

1) A user creates a chart and save it to disk.
2) He sends the file to a different user und this user opens the file. (Both have different Form / chart sizes)
3) If the second user opens the file the callouts are missplaced.

So if we could fix this issue it would be great. If it helps you I can spend a Pro Email for this because this is an important issue for us.