TAChart - CrosshairTool

Für Fehler in Lazarus, um diese von anderen verifizieren zu lassen.
Antworten
Benutzeravatar
kupferstecher
Beiträge: 418
Registriert: Do 17. Nov 2016, 11:52

TAChart - CrosshairTool

Beitrag von kupferstecher »

Zunächst, ich bin mir nicht sicher ob Fehler in der Componente TAChart auch in den Bugtracker gehören. Ich probier es erstmal hier.

Es geht um 2 Sachen.

Erstens: Wenn man die Maus an einen Kurvenpunkt hinbewegt erscheint ja das CrosshairTool (wenns richtig konfiguriert ist). Wenn man dann die Maus im "Einzugsbereich" weiter bewegt, kommt es zum Flackern vom CrosshairTool. Das hat mich immer gestört und ich habe mir jetzt mal den Source angeschaut und meine das Problem gefunden zu haben. Kurz gesagt wird im "MouseMove"-Handler als erste Aktion immer das Crosshairtool gelöscht um dann gleich wieder gezeichnet zu werden. Unten der Versuch, das zu beheben, bei kurzen Tests hat das auch funktioniert. Das CrosshairTool wird darin nur noch gelöscht, wenn sich die Series oder der Index geändert haben. Die Funktion scheint mir aber etwas sperrig, vielleicht kann man das noch optimieren.

Code: Alles auswählen

procedure TDataPointCrosshairTool.MouseMove(APoint: TPoint);
var
  id: IChartDrawer;
  LastSeries,CurrentSeries: TBasicChartSeries;
  LastIndex: Integer;
begin
  //original version (flickering)
  {id := GetCurrentDrawer;
  if Assigned(id) then
    DoHide(id);
  FindNearestPoint(APoint);
  if FSeries = nil then exit;
  FPosition := FNearestGraphPoint;
  if (EffectiveDrawingMode = tdmXor) and Assigned(id) then begin
    id.SetXor(true);
    DoDraw(id);
    id.SetXor(false);
  end;}


  id := GetCurrentDrawer;
  LastSeries:= fSeries;
  fSeries:= nil;
  LastIndex:= fPointIndex;
  FindNearestPoint(APoint);
  if (fSeries = LastSeries) and (fPointIndex = LastIndex) then EXIT;
  CurrentSeries:= fSeries;

  fSeries:= LastSeries;
  if assigned(id) then DoHide(id);

  fSeries:= CurrentSeries;
  if FSeries = nil then EXIT;

  FPosition := FNearestGraphPoint;
  if (EffectiveDrawingMode = tdmXor) and Assigned(id) then begin
    id.SetXor(true);
    DoDraw(id);
    id.SetXor(false);
  end;

end;
Zweitens: Die Farbe des Kreuzes lässt sich nicht ändern, egal was ich im OI unter ChartToolset1DataPointCrosshairTool1 -> CrosshairPen -> Color einstelle, es wird immer schwarz gezeichnet. Ich habe mir auch den Code angeschaut, werde aber nicht ganz schlau daraus.

Viele Grüße!

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: TAChart - CrosshairTool

Beitrag von wp_xyz »

TAChart wird zusammen mit Lazarus ausgeliefert, daher ist der Bugtracker durchaus die richtige Stelle, einen Fehler zu melden. Hier ist aber auch ok.

Ich habe mir das angesehen und die beigefügte Testanwendung zusammengeklickt. Also beim besten Willen, ich sehe da nichts flackern, wenn ich die Maus innerhalt des Fangbereichs des Tools (GrabRadius = 20 px) um einen Datenpunkt herumbewege (Win 11, Laz-main+fpc-3.2.2). Was machst du anders, so dass der Fehler auftritt?

Das "Fadenkreuz" wird normalerweise im XOR-Modus gezeichnet. Dadurch ist die Ausgabe sehr schnell, denn es muss, um den alten Cursor zu löschen, dieser nur nochmals am selben Ort gezeichnet werden. Allerdings hat man dadurch keine Möglichkeit mehr, seine Farbe zu ändern, da sie sich wegen der XOR-Operation automatisch aus der Farbe des Untergrundes ergibt.

