Difference between revisions of "TAChart Tutorial: Chart Tools/de"

From Lazarus wiki
Jump to navigationJump to search
 
(10 intermediate revisions by the same user not shown)
Line 92: Line 92:
 
[[file:ChartToolset Editor.png]]
 
[[file:ChartToolset Editor.png]]
  
Wenn man jetzt das Programm laufen lassen würde, dann würde das Zommen immer noch nciht funktionieren, denn das Werkzeug hat Eigenschaften <tt>ZoomFactor</tt> and <tt>ZoomRatio</tt>, die die Zoomstufen pro Mausrad-Raste definieren. Und diese Eigenschaften stehen immer noch auf ihrem Default-Wert <tt>1</tt>. Also: Setze <tt>ZoomFactor</tt> auf den Wert <tt>1.1</tt> und starte des Programm: Jetzt funktioniert das Zoomen.
+
Wenn man jetzt das Programm laufen lassen würde, dann würde das Zommen immer noch nicht funktionieren, denn das Werkzeug hat Eigenschaften <tt>ZoomFactor</tt> and <tt>ZoomRatio</tt>, die die Zoomstufen pro Mausrad-Raste definieren. Und diese Eigenschaften stehen immer noch auf ihrem Default-Wert <tt>1</tt>. Also: Setze <tt>ZoomFactor</tt> auf den Wert <tt>1.1</tt> und starte des Programm: Jetzt funktioniert das Zoomen.
  
Wenn die das Mausrad zu dir herdrehst, wird der Chart vergrößert, genauso wie wenn du den Bildschirm zu dir herziehen würdest. Viele Programme arbeiten anders herum: bei der genannten Drehung wird der Chart immer kleiner, wie wenn du dich vom Chart wegbewegen würdest. Wenn du dieses Verhalten bevorzugst, dann musst du den <tt>ZoomFactor</tt> auf eine Zahl kleiner als <tt>1</tt> setzen, zum Beispiel auf <tt>0.9</tt>. (Für den Rest des Tutorials werden wir aber beim Wert 1.1. bleiben).
+
Wenn die das Mausrad zu dir herdrehst, wird der Chart vergrößert, genauso wie wenn du den Bildschirm zu dir heranziehen würdest. Viele Programme arbeiten anders herum: bei der genannten Drehung wird der Chart immer kleiner, wie wenn du dich vom Chart wegbewegen würdest. Wenn du dieses Verhalten bevorzugst, dann musst du den <tt>ZoomFactor</tt> auf eine Zahl kleiner als <tt>1</tt> setzen, zum Beispiel auf <tt>0.9</tt>. (Für den Rest des Tutorials werden wir aber beim Wert 1.1. bleiben).
  
 
==== Einige Eigenschaften ====
 
==== Einige Eigenschaften ====
Line 102: Line 102:
 
Was ist <b><tt>FixedPoint</tt></b>? Wenn dieser Wert <tt>true</tt> ist, geht das Zoomen von der Position des Maus-Cursors aus. Wenn man also eine bestimmte Struktur im Chart vergrößern will, kann man hier die Maus auf diese Struktur bewegen und dann das Mausrad drehen. Bei <tt>false</tt> ist das Zoomen immer auf das Zentrum des Chart bezogen; die Position des Maus-Cursors spielt dann keine Rolle. In diesem Fall muss man aber sehr sorgfältig sein, denn man kann auf diese Weise leicht die Orientierung im Chart verlieren.
 
Was ist <b><tt>FixedPoint</tt></b>? Wenn dieser Wert <tt>true</tt> ist, geht das Zoomen von der Position des Maus-Cursors aus. Wenn man also eine bestimmte Struktur im Chart vergrößern will, kann man hier die Maus auf diese Struktur bewegen und dann das Mausrad drehen. Bei <tt>false</tt> ist das Zoomen immer auf das Zentrum des Chart bezogen; die Position des Maus-Cursors spielt dann keine Rolle. In diesem Fall muss man aber sehr sorgfältig sein, denn man kann auf diese Weise leicht die Orientierung im Chart verlieren.
  
==== Using the ExtentSizeLimit ====
+
==== Verwendung von ExtentSizeLimit ====
  
 
[[file:ZoomPan04.png|right]]
 
[[file:ZoomPan04.png|right]]
  
When you zoom ''out'' you will expose empty regions. Because the user can zoom out further and further the chart may eventually disappear. To prevent this we should restrict the axis ranges such that always data are displayed.
+
Wenn man aus einem Chart herauszoomt, wird der Chart immer kleiner, und es werden Bereiche sichtbar, die keine Daten mehr enthalten. Da der Benutzer beliebig weit herauszoomen kann, ist irgendwann der eigentliche Datenbereich des Charts nicht mehr erkennbar. Um das zu verhindern, sollten wir den Achsenbereich so begrenzen, dass auf jeden Fall immer Daten angezeigt werden.
  
For this purpose, TChart has a property '''<code>ExtentSizeLimit</code>'''. Our chart has an x extent from -10 to 10. So we set <code>ExtentSizeLimit.XMax</code> to the difference, 20, and activate this setting by <code>ExtentSizeLimit.UseXMax = true</code>. Now, we can no longer zoom out beyond the original ''x'' extent. Similarly, we should restrict the extent of the ''y'' axis to 2.
+
Zu diesem Zweck hat TAChart die Eigenschaft '''<tt>ExtentSizeLimit</tt>'''. Unser Chart erstreckt sich in der ''x''-Richtung zwischen <tt>-10</tt> und <tt>10</tt>. Daher setzen wir <tt>ExtentSizeLimit.XMax</tt> auf die Differenz <tt>20</tt> und aktivieren diese Einstellung mit <tt>ExtentSizeLimit.UseXMax = true</tt>. Jetzt können wir nicht mehr aus dem ursprünglichen ''x'' Bereich herauszoomen. Analog sollten wir auch den Bereich der ''y''-Achse auf <tt>2</tt> begrenzen.
  
In the same way, we could prevent to zoom ''into'' the chart beyond an x extent of, say, 0.01. For this we would set the properties <code>ExentSizeLimit.XMin</code> and <code>ExtentSizeLimit.UseXMin</code> accordingly.
+
Auf dieselbe Weise könnten wir verhindern, dass zu tief in den Chart hineingezoomt wird und die ''x''-Achse einen Bereich von weniger als z.B. <tt>0.01</tt> umfasst. Dafür würden wir entsprechend die Eigenschaften <tt>ExentSizeLimit.XMin</tt> und <tt>ExtentSizeLimit.UseXMin</tt> verwenden.
  
==== Horizontal and vertical zooming ====
+
==== Horizontales und vertikales Zoomen ====
  
Why are there two parameters that control the amount of zooming, '''<code>ZoomFactor</code>''' and '''<code>ZoomRatio</code>'''? This is because the tool can be used for non-proportional zooming: the ''x'' zoom factor is given by <code>ZoomFactor</code> alone while the ''y'' zoom factor is determined by the product <code>ZoomFactor*ZoomRatio</code>. As long as <code>ZoomRatio=1</code>, zooming occurs isotropically in all directions. When you set <code>ZoomFactor=1</code> and <code>ZoomRatio=1.1</code> we leave the ''x'' direction unchanged, but zoom only along the ''y'' direction. Or, if we set <code>ZoomRatio = 1/ZoomFactor</code> zooming occurs only horizontally along the ''x'' axis.
+
Warum eigentlich gibt es zwei Parameter für die Kontrolle des Zoomens, '''<tt>ZoomFactor</tt>''' und '''<tt>ZoomRatio</tt>'''? Der Grund dafür ist, dass man auf diese Weise einen nicht-proportionalen Zoom-Effekt erzeugen kann, so dass sich z.B. der Chart nur in der horizontalen Richtung verkleinert, ohne sich in der vertikalen Dimension zu verändern. Um das zu verstehen, betrachten wir die ''x'' und ''y'' Richtungen getrennt. Der Zoom-Faktor für die ''x''-Richtung wird durch den Wert von <tt>ZoomFactor</tt> allein definiert, während der Zoom-Faktor für die ''y''-Richtung durch das Produkt <tt>ZoomFactor*ZoomRatio</tt> bestimmt wird. Solange <tt>ZoomRatio=1</tt> ist, geschieht das Zoomen isotrop in alle Richtungen. Wenn wir <tt>ZoomFaktor=1</tt> und <tt>ZoomRatio=1.1</tt> setzen, lassen wir die ''x''-Richtung unverändert, aber zoomen nur in ''y''-Richtung. Oder umgekehrt, wenn wir <tt>ZoomRatio = 1/ZoomFactor</tt> setzen, erfolgt das Zoomen nur entlang der ''x''-Richtung.
  
