Page 1 of 2
Clone Series from a TmpChart ?
Posted: Tue May 12, 2009 8:42 am
by 10545590
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).
Posted: Tue May 12, 2009 10:15 am
by 10545590
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.
Posted: Tue May 12, 2009 10:40 am
by 10545590
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.
Posted: Wed May 13, 2009 8:35 am
by 10545590
Hi !
Can anyone help me with this case ?
Posted: Wed May 13, 2009 10:23 am
by yeray
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.
Posted: Wed May 13, 2009 10:31 am
by 10545590
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.
Posted: Wed May 13, 2009 12:16 pm
by yeray
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!
Posted: Wed May 13, 2009 12:32 pm
by 10545590
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 ?
Posted: Thu May 14, 2009 6:40 am
by 10545590
Hi Yeray,
well I could solve one issue
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.
Posted: Thu May 14, 2009 9:45 am
by yeray
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;
Posted: Thu May 14, 2009 10:25 am
by 10545590
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)
Posted: Thu May 14, 2009 10:47 am
by yeray
Hi Dominik,
I think you forgot to include the dpr file in the zip. Without that delphi isn't able to open the project.
Posted: Thu May 14, 2009 10:53 am
by 10545590
Hi Yeray,
sorry for that.
New upload:
Received Copy Object RTTI.zip Content Type application/x-zip-compressed Length 18798
Posted: Thu May 14, 2009 11:22 am
by yeray
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
Posted: Thu May 14, 2009 11:31 am
by 10545590
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.