Es gibt zusätzlich aber auch die Möglichkeit, jedesmal den ganzen Chart neu zu zeichnen und das Fadenkreuz an der aktuellen Position drüberzumalen. Hier ist eine freie Änderung der Farbe möglich.

Die Eigenschaft DrawingMode legt fest, welche der beiden Möglichkeiten genommen wird: tdmXOR oder tdmNormal. Dazu gibt es noch ein tdmDefault, das ist normalerweise identisch mit tdmXOR, es sei denn, das Betriebssystem erlaubt keinen Zugriff auf den Canvas außerhalb des Paint-Events.

Siehe dazu https://wiki.lazarus.freepascal.org/TAC ... awing_mode

Dein Patch hat den Nebeneffekt, dass der DrawingMode tdmNormal nicht mehr funktioniert.
Dateianhänge
crosshairtool_flicker.zip
(2.41 KiB) 101-mal heruntergeladen

Benutzeravatar
kupferstecher
Beiträge: 418
Registriert: Do 17. Nov 2016, 11:52

Re: TAChart - CrosshairTool

Beitrag von kupferstecher »

Danke schonmal!
wp_xyz hat geschrieben:
Fr 7. Jan 2022, 17:57
Ich habe mir das angesehen und die beigefügte Testanwendung zusammengeklickt. Also beim besten Willen, ich sehe da nichts flackern, wenn ich die Maus innerhalt des Fangbereichs des Tools (GrabRadius = 20 px) um einen Datenpunkt herumbewege (Win 11, Laz-main+fpc-3.2.2). Was machst du anders, so dass der Fehler auftritt?
Ich habe es mit deinem Programm probiert und im ersten Moment dachte ich es ist ok, manchmal kam es aber noch zum Flackern. System ist Win7 auf entsprechender Hardware. Dann habe ich die Property Size auf -1 gestellt, damit ein komplettes Kreuz gezeichnet wird und CrosshairPen.Width auf 3. Dann war das Flackern wieder deutlich zu sehen. Dazu noch den GrabRadius auf 100, um sicher zu sein, dass es nicht am Randbereich liegt. Unter Linuxmint auf der gleichen Hardware war dagegen alles einwandfrei, auch bei CrosshairPen.Width= 50. Vielleicht kannst du nochmal mit breiterer und längerer Linie probieren, ob es dann nicht auch zum Flackern kommt.

Mit DrawingMode tdmNormal flackert bei mir tatsächlich auch unter Windows nichts.

Allerdings hat man dadurch keine Möglichkeit mehr, seine Farbe zu ändern, da sie sich wegen der XOR-Operation automatisch aus der Farbe des Untergrundes ergibt.
Alles klar, verstanden :)
Mit tdmNormal funktioniert die Farbeinstellung, passt also.
Dein Patch hat den Nebeneffekt, dass der DrawingMode tdmNormal nicht mehr funktioniert.
Echt verzwickt... Schauen wir erstmal ob sich das Flackern reproduzieren lässt.

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: TAChart - CrosshairTool

Beitrag von wp_xyz »

Ja. Die Linienbreite 3 bringst... Damit kann ich das reproduzieren, und dein Patch beseitigt das Flackern auch.

Nachdem die neue Routine sich von der alten nur in ein paar Zeilen unterscheidet, könntest du versuchen, deine Änderungen in ein

Code: Alles auswählen

if EffectiveDrawingMode = tdmNormal then...
zu setzen. (ich nehme "EffectiveDrawingMode", weil je nach Betriebssystem tdmDefault = tdmXOR sein kann).

Benutzeravatar
kupferstecher
Beiträge: 418
Registriert: Do 17. Nov 2016, 11:52

Re: TAChart - CrosshairTool

Beitrag von kupferstecher »

