Lazarus

Programming => Graphics and Multimedia => TAChart => Topic started by: bnl on August 04, 2020, 07:38:26 pm

Title: [SOLVED] TAChart - lineColor - what property to use
Post by: bnl on August 04, 2020, 07:38:26 pm
Hi!
I have a Treeview which I fill with a key (to a database)
When selectionChanged in that  TreeView I was to
use that key to draw a graph.
It is a simple line graph
I have 8 - 16 series,
and I want 1 to have a special color (green)
and the rest should have line color red.

I create the series dynamically and add to the graph.
All is well - but for line color.
I tried these three properties
Code: Pascal  [Select][+][-]
  1.    
  2.     Series[cnt].LinePen.Color := clRed;
  3.     Series[cnt].SeriesColor:= clRed     ;
  4.     Series[cnt].Pointer.Pen.Color := clRed   ;
  5.  

and no matter how I do I usually get 1 green, 1 red and the rest black.


here an excerpt from it.
How should I do instead ?


Code: Pascal  [Select][+][-]
  1.  
  2. type
  3.  
  4.   { TForm1 }
  5.  
  6.   TForm1 = class(TForm)
  7.     Button1: TButton;
  8.     Button2: TButton;
  9.     Chart1: TChart;
  10.     Chart1LineSeries1: TLineSeries;
  11.     Edit1: TEdit;
  12.     Edit2: TEdit;
  13.     TrackBar1: TTrackBar;
  14.     TreeView1: TTreeView;
  15.     procedure Button1Click(Sender: TObject);
  16.     procedure Button2Click(Sender: TObject);
  17.     procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
  18.     procedure FormCreate(Sender: TObject);
  19.  
  20.     procedure TreeView1SelectionChanged(Sender: TObject);
  21.   private
  22.     procedure DrawGraph(marketid: string);
  23.  
  24.   public
  25.  
  26.   end;                                              
  27.  
  28.  
  29. var
  30.   Form1: TForm1;
  31.   T: TSQLTransaction;
  32.   All_Markets, runner_price_data, runners_in_race: TSQLQuery;
  33.   PQConn: TPQConnection;
  34.   Series: array[0..15] of TLineSeries;                  
  35.  

this is the bulk of it

Code: Pascal  [Select][+][-]
  1.   while not runners_in_race.EOF do begin
  2.     cnt := cnt + 1;
  3.     selectionid := runners_in_race.FieldByName('selectionid').AsInteger;
  4.     placement := runners_in_race.FieldByName('status').AsString;
  5.     runner_price_data.params.parambyname('marketid').AsString := marketid;
  6.     runner_price_data.params.parambyname('selectionid').AsInteger := selectionid;
  7.     edit1.Text := IntToStr(selectionid);
  8.  
  9.     runner_price_data.Prepare;
  10.     runner_price_data.Open;
  11.     x := 0.0;
  12.     Series[cnt] := TLineSeries.Create(Chart1);
  13.     Series[cnt].BeginUpdate;
  14.  
  15.     if placement = 'WINNER' then begin
  16.       Series[cnt].LinePen.Color := clGreen;
  17.    //   Series[cnt].SeriesColor:= clGreen   ;
  18.     //  Series[cnt].Pointer.Pen.Color := clGreen   ;
  19.     end
  20.     else if placement = 'LOSER' then begin
  21.       Series[cnt].LinePen.Color := clRed;
  22.      // Series[cnt].SeriesColor:= clRed     ;
  23.    //   Series[cnt].Pointer.Pen.Color := clRed   ;
  24.     end
  25.     else begin
  26.       Series[cnt].LinePen.Color := clPurple;
  27.    //   Series[cnt].SeriesColor:= clPurple  ;
  28.    //   Series[cnt].Pointer.Pen.Color := clPurple   ;
  29.     end;
  30.  
  31. //    while not runner_price_data.EOF and (cnt <= length(Series)) do begin
  32.     while not runner_price_data.EOF do begin
  33.       x := x + 1.0;
  34.       backprice := runner_price_data.FieldByName('backprice').AsFloat;
  35.       if backprice <= Real(pos) then
  36.         Series[cnt].AddXy(x, backprice)
  37.       else
  38.         Series[cnt].AddXy(x, Real(pos));
  39.  
  40.       runner_price_data.Next;
  41.     end;
  42.     runner_price_data.Close;
  43.     Series[cnt].EndUpdate;
  44.     Chart1.AddSeries(Series[cnt]);
  45.  
  46.     runners_in_race.Next;
  47.   end;
  48.  
  49.  


