Clone Series from a TmpChart ?

TeeChart VCL for Borland/CodeGear/Embarcadero RAD Studio, Delphi and C++ Builder.
moelski
Newbie
Newbie
Posts: 92
Joined: Tue Jun 19, 2007 12:00 am
Contact:

Clone Series from a TmpChart ?

Post by moelski » Tue May 12, 2009 8:42 am

Hi !

I use this code to Clone Series from a Temp Chart to an available chart:

Code: Select all

    // Dummy Chart erzeugen
    tmpEmpty := TChart.Create(nil);    { Create an empty chart }
    tmpEmpty.Parent := Self;
    try
      LoadChartFromStream(TCustomChart(tmpEmpty), Stream);     // Dummy Chart mit Stream füllen
      // Serien kopieren
      while Serien <> '' do
      begin
        Serie  := StrToInt(Copy(Serien, 1, pos('#', Serien) - 1));
        Serien := Copy(Serien, Pos('#', Serien) + 1, length(Serien));
        TargetChart.AddSeries(CloneChartSeries(tmpEmpty[Serie]));
        TargetChart[TargetChart.SeriesCount - 1].Assign( tmpEmpty[Serie] ) ;
      end;
    finally
      Stream.Free;
      tmpEmpty.Free;                   // Dummy Chart löschen
      TargetListBox.UpdateSeries;
      TargetChart.Refresh;
    end;
This Part is only to get the Series Number:
Serie := StrToInt(Copy(Serien, 1, pos('#', Serien) - 1));
Serien := Copy(Serien, Pos('#', Serien) + 1, length(Serien));
The code runs without errors, but I got no series cloend to my TargetChart. Did I miss anything?

The Stream is ok. If I read the Stream directly to the TargetChart all works fine. But I want to clone only some series (including the data).

moelski
Newbie
Newbie
Posts: 92
Joined: Tue Jun 19, 2007 12:00 am
Contact:

Post by moelski » Tue May 12, 2009 10:15 am

I also tried this:

Code: Select all

    tmpEmpty := TChart.Create(nil);    { Create an empty chart }
    tmpEmpty.Parent := Self;
    try
      LoadChartFromStream(TCustomChart(tmpEmpty), Stream);     // Dummy Chart mit Stream füllen
      // Serien kopieren
      while Serien <> '' do
      begin
        Serie  := StrToInt(Copy(Serien, 1, pos('#', Serien) - 1));
        Serien := Copy(Serien, Pos('#', Serien) + 1, length(Serien));

        tmpEmpty[Serie].ParentChart := TargetChart;

      end;
    finally
      Stream.Free;
      tmpEmpty.Free;                   // Dummy Chart löschen
      TargetListBox.UpdateSeries;
      TargetChart.Refresh;
    end;
The Series is in the TargetChart. But after tmpEmpty.Free the series is gone from the TargetChart.

So I hape you can give me a solution how to copy/clone or move a complete series from a Temp Chart to another chart.

moelski
Newbie
Newbie
Posts: 92
Joined: Tue Jun 19, 2007 12:00 am
Contact:

Post by moelski » Tue May 12, 2009 10:40 am

