[gelöst] TAChart Zoom Y-Achse entsprechend sichtbarer Werte X-Achse

Rund um die LCL und andere Komponenten
Antworten
Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

[gelöst] TAChart Zoom Y-Achse entsprechend sichtbarer Werte X-Achse

Beitrag von Michl »

Hallo,

in einem bestehendem Programm möchte ich den Range der Y-Achse beim Zoom und Panning entsprechend der Werte des dargestellten X-Bereichs der Serie anpassen.

Hätte ich zum Beispiel eine LineSeries, die hundert Werte hätte und ich hätte gezoomt nur 5 Werte (51, 55, 53, 48, 50) sichtbar, sollte der Range der Y-Achse von 48 bis 55 gehen (entsprechend auch Y gezoomt).

Ich vermute, daß die Funktionalität beim TAChart eingebaut ist, konnte sie aber weder per Internetsuche noch durch Testen herausfinden. Probiert habe ich es mit TChart.LogicalExtent. Da es jedes mal zwei mal angepasst wird, flackert es und Panning geht nicht richtig.

Nun frage ich hier, ob jemand weiß, wie es richtig gemacht wird?

Anbei das kleine Testprogramm, wo ich nicht weiterkomme.

Zweite Frage noch hinten dran. Warum funktionert es nicht, Panning bzw. Zoom abzubrechen, wenn ich Escape drücke? In meiner anderen Anwendung funktionert dies. Soweit ich sehen kann, muss man nur EscapeCancels bei den entsprechenden ChartToolSets setzen.

Windows 7, Lazarus 3.4 (rev lazarus_3_4) FPC 3.2.2 x86_64-win64-win32/win64
Dateianhänge
AutoZoom.zip
(2.67 KiB) 140-mal heruntergeladen
Zuletzt geändert von Michl am Do 26. Dez 2024, 11:33, insgesamt 1-mal geändert.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1647
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: TAChart Zoom Y-Achse entsprechend sichtbarer Werte X-Achse

Beitrag von fliegermichl »

Wenn man die ChartTools löscht und die Zuweisung Chart.LogicalExtent := LExtent; auskommentiert, scheint es so zu funktionieren wie gewünscht.
Nur den Zoom zurücksetzen (mit Escape?) hab ich auch nicht hinbekommen.

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

Re: TAChart Zoom Y-Achse entsprechend sichtbarer Werte X-Achse

Beitrag von wp_xyz »

Ich verstehe die Anforderung nicht. Du hast einen Datensatz mit 100 Werten, x läuft zwischen 0 und 100, y zwischen 50 und ca 93 (Werte aus deinem Demo-Programm). jetzt willst du zoomen, so dass "nur 5 Werte (51, 55, 53, 48, 50) sichtbar" sind. Sind das Werte auf der x oder der y-Achse? Falls die Werte auf der x-Achse wären (wieso schreibst du sie dann in der ungeordneten Reihenfolge, in der Series sind sie doch sortiert?), soll sich dann die y-Achse so anpassen, dass sie den benötigten Bereich an y-Werten voll abdeckt? Dieselbe Frage wäre dann, wenn die gezoomten Werte auf der y-Achse wären.

Für das ESC muss der Chart fokussiert sein, sonst bekommt er den Tastendruck gar nicht durchgestellt. Am einfachsten setzst du AutoFocus auf true, dann wird der Chart automatisch fokussiert, wenn sich die Maus in den Chart hineinbewegt. Aber: Falls der Chart Bestandteil eines komplizierteren Formulars ist, insbesondere mit Eingabeelemente (TEdit) und der Fokus sitzt auf einem TEdit, dann verliert dieses Edit den Fokus mit der Mausbewegung in den Chart. Ich könnte mir vorstellen, dass das eine Menge Ärger machen kann.

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: TAChart Zoom Y-Achse entsprechend sichtbarer Werte X-Achse

Beitrag von Michl »