thanks
/Björn
Title: Re: TAChart - lineColor - what property to use
Post by: wp on August 04, 2020, 09:59:35 pm
This is not enough information. Please make a copy of your project and strip everything which is not needed so that only essential parts to reproduce the issue are left. Pack the pas, lfm, lpi, lpr and data files into a shared zip which you can upload under "Attachment and other options" (do not include the exe, res, ppu, o and other compiler-generated files because the upload may become too large otherwise). And use only a simple data base format (sqlite4, dbase, csv) - I don want to install a database server just to answer this question.
Title: Re: TAChart - lineColor - what property to use
Post by: bnl on August 05, 2020, 09:41:56 am
Ok, thanks for wanting to help.
I see I did not mention any versions.
I'm on macOS 10.13.6 (High Sierra) with Lazarus 2.0.6 and freepascal 3.0.4

Instead of migrating the data to another database, I created a read-only user which is in the code (function CreateConnection).
It is reachable from the internet.

compile, start and open the treeview to the left,
then click on any entry in the list.
I want 1 green line, the rest red.
However most get black (and not purple as the code indicates if the if statement at line 137 fails - which is does not - I followed it with debug)


Title: Re: TAChart - lineColor - what property to use
Post by: bnl on August 05, 2020, 09:51:03 am
Here's an attached screenshot. Interestingly, the colors of the legend (top right corner) seems correct
/Björn
Title: Re: TAChart - lineColor - what property to use
Post by: wp on August 05, 2020, 06:10:34 pm
Instead of migrating the data to another database, I created a read-only user which is in the code (function CreateConnection).
It is reachable from the internet.
But I need to install PostgreSQL, at least on Windows and on the Linux that I tested. I downloaded the libpq.dll and stored it in the exe folder of you your demo, no success. Please understand that I do not want to install (and learn) big database client software on my system (and VMs) which I don't need, just to answer a forum question.
Title: Re: TAChart - lineColor - what property to use
Post by: bnl on August 05, 2020, 06:53:20 pm
Quote
But I need to install PostgreSQL,

Hmm, yes. I didn't think of that  - sorry.
I've been digging around the forum and seen some with
similar - but not identical - problems on osx.
It may be a cocoa problem.
Anyway, when I get home (tomorrow ) I''ll try to make a small
reproducible. Thinking of It - I don't think I need a db at all to reproduce.
Thanks for the effort
/Björn 
Title: Re: TAChart - lineColor - what property to use
Post by: bnl on August 06, 2020, 02:03:27 pm
ok - new project with no db
run it and press Draw-button top left.
I seem to often get a BLACK line or two as well
The code should give no reason fo a black line at all