Hey -- these would be nice features for our program! When we look at the keyboard the {{keypress|SHIFT}}, {{keypress|Ctrl}} and {{keypress|Alt}} keys form some kind of coordinate system: {{keypress|Ctrl}} is the origin, {{keypress|SHIFT}} is the ''y'' direction, {{keypress|Alt}} is the ''x'' direction. So we could assign the {{keypress|SHIFT}} key to vertical zooming, the {{keypress|Alt}} key to horizontal zooming, and "no key" to isotropic zooming -- that's easy to remember.  
+
Hey -- das wäre eine schöne Bereicherung für unser Programm! Wenn wir uns auf der Tastatur die Lage der Tasten {{keypress|SHIFT}}, {{keypress|Ctrl}} and {{keypress|Alt}} ansehen, dann erinnern diese an die Achsen eines Koordinatensystems: {{keypress|Ctrl}} ist der Ursprung, {{keypress|SHIFT}} stellt die ''y''- und {{keypress|Alt}} die ''x''-Richtung dar. Daher könnten wir der {{keypress|SHIFT}}-Taste das vertikale Zoomen und der {{keypress|Alt}}-Taste das horizontale Zoomen zuordnen; und "keine Taste" würde dem Fall des isotropen Zoomens entsprechen -- das wäre eine gute "Eselsbrücke", um sich die Tastenkombinationen merken zu können.
  