fliegermichl hat geschrieben: Mo 23. Dez 2024, 11:53 Wenn man die ChartTools löscht und die Zuweisung Chart.LogicalExtent := LExtent; auskommentiert, scheint es so zu funktionieren wie gewünscht.
Jaein. Prinzipiell ist das Standardverhalten fast so, wie ich es wünsche. Ich zoome ein Bereich, wo dann die X- und Y-Achse und die Werte entsprechend gezoomten Fenster darstellen. Leider ist aber ein Zoom möglich, wo Y-Werte außerhalb des Fensters liegen. Daher will ich nur in X-Richtung zoomen können und die Y-Werte sollen den maximalen Chart ausnutzen.

wp_xyz hat geschrieben: Mo 23. Dez 2024, 13:38 Ich verstehe die Anforderung nicht. Du hast einen Datensatz mit 100 Werten, x läuft zwischen 0 und 100, y zwischen 50 und ca 93 (Werte aus deinem Demo-Programm). jetzt willst du zoomen, so dass "nur 5 Werte (51, 55, 53, 48, 50) sichtbar" sind. Sind das Werte auf der x oder der y-Achse? Falls die Werte auf der x-Achse wären (wieso schreibst du sie dann in der ungeordneten Reihenfolge, in der Series sind sie doch sortiert?), soll sich dann die y-Achse so anpassen, dass sie den benötigten Bereich an y-Werten voll abdeckt? Dieselbe Frage wäre dann, wenn die gezoomten Werte auf der y-Achse wären.
Nehme ich an, ich zomme irgendwo und fange dabei 5 Punkte der Series in X-Richtung. Die 5 sichtbaren gezoomten X-Y-Wertepaare wären dann z.B. (10, 51), (11, 55), (12, 53), (13, 48), (14, 50). Also X-Achse läuft von 10 bis 14 und die Y-Achse sollte dann von 48 bis 55 laufen.

Es soll immer nur per Mausklick und X-Verschiebung gezoomt werden können. Die z.B. fünf Werte, die dann gezoomt dargestellt werden, sollen den Chart in der Höhe (in Y-Richtung) komplett ausfüllen. In meinem kleinen Testprogramm funktionert das ja auch, allerdings nur mit der Verbiegung und dem doppelten Zeichnen. Wenn man den Code Chart.LogicalExtent := LExtent; ausklammert, hat man das Standardverhalten. Wenn man zoomt, füllt der gezoomte Bereich nicht den Chart in der Höhe voll aus, sondern wandert nur irgendwo in der Mitte ein paar Pixel nach oben oder unten.
Als Online-Beispiel geht z.B. der Chart hier https://www.onvista.de/aktien/chart/Deu ... 0005557508. Wenn der Chart gezoomt wird, füllt er die komplette Höhe aus und irgnoriert alle Werte die außerhalb liegen.
Ich denke, es ist ein so banales Feature, daß ich einfach nicht weiß wie es heißt und man es recht einfach zuschalten kann.

wp_xyz hat geschrieben: Mo 23. Dez 2024, 13:38 Für das ESC muss der Chart fokussiert sein, sonst bekommt er den Tastendruck gar nicht durchgestellt.
Danke. Das ist der Unterschied. In der eigentlichen Anwendung habe ich bei jedem ChartToolSet in OnBeforeMouseDown ein Chart.SetFocus drin. Das habe ich irgendwie ignoriert bei der Suche nach der Ursache. Ich denke, da hast du mir schon mal in der Vergangenheit geholfen ;)

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

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

Re: TAChart Zoom Y-Achse entsprechend sichtbarer Werte X-Achse

Beitrag von wp_xyz »