Code: Pascal  [Select][+][-]
  1.  
  2. procedure TForm1.Button1Click(Sender: TObject);
  3. var
  4.  cnt : integer;
  5.    x : real;
  6.    r : real;
  7.    i,j : integer;
  8. begin
  9.   cnt := 0;
  10.   for i := 1 to 5 do begin
  11.      x := 0.0;
  12.      //Series[cnt]
  13.      Series[cnt] := TLineSeries.Create(Chart1);
  14.      Series[cnt].BeginUpdate;
  15.      r := Random;
  16.  
  17.      if r < 0.2 then begin
  18.        Series[cnt].LinePen.Color := clGreen;
  19.     ////   Series[cnt].SeriesColor:= clGreen   ;
  20.     // //  Series[cnt].Pointer.Pen.Color := clGreen   ;
  21.      end
  22.      else if r < 0.4 then begin
  23.        Series[cnt].LinePen.Color := clRed;
  24.     //  // Series[cnt].SeriesColor:= clRed     ;
  25.     ////   Series[cnt].Pointer.Pen.Color := clRed   ;
  26.      end
  27.      else begin
  28.        Series[cnt].LinePen.Color := clPurple;
  29.     ////   Series[cnt].SeriesColor:= clPurple  ;
  30.     ////   Series[cnt].Pointer.Pen.Color := clPurple   ;
  31.      end;
  32.  
  33.      for j := 1 to 5 do begin
  34.        x := x + 1.0;
  35.        Series[cnt].AddXy(x, j*r)
  36.      end ; //loop
  37.  
  38.      Series[cnt].EndUpdate;
  39.      Chart1.AddSeries(Series[cnt]);
  40.      cnt := cnt +1;
  41.   end; //loop
  42.  
  43. end;                  
  44.  

As I mentioned before:
imac:~ bnl$ uname -a
Darwin imac.lan 17.7.0 Darwin Kernel Version 17.7.0: Thu Jun 18 21:21:34 PDT 2020; root:xnu-4570.71.82.5~1/RELEASE_X86_64 x86_64
(High Sierra - 10.13.6)

Lazarus 2.0.6
fpc 3.0.4


/Björn
Title: Re: TAChart - lineColor - what property to use
Post by: wp on August 06, 2020, 10:33:20 pm
Thank you -- this is the kind of demo that I can work with.

I tested the project on Windows - everything correct. Then I tried on a VM with macOS 10.14.3 which is painfull slow, but I seems that it is working correctly, too.

One, maybe stupid, note: I noticed that it is a bit difficult to distingush clPurple from clBlack, at least on my monitor. Could you use clYellow instead -- it will definitely make a big difference.

The other note: This issue reminds me of a recent, still unsolved, issue discussed in https://forum.lazarus.freepascal.org/index.php/topic,48842.0.html. In this issue the brush color is changed but not always propagated to the macOS drawing engine. Looks as if we have the same issue here, now with the pen.

Let me investigate further...
Title: Re: TAChart - lineColor - what property to use
Post by: bnl on August 06, 2020, 10:51:42 pm
Quote
The other note: This issue reminds me of a recent, still unsolved, issue discussed in https://forum.lazarus.freepascal.org/index.php/topic,48842.0.html. In this issue the brush color is changed but not always propagated to the macOS drawing engine. Looks as if we have the same issue here, now with the pen.

Yes, not exactly the same but similar.

Three screenshoots attached, in two I've changed purple -> yellow. Still black lines appear.
And in the last one, I also show the legend - which gets the colors correct.
1 green
1 red
3 yellow
while the graph itself has
1 green
1 red
2 black
1 yellow

/Björn
Title: Re: TAChart - lineColor - what property to use
Post by: wp on August 06, 2020, 11:11:23 pm
Please try the following code which avoids the random numbers to get reproducible results.
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button3Click(Sender: TObject);
  2. const
  3.   COLORS: array[1..5] of TColor = (clRed, clGreen, clBlue, clYellow, clPurple);
  4. var
  5.   cnt: Integer;
  6.   ser: TLineseries;
  7. begin
  8.   Chart1.ClearSeries;
  9.   for cnt := 1 to 5 do
  10.   begin
  11.      ser := TLineSeries.Create(Chart1);
  12.      ser.SeriesColor := COLORS[cnt];
  13.      ser.Title := IntToStr(cnt);
  14.      ser.AddXY(0, cnt);
  15.      ser.AddXY(1, cnt+1);
  16.      Chart1.AddSeries(ser);
  17.   end;
  18. end;

Play with the colors in tha COLORS array. You should also try a combination which contains three consecutive yellow colors. I suspect that setting a color to one which has been previously set is not working; this is the issue in the brush.color case.