Habs schon probiert, wahrscheinlich reicht es, bei tdmNormal das EXIT zu unterlassen. Ein kurzer Test war erfolgreich, sowohl für tdmDefault, tdmXor, als auch tdmNormal. Was mir noch aufgefallen ist, ist dass wenn ein Punkt am Rand vom Diagram ist und man bewegt die Maus über den Diagrammrand, dann bleibt das Fadenkreuz auf dem Punkt, egal wie weit man mit der Maus weggeht. Aber das ist auch bei der bestehenden Implementierung so, macht wohl auch nichts aus.
wp_xyz hat geschrieben:
Fr 7. Jan 2022, 19:43
ich nehme "EffectiveDrawingMode", weil je nach Betriebssystem tdmDefault = tdmXOR sein kann
Ich musste drei Mal darüber nachdenken, was das heißen soll, jetzt hab ich es verstanden: In EffectiveDrawingMode wurde das tdmDefault schon "übersetzt". Ich hatte auf ungleich tdmNormal geprüft, aber wenn Default = Normal ist, wäre das auch schief gegangen.

Code: Alles auswählen

procedure TDataPointCrosshairTool.MouseMove(APoint: TPoint);
var
  id: IChartDrawer;
  LastSeries,CurrentSeries: TBasicChartSeries;
  LastIndex: Integer;
begin
  //original version (flickering)
  {id := GetCurrentDrawer;
  if Assigned(id) then
    DoHide(id);
  FindNearestPoint(APoint);
  if FSeries = nil then exit;
  FPosition := FNearestGraphPoint;
  if (EffectiveDrawingMode = tdmXor) and Assigned(id) then begin
    id.SetXor(true);
    DoDraw(id);
    id.SetXor(false);
  end;}


  id := GetCurrentDrawer;
  LastSeries:= fSeries;
  fSeries:= nil;
  LastIndex:= fPointIndex;
  FindNearestPoint(APoint);

  if (EffectiveDrawingMode = tdmXor) and
     (fSeries = LastSeries) and (fPointIndex = LastIndex) then EXIT;
  CurrentSeries:= fSeries;

  fSeries:= LastSeries;
  if assigned(id) then DoHide(id);

  fSeries:= CurrentSeries;
  if FSeries = nil then EXIT;

  FPosition := FNearestGraphPoint;
  if (EffectiveDrawingMode = tdmXor) and Assigned(id) then begin
    id.SetXor(true);
    DoDraw(id);
    id.SetXor(false);
  end;

end;

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: TAChart - CrosshairTool

Beitrag von wp_xyz »

Sieht gut aus. Allerdings gibt es noch ein Problem: Wenn man bei der LineSeries unter ToolTargets die Option nptCustom markiert, dann sollte der Cursor auch von den Zwischenwerten entlang der Verbindungslinie zwischen zwei Punkten gefangen werden. Das funktioniert mit der neuen Version nicht mehr.

