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

TeeChart VCL for Borland/CodeGear/Embarcadero RAD Studio, Delphi and C++ Builder.
moelski
Advanced
Posts: 212
Joined: Mon Apr 23, 2007 12:00 am
Location: Germany
Contact:

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

Post by moelski » Thu Mar 13, 2008 1:07 pm

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 ?

Narcís
Site Admin
Site Admin
Posts: 14730
Joined: Mon Jun 09, 2003 4:00 am
Location: Banyoles, Catalonia
Contact:

Post by Narcís » Thu Mar 13, 2008 3:36 pm

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!
Best Regards,
Narcís Calvet / Development & Support
Steema Software
Avinguda Montilivi 33, 17003 Girona, Catalonia
Tel: 34 972 218 797
http://www.steema.com
Image Image Image Image Image Image
Instructions - How to post in this forum

moelski
Advanced
Posts: 212
Joined: Mon Apr 23, 2007 12:00 am
Location: Germany
Contact:

Post by moelski » Thu Mar 13, 2008 7:07 pm

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.

Narcís
Site Admin
Site Admin
Posts: 14730
Joined: Mon Jun 09, 2003 4:00 am
Location: Banyoles, Catalonia
Contact:

Post by Narcís » Fri Mar 14, 2008 10:32 am

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;
Best Regards,
Narcís Calvet / Development & Support
Steema Software
Avinguda Montilivi 33, 17003 Girona, Catalonia
Tel: 34 972 218 797
http://www.steema.com
Image Image Image Image Image Image
Instructions - How to post in this forum

moelski
Advanced
Posts: 212
Joined: Mon Apr 23, 2007 12:00 am
Location: Germany
Contact:

Post by moelski » Tue Mar 18, 2008 6:46 am

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?

Narcís
Site Admin
Site Admin
Posts: 14730
Joined: Mon Jun 09, 2003 4:00 am
Location: Banyoles, Catalonia
Contact:

Post by Narcís » Tue Mar 18, 2008 10:31 am

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.
Best Regards,
Narcís Calvet / Development & Support
Steema Software
Avinguda Montilivi 33, 17003 Girona, Catalonia
Tel: 34 972 218 797
http://www.steema.com
Image Image Image Image Image Image
Instructions - How to post in this forum

moelski
Advanced
Posts: 212
Joined: Mon Apr 23, 2007 12:00 am
Location: Germany
Contact:

Post by moelski » Thu Mar 20, 2008 11:03 am

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?

Narcís
Site Admin
Site Admin
Posts: 14730
Joined: Mon Jun 09, 2003 4:00 am
Location: Banyoles, Catalonia
Contact:

Post by Narcís » Thu Mar 20, 2008 12:24 pm

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.
Best Regards,
Narcís Calvet / Development & Support
Steema Software
Avinguda Montilivi 33, 17003 Girona, Catalonia
Tel: 34 972 218 797
http://www.steema.com
Image Image Image Image Image Image
Instructions - How to post in this forum

moelski
Advanced
Posts: 212
Joined: Mon Apr 23, 2007 12:00 am
Location: Germany
Contact:

Post by moelski » Thu Mar 20, 2008 12:56 pm

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.

Narcís
Site Admin
Site Admin
Posts: 14730
Joined: Mon Jun 09, 2003 4:00 am
Location: Banyoles, Catalonia
Contact:

Post by Narcís » Thu Mar 20, 2008 3:14 pm

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;
Best Regards,
Narcís Calvet / Development & Support
Steema Software
Avinguda Montilivi 33, 17003 Girona, Catalonia
Tel: 34 972 218 797
http://www.steema.com
Image Image Image Image Image Image
Instructions - How to post in this forum

moelski
Advanced
Posts: 212
Joined: Mon Apr 23, 2007 12:00 am
Location: Germany
Contact:

Post by moelski » Wed Mar 26, 2008 11:14 am

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 ?

Narcís
Site Admin
Site Admin
Posts: 14730
Joined: Mon Jun 09, 2003 4:00 am
Location: Banyoles, Catalonia
Contact:

Post by Narcís » Wed Mar 26, 2008 11:22 am

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.
Best Regards,
Narcís Calvet / Development & Support
Steema Software
Avinguda Montilivi 33, 17003 Girona, Catalonia
Tel: 34 972 218 797
http://www.steema.com
Image Image Image Image Image Image
Instructions - How to post in this forum

moelski
Advanced
Posts: 212
Joined: Mon Apr 23, 2007 12:00 am
Location: Germany
Contact:

Post by moelski » Wed Mar 26, 2008 11:28 am

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 :(

Narcís
Site Admin
Site Admin
Posts: 14730
Joined: Mon Jun 09, 2003 4:00 am
Location: Banyoles, Catalonia
Contact:

Post by Narcís » Wed Mar 26, 2008 1:01 pm

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.
Best Regards,
Narcís Calvet / Development & Support
Steema Software
Avinguda Montilivi 33, 17003 Girona, Catalonia
Tel: 34 972 218 797
http://www.steema.com
Image Image Image Image Image Image
Instructions - How to post in this forum

moelski
Advanced
Posts: 212
Joined: Mon Apr 23, 2007 12:00 am
Location: Germany
Contact:

Post by moelski » Thu Mar 27, 2008 7:09 am

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.

Post Reply