[EDIT]
I could reproduce the issue on macOS when I select the following colors in above array: clred, clBlue, clYellow, clYellow, clYellow; only the center yellow line is correct, the others are black.
Title: Re: TAChart - lineColor - what property to use
Post by: bnl on August 07, 2020, 02:35:53 pm
Quote
I could reproduce the issue on macOS when I select the following colors in above array: clred, clBlue, clYellow, clYellow, clYellow; only the center yellow line is correct, the others are black.

Hmm, so can I. But not in the same way.
I get (from bottom and up) red, blue,BLACK,yellow,BLACK
Or is that what you mean by the center yellow?
(I interpret 'center yellow line' as the 3rd line, and that that one is yellow, but it could be the 4th as well , the one in center of the should-be-yellow lines)

I'll attach a screenshot as well

/Björn


Title: Re: TAChart - lineColor - what property to use
Post by: wp on August 07, 2020, 02:39:20 pm
Yes, exactly like here.
Title: Re: TAChart - lineColor - what property to use
Post by: bnl on August 07, 2020, 02:49:09 pm
Quote
Play with the colors in tha COLORS array. You should also try a combination which contains three consecutive yellow colors. I suspect that setting a color to one which has been previously set is not working; this is the issue in the brush.color case.

Not quite - or it is nob obvious to me.
I set the array to red,blue,red,blue,red,blue (and increased it to 6 elements)
This was fine
but
red,red,red,blue,blue,blue is not.
Only the FIRST occurrence works, the others are black.
so it became
red,BLACK,BLACK, blue,BLACK,BLACK

with the yellow ones, the SECOND occurrence was correct, but not the first

I'll attach these screenshot as well. The legend shows how it was intended

/Björn
Title: Re: TAChart - lineColor - what property to use
Post by: wp on August 07, 2020, 04:04:54 pm
Yes I don't understand the sequence of "correct" and "black" either.

But I found a workaround in the mean-time: When you open unit TADrawerCanvas.pas (in folder "components/tachart" of your Lazarus installation), find TCanvasDrawer.SetPen and, in the "else" part of the "if FXor" instruction add code which makes sure that the Pen.Color is different from the new value:

Code: Pascal  [Select][+][-]
  1. procedure TCanvasDrawer.SetPen(APen: TFPCustomPen);
  2. begin
  3.   with GetCanvas do begin
  4.     if FXor then begin
  5.       ...
  6.     end
  7.     else begin
  8.       {$ifdef LCLCocoa}   // ADD THIS FROM HERE...
  9.       // Workaround for series color issue in cocoa: make sure that color is different
  10.       if Pen.Color = clWhite then
  11.         Pen.Color := clBlack
  12.       else
  13.         Pen.Color := clWhite;
  14.       {$endif}    // ... TO HERE
  15.  
  16.       if APen is TPen then
  17.         Pen.Assign(APen)
  18.       else  begin
  19.         Pen.Color := FPColorToChartColor(APen.FPColor);
  20.         ...
  21.       end;
  22.     ....
  23.   end;
  24. end;