How can we implement this feature? Add two more ZoomMouseWheelTools to the form. The first one will do the vertical zoom, so name it <code>ChartToolset1ZoomMousewheelTool_vert</code>, select <code>ssShift</code> in the <code>Shift</code> property, and set <code>ZoomRatio = 0.90909090909</code> (which is approximately equal to 1/1.1). The other tool will be for the horizontal zoom, name it
+
Wie kann man das implementieren? Setze zwei ZoomMouseWheelTools auf das Formular. Das erste ist für das vertikale Zoomen zuständig. Daher nenne es <tt>ChartToolset1ZoomMousewheelTool_vert</tt>, weise ihm dem <tt>Shift</tt>-Wert <tt>ssShift</tt> zu und setze <tt>ZoomRatio = 1.1</tt> (lasse <tt>ZoomFactor</tt> auf <tt>1</tt>). Das andere Tool soll für das horizontale Zoomen sorgen. Nenne es <tt>ChartToolset1ZoomMousewheelTool_hor</tt>, setze <tt>Shift</tt> auf <code>ssAlt</code> und <tt>ZoomFactor = 1.1</tt> und <tt>ZoomRatio = 0.90909090909</tt> ((das ist ungefähr gleich <tt>1/1.1</tt>).
<code>ChartToolset1ZoomMousewheelTool_hor</code>, set <code>Shift</code> to <code>ssAlt</code>, and set <code>ZoomFactor = 1.1</code> and <code>ZoomRatio = 0.90909090909</code>.
 
  
When you run the program you may notice that zooming stops working after you have pressed the {{keypress|Alt}} key. This is caused by the fact that the {{keypress|Alt}} key plays a special role for menu key handling. To reactivate the program you have to click into the chart.
+
Wenn du das Programm laufen lässt, bemerkst du vielleicht, dass das Zoomen nicht mehr funktioniert, nachdem du die {{keypress|Alt}}-Taste gedrückt hast. Das liegt daran, dass diese Taste auch für die Behandlung von Menü-Tasten verwendet wird. Klicke einfach in den Chart, um das Zoom-Tool zu reaktivieren. Oder wähle eine andere Aktivierungstaste für das horizontale Zoom-Tool.
Or select another activation key for the horizontal zoom tool.
 
  
== Panning by the mouse-wheel ==
+
== Verschieben mit dem Mausrad ==
  
Let's move on the panning. Why don't we use the mousewheel also for panning? Here, panning can go only in one direction -- either ''x'' or ''y''. So let us add two <code>TPanMouseWheelTool</code>s and replace numbers at the end of their names by "_vert" and "_hor". The tool for vertical panning should be activated by the {{keypress|SHIFT}} key again, the tool for horizontal panning by the {{keypress|Alt}} key.
+
Machen wir weiter mit den Verschieben ("Panning"). Warum eigentlich verwenden wir nicht das Mausrad auch für diesen Zweck? Das Verschieben soll nur entlang der Achsenrichtungen möglich sein, also entweder in ''x''- oder in ''y''-Richtung. Daher fügen wir zwei <tt>TPanMouseWheel</tt>-Komponenten zum <tt>ChartToolset</tt> hinzu und ersetzen im Komponenten-Namen die automatisch angehängten Nummern durch "_hor" und "_vert". Das Tool für das vertikale Verschieben soll wieder mit der {{keypress|Umsch}} Taste, das für das horizontale Veschieben mit der {{keypress|Alt}} Taste aktiviert werden.
  
There is a property <code>WheelUpDirection</code> which defaults to <code>pdUp</code>. This means that scrolling of the mouse-wheel is translated to a vertical panning direction. This setting is not correct for the horizontal panning tool, select <code>pdRight</code> or <code>pdLeft</code> instead.
+
Zur Festlegung der Verschiebungsrichtung gibt es eine Eigenschaft <tt>WheelUpDirection</tt> mit Default-Wert <tt>pdUp</tt>. Das bedeutet, dass die Drehung am Mausrad in vertikales Verschieben übersetzt wird. Diese Einstellung muss beim Werkzeug für die horizontale Verschiebung auf <tt>pdRight</tt> or <tt>pdLeft</tt> geändert werden.
  
Run the program. When you rotate the mouse wheel with {{keypress|SHIFT}} or {{keypress|Alt}} down the chart zooms, but does not pan. What's wrong?
+
Starte des Programm. Wenn du am Mausrad mit gedrückter {{keypress|Umsch}} oder {{keypress|Alt}} Taste drehst, verschiebt sich der Chart aber nicht, sondern verändert sein Größe wie beim Zoomen... Was ist falsch?
  
The reason is that we are using the same <code>Shift</code> settings for the zooming and panning tools. Well, we could use, for example, the {{keypress|Ctrl}} key and assign it additionally to the panning tools. Then vertical panning, for example, would occur by pressing {{keypress|Ctrl}}+{{keypress|SHIFT}}, vertical zooming would occur with {{keypress|SHIFT}} alone.
+
Das liegt daran, dass wir für die Zoom- und Verschiebungstools dieselben <tt>Shift</tt>-Einstellungen verwenden. Als Ausweg könnten wir bei den Verschiebungstools zusätzlich auch noch die Einstellung {{keypress|Strg}} zu den <tt>Shift</tt>-Einstellungen hinzufügen. Damit würde das vertikale Verschieben zum Beispiel mit der Tastenkombination {{keypress|Strg}}+{{keypress|Umsch}} aktiviert, das vertikale Zoomen dagegen würde nur {{keypress|Umsch}} allein benötigen.
  
Let's go another way here to demonstrate usage of the <code>Enabled</code> property. Add a toolbar to the form with two buttons to activate either zooming or panning. Rename the first button to <code>ZoomToolbutton</code> and set <code>Down</code> to <code>true</code>. Rename the second button to <code>PanToolbutton</code>. For both buttons, set the following properties:
+
Das ist in der Bedienung aber etwas umständlich. Daher zeigen wir hier einen anderen Ansatz, auch um die Verwendung der Eigenschaft <tt>Enabled</tt> zu demonstrieren. Füge dem Formular eine <tt>Toolbar</tt> mit zwei <tt>ToolButtons</tt> hinzu, die entweder das Zoomen oder das Verschieben einschalten sollen. Nenne den ersten Button <tt>ZoomToolbutton</tt> und setze sein <tt>Down</tt>-Eigenschaft auf <tt>true</tt>. Nenne den zweiten Button entsprechend <tt>PanToolbutton</tt>, aber lasse sein <tt>Down</tt> auf <tt>false</tt>. Außerdem setze bei beiden Schaltern die folgenden Eigenschaften:
  
* <code>Grouped = true</code>
+
* <tt>Grouped = true</tt>
* <code>Style = tbsCheck</code>
+
* <tt>Style = tbsCheck</tt>
  
and assign their <code>OnClick</code> to the following event handler:
+
und weise ihrem <tt>OnClick</tt>-Ereignis den folgenden Code zu:
  
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
Line 153: Line 151:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
This enables either the zooming or the panning tools, depending on which toolbutton is down. To synchronize the chart tools' <code>Enabled</code> with the buttons' <code>Down</code> you should disable both panning tools (or call <code>ZoomPanToolbuttonClick(nil)</code> in the <code>OnCreate</code> event of the form).
+
Das schaltet entweder die Zoom- oder die Verschiebungs-Tools ein, je nachdem welcher <tt>ToolButton</tt> gedrückt ist. Damit die <tt>Enabled</tt>-Einstellungen sich in den <tt>Down</tt>-Eigenschaften der <tt>ToolButtons</tt> widerspiegeln, sollten wir noch die beiden Verschiebungs-Tools ausschalten (<tt>Enabled = false</tt>) -- oder die Methode <tt>ZoomPanToolbuttonClick(nil)</tt> im <tt>OnCreate</tt> Ereignis des Formulars aufrufen.
  
Some observations when you run the program:
+
Einige Beobachtungen, wenn das Programm läuft:
  
* It's a matter of taste, but maybe you feel that the vertical zoom direction is '''opposite''' to the direction the mouse wheel is rotated. You can change this by setting the <code>WheelUpDirection</code> of the vertical panning tool to <code>pdDown</code>.  
+
* Es ist Geschmackssache, aber vielleicht bist du auch der Meinung, dass sich der gezoomte Chart-Ausschnitt entgegengesetzt zu der Richtung verschiebt, in der das Mausrad gedreht wird? Das kann man ändern, indem die Eigenschaft <tt>WheelUpDirection</tt> des vertikalen Zoom-Tools auf <tt>pdDown</tt> umgestellt wird.  
* '''Empty chart regions without data''' can become visible again. This is because the <code>ExtentSizeLimit</code> checks only the width and/or height of the extent. To take care of this the PanMousewheelTool has a set of properties <code>LimitToExtent</code>. Activate all options to disallow horizontal and vertical panning beyond the original extent with data.
+
* Chart-Bereiche ohne Daten können wieder sichtbar werden. Das liegt daran, dass die <tt>ExtentSizeLimit</tt>-Eigenschaft nur für die Prüfung der Breite und/oder Höhe des Charts zuständig ist. Das kann mit Hilfe der Eigenschaften <tt>LimitToExtent</tt> des <tt>PanMouseWheelTool</tt> geändert werden: Aktiviere alle Optionen, um vertikales und horizontales Verschieben über den ursprünglichen Datenbereich hinaus zu verbieten.
* Maybe you want to change the '''speed''' of panning. This can be done by adapting the <code>Step</code> property.
+
* Vielleicht möchtest du die Geschwindigkeit des Verschiebens verändern? Passe hierzu den Wert der Eigenschaft <tt>Step</tt> an.
* You may also notice that it is quite cumbersome to restore the original extent of the chart. It would be fine to have a button ''"Reset"'' in the toolbar which undoes any zooming and panning operations. That's easy: add a third button to the toolbar, name it <code>RestoreToolbutton</code>, set its <code>Caption</code> to ''"Reset"'', and assign its <code>OnClick</code> event to the following procedure:
+
* Du findest wahrscheinlich auch, dass es recht umständiglich ist, den ursprünglichen, nicht verschobenen Chart-Bereich wiederherzustellen. Das könnte man mit einem Schalter ''"Reset"'' in der Toolbar vereinfachen, der jede Zoom-/Verschiebungs-Operation rückgängig macht. Das ist leicht: Platziere einen dritten Button auf die <tt>Toolbar</tt>, nenne ihn <tt>RestoreToolbutton</tt>, schreibe als seine <tt>Caption</tt> den Text ''"Reset"'' und weise seinem <tt>OnClick</tt>-Ereignis den folgenden Code zu:
  
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
Line 169: Line 167:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
The <code>ZoomFull</code> procedure restores the chart extent to its original value. Here's what our program looks like now:
+
Der Aufruf von <tt>ZoomFull</tt> setzt die Achsen wieder auf die ursprünglichen Grenzen zurück. Hier ist nun, wie unser Programm aktuell aussieht:
  
 
[[file:ZoomPan04a.png]]
 
[[file:ZoomPan04a.png]]
  
== Reading data from the series ==
+
== Datenwerte aus der Datenreihe auslesen ==
  
You can add more zooming and panning tools to the toolset to get a practical user interface. But we will stop here with the extent tools and move on to the data tools.
+
Man könnte noch weitere Zoom- und Verschiebungs-Werkzeuge ins Toolset aufnehmen und so eine praktische Benutzeroberfläche aufbauen. Aber das wollen wir hier nicht weiter vertiefen, sondern uns nun den Daten-Tools zuwenden.
  
=== Using a TDataPointClickTool ===
+
=== Ein TDataPointClickTool verwenden ===
  
Our task is to read the x,y coordinates of the series underneath the mouse cursor. A neat tool to achieve this is the <code>TDatapointClickTool</code>. Add it to the toolset. Set <code>Shift</code> to <code>ssLeft</code> to activate it by a left button click. And add a <code>TStatusbar</code> to the form where we will display the requested information.  
+
Stellen wir uns die Aufgabe, die <tt>x,y</tt>-Koordinaten von Datenpunkten unter dem Maus-Cursor auszulesen. Ein dafür geeignetes Werkzeug ist das <tt>TDatapointClickTool</tt>. Füge es zum Toolset hinzu. Setze <tt>Shift</tt> auf <tt>ssLeft</tt>, um es durch einen Klick mit der linken Maustaste zu aktivieren. Und füge dem Formular eine <tt>TStatusbar</tt> hinzu, auf der wir die erhaltenen Daten anzeigen wollen.
  
Whenever a click occurs on (or sufficiently near) a data point the relevant data are stored in the tool. For example, there is a <code>Series</code> property which identifies onto which series the click occurred. Similarly, there is a property <code>PointIndex</code> which tells the index of the hit point within its series. The tool provides an <code>OnPointClick</code> event which fires when a point is clicked and from where we can query the requested information. We can use the following event handler to extract the information for displaying in the status bar:
+
Immer wenn ein Mausklick auf einem Datenpunkt erfolgt (oder ausreichend nahe), werden die relevanten Daten über den Datenpunkt im Tool gespeichert. Zum Beispiel gibt es eine Eigenschaft <tt>Series</tt>, die festhält, auf welcher Datenreihe der Klick erfolgt ist. Entsprechend gibt es auch eine Eigenschaft <tt>PointIndex</tt>, über die man den Index des angeklickten Punktes innerhalb der Datenreihe erfährt. Weiterhin stellt das Werkzeug ein Ereignis <tt>OnPointClick</tt> zur Verfügung; es wird ausgelöst, wenn auf einen Datenpunkt geklickt wurde, und wir können damit die gewünschte Information auslesen. Der folgende Ereignis-Behandlungscode stellt die ausgelesene Information in der Statuszeile dar:
  
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
Line 201: Line 199:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
The parameter <code>ATool</code> passed to the event is rather general, thus we have to type-cast it to <code>TDatapointClickTool</code> to get access to the mentioned properties. Similarly, the <code>Series</code> is of a very basic type as well, another type-cast is needed. Be careful with type-casts, make sure that the class types are correct. Therefore, we check the types by means of <code>is</code>.
+
Der Parameter <tt>ATool</tt>, der dem Ereignis mitgegeben wird, hat einen sehr allgemeinen Typ. Daher müssen wir eine Typ-Umwandlung in ein <tt>TDatapointClickTool</tt> vornehmen, um an die gewünschte Information zu gelangen. Genauso hat die vom Tool gespeicherte Eigenschaft <tt>Series</tt> einen sehr allgemeinen Typ, so dass auch hier eine Typumwandlung erforderlich ist. Aber sei vorsichtig bei Typumwandlungen, versichere dich immer mit Hilfe des Operators <tt>is</tt>, ob die Typen richtig sind.
  
When you run the program and click on a data point you will see its coordinates along with the title of its series in the status bar.
+
Wenn wir nun das Programm starten und auf einem Datenpunkt klicken, bekommen wir die Koordinaten des Punktes zusammen mit dem Titel der Datenreihe in der Statuszeile angezeigt.
  
 
[[file:ZoomPan05.png]]
 
[[file:ZoomPan05.png]]
  
=== Showing permanent labels ===
+
=== Anzeigen von permanenten Labels ===
  
Unfortunately, there is no indication where exactly the click occurred. It would be better if we could display a label above the clicked point to show the requested information.
+
Leider kann man nicht feststellen, wo genau der Klick erfolgt ist. Es wäre besser, wenn wir über dem angeklickten Datenpunkt eine Markierung ("Label") mit der gewünschten Information anzeigen könnten.
  
Here we can take advantage of the fact that the series in our chart get their data from  <code>TListChartSource</code>s. They have no special components on the form, because this is the default behavior, and each <code>TChartSeries</code> (from which <code>TLineSeries</code> inherits) has a non-visual built-in listsource which can be accessed by a (public) property <code>ListSource</code>.
+
Hierzu verwenden wir die Tatsache, dass die Datenreihen im Chart ihre Daten jeweils aus einer <tt>TListChartSource</tt> beziehen. Auf dem Formular gibt es dafür keine spezielle Komponente, weil der Datenreihentyp <tt>TChartSeries</tt>, von dem sich <tt>TLineSeries</tt> ableitet, diese als eingebautet Datenquelle enthält und über die öffentliche Eigenschaft <tt>ListSource</tt> zugänglich macht.
  
Why is this important? Because the items stored in a listsource provide extra storage for an additional text:
+
Warum ist das wichtig? Weil die in der <tt>ListSource</tt> gespeicherten Einträge vom Typ <tt>TChartDateItem</tt> im Feld <tt>Text</tt> Speicherplatz für zusätzlichen Text vorhalten:
  
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
Line 224: Line 222:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
To modify the <code>Text</code> assigned to a data point at a given <code>index</code> you can call
+
Um den Inhalt des <tt>Text</tt>-Feldes zu verändern, der einem Datenpunkt mit vorgegebenem <tt>Index</tt> zugewiesen ist, verwenden wir folgenden Code:
  
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
Line 230: Line 228:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
or, in later versions of Lazarus, there is a <code>SetText</code> method for abbreviation:
+
In neueren Lazarus-Versionen gibt es dafür zur Vereinfachtung auch die Methode <tt>SetText</tt>:
  
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
Line 236: Line 234:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
(You can learn more about list sources from [[TAChart_Tutorial:_ListChartSource,_Logarithmic_Axis,_Fitting#ListChartSource| the tutorial on them]].)
+
(Du kannst mehr über <tt>TChartListSource</tt> in dem [[TAChart_Tutorial:_ListChartSource,_Logarithmic_Axis,_Fitting#ListChartSource|betreffenden Tutorial]] erfahren.)
  
Since we did not use this feature all <code>Text</code> entries are empty. But when we click onto a data point, we can transfer the coordinate info into the <code>Text</code> member of the ChartDataItem by adding the indicated line to our <code>OnPointClick</code> event handler of the datapoint click tool (BTW, we don't need the status bar any longer, so delete it and remove the corresponding code from the event handler):
+
Zu Beginn sind alle <tt>Text</tt> Einträge leer. Aber wenn wir auf einen Datenpunkt geklickt haben, transferieren wir die Koordinaten-Information in das <tt>Text</tt>-Feld des zugehörigen <tt>ChartDataItem</tt>, indem wir die obige Code-Zeile in die die <tt>OnPointClick</tt>-Ereignisbehandlungsroutine des <tt>DataPointClickTool</tt>s einbauen. Die Statuszeile, übrigens, benötigen wird nicht mehr - lösche sie und entferne den entsprechenden Code aus der Ereignisbehandlungs-Routine:
  
 
<syntaxhighlight lang=pascal>
 
<syntaxhighlight lang=pascal>
Line 251: Line 249:
 
         x := GetXValue(PointIndex);
 
         x := GetXValue(PointIndex);
 
         y := GetYValue(PointIndex);
 
         y := GetYValue(PointIndex);
         { --- next line removed --- }
+
         { --- die nächste Zeile wurde entferne --- }
 
//        Statusbar1.SimpleText := Format('%s: x = %f, y = %f', [Title, x, y]);
 
//        Statusbar1.SimpleText := Format('%s: x = %f, y = %f', [Title, x, y]);
         { --- next line added --- }
+
         { --- die nächste Zeile wirde hinzugefügt --- }
 
         ListSource.Item[PointIndex]^.Text := Format('x = %f'#13#10'y = %f', [x,y]);
 
         ListSource.Item[PointIndex]^.Text := Format('x = %f'#13#10'y = %f', [x,y]);
 
         ParentChart.Repaint;  
 
         ParentChart.Repaint;  
         // in newer Lazarus versions you can use (which already contains the Repaint):
+
         // in neueren Lazarus-Versionen kann man stattdessen auch die folgende Zeile verwenden (die schon das Repaint enthält):
 
         // ListSource.SetText(PointIndex, Format('x = %f'#13#10'y = %f', [x,y]));
 
         // ListSource.SetText(PointIndex, Format('x = %f'#13#10'y = %f', [x,y]));
 
       end;
 
       end;
Line 262: Line 260:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Now we have to make sure that the labels are displayed. For this purpose, the <code>TChartSeries</code> which is an ancestor of <code>TLineSeries</code> has a property <code>Marks</code>. In the sub-properties you find the option <code>Style</code> which is set to <code>smsNone</code> by default, meaning that no labels are displayed. You see in the object inspector that there is a variety of information than can be displayed in the marks, but you'll need here the option <code>smsLabel</code> which shows the text of the ChartDataItems. Another modification may be useful: Set <code>LinkPen.Color</code> to some darker color, otherwise the connecting line between label and data point will not be visible on the white background. You may also want to play with some visual enhancements like <code>CalloutAngle</code> or <code>Shape</code>.
+
Nun müssen wir uns darum kümmern, dass die Labels auch angezeigt werden. Zu diesem Zweck hat <tt>TChartSeries</tt>, der Vorfahr von <tt>TLineSeries</tt>, die Eigenschaft <tt>Marks</tt>. In deren Unter-Eigenschaften findet sich die Option <tt>Style</tt>, die standardmäßig auf <tt>smsNone</tt> gesetzt ist -- das bedeutet, dass Labels in der Grundeinstellung gar nicht dargestellt werden. Im Objekt-Inspektor sieht man, dass <tt>Style</tt> eine Vielzahl von Einstellmöglichkeiten bietet. Wir brauchen hier die Option <tt>smsLabel</tt>, die die Ausgabe des <tt>Text</tt>-Labels bewirkt. Eine weitere sinnvolle Änderung wäre, <tt>LinkPen.Color</tt> auf eine dunklere Farbe zu setzen, andernfalls ist die Verbindungslinie zwischen Label und Datenpunkt auf dem weißen Untergrund nicht zu erkennen. Vielleicht spielst du auch mit den anderen Einstellmöglichkeiten, wie <tt>CalloutAngle</tt> oder <tt>Shape</tt>.
  
You have to do the same changes for all three series in the chart.
+
Dieselben Änderungen müssen an allen drei Datenreihen des Chart vorgenommen werden.
  
When you run the project and click on the series you get nice permanent labels. Please note that the markers stay in place even if you change the viewport by zooming or panning.
+
Wenn du nun das Projekt startest und auf Datenpunkten klickst, bekommst du schöne permantente Labels. Beachte, dass die Markierungen erhalten bleiben, selbst wenn man den sichtbaren Chart-Ausschnitt durch Zoomen oder Verschieben verändert.
  
 
[[file:ZoomPan06.png]]
 
[[file:ZoomPan06.png]]
  
=== Removing permanent labels ===
+
=== Permanente Labels entfernen ===
  
Once in a while you may find that you have clicked at the wrong point and you may want to remove a label.
+
Gelegentlich wirst du feststellen, dass du einen falschen Datenpunkt angeklickt hast, und willst deshalb das Label entfernen.
  
The basic idea for this feature is as follows: add another <code>TDatapointClickTool</code>, assign it to the right mouse button and, in the <code>OnDatapointClick</code> event handler, we remove the text from the clicked ChartDataItem. Try doing it yourself -- you should now know all the necessary steps.
+
Die grundlegende Idee dafür ist folgendermaßen: füge ein weiteres <tt>TDatapointClickTool</tt> hinzu, weise es der rechten Maustaste zu und entferne in der <tt>OnDatapointClick</tt>-Ereignisbehandlung den Text von dem <tt>ChartDataItem</tt>. Versuche selbst, den Code zu schreiben -- du solltest jetzt alle nötigen Schritte kennen.
  
== Related tutorials ==
+
== Ähnliche Tutorials ==
  
* [[TAChart_Tutorial:_Getting_started|TAChart Tutorial: Getting started]]: explains how the chart used here is created.
+
* [[TAChart_Tutorial:_Getting_started|TAChart Tutorial: Getting started]]: erklärt, wie der hier verwendete Chart erzeugt wurde.
* [[TAChart_Tutorial:_ListChartSource,_Logarithmic_Axis,_Fitting|TAChart Tutorial: ListChartSource, Logarithmic Axis, Fitting]]: discusses usage of series <code>Marks</code>.
+
* [[TAChart_Tutorial:_ListChartSource,_Logarithmic_Axis,_Fitting|TAChart Tutorial: ListChartSource, Logarithmic Axis, Fitting]]: diskutiert die Verwendung der <tt>Marks</tt> von Datenreihen.
* [[TAChart_Tutorial:_ColorMapSeries,_Zooming|TAChart Tutorial: ColorMapSeries, Zooming]]: demonstrates other aspects of zooming and panning, like using the ZoomDragTool, PanDragTool and setting up a zoom history.
+
* [[TAChart_Tutorial:_ColorMapSeries,_Zooming|TAChart Tutorial: ColorMapSeries, Zooming]]: demonstriert weitere Aspekte des Zoomens und Verschiebens wie die Verwendung des ZoomDrag- und PanDrag-Tools, sowie die Erstellung eines Zoom-Verlaufs.
  
== Source code ==
+
== Quell-Code ==
  
 
=== project.lpr ===
 
=== project.lpr ===

Latest revision as of 11:14, 18 October 2022

Deutsch (de) English (en)

Einführung

ZoomPan06.png

Charts sind leistungsfähige Werkzeuge, um Beziehungen zwischen Daten darzustellen. Sie sind besonders mächtig, wenn der Benutzer mit ihnen interaktiv arbeiten kann, zum Beispiel:

  • in eine Datenreihe mit sehr, sehr vielen Datenpunkten hineinzoomen, um mehr Details zu sehen,
  • den sichtbaren Ausschnitt verschieben,
  • Anzeigen von Datenwerten für die Datenreihen, oder Bestimmen von charakterischen Parametern aus dem Plot.

Das TAChart Package ist mit einer leistungsfähigen Sammlung von Werkzeugen ausgestattet, die hilfreich sind für die Erstellung interaktiver Charts. In diesem Tutorial wollen wir zeigen, wie man diese Werkzeuge anwenden kann.

Wenn du nicht mit TAChart vertraut bist, empfehlen wir dir, einen Blick auf das "Getting started"-Tutorial zu werfen. Natürlich musst du grundlegende Erfahrung im Umgang mit Lazarus und FPC haben.

Vorbereitung

Zunächst brauchen wir einen Chart, mit dem wir spielen können. Statt einen Chart neu zu erzeugen, verwenden wir den im Tutorial Getting started erzeugten einfachen Chart. In diesem Projekt hatten wir mit Hilfe von TLineSeries-Komponenten mathematischen Funktionen für x zwischen -10 and 10 graphisch dargestellt. Die Datenwerte wurden in der internen ListChartSource jeder Datenreihe gespeichert. Kopiere die Projekt-Dateien (project1.lpi, project1.lpr, unit1.pas, unit1.lfm) in einen separaten Ordner für das neue Tutorial.

tachart getting started step6.png

Zoomen und Verschieben - der leichte Weg

Zoomen

Das Erzeugen einer Ausschnittsvergrößerung eines Charts wollen wird mit dem englischen Wort "Zoomen" bezeichnen. Das ist ein ganz einfacher Vorgang -- man muss dafür keine einzige Zeile Code schreiben, denn diese Möglichkeit ist schon intern in der TChart-Komponente eingebaut. Der Benutzer muss nur mit gedrückter linker Maus-Taste um das zu vergrößernde Detail herum ein Rechteck aufziehen. Wichtig ist, dass die Maus von der Ecke links/oben zur Ecke rechts/unten gezogen werden muss, um den Zoom-Effekt zu erhalten. Wenn die Maustaste losgelassen wird, wird der Inhalt des Rechtecks vergrößert und füllt dann den kompletten Chart aus. Einfach.

ZoomPan01.png ZoomPan02.png

Man kann diesen Vorgang wiederholen, um tiefer und tiefer in den Chart hineinzuzoomen.

Um zur originalen, unvergrößerten Ansicht zurückzukehren, klickt man einfach mit der linken Maustaste irgendwo in den Chart oder zieht die Maus mit gedrückter linker Taste in beliebige Richtung - natürlich nicht von links/oben nach rechts/unten, denn das würde weiter hineinzoomen.

Falls du aus irgendeinem Grund das Zoomen verbieten möchtest, kannst du die TChart-Eigenschaft AllowZoom auf False schalten. Darüber hinaus gibt es es keine Möglichkeit, das eingebaute Zoom-Verhalten zu beeinflussen. Aber nicht verzagen: etwas weiter unten diskutieren wir die Chart-Tools, und damit hat man fast unbegrenzte Möglichkeiten, mit dem Chart zu interagieren.

Verschieben

Nachdem du in den Chart hineingezoomt hast, willst du vielleicht den Ausschnitt etwas verschieben, um eine bessere Ansicht zu finden. Dieses Verschieben wird auf Englisch als "Panning" bezeichnet. Auch hier gibt es in TChart-Komponente schon eine eingebaute Methode: Verschiebe die Maus mit gedrückter rechter Maustaste, und der sichtbare Ausschnitt folgt der Bewegung der Maus. Weitere Kontrollmöglichkeiten hat man mit Hilfe der Chart-Tools.

ZoomPan03.png

Genauso wie beim Zoomen kann man auch hier den Originalausschnitt wiederherstellen, indem man mit der Maus irgendwo in den Chart klickt, oder die Maus mit gedrückter linker Taste in beliebige Richtung zieht (außer von links/oben nach rechts/unten). Bitte beachte, dass man hier die linke Maustaste verwendet, obwohl das Verschieben mit der rechten Maustaste erfolgt ist.

Seit Lazarus v2.1+ hat TChart eine Eigenschaft AllowPanning mit der man das eingebaute Verschieben unterbinden kann. Wie wir aber gleich sehen werden, kann man das Verschieben auch mit Hilfe der Chart-Tools ausschalten, so dass ältere Lazarus-Versionen hier keinen Nachteil haben.

Grundlegendes über die Chart-Tools

Was sind die Chart-Tools?

Wie du gesehen hast, kann die Programmierung der Zoom- und Panning-Möglichkeiten nicht einfacher sein -- man muss gar nichts tun, diese Möglichkeiten sind schon vorhanden. Aber andererseits sind die eingebauten Routinen recht eingeschränkt: Zoomen funktioniert nur mit Hilfe der linken Maustaste, Verschieben nur mit Hilfe der rechten, es gibt keine Unterstützung des Mausrades, das Zoomen/Verschieben kann nicht auf die horizontale oder vertikale Richtung beschränkt werden, Datenwerte könnten nicht ausgelesen werden, usw, usf.

Für vielseitige Möglichkeiten der Benutzerinteraktion wurden mehrere Chart-Tools der TAChart-Bibliothek hinzugefügt. Jedes Werkzeug ist für eine bestimmte Aufgabe spezialisiert. Aus Benutzersicht gibt es zwei Typen von Werkzeugen: Wertebereichs-Tools und Daten-Tools.

Eigenschaften der Chart-Tools

All diese Werkzeuge haben eine Eigenschaft namens Shift, die eine Menge von "Tasten-Codes" enthält, mit denen das Tool aktiviert werden kann. Dies sind die in der LCL standardmäßig vorhandenen Elemente der Aufzählung TShiftState.

Ein Beispiel: Wenn die Shift-Eigenschaft des Tools die Elemente ssCtrl und ssLeft enthält, dann wird das Tool aktiv, wenn der Benutzer die Ctrl-Taste und die linke Maustaste drückt. Beachte, dass alle Bedingungen erfüllt sein müssen, das Werkzeug wird nicht aktiv, falls nur die linke Maustaste allein gedückt wird. Jedes Werkzeug muss seine eigene, eindeutige Kombination von Shift-Kombinationen haben.

Eine Bemerkung über die Shift-Werte, die der Tastatur zugeordnet sind: Um Nachrichten über Tastaturereignisse erhalten zu können, muss der Chart fokussiert sind. Falls Werkzeuge nicht wie erwartet funktionieren, versuche, Chart.SetFocus aufzurufen; dadurch wird der Chart automatisch fokussiert, wenn der Benutzer die Maus in den Chart bewegt. Der Nachteil der AutoFocus-Methode darf aber nicht unerwähnt bleiben: wenn der Benutzer gerade Text z.B. in ein Memo eintippt, die Maus über den Chart bewegt und dann weitertippen möchte, hat das Memo den Fokus verloren, und nachfolgende Tastendrücke werden ignoriert...

Es gibt eine weitere Bedingung, die erfüllt sein muss, damit ein Tool aktiv wird: Seine Eigenschaft Enabled muss auf true gesetzt sein (aber das ist die Default-Einstellung). Diese Eigenschaft ist nützlich, wenn mehrere Tools sich dieselben Shift-Kombinationen teilen und durch anderen Code aktiviert werden sollen, z.B. durch Klicken auf Buttons auf einer Toolbar.

Alle Werkzeuge haben verschiedene Ereignisse gemeinsam, die erzeugt werden, bevor oder nachdem eine Taste oder eine Maus-Taste gedrückt oder losgelassen wurde. Bei der Maus werden auch Ereignisse ausgelöst, bevor und nachdem die Maus bewegt wurde. Normalerweise funktionieren die Chart-Tools, ohne dass diese Ereignisse behandelt werden. Aber sie sind nötig, wenn zusätzliche Funktionalität bereitgestellt werden soll. Wir werden dazu weiter unten in diesem Tutorial ein Beispiel finden.

TChartToolset

TChartToolset-Icon2.png

Alle Chart-Tools sind einer speziellen Komponenten zusammengeführt, TChartToolset. Sie befindet sich auf der Komponentenpalette irgendwo in der Mitte der Chart-Seite und hat das Icon mit dem roten Schraubenzieher.

Die TChartToolset-Komponente ermöglicht die Kommunikation zwischen den einzelnen Tools und derm Chart. Aus diesem Grund hat der Chart eine Eigenschaft Toolset, die man mit der TChartToolset-Instanz verbinden muss. Beachte, dass das eine 1:n-Beziehung ist, d.h. ein Toolset kann von mehreren Charts verwendet werden - ein schönes Feature! Ich sollte auch erwähnen, dass ich häufig vergesse, diese Verbindung herzustellen -- und als Folge einige Zeit aufwenden muss, um herauszufinden, warum die Werkzeuge nicht funktionieren...

Zoomen mit dem Mausrad

Aber jetzt genug mit der Theorie! Es ist Zeit für ein praktisches Beispiel.

Zunächst sollten wir die Werkzeuge zu unserem Demo-Chart hinzufügen. Zu diesem Zweck benötigen wir ein TChartToolset TChartToolset-Icon.png. Setze diese Komponente auf das Formular und verbinde sie mit der Eigenschaft Toolset des Chart.

Obwohl wir noch gar keine Workzeuge im Toolset angelegt haben, ist jetzt vielleicht ganz lehrreich, das Programm zu kompilieren und laufen zu lassen. Versuche, so wie eingangs erwähnt, in den Chart zu zoomen oder den Ausschnitt zu verschieben. Oh -- funktioniert nicht mehr! Das ist eine wichtige Beobachtung: Dadurch dass wir ein TChartToolset mit dem Chart verbunden haben, wurde das im Chart eingebaute Toolset ersetzt. Aus diesem Grund funktionieren die eingebauten Werkzeuge für Zoomen und Panning nicht mehr.

Um diese Funktionen wieder zu ermöglichen, müssen wir ein oder mehrere Tools für Zoomen und Verschieben in die Toolset-Komponente einbauen.

Was wollen wir erreichen? Vielleicht könnten wir statt der linken und rechten Maustaste das Mausrad für Zoomen/Verschieben verwenden? Und wir könnten mit der nun freigewordenen linken Maustaste ein Tool aktivieren, mit dem man durch Klicken auf einen Datenpunkt dessen Werte auslesen und anzeigen kann.

Um für das Zoomen ein Mausrad-Tool zu verwenden, musst du auf dem ChartToolset1 auf dem Formular oder im Objekt-Baum des Objekt-Inspektores doppelklicken, oder mache jeweils dort einen Rechtsklick und wähle den Eintrag "Werkzeuge bearbeiten" - genauso wie man bei allen Collection-Editoren von TAChart vorgeht. Drücke auf den "+ Hinzuf"-Schalter und wähle "Zoomen mit Mausrad" aus der heruntergeklappten Liste. Damit erscheint ein neues Element ChartToolset1ZoomMousewheelTool1 im Objekt-Baum als Kind-Knoten des ChartToolset1.

ChartToolset Editor.png

Wenn man jetzt das Programm laufen lassen würde, dann würde das Zommen immer noch nicht funktionieren, denn das Werkzeug hat Eigenschaften ZoomFactor and ZoomRatio, die die Zoomstufen pro Mausrad-Raste definieren. Und diese Eigenschaften stehen immer noch auf ihrem Default-Wert 1. Also: Setze ZoomFactor auf den Wert 1.1 und starte des Programm: Jetzt funktioniert das Zoomen.

Wenn die das Mausrad zu dir herdrehst, wird der Chart vergrößert, genauso wie wenn du den Bildschirm zu dir heranziehen würdest. Viele Programme arbeiten anders herum: bei der genannten Drehung wird der Chart immer kleiner, wie wenn du dich vom Chart wegbewegen würdest. Wenn du dieses Verhalten bevorzugst, dann musst du den ZoomFactor auf eine Zahl kleiner als 1 setzen, zum Beispiel auf 0.9. (Für den Rest des Tutorials werden wir aber beim Wert 1.1. bleiben).

Einige Eigenschaften

Sehen wir uns nun einige Properties des Mausrad-Zoom-Werkzeugs an: Es gibt ein AnimationInterval und AnimationsSteps - mit denen kann man eine Animation des Zoom-Effekts erzeugen.

Was ist FixedPoint? Wenn dieser Wert true ist, geht das Zoomen von der Position des Maus-Cursors aus. Wenn man also eine bestimmte Struktur im Chart vergrößern will, kann man hier die Maus auf diese Struktur bewegen und dann das Mausrad drehen. Bei false ist das Zoomen immer auf das Zentrum des Chart bezogen; die Position des Maus-Cursors spielt dann keine Rolle. In diesem Fall muss man aber sehr sorgfältig sein, denn man kann auf diese Weise leicht die Orientierung im Chart verlieren.

Verwendung von ExtentSizeLimit

ZoomPan04.png

Wenn man aus einem Chart herauszoomt, wird der Chart immer kleiner, und es werden Bereiche sichtbar, die keine Daten mehr enthalten. Da der Benutzer beliebig weit herauszoomen kann, ist irgendwann der eigentliche Datenbereich des Charts nicht mehr erkennbar. Um das zu verhindern, sollten wir den Achsenbereich so begrenzen, dass auf jeden Fall immer Daten angezeigt werden.

Zu diesem Zweck hat TAChart die Eigenschaft ExtentSizeLimit. Unser Chart erstreckt sich in der x-Richtung zwischen -10 und 10. Daher setzen wir ExtentSizeLimit.XMax auf die Differenz 20 und aktivieren diese Einstellung mit ExtentSizeLimit.UseXMax = true. Jetzt können wir nicht mehr aus dem ursprünglichen x Bereich herauszoomen. Analog sollten wir auch den Bereich der y-Achse auf 2 begrenzen.

Auf dieselbe Weise könnten wir verhindern, dass zu tief in den Chart hineingezoomt wird und die x-Achse einen Bereich von weniger als z.B. 0.01 umfasst. Dafür würden wir entsprechend die Eigenschaften ExentSizeLimit.XMin und ExtentSizeLimit.UseXMin verwenden.

Horizontales und vertikales Zoomen

Warum eigentlich gibt es zwei Parameter für die Kontrolle des Zoomens, ZoomFactor und ZoomRatio? Der Grund dafür ist, dass man auf diese Weise einen nicht-proportionalen Zoom-Effekt erzeugen kann, so dass sich z.B. der Chart nur in der horizontalen Richtung verkleinert, ohne sich in der vertikalen Dimension zu verändern. Um das zu verstehen, betrachten wir die x und y Richtungen getrennt. Der Zoom-Faktor für die x-Richtung wird durch den Wert von ZoomFactor allein definiert, während der Zoom-Faktor für die y-Richtung durch das Produkt ZoomFactor*ZoomRatio bestimmt wird. Solange ZoomRatio=1 ist, geschieht das Zoomen isotrop in alle Richtungen. Wenn wir ZoomFaktor=1 und ZoomRatio=1.1 setzen, lassen wir die x-Richtung unverändert, aber zoomen nur in y-Richtung. Oder umgekehrt, wenn wir ZoomRatio = 1/ZoomFactor setzen, erfolgt das Zoomen nur entlang der x-Richtung.

Hey -- das wäre eine schöne Bereicherung für unser Programm! Wenn wir uns auf der Tastatur die Lage der Tasten Shift, Ctrl and Alt ansehen, dann erinnern diese an die Achsen eines Koordinatensystems: Ctrl ist der Ursprung, Shift stellt die y- und Alt die x-Richtung dar. Daher könnten wir der Shift-Taste das vertikale Zoomen und der Alt-Taste das horizontale Zoomen zuordnen; und "keine Taste" würde dem Fall des isotropen Zoomens entsprechen -- das wäre eine gute "Eselsbrücke", um sich die Tastenkombinationen merken zu können.

Wie kann man das implementieren? Setze zwei ZoomMouseWheelTools auf das Formular. Das erste ist für das vertikale Zoomen zuständig. Daher nenne es ChartToolset1ZoomMousewheelTool_vert, weise ihm dem Shift-Wert ssShift zu und setze ZoomRatio = 1.1 (lasse ZoomFactor auf 1). Das andere Tool soll für das horizontale Zoomen sorgen. Nenne es ChartToolset1ZoomMousewheelTool_hor, setze Shift auf ssAlt und ZoomFactor = 1.1 und ZoomRatio = 0.90909090909 ((das ist ungefähr gleich 1/1.1).

Wenn du das Programm laufen lässt, bemerkst du vielleicht, dass das Zoomen nicht mehr funktioniert, nachdem du die Alt-Taste gedrückt hast. Das liegt daran, dass diese Taste auch für die Behandlung von Menü-Tasten verwendet wird. Klicke einfach in den Chart, um das Zoom-Tool zu reaktivieren. Oder wähle eine andere Aktivierungstaste für das horizontale Zoom-Tool.

Verschieben mit dem Mausrad

Machen wir weiter mit den Verschieben ("Panning"). Warum eigentlich verwenden wir nicht das Mausrad auch für diesen Zweck? Das Verschieben soll nur entlang der Achsenrichtungen möglich sein, also entweder in x- oder in y-Richtung. Daher fügen wir zwei TPanMouseWheel-Komponenten zum ChartToolset hinzu und ersetzen im Komponenten-Namen die automatisch angehängten Nummern durch "_hor" und "_vert". Das Tool für das vertikale Verschieben soll wieder mit der Umsch Taste, das für das horizontale Veschieben mit der Alt Taste aktiviert werden.

Zur Festlegung der Verschiebungsrichtung gibt es eine Eigenschaft WheelUpDirection mit Default-Wert pdUp. Das bedeutet, dass die Drehung am Mausrad in vertikales Verschieben übersetzt wird. Diese Einstellung muss beim Werkzeug für die horizontale Verschiebung auf pdRight or pdLeft geändert werden.

Starte des Programm. Wenn du am Mausrad mit gedrückter Umsch oder Alt Taste drehst, verschiebt sich der Chart aber nicht, sondern verändert sein Größe wie beim Zoomen... Was ist falsch?

Das liegt daran, dass wir für die Zoom- und Verschiebungstools dieselben Shift-Einstellungen verwenden. Als Ausweg könnten wir bei den Verschiebungstools zusätzlich auch noch die Einstellung Strg zu den Shift-Einstellungen hinzufügen. Damit würde das vertikale Verschieben zum Beispiel mit der Tastenkombination Strg+Umsch aktiviert, das vertikale Zoomen dagegen würde nur Umsch allein benötigen.

Das ist in der Bedienung aber etwas umständlich. Daher zeigen wir hier einen anderen Ansatz, auch um die Verwendung der Eigenschaft Enabled zu demonstrieren. Füge dem Formular eine Toolbar mit zwei ToolButtons hinzu, die entweder das Zoomen oder das Verschieben einschalten sollen. Nenne den ersten Button ZoomToolbutton und setze sein Down-Eigenschaft auf true. Nenne den zweiten Button entsprechend PanToolbutton, aber lasse sein Down auf false. Außerdem setze bei beiden Schaltern die folgenden Eigenschaften:

  • Grouped = true
  • Style = tbsCheck

und weise ihrem OnClick-Ereignis den folgenden Code zu:

procedure TForm1.ZoomPanToolbuttonClick(Sender: TObject);
begin
  ChartToolset1ZoomMouseWheelTool_iso.Enabled := ZoomToolbutton.Down;
  ChartToolset1ZoomMouseWheelTool_vert.Enabled := ZoomToolbutton.Down;
  ChartToolset1ZoomMouseWheelTool_hor.Enabled := ZoomToolbutton.Down;

  ChartToolset1PanMouseWheelTool_vert.Enabled := PanToolbutton.Down;
  ChartToolset1PanMouseWheelTool_hor.Enabled := PanToolbutton.Down;
end;

Das schaltet entweder die Zoom- oder die Verschiebungs-Tools ein, je nachdem welcher ToolButton gedrückt ist. Damit die Enabled-Einstellungen sich in den Down-Eigenschaften der ToolButtons widerspiegeln, sollten wir noch die beiden Verschiebungs-Tools ausschalten (Enabled = false) -- oder die Methode ZoomPanToolbuttonClick(nil) im OnCreate Ereignis des Formulars aufrufen.

Einige Beobachtungen, wenn das Programm läuft:

  • Es ist Geschmackssache, aber vielleicht bist du auch der Meinung, dass sich der gezoomte Chart-Ausschnitt entgegengesetzt zu der Richtung verschiebt, in der das Mausrad gedreht wird? Das kann man ändern, indem die Eigenschaft WheelUpDirection des vertikalen Zoom-Tools auf pdDown umgestellt wird.
  • Chart-Bereiche ohne Daten können wieder sichtbar werden. Das liegt daran, dass die ExtentSizeLimit-Eigenschaft nur für die Prüfung der Breite und/oder Höhe des Charts zuständig ist. Das kann mit Hilfe der Eigenschaften LimitToExtent des PanMouseWheelTool geändert werden: Aktiviere alle Optionen, um vertikales und horizontales Verschieben über den ursprünglichen Datenbereich hinaus zu verbieten.
  • Vielleicht möchtest du die Geschwindigkeit des Verschiebens verändern? Passe hierzu den Wert der Eigenschaft Step an.
  • Du findest wahrscheinlich auch, dass es recht umständiglich ist, den ursprünglichen, nicht verschobenen Chart-Bereich wiederherzustellen. Das könnte man mit einem Schalter "Reset" in der Toolbar vereinfachen, der jede Zoom-/Verschiebungs-Operation rückgängig macht. Das ist leicht: Platziere einen dritten Button auf die Toolbar, nenne ihn RestoreToolbutton, schreibe als seine Caption den Text "Reset" und weise seinem OnClick-Ereignis den folgenden Code zu:
procedure TForm1.ResetToolButtonClick(Sender: TObject);
begin
  Chart1.ZoomFull;
end;

Der Aufruf von ZoomFull setzt die Achsen wieder auf die ursprünglichen Grenzen zurück. Hier ist nun, wie unser Programm aktuell aussieht:

ZoomPan04a.png

Datenwerte aus der Datenreihe auslesen

Man könnte noch weitere Zoom- und Verschiebungs-Werkzeuge ins Toolset aufnehmen und so eine praktische Benutzeroberfläche aufbauen. Aber das wollen wir hier nicht weiter vertiefen, sondern uns nun den Daten-Tools zuwenden.

Ein TDataPointClickTool verwenden

Stellen wir uns die Aufgabe, die x,y-Koordinaten von Datenpunkten unter dem Maus-Cursor auszulesen. Ein dafür geeignetes Werkzeug ist das TDatapointClickTool. Füge es zum Toolset hinzu. Setze Shift auf ssLeft, um es durch einen Klick mit der linken Maustaste zu aktivieren. Und füge dem Formular eine TStatusbar hinzu, auf der wir die erhaltenen Daten anzeigen wollen.

Immer wenn ein Mausklick auf einem Datenpunkt erfolgt (oder ausreichend nahe), werden die relevanten Daten über den Datenpunkt im Tool gespeichert. Zum Beispiel gibt es eine Eigenschaft Series, die festhält, auf welcher Datenreihe der Klick erfolgt ist. Entsprechend gibt es auch eine Eigenschaft PointIndex, über die man den Index des angeklickten Punktes innerhalb der Datenreihe erfährt. Weiterhin stellt das Werkzeug ein Ereignis OnPointClick zur Verfügung; es wird ausgelöst, wenn auf einen Datenpunkt geklickt wurde, und wir können damit die gewünschte Information auslesen. Der folgende Ereignis-Behandlungscode stellt die ausgelesene Information in der Statuszeile dar:

procedure TForm1.ChartToolset1DataPointClickTool1PointClick(
  ATool: TChartTool; APoint: TPoint);
var
  x, y: Double;
begin
  with ATool as TDatapointClickTool do 
    if (Series is TLineSeries) then 
      with TLineSeries(Series) do begin
        x := GetXValue(PointIndex);
        y := GetYValue(PointIndex);
        Statusbar1.SimpleText := Format('%s: x = %f, y = %f', [Title, x, y]);
      end
    else
      Statusbar1.SimpleText := '';  
end;

Der Parameter ATool, der dem Ereignis mitgegeben wird, hat einen sehr allgemeinen Typ. Daher müssen wir eine Typ-Umwandlung in ein TDatapointClickTool vornehmen, um an die gewünschte Information zu gelangen. Genauso hat die vom Tool gespeicherte Eigenschaft Series einen sehr allgemeinen Typ, so dass auch hier eine Typumwandlung erforderlich ist. Aber sei vorsichtig bei Typumwandlungen, versichere dich immer mit Hilfe des Operators is, ob die Typen richtig sind.

Wenn wir nun das Programm starten und auf einem Datenpunkt klicken, bekommen wir die Koordinaten des Punktes zusammen mit dem Titel der Datenreihe in der Statuszeile angezeigt.

ZoomPan05.png

Anzeigen von permanenten Labels

Leider kann man nicht feststellen, wo genau der Klick erfolgt ist. Es wäre besser, wenn wir über dem angeklickten Datenpunkt eine Markierung ("Label") mit der gewünschten Information anzeigen könnten.

Hierzu verwenden wir die Tatsache, dass die Datenreihen im Chart ihre Daten jeweils aus einer TListChartSource beziehen. Auf dem Formular gibt es dafür keine spezielle Komponente, weil der Datenreihentyp TChartSeries, von dem sich TLineSeries ableitet, diese als eingebautet Datenquelle enthält und über die öffentliche Eigenschaft ListSource zugänglich macht.

Warum ist das wichtig? Weil die in der ListSource gespeicherten Einträge vom Typ TChartDateItem im Feld Text Speicherplatz für zusätzlichen Text vorhalten:

type
  TChartDataItem = object
    X, Y: Double;
    Text: String;
    // ... 
  end;

Um den Inhalt des Text-Feldes zu verändern, der einem Datenpunkt mit vorgegebenem Index zugewiesen ist, verwenden wir folgenden Code:

ListSource.Item[index]^.Text := 'some text';

In neueren Lazarus-Versionen gibt es dafür zur Vereinfachtung auch die Methode SetText:

ListSource.SetText(index, 'some text');

(Du kannst mehr über TChartListSource in dem betreffenden Tutorial erfahren.)

Zu Beginn sind alle Text Einträge leer. Aber wenn wir auf einen Datenpunkt geklickt haben, transferieren wir die Koordinaten-Information in das Text-Feld des zugehörigen ChartDataItem, indem wir die obige Code-Zeile in die die OnPointClick-Ereignisbehandlungsroutine des DataPointClickTools einbauen. Die Statuszeile, übrigens, benötigen wird nicht mehr - lösche sie und entferne den entsprechenden Code aus der Ereignisbehandlungs-Routine:

procedure TForm1.ChartToolset1DataPointClickTool1PointClick(
  ATool: TChartTool; APoint: TPoint);
var
  x, y: Double;
begin
  with ATool as TDatapointClickTool do
    if (Series <> nil) then
      with (Series as TLineSeries) do begin
        x := GetXValue(PointIndex);
        y := GetYValue(PointIndex);
        { --- die nächste Zeile wurde entferne --- }
//        Statusbar1.SimpleText := Format('%s: x = %f, y = %f', [Title, x, y]);
        { --- die nächste Zeile wirde hinzugefügt --- }
        ListSource.Item[PointIndex]^.Text := Format('x = %f'#13#10'y = %f', [x,y]);
        ParentChart.Repaint; 
        // in neueren Lazarus-Versionen kann man stattdessen auch die folgende Zeile verwenden (die schon das Repaint enthält):
        // ListSource.SetText(PointIndex, Format('x = %f'#13#10'y = %f', [x,y]));
      end;
end;

Nun müssen wir uns darum kümmern, dass die Labels auch angezeigt werden. Zu diesem Zweck hat TChartSeries, der Vorfahr von TLineSeries, die Eigenschaft Marks. In deren Unter-Eigenschaften findet sich die Option Style, die standardmäßig auf smsNone gesetzt ist -- das bedeutet, dass Labels in der Grundeinstellung gar nicht dargestellt werden. Im Objekt-Inspektor sieht man, dass Style eine Vielzahl von Einstellmöglichkeiten bietet. Wir brauchen hier die Option smsLabel, die die Ausgabe des Text-Labels bewirkt. Eine weitere sinnvolle Änderung wäre, LinkPen.Color auf eine dunklere Farbe zu setzen, andernfalls ist die Verbindungslinie zwischen Label und Datenpunkt auf dem weißen Untergrund nicht zu erkennen. Vielleicht spielst du auch mit den anderen Einstellmöglichkeiten, wie CalloutAngle oder Shape.

Dieselben Änderungen müssen an allen drei Datenreihen des Chart vorgenommen werden.

Wenn du nun das Projekt startest und auf Datenpunkten klickst, bekommst du schöne permantente Labels. Beachte, dass die Markierungen erhalten bleiben, selbst wenn man den sichtbaren Chart-Ausschnitt durch Zoomen oder Verschieben verändert.

ZoomPan06.png

Permanente Labels entfernen

Gelegentlich wirst du feststellen, dass du einen falschen Datenpunkt angeklickt hast, und willst deshalb das Label entfernen.

Die grundlegende Idee dafür ist folgendermaßen: füge ein weiteres TDatapointClickTool hinzu, weise es der rechten Maustaste zu und entferne in der OnDatapointClick-Ereignisbehandlung den Text von dem ChartDataItem. Versuche selbst, den Code zu schreiben -- du solltest jetzt alle nötigen Schritte kennen.

Ähnliche Tutorials

Quell-Code

project.lpr

program project1;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  Interfaces, // this includes the LCL widgetset
  Forms, Unit1, tachartlazaruspkg
  { you can add units after this };

{$R *.res}

begin
  RequireDerivedFormResource := True;
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

unit1.pas

unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, TAGraph, TASeries, TATools, Forms, Controls,
  Graphics, Dialogs, ComCtrls, types;

type

  { TForm1 }

  TForm1 = class(TForm)
    Chart1: TChart;
    ChartToolset1: TChartToolset;
    ChartToolset1DataPointClickTool1: TDataPointClickTool;
    ChartToolset1DataPointClickTool2: TDataPointClickTool;
    ChartToolset1PanMouseWheelTool_Hor: TPanMouseWheelTool;
    ChartToolset1PanMouseWheelTool_Vert: TPanMouseWheelTool;
    ChartToolset1ZoomMouseWheelTool_iso: TZoomMouseWheelTool;
    ChartToolset1ZoomMouseWheelTool_Hor: TZoomMouseWheelTool;
    ChartToolset1ZoomMouseWheelTool_Vert: TZoomMouseWheelTool;
    SinSeries: TLineSeries;
    CosSeries: TLineSeries;
    SinCosSeries: TLineSeries;
    ToolBar1: TToolBar;
    ResetToolButton: TToolButton;
    ZoomToolbutton: TToolButton;
    PanToolbutton: TToolButton;
    procedure ChartToolset1DataPointClickTool1PointClick(
      ATool: TChartTool; APoint: TPoint);
    procedure ChartToolset1DataPointClickTool2PointClick(
      ATool: TChartTool; APoint: TPoint);
    procedure FormCreate(Sender: TObject);
    procedure ResetToolButtonClick(Sender: TObject);
    procedure ZoomPanToolbuttonClick(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}


{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
const
  N = 100;
  MIN = -10;
  MAX = 10;
var
  i: Integer;
  x: Double;
begin
  for i:=0 to N - 1 do begin
    x := MIN + (MAX - MIN) * i / (N - 1);
    SinSeries.AddXY(x, sin(x));
    CosSeries.AddXY(x, cos(x));
    SinCosSeries.AddXY(x, sin(x)*cos(x));
  end;
end;

procedure TForm1.ResetToolButtonClick(Sender: TObject);
begin
  Chart1.ZoomFull;
end;

procedure TForm1.ChartToolset1DataPointClickTool1PointClick(
  ATool: TChartTool; APoint: TPoint);
var
  x,y: Double;
begin
  with ATool as TDatapointClickTool do
    if (Series <> nil) then
      with (Series as TLineSeries) do begin
        x := GetXValue(PointIndex);
        y := GetYValue(PointIndex);
//        Statusbar1.SimpleText := Format('%s: x = %f, y = %f', [Title, x, y]);
        ListSource.Item[PointIndex]^.Text := Format('x = %f'#13#10'y = %f', [x,y]);
        ParentChart.Repaint;
      end;
end;

procedure TForm1.ChartToolset1DataPointClickTool2PointClick(
  ATool: TChartTool; APoint: TPoint);
begin
  with ATool as TDatapointClickTool do
    if (Series <> nil) then
      with (Series as TLineSeries) do begin
        ListSource.Item[PointIndex]^.Text := '';
        ParentChart.Repaint;
      end;  
end;

procedure TForm1.ZoomPanToolbuttonClick(Sender: TObject);
begin
  ChartToolset1ZoomMouseWheelTool_iso.Enabled := ZoomToolbutton.Down;
  ChartToolset1ZoomMouseWheelTool_vert.Enabled := ZoomToolbutton.Down;
  ChartToolset1ZoomMouseWheelTool_hor.Enabled := ZoomToolbutton.Down;

  ChartToolset1PanMouseWheelTool_vert.Enabled := PanToolbutton.Down;
  ChartToolset1PanMouseWheelTool_hor.Enabled := PanToolbutton.Down;
end;

end.

unit1.lfm

object Form1: TForm1
  Left = 554
  Height = 284
  Top = 341
  Width = 347
  Caption = 'Form1'
  ClientHeight = 284
  ClientWidth = 347
  OnCreate = FormCreate
  LCLVersion = '1.1'
  object Chart1: TChart
    Left = 0
    Height = 258
    Top = 26
    Width = 347
    AxisList = <    
      item
        Grid.Color = clSilver
        Minors = <>
        Title.LabelFont.Orientation = 900
        Title.LabelFont.Style = [fsBold]
        Title.Visible = True
        Title.Caption = 'y axis'
      end    
      item
        Grid.Color = clSilver
        Alignment = calBottom
        Minors = <>
        Title.LabelFont.Style = [fsBold]
        Title.Visible = True
        Title.Caption = 'x axis'
      end>
    BackColor = clWhite
    ExtentSizeLimit.UseXMax = True
    ExtentSizeLimit.UseXMin = True
    ExtentSizeLimit.UseYMax = True
    ExtentSizeLimit.XMax = 20
    ExtentSizeLimit.XMin = 0.01
    ExtentSizeLimit.YMax = 2
    Foot.Brush.Color = clBtnFace
    Foot.Font.Color = clBlue
    Legend.Alignment = laBottomCenter
    Legend.ColumnCount = 3
    Legend.Visible = True
    Title.Brush.Color = clBtnFace
    Title.Font.Color = clBlue
    Title.Font.Style = [fsBold]
    Title.Text.Strings = (
      'My first chart'
    )
    Title.Visible = True
    Toolset = ChartToolset1
    Align = alClient
    ParentColor = False
    object SinSeries: TLineSeries
      Marks.Format = '%2:s'
      Marks.LinkPen.Color = clGray
      Marks.Style = smsLabel
      Title = 'y=sin(x)'
      LinePen.Color = clRed
    end
    object CosSeries: TLineSeries
      Marks.Format = '%2:s'
      Marks.LinkPen.Color = clGray
      Marks.Style = smsLabel
      Title = 'y=cos(x)'
      LinePen.Color = clBlue
    end
    object SinCosSeries: TLineSeries
      Marks.Format = '%2:s'
      Marks.LinkPen.Color = clGray
      Marks.Style = smsLabel
      Title = 'y=sin(x)*cos(x)'
      LinePen.Color = clGreen
    end
  end
  object ToolBar1: TToolBar
    Left = 0
    Height = 26
    Top = 0
    Width = 347
    Caption = 'ToolBar1'
    EdgeBorders = [ebBottom]
    ShowCaptions = True
    TabOrder = 1
    object ZoomToolbutton: TToolButton
      Left = 1
      Top = 0
      Caption = 'Zoom'
      Down = True
      Grouped = True
      OnClick = ZoomPanToolbuttonClick
      Style = tbsCheck
    end
    object PanToolbutton: TToolButton
      Left = 41
      Top = 0
      Caption = 'Pan'
      Grouped = True
      OnClick = ZoomPanToolbuttonClick
      Style = tbsCheck
    end
    object ResetToolButton: TToolButton
      Left = 69
      Top = 0
      Caption = 'Reset'
      OnClick = ResetToolButtonClick
    end
  end
  object ChartToolset1: TChartToolset
    left = 153
    top = 66
    object ChartToolset1ZoomMouseWheelTool_iso: TZoomMouseWheelTool
      ZoomFactor = 1.1
    end
    object ChartToolset1ZoomMouseWheelTool_Hor: TZoomMouseWheelTool
      Shift = [ssAlt]
      ZoomFactor = 1.1
      ZoomRatio = 0.90909090909091
    end
    object ChartToolset1ZoomMouseWheelTool_Vert: TZoomMouseWheelTool
      Shift = [ssShift]
      ZoomRatio = 1.1
    end
    object ChartToolset1PanMouseWheelTool_Hor: TPanMouseWheelTool
      Shift = [ssAlt]
      LimitToExtent = [pdLeft, pdUp, pdRight, pdDown]
      WheelUpDirection = pdLeft
    end
    object ChartToolset1PanMouseWheelTool_Vert: TPanMouseWheelTool
      Shift = [ssShift]
      LimitToExtent = [pdLeft, pdUp, pdRight, pdDown]
      WheelUpDirection = pdDown
    end
    object ChartToolset1DataPointClickTool1: TDataPointClickTool
      Shift = [ssLeft]
      OnPointClick = ChartToolset1DataPointClickTool1PointClick
    end
    object ChartToolset1DataPointClickTool2: TDataPointClickTool
      Shift = [ssRight]
      OnPointClick = ChartToolset1DataPointClickTool2PointClick
    end
  end
end