Michl hat geschrieben: Mo 23. Dez 2024, 20:36 Ich denke, es ist ein so banales Feature, daß ich einfach nicht weiß wie es heißt und man es recht einfach zuschalten kann.
Nein, das ist keineswegs banal. Denn jede Änderung des Extent löst wieder ein OnExtentChanged-Event aus, und das ganze wird sehr schnell unübersichtlich. Eigentlich müsste man in die MouseUp-Behandlung des Tools reingehen, da hat man das aktuell aktive Auswahl-Rechteck für den neuen Zoom-Bereich als "SelectionRect" zur Verfügung und könnte angepasst werden, bevor der LogicalExtent geändert wird. Aber hier wird nirgendwo ein Event ausgelöst, so dass man eigenen Code einhängen könnte.

Ich habe nun in Laz/Main ein neues Event

Code: Alles auswählen

OnCalculateNewExtent(ATool: TChartTool; var ANewExtent: TDoubleRect)
eingebaut, das aufgerufen wird, bevor der neue LogicalExtent des Chart gesetzt wird. Wenn man hier nun, im x-Bereich zwischen ANewExtent.a.x und ANewExtent.b.x, den Bereich der y-Werte bestimmt und damit in den y-Bereich in ANewExtent neu einsetzt, dann sollte deine Problemstellung funktionieren:

Code: Alles auswählen

procedure TForm1.ChartToolsetZoomDragTool1CalculateNewExtent(ATool: TChartTool;
  var ANewExtent: TDoubleRect);
var
  newExt: TDoubleRect;
  ymin, ymax: Double;
  ser: TBasicPointSeries;
  i: Integer;
begin
  ResetRange(newExt.a.X, newExt.b.X);
  ResetRange(newExt.a.Y, newExt.b.Y);
  for i := 0 to ATool.Chart.SeriesCount-1 do
    if (ATool.Chart.Series[i] is TBasicPointSeries) then
    begin
      ResetRange(ymin, ymax);
      ser := TBasicPointSeries(ATool.Chart.Series[i]);
      if ser.IsRotated then
      begin
        ser.FindYRange(ANewExtent.a.Y, ANewExtent.b.Y, ymin, ymax);
        UpdateMinMax(ymin, newExt.a.X, newExt.b.X);
        UpdateMinmax(ymax, newExt.a.X, newExt.b.X);
      end else
      begin
        ser.FindYRange(ANewExtent.a.X, ANewExtent.b.X, ymin, ymax);
        UpdateMinMax(ymin, newExt.a.Y, newExt.b.Y);
        UpdateMinMax(ymax, newExt.a.Y, newExt.b.Y);
      end;
    end;
  if newExt.a.X <> Infinity then ANewExtent.a.X := newExt.a.X;
  if newExt.a.Y <> Infinity then ANewExtent.a.Y := newExt.a.Y;
  if newExt.b.X <> -Infinity then ANewExtent.b.X := newExt.b.X;
  if newExt.b.Y <> -Infinity then ANewExtent.b.Y := newExt.b.Y;
end;
Dieser Code ist etwas allgemeiner geschrieben, er funktioniert sowohl mit Animation (also Dragtool.AnimationInterval=10 und DragTool.AnimationSteps=20), als auch mit vertauschten x- und y-Achsen.

Bin noch nicht ganz sicher, evtl sollte man diesen Code auch gleich mit ins Tool packen und einen neuen Wert für RatioLimit einführen, zrlStretched (wie zrlFixedY, nur halt mit angepasster Y-Scala, bzw. bei vertauschen Achsen wie zrlFixedX mit angepasster X-Scala).

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: TAChart Zoom Y-Achse entsprechend sichtbarer Werte X-Achse

Beitrag von Michl »

Oha, danke für die Arbeit und das zu Weihnachten! Ich dachte wirklich, das ist von Hause aus implementiert, sofort abrufbar und ich einfach nicht mit den richtigen Begriffen suchfähig bin.
Also ja, ich wäre dafür dieses Feature von Hause aus zur Verfügung zu stellen und ein neuen Wert für RatioLimit einzufügen, wie zrlStretchedY (oder zrlStretchedX)!!