Tooltargets (https://wiki.lazarus.freepascal.org/TAC ... Data_tools) geben an, welcher "Teil" der Series mit dem Tool interagiert. nptCustom ist bei der LineSeries so gestaltet, dass das Tool die Punkte der Verbindungslinie erkennt.

Benutzeravatar
kupferstecher
Beiträge: 418
Registriert: Do 17. Nov 2016, 11:52

Re: TAChart - CrosshairTool

Beitrag von kupferstecher »

Ok, unten auch das noch berücksichtigt. Allerdings scheint mir das jetzt eher ein Workaround zu sein. Bei den Bewegungen auf der Linie kommt es dann auch wieder zum Flackern. Ein echtes Beheben des Problems geht wahrscheinlich nur auf der Ebene des Drawers?

Code: Alles auswählen

procedure TDataPointCrosshairTool.MouseMove(APoint: TPoint);
var
  id: IChartDrawer;
  LastSeries,CurrentSeries: TBasicChartSeries;
  LastIndex: Integer;
begin
  //original version (flickering)

  //{$DEFINE ORIG}

  {$IFDEF ORIG}
  id := GetCurrentDrawer;
  if Assigned(id) then
    DoHide(id);
  FindNearestPoint(APoint);
  if FSeries = nil then exit;
  FPosition := FNearestGraphPoint;
  if (EffectiveDrawingMode = tdmXor) and Assigned(id) then begin
    id.SetXor(true);
    DoDraw(id);
    id.SetXor(false);
  end;
  {$ELSE}

  id := GetCurrentDrawer;
  LastSeries:= fSeries;
  fSeries:= nil;
  LastIndex:= fPointIndex;
  FindNearestPoint(APoint);

  if  (EffectiveDrawingMode = tdmXor) //always has to redraw in tdmNormal
  and (fPointIndex >= 0)              //always (re-) draw if position is not a line point
  and (fSeries = LastSeries) and (fPointIndex = LastIndex)
    then EXIT;

  CurrentSeries:= fSeries;

  fSeries:= LastSeries;
  if assigned(id) then DoHide(id);

  fSeries:= CurrentSeries;
  if FSeries = nil then EXIT;

  FPosition := FNearestGraphPoint;
  if (EffectiveDrawingMode = tdmXor) and Assigned(id) then begin
    id.SetXor(true);
    DoDraw(id);
    id.SetXor(false);
  end;

{$ENDIF}

end;

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: TAChart - CrosshairTool

Beitrag von wp_xyz »

Gut, das könnte ich so hochladen. Es fällt mir aber noch auf, dass, wenn man entlang der Zwischenlinie zieht, eigentlich nur die horizontale Linie flackert, nicht aber die vertikale. Beide werden aber mit derselben Prozedur gemalt. Das kommt mir so vor, als ob da noch ein Bug schlummert, wahrscheinlich die eigentliche Ursache für dieses Problem...
Dateianhänge
crosshairtool_flicker-2.zip
(2.89 KiB) 98-mal heruntergeladen

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: TAChart - CrosshairTool

Beitrag von wp_xyz »

Hab mir ein von TAChart unabhängiges Test-Programm zusammengeklickt, in dem mit Hilfe der Cursor-Tasten ein schwarzes Fadenkreuz über eine weiße Paintbox bewegt wird. Das Fadenkreuz wird im XOR-Modus gezeichnet. Hier flackert nichts...

Das deutet darauf hin, dass TAChart mit XOR tatsächlich ein Problem hat.
Dateianhänge
xor_mode.zip
(2.05 KiB) 89-mal heruntergeladen

Benutzeravatar
kupferstecher
Beiträge: 418
Registriert: Do 17. Nov 2016, 11:52

Re: TAChart - CrosshairTool

Beitrag von kupferstecher »

wp_xyz hat geschrieben:
Sa 8. Jan 2022, 20:06
Es fällt mir aber noch auf, dass, wenn man entlang der Zwischenlinie zieht, eigentlich nur die horizontale Linie flacker
Bei mir flackern beide. Aber die Horizontale tatsächlich deutlich öfter. Mit dem Code in TDataPointCrosshairTool.DoDraw (siehe unten) wäre das m.E. auch plausibel, weil die vertikale Linie zuerst gezeichnet wird und seit dem Löschen damit weniger Zeit vergangen ist. Es scheit also, dass Windows sofort eine Neuzeichnung anregt und nicht erst nachdem die Routinen verlassen wurden.

Code: Alles auswählen

procedure TDataPointCrosshairTool.DoDraw(ADrawer: IChartDrawer);
begin
[...]
  if Shape in [ccsVertical, ccsCross] then
    if Size < 0 then
      FChart.DrawLineVert(ADrawer, p.X)
    else
      ADrawer.Line(p - Point(0, Size), p + Point(0, Size));
  if Shape in [ccsHorizontal, ccsCross] then
    if Size < 0 then
      FChart.DrawLineHoriz(ADrawer, p.Y)
    else
      ADrawer.Line(p - Point(Size, 0), p + Point(Size, 0));
[...]
  inherited;
end;
wp_xyz hat geschrieben:
So 9. Jan 2022, 13:38
Hab mir ein von TAChart unabhängiges Test-Programm zusammengeklickt, in dem mit Hilfe der Cursor-Tasten ein schwarzes Fadenkreuz über eine weiße Paintbox bewegt wird.
(Das wäre doch dem Modus drawingMode=tdmNormal vergleichbar, oder? Also immer ein Neuzeichnen der ganzen Paintbox inklusive Fadenkreuz in PaintBox1Paint.) Edit: nein, hab den Code jetzt verstanden, Paint wird ja zweimal aufgerufen, erst zum Löschen mittels XOR und dann um die neue Fadenkreuzposition zu zeichnen. Hm...
Edit2: Doch nicht, da stimmt was nicht im Programm.

(Im CrosshairTool ist die Reihenfolge DoHide (was ein DoDraw enthält) gefolgt vom DoDraw. Offensichtlich gelangt das DoHide teilweise auf den Bildschirm bevor das neue DoDraw angezeigt wird, was dann zum Flackern führt.)

Benutzeravatar
kupferstecher
Beiträge: 418
Registriert: Do 17. Nov 2016, 11:52

Re: TAChart - CrosshairTool

Beitrag von kupferstecher »

Also ich komm nicht richtig weiter. Der Paintbox-Hintergrund wird ja immer neu gezeichnet. Dadurch ist die XOR-Version sinnlos, kann sich nicht selber übermalen, sondern zeichnet immer auf den leeren Hintergrund. Ich hab es auch mit einem TCustomControl versucht, auch dort wird immer der Hintergrund automatisch gelöscht. Und ich weiß nicht wie man das verhindern kann oder wie TAChart das macht.

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: TAChart - CrosshairTool

Beitrag von wp_xyz »

Natürlich, das war ein Denkfehler. Wenn ich nur die Linien zeichne, dann flackert genauso wie bei TAChart immer nur die horizontale Linie. Dieselbe Beobachtung, wenn ich das Projekt nach Delphi übertrage. Läuft es unter Linux/gtk2, kein Flackern. Unter Linux/qt5 tut sich gar nichts - wahrscheinlich kann man da nur im OnPaint Event zeichnen.

Ich denke, da kann man nicht viel machen, zumindest nicht mit dem Standard-Canvas.

Code: Alles auswählen

procedure TForm1.PaintBox1Paint(Sender: TObject);
begin
  Paintbox1.Canvas.Brush.Color := clWhite;
  Paintbox1.Canvas.FillRect(0, 0, Paintbox1.Width, Paintbox1.Height);
  DrawLines;
end;

procedure TForm1.DrawLines;
begin
  Paintbox1.Canvas.Pen.Width := 5;
  Paintbox1.Canvas.Pen.Color := clWhite;
  Paintbox1.Canvas.Pen.Mode := pmXOR;
  Paintbox1.Canvas.Line(X, 0, X, Paintbox1.Height);
  Paintbox1.Canvas.Line(0, Y, Paintbox1.Width, Y);
  Paintbox1.Canvas.Pen.Mode := pmCopy;
end;

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
  if (Key = VK_LEFT) and (X > 0) then
  begin
    DrawLines;
    dec(X);
    DrawLines;
  end else
  if (Key = VK_RIGHT) and (X < Paintbox1.Width) then
  begin
    DrawLines;
    inc(X);
    DrawLines;
  end else
  if (Key = VK_UP) and (Y > 0) then
  begin
    DrawLines;
    dec(Y);
    DrawLines;
  end else
  if (Key = VK_DOWN) and (Y < Paintbox1.Height) then
  begin
    DrawLines;
    inc(Y);
    DrawLines;
  end;
end;

Benutzeravatar
kupferstecher
Beiträge: 418
Registriert: Do 17. Nov 2016, 11:52

Re: TAChart - CrosshairTool

Beitrag von kupferstecher »

Ok, pflegst du die letzte Version dann ein? Dass zumindest auf den Kurvenpunkten kein Flackern auftritt.

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: TAChart - CrosshairTool

Beitrag von wp_xyz »

Ist jetzt im trunk und für fixes/v2.2.2 angemeldet. Damit die Hintergründe nicht verloren gehen, habe zusätzlich ich einen Bugreport darüber geschrieben (https://gitlab.com/freepascal.org/lazar ... sues/39548).

Benutzeravatar
kupferstecher
Beiträge: 418
Registriert: Do 17. Nov 2016, 11:52

Re: TAChart - CrosshairTool

Beitrag von kupferstecher »

Vielen, vielen Dank!

Antworten