OnGetPointerStyle lazarus / 55-TAChart ---------------------------------------------------------------------- mtanner · 2016-04-04 05:03 Does TAChart provide the equivalent of the TeeChart TLineSeries event OnGetPointerStyle? I did some searching of the Lazarus code but could not find it. ---------------------------------------------------------------------- wp · 2016-04-04 09:09 No, there is none. But there is an "OnDrawPointer" which you can use to draw any pointer style, e.g. bitmaps as pointers (recently, there was a forum question with this topic). What is TeeChart's OnGetPointerStyle good for? ---------------------------------------------------------------------- mtanner · 2016-04-04 09:48 In some of my Delphi apps I use OnGetPointerStyle to plot only some of the points, such as when points are clustering together (I've submitted other posts on this subject). If you want, say, every third pointer to be actually drawn then you ca in OnGetPointerStyle say something like (code may not be exactly correct but it shows the idea) If not (ValueIndex mod 3)=0 then Pointer.Style:=psNone; I'll have a look and see if I can do a similar thing with OnDrawPointer. ---------------------------------------------------------------------- wp · 2016-04-04 12:23 Ok, I understand now. For your purpose, the OnDrawPointer has the issue that it is called after the pointer already has been drawn; it also requires a TCanvas instead of IChartDrawer, i.e. it is not usable if, e.g., TBGRABitmap is used for painting. I added a new event, OnOwnerDrawPointer, to TLineSeries which is called before painting of the regular pointer; if an event handler is assigned the regular pointer is not not drawn at all, you must provide all the required code for drawing the series pointer. In your case, for displaying only every nth pointer, do this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Chart1OwnerDrawPointer(ASender: TChartSeries; ADrawer: IChartDrawer;   AIndex: Integer; ACenter: TPoint);var  lineser: TLineSeries;begin  if AIndex mod 3 = 0 then // suppose N = 3  begin  lineser := TLineSeries(ASender);    lineser.Pointer.Draw(ADrawer, ACenter, lineser.Pointer.Brush.Color);  end;  // the pointers for the other indexes are not drawn at allend; I also added an example to (lazarus)/tachart/demos/line. The demo shows also how to paint a bitmap image instead of a built-in pointer. The new event and the modified demo is available only in Lazarus trunk. It will probably not be backported to Laz 1.6.2 because it is a new feature and requires some more testing. ---------------------------------------------------------------------- mtanner · 2016-04-04 13:26 Thank you. Actually before your reply I tried using OnDrawPointer, and it seemed to work. My code is: Freq is an integer containing how frequently points are to appear PtrStyles is an array of pointer styles procedure TMyForm.MySerDrawPointer(ASender:TChartSeries;   ACanvas:TCancas; AIndex:integer; ACenter:TPoint);    If (AIndex mod Freq)<>0    then TLineSeries(ASender).Pointer.Style:=psNone    else  TLineSeries(ASender).Pointer.Style:=PtrStyles[ASender.Tag]; end; PtrStyles is application-defined table of pointer styles. Tag for each series is set to pick the style wanted for the series. Obviously there is some application housekeeping around this, and I've simplified the code to remove extraneous complications. But this seems to work ok. The disadvantage of this scheme, and I think your new one, is that in Delphiu/TeeChart it was ufficient to write  If (Freq mod AIndex)<>0 then TLineSeries(ASender).Pointer.Style:=psNone. sine the overriding of the pointer style only lasts for that point, and then reverts to whatever it was set to before. In the above schemes the selection of psNone remains in force for the rest of the points, so has to be explicitly rest to whatever it should be for an actual drawn point, which means the housekeeping is needed in order to know what to reset it to. If you could provide a way of making the override last just for the current point, that would save some code. ---------------------------------------------------------------------- mtanner · 2016-04-04 13:34 Still being a bit new to Lazarus, I'm not sure what "Lazarus Trunk" is - I assume it some kind of beta or pre-beta version.  I think your solution could work fine for me, but I'll leave the not-every-point feature out of my application for now, until it's in the mainstream relese. How difficult would it be to add a parameter to a TLineSeries, called something like Pointer.Frequency, and if this value is >0 then do code you suggest automatically?. ---------------------------------------------------------------------- wp · 2016-04-04 14:54 Quote from: mtanner on April 04, 2016, 03:34:22 pmHow difficult would it be to add a parameter to a TLineSeries, called something like Pointer.Frequency, and if this value is >0 then do code you suggest automatically?. It not a matter of being difficult (it's not), it's a matter of how many dedicated features will be stuffed into TAChart which only a single user will need and which can be accomplished in other ways. We had this discussion before (http://forum.lazarus.freepascal.org/index. php/topic,31557.msg205391.html#msg205391), your solution is that you must select a higher density for your x values at steep plot features and a lower density at flat features. Even if you insist on the "plot every n-th point" you can achieve this with the OnCustomDrawPointer (I renamed it from OnOwnerDrawPointer) and with the new OnGetPointerStyle event which I just added for Delphi compatibility. And even if you don't want to install trunk (yes - it is the developer version) you still have the old OnDrawPointer event. A comment on your OnDrawPointer code: I did not test it, but it is possible that your application may run into an infinite loop because OnDrawPointer is called from the Paint method of the chart, but changing the pointer style triggers another Paint cycle which alters the pointer styles again etc. For safety, disable the notification mechanism (this works in the code for the new OnGetPointerStyle event): Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TMyForm.MySerDrawPointer(ASender:TChartSeries;  ACanvas:TCancas; AIndex:integer; ACenter:TPoint);begin  TLineSeries(ASender).Pointer.SetOwner(nil);  // there will be no "owner" being notified of the new style  if (AIndex mod Freq)<>0    then TLineSeries(ASender).Pointer.Style:=psNone    else TLineSeries(ASender).Pointer.Style:=PtrStyles[ASender.Tag];  TLineSeries(ASender).Pointer.SetOwner(Chart1);  // restore the old notifcation end; ---------------------------------------------------------------------- mtanner · 2016-04-04 15:08 I take your point about dedicated features entirely.  The OnDrawPointer will do the job perfectly well. I did notice when testing my possible solution some delays in chart painting, which may have been  the loop you speak of. I didn't really investigate this as I just closed the application from the main form  - test graph was in a secondary form. I think we can regard this thread as closed now - very usefully from my point of view. Thanks. ---------------------------------------------------------------------- mtanner · 2016-04-04 15:22 Actually I just read over your replies again to get the details straight in my head. If you have provided OnGetPointerStyle evenmt that is great. If it works like the Delphu/TeeChart implementation, that is any changing of the Pointer.Style lasts only for the current point, then that reduces the code needed in an application significantly, because there is then no need for the housekeeping to be able to reset the Pointer.Style for subsequent points.  I'd appreciate  clarification if this is the case please. I'm coming from a fairly old version of Delphi/TeeChart (to avoid very expensive corequisite upgrades and give me the option of porting to Linux) and in general I find Lazarus/TAChart impressively direct and straightforward. I used TeeChart for quite a few years, so I worked out how to do obscure or specialised things with it - so I'm having to climb a leaning curve. ---------------------------------------------------------------------- wp · 2016-04-04 15:43 Yes, the new OnGetPointerStyle event is exactly like in TeeChart: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TSeriesPointerStyleEvent = function (ASender: TChartSeries;    AValueIndex: Integer): TSeriesPointerStyle of object; But note that TeeChart declares this event as a function which forces you to return a valid SeriesStyle in all cases. The following code will not work: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function TForm1.Chart1LineSeries1GetPointerStyle(ASender: TChartSeries;  AValueIndex: Integer): TSeriesPointerStyle;begin  if AValueIndex mod 3 <> 0 then Result := psNoneend;because the function result is not defined if AValueIndex mod 3 = 0. The correct handler requires a bit more typing: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function TForm1.Chart1LineSeries1GetPointerStyle(ASender: TChartSeries;  AValueIndex: Integer): TSeriesPointerStyle;begin  if AValueIndex mod 3 <> 0 then Result := psNone else    Result := TLineSeries(ASender).Pointer.Style;end;If they would have used a procedure with a var parameter as return then it would have been possible to use the predefined style as a default. I wonder why they did it like that... What is your opinion? Do you want me to change it to a procedure? This would break Delphi compatibility in this place, but TAChart isn't Delphi-compatible anyway. ---------------------------------------------------------------------- mtanner · 2016-04-22 09:30 Sorry for delay in replying, I've been away for a while. I don't have a strong view on function vs procedure, but if I am asked to choose I'd go for procedure with the values to be returned as var parameters. This is only because it seems more natural to me because of familiarity with (old) TeeChart/Delphi, which is not much of a reason.  A better reason is that procedure, with values that can be altered being set to a default value, is slightly more proof against programming errors. It also allows putting the event routines in as empty placeholders while other parts of the program are worked on, and coming back to the fill out the placeholder later. ---------------------------------------------------------------------- wp · 2016-04-22 11:04 OnGetPointerStyle is a procedure now, it really makes more sense than having a function here. If you had used this event during the past few weeks don't forget to update your event handler signature because your application will crash if you don't. Quote from: mtanner on April 22, 2016, 11:30:02 amIt also allows putting the event routines in as empty placeholders while other parts of the program are worked on, and coming back to the fill out the placeholder later. I would not recommend that. Sometimes even the presence of an event handler, even if it is empty, triggers a deviation from standard behavior! Here's an example from TAChart, but I am sure that there are cases also everywhere in the LCL: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TChart.Draw(...);...    if Legend.Visible then begin      if Assigned(FOnDrawLegend) then        FOnDrawlegend(Self, ldd.FDrawer, ldd.FItems, ldd.FItemSize, ldd.FBounds,          ldd.FColCount, ldd.FRowCount)      else        Legend.Draw(ldd);    end;  This means that if there is an event handler for FOnDrawLegend the entire legend must be drawn by it, the built-in drawing routine (Legend.Draw(...)) is not called.