Ich habe Lazarus mal auf den letzten Stand gebracht und mein Projekt entsprechend verändert. Vielen Dank, es funktioniert! Ich habe den Code von meinem vorherigen Testprojekt genommen, da ich nicht weiß, was bei dem von dir geposteteten Code ResetRange macht.

Ein letzten Weihnachtswunsch hätte ich noch. Kannst du das gleiche Event noch beim ChartToolsetPanDragTool einfügen, damit ich auch beim Panning das selbe Verhalten erzielen kann?
Weiß nicht, ob es noch bei anderen ChartTools sinnvoll wäre.
Zuletzt geändert von Michl am Do 26. Dez 2024, 11:21, insgesamt 1-mal geändert.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

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

Re: TAChart Zoom Y-Achse entsprechend sichtbarer Werte X-Achse

Beitrag von wp_xyz »

Michl hat geschrieben: Di 24. Dez 2024, 14:43 Also ja, ich wäre dafür dieses Feature von Hause aus zur Verfügung zu stellen und ein neuen Wert für RatioLimit einzufügen, wie zrlStretchedY (oder zrlStretchedX)!!
Das hatte ich angefangen, aber dann wieder aufgegeben, weil es einen Rattenschwanz von Fallunterscheidungen hinter sich her zieht:
- Wenn mehrere Series im Chart sind, bezieht sich das Stretch meistens auf alle Series, aber vielleicht einmal auch nur auf eine oder zwei Series. Welche?
- Vielleicht will der User die Achse nur bei "schönen" Labels beginnen und enden lassen.
- Ganz unübersichtlich wird es, wenn der Chart mit AutoScaleAxisTransformations in mehrere "Tafeln" eingeteilt ist.
Da stelle ich lieber nur das OnCalculateNewExtent-Event bereit und überlasse dem User die Verantwortung, das Verhalten nach seinen Anforderungen im Event-Handler selbst zu implementieren. Wahrscheinlich überarbeite ich aber dein Beispiel und füge es noch zu den TAChart-Demos [EDIT: erledigt, im Ordner tachart/demo/tools_calculate_new_extent].
Michl hat geschrieben: Di 24. Dez 2024, 14:43 da ich nicht weiß, was bei dem von dir geposteteten Code ResetRange macht.
Oh, das hatte ich beim Copy&Paste übersehen:

Code: Alles auswählen

  procedure ResetRange(out min, max: Double);
  begin
    min := Infinity;
    max := -Infinity;
  end;
Michl hat geschrieben: Di 24. Dez 2024, 14:43 Kannst du das gleiche Event noch beim ChartToolsetPanDragTool einfügen, damit ich auch beim Panning das selbe Verhalten erzielen kann?
OnCalculateNewExtent gibt es nun bei allen Zoom/Drag-Tools. Man kann denselben Event-Handler bei all diesen Tools verwenden.

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: TAChart Zoom Y-Achse entsprechend sichtbarer Werte X-Achse

Beitrag von Michl »

Super. Funktioniert prima.

In meinem Testprojekt ist nur noch das Einhängen in die Eventhandler OnCalculateNewExtent nötig:

Code: Alles auswählen

procedure TForm1.ChartToolsetCalculateNewExtent(ATool: TChartTool; var ANewExtent: TDoubleRect);
var
  LSeries: TLineSeries;
  YMin, YMax: Double;
begin
  LSeries := TLineSeries(Chart.Series[0]);
  YMin := 1000;
  YMax := -1000;
  LSeries.FindYRange(ANewExtent.a.X, ANewExtent.b.X, YMin, YMax);
  ANewExtent.a.Y := YMin;
  ANewExtent.b.Y := YMax;
end; 
Vielen Dank! Für mich passt das so.
Dateianhänge
AutoZoomNeu.zip
(2.45 KiB) 125-mal heruntergeladen

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

Antworten