Page 1 of 1

Proposed patches for TCHartSeries.Datasource property

Posted: Fri Jul 24, 2009 11:49 am
by 10553957
The Datasource property clashes with the Datasourceslist property. The datasourceslist is stored using the defineproperties method, and only when Datasources.COunt>1.

The problem is that the datasource property is always stored, causing an overwrite :shock: of the earlier loaded datasources list when the chart series setup is loaded later on.
Therefore the datasource property should be defined as

Code: Select all

property DataSource:TComponent read GetDataSource write SetDataSource stored StoreDatasource;
and private function StoreDatasource should be implemented as follows:

Code: Select all

function TChartSeries.StoreDatasource:boolean;
begin
  Result:=DataSources.Count=1;
end;
Also something should happen to the TChartSeries.Notification method which causes the datasources list to runs out of sync :x when series are destroyed. Only the Datasource property is checked in the original code, and the datasources list is forgotten :roll: .

Code: Select all

procedure TChartSeries.Notification( AComponent: TComponent;
                                     Operation: TOperation);
begin
  inherited;
  if Operation=opRemove then
{$ifdef HH_PATCH_TC_DATASOURCE}
  begin
{$endif}
     if AComponent=FTeeFunction then
        FTeeFunction:=nil
     else
{$ifdef HH_PATCH_TC_DATASOURCE}
      // Update Datasources list
      if assigned(Datasources) and (Datasources.IndexOf(AComponent)>=0) then
      begin
        DataSources.Remove(AComponent);
      end;
{$else}
      { if "AComponent" is the same as series datasource,
        then set datasource to nil }
     if AComponent=DataSource then
        InternalRemoveDataSource(AComponent);  // 7.0
{$endif}
{$ifdef HH_PATCH_TC_DATASOURCE}
  end;
{$endif}
end;
Finally there is another bug in the datasources handling. When there are multiple datasources and the first one is empty, the depending series are not or wrongly displayed. For this I patched the TCHartSeries.GetDatasource method so it will primarily return the first datasource containing data points. And falls back to the original, returning Datasources[0] or nil.

Code: Select all

Function TChartSeries.GetDataSource:TComponent;
VAR i:integer;
begin
  Result:=nil;
  if Assigned(FDataSources) and (FDataSources.Count>0) then
  begin
    for i:=0 to FDataSources.Count-1 do
    begin
      if TChartSeries(FDataSources[i]).Count>0 then
      begin
        Result:=TChartSeries(FDataSources[i]);
        break; // found a series with data
      end;
    end;
    if not assigned(Result) then 
      result:=FDataSources[0];
  end;
end;
Regards - Hans

Re: Proposed patches for TCHartSeries.Datasource property

Posted: Mon Jul 27, 2009 3:13 pm
by yeray
Hi Hans,

If I understand well the following code should reproduce the issues you are explaining. But I can't reproduce it with v8.

Code: Select all

var Bar1, Bar2, Bar3: TBarSeries;
    Line1: TLineSeries;

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

  Bar1 := TBarSeries.Create(self);
  Bar2 := TBarSeries.Create(self);
  Bar3 := TBarSeries.Create(self);
  Chart1.AddSeries(Bar1);
  Chart1.AddSeries(Bar2);
  Chart1.AddSeries(Bar3);

  Line1 := TLineSeries.Create(self);
  Chart1.AddSeries(Line1);
  Line1.FunctionType := TAverageTeeFunction.Create(self);

  for I := 0 to 4 do
  begin
    Bar1.Add(100);
    Bar2.Add(200);
    Bar3.Add(300);
  end;

  Line1.DataSources.Add(Bar1);
  Line1.DataSources.Add(Bar2);
  Line1.DataSources.Add(Bar3);
  Line1.CheckDataSource;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Line1.DataSource := Bar3;
  Line1.CheckDataSource;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  Bar1.Clear;
  Line1.CheckDataSource;
end;
After loading the chart, the average function shows the average of the three functions (DataSourcesList) that is 200.
After pressing button 1, nothing happens. If I understood well, assigning a DataSource should take preference from the DataSourcesList (where there are still the three bars).
After pressing button 2, the new DatasourcesList (now with 2 series) seems to be checked fine.

Re: Proposed patches for TCHartSeries.Datasource property

Posted: Tue Jul 28, 2009 7:46 am
by 10553957
Hi Yeray

It seems hard to find out again what exactly went wrong with the datasources. But anyway, the 'stored' predicate is still useful for the datasource property.

Check this piece of dfm,

Code: Select all

    object BarSum: TLineSeries
      Marks.Arrow.Visible = True
      Marks.Callout.Brush.Color = clBlack
      Marks.Callout.Arrow.Visible = True
      Marks.Visible = False
      DataSource = BarSeries1 
      Pointer.InflateMargins = True
      Pointer.Style = psRectangle
      Pointer.Visible = False
      XValues.Name = 'X'
      XValues.Order = loAscending
      YValues.Name = 'Y'
      YValues.Order = loNone
      DataSources = (
        'BarSeries1'
        'BarSeries2'
        'BarSeries3')
      object TeeFunction1: TAddTeeFunction
      end
    end