This won´t work, too: :(

Code: Select all

var
  SerieCopy : TChartSeries;
......
    tmpEmpty := TChart.Create(nil);    { Create an empty chart }
    tmpEmpty.Parent := Self;
    try
      LoadChartFromStream(TCustomChart(tmpEmpty), Stream);     // Dummy Chart mit Stream füllen
      // Serien kopieren
      while Serien <> '' do
      begin
        Serie  := StrToInt(Copy(Serien, 1, pos('#', Serien) - 1));
        Serien := Copy(Serien, Pos('#', Serien) + 1, length(Serien));

        SerieCopy := CloneChartSeries(tmpEmpty[Serie]);
        SerieCopy.ParentChart := TargetChart;
        TargetChart.AddSeries(SerieCopy );
      end;
    finally
      Stream.Free;
      tmpEmpty.Free;                   // Dummy Chart löschen
      TargetListBox.UpdateSeries;
      TargetChart.Refresh;
    end;
So I really need a solution for that. Hope an any help.

moelski
Newbie
Newbie
Posts: 92
Joined: Tue Jun 19, 2007 12:00 am
Contact:

Post by moelski » Wed May 13, 2009 8:35 am

Hi !

Can anyone help me with this case ?

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

Post by Yeray » Wed May 13, 2009 10:23 am

Hi Dominik,

I'm trying to test your code but I'm not sure to understand how do you initialize Serien string before testing its value in the while condition.
Please, could you send us a simple example project we can run "as-is" to reproduce the problem here?
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

moelski
Newbie
Newbie
Posts: 92
Joined: Tue Jun 19, 2007 12:00 am
Contact:

Post by moelski » Wed May 13, 2009 10:31 am

Hi Yeray,
Serie := StrToInt(Copy(Serien, 1, pos('#', Serien) - 1));
Serien := Copy(Serien, Pos('#', Serien) + 1, length(Serien));
Serien ist just a string which contains series indexes. For example:
0#3#
Serie is the first number of this string - in this case 0. I use this number to select the correct series from the temp chart (tmpEmpty[Serie]).

I loop through the "serien" string until it is empty.

Reproducing my situation is easy:
- create a nonvisible tempchart (tmpEmpty in my case)
- load a stream into the temp chart
- clone (or move) some series (including the data & properties like color, width, ...) from the temp chart to the existing and visible chart.
- delete the temp chart

That´s all.

The code should work with all kinds of series.

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

Post by Yeray » Wed May 13, 2009 12:16 pm

Hi Dominik,

It seems that deleting the temp chart deletes the cloned series. Probably there is a problem with shared pointers references. Here there is an example that shows it:

Code: Select all

uses series, teestore, teeeditpro;

var Stream: TMemoryStream;
    tmpEmpty: TChart;

procedure TForm1.FormCreate(Sender: TObject);
var i: Integer;
begin
  for i:=0 to 3 do
  begin
    Chart1.AddSeries(TLineSeries.Create(self));
    Chart1[i].FillSampleValues(25);
    Chart1[i].Title := 'Series' + IntToStr(i);
  end;

  Stream := TMemoryStream.Create;
  SaveChartToStream(TCustomChart(Chart1), Stream, true, false);
end;

procedure TForm1.Button1Click(Sender: TObject);
var SerieCopy: TChartSeries;
begin
  tmpEmpty := TChart.Create(self);

  try
    Stream.Position := 0;
    LoadChartFromStream(TCustomChart(tmpEmpty), Stream);
    SerieCopy := CloneChartSeries(tmpEmpty[0]);
    Chart2.AddSeries(SerieCopy);
  finally
    Stream.Free;
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  tmpEmpty.Free;
end;
So, to avoid this, you could not to delete your tmpEmpty chart until the exit of your application. Or a little bit more tricky, you could use another stream to save the final chart before freeing the tmpEmpty chart:

Code: Select all

procedure TForm1.Button1Click(Sender: TObject);
var SerieCopy: TChartSeries;
    tmpStream: TMemoryStream;
begin
  tmpEmpty := TChart.Create(self);

  try
    Stream.Position := 0;
    LoadChartFromStream(TCustomChart(tmpEmpty), Stream);
    SerieCopy := CloneChartSeries(tmpEmpty[0]);
    Chart2.AddSeries(SerieCopy);
    tmpStream := TMemoryStream.Create;
    SaveChartToStream(TCustomChart(Chart2), tmpStream, true, false);
    tmpStream.Position := 0;
    LoadChartFromStream(TCustomChart(Chart2), tmpStream);
  finally
    Stream.Free;
    tmpStream.Free;
    tmpEmpty.Free;
  end;
end;
I hope this helps!
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

moelski
Newbie
Newbie
Posts: 92
Joined: Tue Jun 19, 2007 12:00 am
Contact:

Post by moelski » Wed May 13, 2009 12:32 pm

Hi Yeray,
to avoid this, you could not to delete your tmpEmpty chart until the exit of your application
That´s not a good solution ...
Or a little bit more tricky, you could use another stream to save the final chart before freeing the tmpEmpty chart
Well that are a lot of Save and Load operations ...

Is there no solution to copy the series? I don´t need a real clone. A copy would be ok because I don´t need the tempchart after the copy action.

Could you think about something like this:
- get the series type in TmpChart
- create the same series in Target Chart
- copy data and settings to the new series
- delete Tmpchart

Would this be possible ?

moelski
Newbie
Newbie
Posts: 92
Joined: Tue Jun 19, 2007 12:00 am
Contact:

Post by moelski » Thu May 14, 2009 6:40 am

Hi Yeray,

well I could solve one issue :D
I can copy the settings using RTTI. This code works well:

Code: Select all

procedure CopyObject(ObjFrom, ObjTo: TObject);
var
  PropInfos: PPropList;
  PropInfo: PPropInfo;
  Count, Loop: Integer;
  OrdVal: Longint;
  StrVal: String;
  FloatVal: Extended;
  MethodVal: TMethod;
begin
  { Iterate thru all published fields and properties of source }
  { copying them to target }

  { Find out how many properties we'll be considering }
  Count := GetPropList(ObjFrom.ClassInfo, tkAny, nil);
  { Allocate memory to hold their RTTI data }
  GetMem(PropInfos, Count * SizeOf(PPropInfo));
  try
    { Get hold of the property list in our new buffer }
    GetPropList(ObjFrom.ClassInfo, tkAny, PropInfos);
    { Loop through all the selected properties }
    for Loop := 0 to Count - 1 do
    begin
      PropInfo := GetPropInfo(ObjTo.ClassInfo, PropInfos^[Loop]^.Name);
      { Check the general type of the property }
      { and read/write it in an appropriate way }
      case PropInfos^[Loop]^.PropType^.Kind of
        tkInteger, tkChar, tkEnumeration,
        tkSet, tkClass{$ifdef Win32}, tkWChar{$endif}:
        begin
          if UpperCase(PropInfos^[Loop]^.Name) <> 'PARENTCHART' then begin
            OrdVal := GetOrdProp(ObjFrom, PropInfos^[Loop]);
            if Assigned(PropInfo) then
              SetOrdProp(ObjTo, PropInfo, OrdVal);
          end;
        end;
        tkFloat:
        begin
          FloatVal := GetFloatProp(ObjFrom, PropInfos^[Loop]);
          if Assigned(PropInfo) then
            SetFloatProp(ObjTo, PropInfo, FloatVal);
        end;
        {$ifndef DelphiLessThan3}
        tkWString,
        {$endif}
        {$ifdef Win32}
        tkLString,
        {$endif}
        tkString:
        begin
          { Avoid copying 'Name' - components must have unique names }
          if UpperCase(PropInfos^[Loop]^.Name) = 'NAME' then
            Continue;
          StrVal := GetStrProp(ObjFrom, PropInfos^[Loop]);
          if Assigned(PropInfo) then
            SetStrProp(ObjTo, PropInfo, StrVal);
        end;
        tkMethod:
        begin
          MethodVal := GetMethodProp(ObjFrom, PropInfos^[Loop]);
          if Assigned(PropInfo) then
            SetMethodProp(ObjTo, PropInfo, MethodVal);
        end
      end
    end
  finally
    FreeMem(PropInfos, Count * SizeOf(PPropInfo));
  end;
end;
But now there are two problems left and I hope you can help me with that.

1) How can I get the Class of a series?
Example ... Lets say we have a chart with one TFastLineSeries. Now I have to create the series in the target chart first. This can be done with this code:

Code: Select all

var series1: TFastLineSeries; 
begin
  series1 := TFastLineSeries.Create(nil);
  Chart2.AddSeries(series1);
But the is fixed to TFastLine. I need a general procedure for creating the series in the TargetChart. Something like this:

Code: Select all

var series1: TChartSeries; 
begin
  series1 := TXXXXXXXXXXXSeries.Create(nil);
  Chart2.AddSeries(series1);
TXXXXXXXXXXXSeries is the class I need to know.

Could you give me a piece of code which detects the class in the SourceChart and create the same kind of Series in the TargetChart?

2) What´s the best way to copy the data from Sourceseries to the Targetseries? Changing the DataSource isn´t a good solution.
Again I need a general solution to copy the data from series to series - no matter what kind of series I use.

