Page 1 of 1

TeeChart, Delphi: creation + saving chart to file in TThread

Posted: Tue Apr 08, 2008 2:15 pm
by 8437493
Hi,
We have some problem with TeeChart when trying to create and save it to bitmap from the separate thread in gui application.

Some result images(bmp) are corrupted. I.e. some files are ok, but another images empty or not fully painted(for example only legend is painted and nothing more). Sometimes "Out of resources" exception is raised inside the TeeChart.

The problem exists in TeeChart 5(Delphi 5)/6(Delphi 7) and in versions that included in Delphi 2006 and Delphi 2007

We have find out that there is no this problem in console application, only in gui application. Also, somehow this issue is depends on window messages of main form - for example when mouse is over the main form or timer works. In this case we have more "corrupted" bmps

We have a test project to show this problem. (can't find how to attach a file?)
http://forum.ixbt.com/post.cgi?id=attach:26:38134:0:1

Test project creates and saves chart to chartX.bmp file in separate thread in loop and compare this resulted file with "base" file chart.bmp. If ok - it generates this file again and check again.. When files are different - message will be shown

This is short description of how we use TeeChart in this project:

Code: Select all

  TTester = class(TThread)
    procedure Execute(); override;
  end;

procedure CreateChartFile(ATitle: String; const AFileName: WideString);
var
  Chart: TChart;
  Series : TBarSeries;
  i: Integer;
  Rows: Integer;
  ChartWidth, ChartHeight: Integer;
begin
  Rows := 20;
  ChartWidth := 800;
  ChartHeight := 400;

  Chart := TChart.Create(nil);
  try
    Series := TBarSeries.Create(nil);
    TBarSeries(Series).BarStyle := bsRectangle;
    Chart.LeftAxis.Title.Caption := 'Left Axis';

    Chart.AddSeries(Series);

    Chart.Legend.TextStyle := ltsPlain;
    Chart.Legend.Alignment := laRight;

    Chart.Height := ChartHeight;
    Chart.Width := ChartWidth;
    Chart.Color := clWhite;
    Chart.BevelInner := bvNone;
    Chart.BevelOuter := bvNone;
    Chart.BottomWall.Color := clWhite;
    Chart.LeftWall.Color := clWhite;

    Chart.Title.Text.Text := ATitle;
    with Chart.Title.Font do
    begin
     Name := 'Arial';
     Size := 12;
     Style := [fsBold];
    end;

    // series

    Series.Marks.Visible := False;

    for i := 0 to Rows - 1 do
     Series.Add(i, 'label'+IntToStr(i));

    Series.SeriesColor := clBlack;
    Series.ColorEachPoint := true;

    // here we were trying a couple of another methods to save to bitmap with same result
    Chart.SaveToBitmapFile(AFileName);
  finally
    Chart.Free;
  end;
end;

procedure TTester.Execute();
var error: boolean;
begin
  // base chart
  DeleteFile(AppRoot+'chart.bmp');
  CreateChartFile('Test chart', AppRoot+'chart.bmp');

  n := 0;
  error := false;

  try

    while (not terminated) do
     begin
       // delete previous
       if (FileExists(AppRoot+'chartX.bmp'))
        then ASSERT(DeleteFile(AppRoot+'chartX.bmp'));

       // create next
       CreateChartFile('Test chart', AppRoot+'chartX.bmp');

       // compare
       if (not equalfiles(AppRoot+'chart.bmp', AppRoot+'chartX.bmp'))
        then
         begin
           Synchronize(Form1.TesterFailed);
           error := true;
           break;
         end;

       inc(n);
     end;

  finally

    if (not error)
     then Synchronize(Form1.TesterFinished);

  end;
end;
But we have one Delphi5+TeeChart5(6?) gui program which generates chart images in separate thread without this problem.. And we are now trying to determinate why there is no this problem in that program.

Posted: Thu Apr 10, 2008 5:20 pm
by 8437493
can you check this and give us some workaround?

Posted: Mon Apr 14, 2008 9:38 pm
by 8437493
can anyone say anything about this problem?
we are registered users of TeeChart and have the problem.

Posted: Thu Apr 17, 2008 11:44 am
by Pep
Hi,

sorry for delay, it's not a common problem. If I understand, are you saying that the same or a similar app is working fine on another machine ?

Have you tried using :
tmpBitmap.Canvas.CopyRect(Rect(0,0,tmpBitmap.Width,tmpBitmap.Height),
AChart.Canvas.ReferenceCanvas,CustomRect);

instead of :
AChart.Draw(tmpBitmap.Canvas,Rect(0,0,tmpBitmap.Width,tmpBitmap.Height));

?

Posted: Thu Apr 17, 2008 12:33 pm
by 8437493
Hi,
Thanks for the reply

Have you tried the test project I have pointed on in the first message? (it is an archive with Delphi projects files) Do you have the same errors as described?

> If I understand, are you saying that the same or a similar app is working fine on another machine ?

Not exactly. We have made some research and find out, that problem occurs with TeeChart only if it is used in separate thread in GUI application. Other ours applications are console or use TeeChart in DLL - in this case there is no any problems.

As soon as TeeChart is used in separate thread in GUI application(like an test project) - the result image is crashed very often. Chart object is accessed from the only one thread(the thread where the object was created)(so there is no problem with concurrent access to object)

I'll try your suggestion and let you know

Posted: Thu Apr 17, 2008 2:04 pm
by 8437493
Please note that we create chart in separate thread as:

Chart := TChart.Create(nil);

and when we are trying this code:

BMP := TBitmap.Create();
BMP.Width := Chart.Width;
BMP.Height := Chart.Height;
BMP.PixelFormat := pf24bit;

BMP.Canvas.CopyRect(Rect(0,0,BMP.Width,BMP.Height),
Chart.Canvas.ReferenceCanvas,
Rect(0,0,BMP.Width,BMP.Height));

we receive this error:

---------------------------
Debugger Exception Notification
---------------------------
Project ChartBugTester.exe raised exception class EInvalidOperation with message 'Control '' has no parent window'.
---------------------------
Break Continue Help
---------------------------

Posted: Thu Apr 17, 2008 7:49 pm
by 8437493
Please look at http://qc.borland.com/wc/qcmain.aspx?d=43018
and at Graphics.pas FreeMemoryContexts function:
{ FreeMemoryContexts is called by the VCL main winproc to release
memory DCs after every message is processed (garbage collection).
Only memory DCs not locked by other threads will be freed.
}

And give me please an answer : do you use .Lock/.Unlock for _every_ Canvas(and may be another primitives) used during the Chart.Draw function?
Can TeeChart be used inside separate thread in GUI application? Please contact your developers if you are not sure.

At this point of view I can see that TeeChart _can't_ be used inside separate thread in GUI application. Starting from Delphi5 to including Delphi2007. And this is the reason of the problem.

Please correct me or confirm my conclusions.

Posted: Tue Apr 22, 2008 11:53 am
by Pep
Hi,

we do not use Lock/Unlock for our Canvas methods, specially due to the slowness this could become.
You could try to lock outside the Chart sources when a method has to be used and unlock it after the call, by using similar code to :

Chart1.Canvas.ReferenceCanvas.Lock;
Chart1.DrawXXX or TeeCreatebitmap. // Chart draw methods
Chart1.Canvas.ReferenceCanvas.Unlock;

We have a defined constant which was introduced to TeeChart sources in order to help on this, you can check it by setting the following conditional defines to your project : TEECANVASLOCKS.