The line DataSource = BarSeries1 should be omitted as it conflicts with the Datasources = (...) setting. Luckily, de datasources property is stored AFTER the datasource property so no-one will notice this. But one should never rely on the order in which properties are stored.

I'm still working on reproducing the error for which the other datasources patch was required.

regards - Hans

Re: Proposed patches for TCHartSeries.Datasource property

Posted: Tue Jul 28, 2009 8:06 am
by 10553957
Hi Yeray

:idea: I have managed to reproduce the trouble:

1) Run my testapp, attached to this post.
2) Click btTestSTack to add some data th the chart
3) click btTestDatasource 1, this frees BarSeries1

Hey!? My BarSum series is empty! :shock: This happens because the datasources list is cleared when 'by coincidence' the first datasource series is destroyed. This should not happen since there are 2 more datasources.

8) PS there also is a test incorporated for exporting the form to a file Formresource1.txt to demonstrate the storage of the datasource and datasources properties. (btTestDatasourceStorage)

Regards - Hans

Re: Proposed patches for TCHartSeries.Datasource property

Posted: Tue Jul 28, 2009 8:41 am
by yeray
Hi Hans,

I used your application to see how DataSource and DataSourcesList seem to be correctly synchronized:

1) Run my testapp, attached to this post.
2) Click btTestSTack to add some data the the chart
2.1) Click btTestDataSourceStorage to generate the corresponding txt with all the properties:

Code: Select all

//...
object BarSum: TLineSeries
  //...
  DataSource = BarSeries1
  //...
  DataSources = (
        'BarSeries1'
        'BarSeries2'
        'BarSeries3')
3) click btTestDatasource1, this frees BarSeries1
3.1) Click again btTestDataSourceStorage to generate the txt with the actual properties. As you'll see, Bar1 has been removed from the DataSourcesList and DataSource has been changed also for BarSeries2:

Code: Select all

//...
object BarSum: TLineSeries
  //...
  DataSource = BarSeries2
  //...
  DataSources = (
        'BarSeries2'
        'BarSeries3')
Then, I've added a BarSum.CheckDataSource call at btTestDatasource1 after freeing BarSeries1 and now the line doesn't disappear.

Anyway, having a DataSourcesList, probably there is no need to have a DataSource property. So I've added it to the wish list to be revised for future releases.

On the other hand, I'm still not reproducing the problem of having a series without data in the DataSourcesList.

Re: Proposed patches for TCHartSeries.Datasource property

Posted: Tue Jul 28, 2009 8:46 am
by 10553957
Yeray,

The patch provided for TChartSeries.GetDatasource seems to be uncessesary for Tee8 as the new CalculateFunctionMany routine no longer relies on only the GetDatasource returned series to determine the # points to process. That's a lot better fix than mine... :roll:

Code: Select all

<snip>
      // Find datasource with bigger number of points... 5.02
      with ParentSeries do
      for t:=0 to DataSources.Count-1 do
      if Assigned(DataSources[t]) and
         (TChartSeries(DataSources[t]).Count>Source.Count) then
      begin
        Source:=TChartSeries(DataSources[t]);
        XList:=Source.NotMandatoryValueList;
      end;
<snip>
So the only fixes left for the datasources are:
1) The fix for the "stored" predicate of the Datasource property
2) The fix for the Notification, which I have currently boiled down to

Code: Select all

procedure TChartSeries.Notification( AComponent: TComponent;
                                     Operation: TOperation);
begin
  inherited;
  if Operation=opRemove then
{$ifdef HH_PATCH_TC_DS_NOTIFY}
  begin
{$endif}
     if AComponent=FTeeFunction then
        FTeeFunction:=nil
     else
{$ifdef HH_PATCH_TC_DS_NOTIFY}
     if (FDatasources<>nil) and (FDatasources.Indexof(aComponent)>=0) then
        InternalRemoveDataSource(AComponent);
{$else}
      { if "AComponent" is the same as series datasource,
        then set datasource to nil }
     if AComponent=DataSource then
        InternalRemoveDataSource(AComponent);  // 7.0
{$endif}
{$ifdef HH_PATCH_TC_DS_NOTIFY}
  end;
{$endif}
end;
Regards - Hans

Re: Proposed patches for TCHartSeries.Datasource property

Posted: Tue Jul 28, 2009 10:20 am
by yeray
Hi Hans,

You patches look good. I've added them to the wish list to be revised asap (TV52014317).

Re: Proposed patches for TCHartSeries.Datasource property

Posted: Mon Mar 29, 2010 2:12 pm
by narcis
Hi Hans,

Notice that I have implemented your enhancement suggestions to TeeChart v8 and TeeChart 2010.

Re: Proposed patches for TCHartSeries.Datasource property

Posted: Mon Mar 29, 2010 3:04 pm
by 10553957
Thx

Hans