Hallo liebe Forenmitglieder,
erneut eine (für mich) tiefergehende Frage zur TChart Komponente:
Basierend auf dem TChart "toolsdemo" (..\Lazarus\components\tachart\demo\tools\) in einer Standardinstallation (Lazarus 1.8, Windws7 64bit)
möchte ich
1. das Intervall der x und y-Achse (wenn mehrere vorhanden, jede y-Achse einzeln) mit der Maus
(rechte Maustaste + Mauswheel up/down) vergrößern oder verkleinern für die jeweils ausgewählte Achse (über MouseOver oder
angeklickte Achse, sofern das überhaupt geht).
Gibt es dazu einen Maus Listener auf den jeweiligen Achsen?
2. in diesem Zusammenhang habe ich mit DataTools experimentiert mit dem ich mir die Funktionswerte meiner Serien
an einer bestimmten X Koordinaten (wo sich gerade meine Maus befindet) anzeigen möchte. Wenn ich selbst z.B. ein DataTool integriere
sind (bekanntermaßen) die schönen voreingestellten panning (draginng) und zooming (Mausklick und Rechteckausschnitt wählen)
Funktionen weg. Wie reaktiviere ich diese korrekterweise bei einem selbst erstellen (zusätzlichen) Datatool?
Zusammengefasst suche ich also eine Möglichkeit diese Funktionen
- "Drag to Zoom" (linke Maustaste gedrückt und Rechteck(ausschnitt) aufziehen -> Standard Zooming)
- "Mouse wheel to zoom" (analog wie im "toolsdemo", hinein und hinauszoomen in einen Chart nur mit dem Mausrad)
- "Panning" (verschieben der Ansicht durch rechte Maustaste drücken und ziehen -> Standard Panning)
- "Mouse wheel to Zoom Axis" (neu?!?) auf den jeweiligen Achse
- Anzeige des (ggf. interpolierten) y-Achsen Funktionswerte meiner Serie an der Mausposition (im Chart) z.B. in einer Statusbar
(ohne auf die oben genannten Funktionen verzichten zu müssen)
alle gleichzeitig in einem Chart umzusetzen. Geht das und wenn ja wie (am besten)?
Vielen Dank im Voraus für jede Antwort
Gruß
Helios
TChart mit zusätzlichen Datatools + Axis Mouse Listener?
Re: TChart mit zusätzlichen Datatools + Axis Mouse Listener?
Helios hat geschrieben:- "Drag to Zoom" (linke Maustaste gedrückt und Rechteck(ausschnitt) aufziehen -> Standard Zooming)
- "Panning" (verschieben der Ansicht durch rechte Maustaste drücken und ziehen -> Standard Panning)
- Eine ChartToolSet-Komponente aufs Formular klicken und mit dem Chart verbinden (Property "Toolset")
- Doppelklick auf dem ChartToolset, ein ZoomDragTool und ein ZoomPanTool hinzufügen
- das ZoomDragTool wird mit Shift = ssLeft, der ZoomPanTool mit Shift = ssRight aktiviert--> damit hast du wieder den Zustand wie bei den eingebauten Tools. Gleichzeitig hast du aber auch Zugriff auf weitere Parameter um Details des Zoom bzw. Unzoom-Vorgangs zu kontrollieren. Für das folgende rate ich noch beim ZoomTool das Flag zreClick bei "RestoreExtentOn" zu deaktiveren, weil wir den Click allein noch brauchen - siehe unten. Der ungezoomte Zustand wird nun durch einen beliebigen Dragvorgang wiederhergestellt, der nur nicht von links oben nach rechts unten gehen darf (das ist die Richtung, um tiefer hineinzuzoomen)
Helios hat geschrieben:- "Mouse wheel to zoom" (analog wie im "toolsdemo", hinein und hinauszoomen in einen Chart nur mit dem Mausrad)
- "Mouse wheel to Zoom Axis" (neu?!?) auf den jeweiligen Achse
- Für "Mouse wheel to zoom" ein ZoomMouseWheel-Tool zum Toolset hinzufügen. Für isotropes Zoomen (alle Achsen gleichzeitig) ZoomFactor auf (z.B.) 1.1 (d.h. 10% Zoom pro Mausrad-Schritt)
- Um nur entlang der x-Achse zu zoomen (bzw. nur entlang der y-Achse) muss man gleichzeit ZoomRatio anpassen. Das Vorgehen ist hier (http://wiki.lazarus.freepascal.org/TACh ... click_tool) für das ZoomClickTool abgeleitet, gilt aber genauso für das Mausrad.
- TAChart stellt m.E. keine Routinen zur Verfügung, um einen Klick auf einer Achse zu erkennen. Im einfachsten Fall würde ich den Clickpunkt mit Chart.ImageToGraph in Graph-Koordinaten umrechnen und dann prüfen, ob der umgerechnete Punkt (GraphClickPkt) im Chart.LogicalExtent liegt. Wenn ja, erfolgte der Klick im Series-Bereich --> nicht auf einer Achse. Wenn ext := Chart.LogicalExtent und GraphiClickPkt.Y < ext.a.Y dann erfolgte der Klick außerhalb des Series-Bereichs irgendwo im Bereich der x-Achse und man kann die Zoomfactor/ZoomRatio-Werte für horizontales Zoomen einstellen. Andererseits, wenn GraphClickPkt.X < ext.a.x, wurde im Bereich der y-Achse geklickt, und man kann die Zoomfactor/ZoomRatio-werte für vertikales Zoomen einstellen. Schließlich, wenn innerhalb des Series-Bereichs geklickt wurde, könnte man wieder auf isotropes Zoomen zurücksetzen - hier ist es sinnvoll, wenn ein Klick den Zoombereich nicht zurücksetzt - daher die Bemerkung oben wegen RestoreExtentOn.
- Natürlich, wenn mehrere Achsen vorhanden sind, wird's schwieriger...
Helios hat geschrieben:-Anzeige des (ggf. interpolierten) y-Achsen Funktionswerte meiner Serie an der Mausposition (im Chart) z.B. in einer Statusbar
- Zur Abfrage der Mausposition bzgl der Series ist wahrscheinlich ein DatapointCrosshairTool am besten geeignet. Falls du den CrossHair-Cursor nicht willst, einfach Size auf 0 setzen. Wenn du kein Shift deklariest, springt der Cursor innerhalb des GrabRadius immer zum nächsten Datenpunkt. Um den Text in die Statuszeile zu schreiben, hängst du dich ins Ereignis "OnDraw" - das ist zwar gedacht, spezielle Cursor-Formen zu erzeugen, aber man kann es natürlich für alles mögliche verwenden.
- Dass der Cursor den Interpolationslinien folgt, geht bei der TLineSerie noch nicht, dagegen bei einer oder beiden SplineSeries -- das werde ich in einer ruhigen Stunde einmal nachrüsten.
Code: Alles auswählen
unit Unit1;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ComCtrls, ExtCtrls,
StdCtrls, TAGraph, TASeries, TASources, TATools, Types;
type
{ TForm1 }
TForm1 = class(TForm)
Chart1: TChart;
Chart1LineSeries1: TLineSeries;
Chart1LineSeries2: TLineSeries;
ChartToolset1: TChartToolset;
ChartToolset1DataPointCrosshairTool1: TDataPointCrosshairTool;
ChartToolset1PanDragTool1: TPanDragTool;
ChartToolset1ZoomDragTool1: TZoomDragTool;
ChartToolset1ZoomMouseWheelTool1: TZoomMouseWheelTool;
Label1: TLabel;
Panel1: TPanel;
RandomChartSource1: TRandomChartSource;
RandomChartSource2: TRandomChartSource;
StatusBar1: TStatusBar;
procedure Chart1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure ChartToolset1DataPointCrosshairTool1Draw(
ASender: TDataPointDrawTool);
procedure ChartToolset1ZoomDragTool1BeforeMouseDown(ATool: TChartTool;
APoint: TPoint);
private
FClickedAxis: String;
public
end;
var
Form1: TForm1;
implementation
{$R *.lfm}
uses
TAChartUtils, // TDoubleRect
TACustomSeries; // für TChartSeries
const
ZOOM_FACTOR = 1.1;
{ TForm1 }
procedure TForm1.Chart1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
extImg: TRect;
ext: TDoubleRect;
P: TDoublePoint;
begin
ext := Chart1.LogicalExtent;
P := Chart1.ImageToGraph(Point(X, Y));
if (P.X < ext.a.X) and (P.Y >= ext.a.Y) and (P.Y <= ext.b.Y) then begin
FClickedAxis := 'y';
ChartToolset1ZoomMouseWheelTool1.ZoomFactor := 1.0;
ChartToolset1ZoomMouseWheelTool1.ZoomRatio := ZOOM_FACTOR;
end else
if (P.Y < ext.a.Y) and (P.X >= ext.a.X) and (P.X <= ext.b.X) then begin
FClickedAxis := 'x';
ChartToolset1ZoomMouseWheelTool1.Zoomfactor := ZOOM_FACTOR;
ChartToolset1ZoomMouseWheelTool1.ZoomRatio := 1.0/ ZOOM_FACTOR;
end else begin
FClickedAxis := '(none)';
ChartToolset1ZoomMouseWheelTool1.ZoomFactor := ZOOM_FACTOR;
ChartToolset1ZoomMouseWheeltool1.ZoomRatio := 1.0;
end;
Statusbar1.SimpleText := 'Clicked axis: ' + FClickedAxis;
end;
procedure TForm1.ChartToolset1DataPointCrosshairTool1Draw(
ASender: TDataPointDrawTool);
var
ser: TChartSeries;
idx: Integer;
begin
ser := ASender.Series as TChartSeries;
idx := ASender.PointIndex;
if ser = nil then
Statusbar1.SimpleText := ''
else
Statusbar1.SimpleText := Format('Series "%s": x = %.2f, y=%.2f', [ser.Title, ser.XValue[idx], ser.YValue[idx]]);
end;
procedure TForm1.ChartToolset1ZoomDragTool1BeforeMouseDown(ATool: TChartTool;
APoint: TPoint);
begin
Chart1MouseDown(nil, mbLeft, ATool.Shift, Apoint.X, APoint.Y);
end;
end.
Code: Alles auswählen
object Form1: TForm1
Left = 310
Height = 481
Top = 127
Width = 668
Caption = 'Form1'
ClientHeight = 481
ClientWidth = 668
LCLVersion = '1.9.0.0'
object Chart1: TChart
Left = 0
Height = 337
Top = 0
Width = 668
AxisList = <
item
Marks.LabelBrush.Style = bsClear
Minors = <>
Title.LabelFont.Orientation = 900
Title.LabelBrush.Style = bsClear
end
item
Alignment = calBottom
Marks.LabelBrush.Style = bsClear
Minors = <>
Title.LabelBrush.Style = bsClear
end>
Foot.Brush.Color = clBtnFace
Foot.Font.Color = clBlue
Legend.Visible = True
Title.Brush.Color = clBtnFace
Title.Font.Color = clBlue
Title.Text.Strings = (
'TAChart'
)
Toolset = ChartToolset1
Align = alClient
OnMouseDown = Chart1MouseDown
object Chart1LineSeries1: TLineSeries
Title = 'Series 1'
LinePen.Color = clRed
ShowPoints = True
Source = RandomChartSource1
end
object Chart1LineSeries2: TLineSeries
Title = 'Series 2'
LinePen.Color = clBlue
ShowPoints = True
Source = RandomChartSource2
end
end
object StatusBar1: TStatusBar
Left = 0
Height = 23
Top = 458
Width = 668
Panels = <>
end
object Panel1: TPanel
Left = 0
Height = 121
Top = 337
Width = 668
Align = alBottom
AutoSize = True
BevelOuter = bvNone
ClientHeight = 121
ClientWidth = 668
TabOrder = 2
object Label1: TLabel
Left = 8
Height = 105
Top = 8
Width = 374
AutoSize = False
BorderSpacing.Left = 8
BorderSpacing.Top = 8
BorderSpacing.Bottom = 8
Caption = 'Drag top-left to bottom-right --> zoom'#13#10'Drag in different direction --> unzoom'#13#10'Mouse-wheel --> zoom'#13#10'- Click near x axis (outside chart area): Mouse wheel zooms horizontally'#13#10'- Click near y axis (outside chart area): Mouse wheel zooms verticlly'#13#10'- Click inside chart aread: Mouse wheel zoom isotropically'#13#10'Move mouse near data point --> display data values in status bar'#13#10'Drag right button --> pan'
ParentColor = False
end
end
object RandomChartSource1: TRandomChartSource
PointsNumber = 5
RandSeed = 1392190234
XMax = 1
XMin = 0
YMax = 1
YMin = 0
left = 153
top = 49
end
object ChartToolset1: TChartToolset
left = 247
top = 176
object ChartToolset1ZoomDragTool1: TZoomDragTool
Shift = [ssLeft]
OnBeforeMouseDown = ChartToolset1ZoomDragTool1BeforeMouseDown
Brush.Style = bsClear
RestoreExtentOn = [zreDragTopLeft, zreDragTopRight, zreDragBottomLeft]
end
object ChartToolset1PanDragTool1: TPanDragTool
Shift = [ssRight]
end
object ChartToolset1ZoomMouseWheelTool1: TZoomMouseWheelTool
ZoomFactor = 1.1
end
object ChartToolset1DataPointCrosshairTool1: TDataPointCrosshairTool
OnDraw = ChartToolset1DataPointCrosshairTool1Draw
Size = 12
end
end
object RandomChartSource2: TRandomChartSource
PointsNumber = 10
RandSeed = 1392190234
XMax = 1
XMin = 0
YMax = 1.5
YMin = 0.3
left = 153
top = 112
end
end
-
- Lazarusforum e. V.
- Beiträge: 107
- Registriert: Mi 29. Jun 2011, 22:36
- OS, Lazarus, FPC: Lazarus 2.2.6 Windows 10 64Bit / Lazarus 2.0.12 Debian 11.7 „Bullseye" 64Bit
- CPU-Target: 64Bit
- Wohnort: Leonberg
Re: TChart mit zusätzlichen Datatools + Axis Mouse Listener?
Hallo wp_xyz,
entschuldige die sehr, sehr späte Rückmeldung (ich brauchte Zeit für die Umsetzung und für den Umstieg auf Lazarus 1.8.2).
Deine Vorschläge habe ich 1:1 umsetzen können und laufen "out of the box". Gigantisch! Vielen, vielen Dank!
Bei Deinem Hinweis:
hast Du sicherlich meine nächste Frage vorausgeahnt:-)
In dem Beispiel von meinem letzten Thread (https://www.lazarusforum.de/viewtopic.php?f=18&t=11455) hatte ich mehrere Y-Achsen übereinander benötigt.
Dieses Beispiel würde ich gerne nochmal aufgreifen, und meine Frage ist, wie ich eine ausgewählte Serie (von vielen) bzgl. der Y-Achse mit dem Mausrad skalieren kann
die nicht ausgewählten Serien sollen dabei ihre Skalierung behalten.
Und eine weitere Frage: Kann ich die graphischen Einstellungen der Serien/Achsen/Transformationen ermitteln, damit ich Sie ggf. abspeichern
und aus einer Datei wieder herauslesen kann, damit der Benutzer wieder die gleiche Ansicht erhält, wie er Sie vor dem Verlassen der Applikation
eingestellt waren?
Vielen Dank und Gruß
Helios
entschuldige die sehr, sehr späte Rückmeldung (ich brauchte Zeit für die Umsetzung und für den Umstieg auf Lazarus 1.8.2).
Deine Vorschläge habe ich 1:1 umsetzen können und laufen "out of the box". Gigantisch! Vielen, vielen Dank!
Bei Deinem Hinweis:
- Natürlich, wenn mehrere Achsen vorhanden sind, wird's schwieriger...
hast Du sicherlich meine nächste Frage vorausgeahnt:-)
In dem Beispiel von meinem letzten Thread (https://www.lazarusforum.de/viewtopic.php?f=18&t=11455) hatte ich mehrere Y-Achsen übereinander benötigt.
Dieses Beispiel würde ich gerne nochmal aufgreifen, und meine Frage ist, wie ich eine ausgewählte Serie (von vielen) bzgl. der Y-Achse mit dem Mausrad skalieren kann
die nicht ausgewählten Serien sollen dabei ihre Skalierung behalten.
Und eine weitere Frage: Kann ich die graphischen Einstellungen der Serien/Achsen/Transformationen ermitteln, damit ich Sie ggf. abspeichern
und aus einer Datei wieder herauslesen kann, damit der Benutzer wieder die gleiche Ansicht erhält, wie er Sie vor dem Verlassen der Applikation
eingestellt waren?
Vielen Dank und Gruß
Helios
Re: TChart mit zusätzlichen Datatools + Axis Mouse Listener?
Das ist ein ziemlicher Brocken...
Alle die Chart-Extent-Tools (Zoom, Pan) orientieren sich am "Extent" des Chart, d.h. dem gesamten Achsenbereich zwischen Min und Max; Zoomen eines Teilbereichs ist nicht vorgesehen. Das heißt natürlich nicht, dass das nicht möglich ist - Man braucht ein UserDefined ChartTool, muss berechnen, auf welchem Achsenpanel sich die Maus befindet, wenn das Rad gedreht wird (OnAfterMouseWheelUp/Down, oder in OnBeforeMouseWheelUp/Doen) und dann muss man die Achsenkoordinaten, die auf die entsprechenden Panel-Grenzen (t.Minvalue, t.MaxValue) abgebildet werden, auseineinanderziehen bzw. zusammenstauchen - nur habe ich gerade keine Idee, wie man die Grenzen des sichtbaren Achsenbereichs ablegen kann ohne eine Riesenzusatzdatenstruktur anlegen zu müssen.
Auch für die zweite Frage, nach dem Speichern/Laden eines Chart, habe ich keine abschließende Antwort. Gemeint ist natürlich nicht das Speichern in Graphikformaten, sondern so wie in einer lfm-Datei. Hier würde ich den Streaming-Vorgang für Formularkomponenten kapern, das allgemeine Vorgehen ist im wiki beschrieben: http://wiki.freepascal.org/Streaming_co ... o_a_stream. Der folgende Code legt einen Chart als Ausschnitt einer lfm-Datei ab:
Wenn du dir diese Datei im Text-Editor ansiehst, fällt dir vielleicht auf, dass keine Series enthalten sind - klar, in deinem Beispielcode weiter oben ("ButtonAddClick") erhalten die Series beim Erzeugen den Owner "self", also das Formular; mein Code speichert aber nur den Chart ab. Es spricht aber nichts dagegeben, den Chart als Owner zu nehmen. Die Transformations habe ich noch nicht in die Datei schreiben können, eigentlich sollte es genauso funktionieren wie bei den Series, aber irgendwas scheint da anders zu sein.
Auch das Einlesen habe ich noch nicht hingekriegt. Du kannst es ja mal selbst versuchen - alles wesentliche müsste in dem genannten wiki-Artikel stehen.
Alle die Chart-Extent-Tools (Zoom, Pan) orientieren sich am "Extent" des Chart, d.h. dem gesamten Achsenbereich zwischen Min und Max; Zoomen eines Teilbereichs ist nicht vorgesehen. Das heißt natürlich nicht, dass das nicht möglich ist - Man braucht ein UserDefined ChartTool, muss berechnen, auf welchem Achsenpanel sich die Maus befindet, wenn das Rad gedreht wird (OnAfterMouseWheelUp/Down, oder in OnBeforeMouseWheelUp/Doen) und dann muss man die Achsenkoordinaten, die auf die entsprechenden Panel-Grenzen (t.Minvalue, t.MaxValue) abgebildet werden, auseineinanderziehen bzw. zusammenstauchen - nur habe ich gerade keine Idee, wie man die Grenzen des sichtbaren Achsenbereichs ablegen kann ohne eine Riesenzusatzdatenstruktur anlegen zu müssen.
Auch für die zweite Frage, nach dem Speichern/Laden eines Chart, habe ich keine abschließende Antwort. Gemeint ist natürlich nicht das Speichern in Graphikformaten, sondern so wie in einer lfm-Datei. Hier würde ich den Streaming-Vorgang für Formularkomponenten kapern, das allgemeine Vorgehen ist im wiki beschrieben: http://wiki.freepascal.org/Streaming_co ... o_a_stream. Der folgende Code legt einen Chart als Ausschnitt einer lfm-Datei ab:
Code: Alles auswählen
procedure TForm1.Button1Click(Sender: TObject);
var
stream: TFileStream;
begin
stream := TFileStream.Create('Chart.dat', fmCreate);;
try
WriteComponentAsTextToStream(stream, Chart);
finally
stream.Free;
end;
end;
Wenn du dir diese Datei im Text-Editor ansiehst, fällt dir vielleicht auf, dass keine Series enthalten sind - klar, in deinem Beispielcode weiter oben ("ButtonAddClick") erhalten die Series beim Erzeugen den Owner "self", also das Formular; mein Code speichert aber nur den Chart ab. Es spricht aber nichts dagegeben, den Chart als Owner zu nehmen. Die Transformations habe ich noch nicht in die Datei schreiben können, eigentlich sollte es genauso funktionieren wie bei den Series, aber irgendwas scheint da anders zu sein.
Auch das Einlesen habe ich noch nicht hingekriegt. Du kannst es ja mal selbst versuchen - alles wesentliche müsste in dem genannten wiki-Artikel stehen.
-
- Lazarusforum e. V.
- Beiträge: 107
- Registriert: Mi 29. Jun 2011, 22:36
- OS, Lazarus, FPC: Lazarus 2.2.6 Windows 10 64Bit / Lazarus 2.0.12 Debian 11.7 „Bullseye" 64Bit
- CPU-Target: 64Bit
- Wohnort: Leonberg
Re: TChart mit zusätzlichen Datatools + Axis Mouse Listener?
Hallo wp_xyz,
Danke für Deine Antwort zu noch so später Stunde.
Dann gibt es somit noch eine weites Feld für mich zum forschen und ausprobieren:-)
Schöne Restarbeitswoche!
Gruß und gute Nacht!
Helios
Danke für Deine Antwort zu noch so später Stunde.
Dann gibt es somit noch eine weites Feld für mich zum forschen und ausprobieren:-)
Schöne Restarbeitswoche!
Gruß und gute Nacht!
Helios