It would be great if you could help me with this two questions.

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

Post by Yeray » Thu May 14, 2009 9:45 am

Hi Dominik,

1. You could do as follows:

Code: Select all

  var series: TChartSeries;
  //...
  series := TChartSeriesClass(Chart1[0].ClassType).Create(self);
  Chart1.AddSeries(series);
2. Here I think that you have two options:
a. Export and Import the data using a file (txt, xml,...)
b. Assign ValueList, Colors and Labels from one series to the other.

Here you have an example:

Code: Select all

procedure TForm1.FormCreate(Sender: TObject);
begin
  Chart1.AddSeries(TPointSeries.Create(self));
  Chart1[0].FillSampleValues(25);
  Chart1[0].Color := clRed;
  Chart1[0].Labels[10] := 'my custom label';
end;

procedure TForm1.Button1Click(Sender: TObject);
var SeriesSource, SeriesCopy: TChartSeries;
    i: Integer;
begin
  SeriesSource := Chart1[0];
  SeriesCopy := TChartSeriesClass(SeriesSource.ClassType).Create(self);
  Chart2.AddSeries(SeriesCopy);
  SeriesCopy.FillSampleValues(25);

  for i:=0 to SeriesSource.ValuesList.Count-1 do
    with SeriesCopy.ValuesList[i] do
    begin
      Value:=SeriesSource.ValuesList[i].Value;
      Count:=SeriesSource.ValuesList[i].Count;
      Modified:=true;
    end;


  for i:=0 to SeriesSource.Count-1 do
  begin
    SeriesCopy.ValueColor[i] := SeriesSource.ValueColor[i];
    SeriesCopy.Labels[i] := SeriesSource.Labels[i];
  end;

  SeriesCopy.Repaint;
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