The reason is that for some reason (which I don't understand) the pen must call FreeReference in Cocoa before changing a color, but this does not happen because the TPen's Color setter code is exited immediately when the LCL color value does not change.

After this modification recompile the TAChart package, and the series colors will be correct.

Of course, this is only a workaround which will probably slow down execution of TAChart on cocoa.  The real cause must be somewhere in the widgetset part of LCL graphics. Unfortinately I do not understand anything about the internals of macOS and its graphics. For a bug report I need a very simple example without TAChart (which is too complex). Something like this OnPaint code for a Paintbox on a form:

Code: Pascal  [Select][+][-]
  1. procedure TForm1.PaintBox1Paint(Sender: TObject);
  2. var
  3.   y: Integer;
  4. begin
  5.   with Paintbox1 do
  6.   begin
  7.     Canvas.Brush.Color := clWhite;
  8.     Canvas.FillRect(0, 0, Width,Height);
  9.  
  10.     Canvas.Pen.Color := clRed;
  11.     Canvas.Line(0, y, Width, y);
  12.  
  13.     inc(y, 5);
  14.     Canvas.Pen.Color := clYellow;
  15.     Canvas.Line(0, y, Width, y);
  16.  
  17.     // What to do here to break the pen.Reference?
  18.  
  19.     inc(y, 5);
  20.     Canvas.Pen.Color := clYellow;
  21.     Canvas.Line(0, y, Width, y);
  22.  
  23.     inc(y, 5);
  24.     Canvas.Pen.Color := clYellow;
  25.     Canvas.Line(0, y, Width, y);
  26.  
  27.     inc(y, 5);
  28.     Canvas.Pen.Color := clYellow;
  29.     Canvas.Line(0, y, Width, y);
  30.   end;
  31. end;

As expected this code is working correctly, 1 red and 4 yellow lines. Something is happening in TAChart which breaks the pen's reference. When we find something to insert between the 1st and 2nd yellow line drawing code which makes the subsequent yellow lines to be drawn in black, we have a good example for the bug report.
Title: Re: TAChart - lineColor - what property to use [SOLVED/work-around]
Post by: bnl on August 07, 2020, 04:44:54 pm
Quote
But I found a workaround in the mean-time

Thanks, that did the trick.
While a work-around, it sure is good enough for me.
Thanks again for you time and effort
/Björn
Title: Re: TAChart - lineColor - what property to use
Post by: wp on August 07, 2020, 11:53:56 pm
Now I found the missing piece: it is clipping. Series are drawn within a clipped rectangle. And when I activate Canvas.Clipping in my simple demo the clipped line turns into black and - even more - forgets the line width and the pen style. Of course, cocoa only, win32 is fine.

[EDIT]
Bugreport submitted: https://bugs.freepascal.org/view.php?id=37520

Title: Re: TAChart - lineColor - what property to use
Post by: bnl on August 08, 2020, 12:07:29 am
Yes , when I click the clipping checkbox, the red dotted line turns into a (shorter) black line
Title: Re: [SOLVED/Work-around] TAChart - lineColor - what property to use
Post by: wp on August 09, 2020, 03:20:32 pm
The clippling bug in the cocoa canvas has been fixed by Dmitry. In my own tests, series color selection should work now in cocoa. Please test yourself and report back.

Note: you need Lazarus trunk to get the fix. Alternatively - without warranty - you could do this:
Code: Pascal  [Select][+][-]
  1. procedure TCocoaContext.ClearClipping;
  2. var
  3.   Trans: CGAffineTransform;
  4.   cgc: CGContextRef;
  5. begin
  6.   if FClipped then
  7.   begin
  8.     cgc := CGContext();
  9.     Trans := CGContextGetCTM(cgc);
  10.     CGContextRestoreGState(cgc);
  11.     ApplyTransform(Trans);
  12.     if Assigned(FPen) then FPen.Apply(Self);      // <----- INSERTED
  13.     if Assigned(FBrush) then FBrush.Apply(Self);  // <----- INSERTED
  14.   end;
  15. end;
As I said -- I cannot give a guarantee that this change does not depend on other changes missing. So, please make a backup copy of the modified file so that you can restore the old state in case Lazarus does not compile.
Title: Re: [SOLVED/Work-around] TAChart - lineColor - what property to use
Post by: bnl on August 09, 2020, 09:57:03 pm
Quote
Note: you need Lazarus trunk to get the fix. Alternatively - without warranty - you could do this:
...
Afterwards rebuild the IDE.

Hmm, I'll need to do some reading up on details for that. I think I got Lazarus/fpc installed via macports.
I'm on vacation now, I hope to get to my dev computer during next week

/Björn
Title: Re: [SOLVED] TAChart - lineColor - what property to use
Post by: bnl on August 11, 2020, 05:13:16 pm
So I found fpcupdeluxe, and downloaded/compiled trunk of Lazarus and fpc.
It works as expected now, I get colors as I want.
Thanks
/Björn
TinyPortal © 2005-2018