moelski
Newbie
Newbie
Posts: 92
Joined: Tue Jun 19, 2007 12:00 am
Contact:

Post by moelski » Thu May 14, 2009 10:25 am

Hi Yeray,

tested it with some FastLine series and it works. But please see the sample. I got an error:
Received Copy Object RTTI.zip Content Type application/x-zip-compressed Length 13381

Erste Gelegenheit für Exception bei $7C812AFB. Exception-Klasse EListError mit Meldung 'Listenindex überschreitet das Maximum (-1)'. Prozess CopyEg.exe (3416)

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

Post by Yeray » Thu May 14, 2009 10:47 am

Hi Dominik,

I think you forgot to include the dpr file in the zip. Without that delphi isn't able to open the project.
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

moelski
Newbie
Newbie
Posts: 92
Joined: Tue Jun 19, 2007 12:00 am
Contact:

Post by moelski » Thu May 14, 2009 10:53 am

Hi Yeray,

sorry for that.

New upload:
Received Copy Object RTTI.zip Content Type application/x-zip-compressed Length 18798

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

Post by Yeray » Thu May 14, 2009 11:22 am

Hi Dominik,

I'm afraid I cannot reproduce any error in your application. Could you please tell me the steps I should follow to reproduce it?

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

moelski
Newbie
Newbie
Posts: 92
Joined: Tue Jun 19, 2007 12:00 am
Contact:

Post by moelski » Thu May 14, 2009 11:31 am

Hi Yeray,

as soon as I press the "Copy ->" Button I got the error (EListError) in TeEngine (Function TChartSeries.GetValueColor(ValueIndex:Integer):TColor;)

I used Delphi 2007 with TChart 8.04.

Post Reply