TAChart Tutorial: Chart Tools
- 1 Introduction
- 2 Preparation
- 3 Zooming and Panning - the easy way
- 4 Using chart tools
Charts are powerful to display relationships between data. They get even more powerful if the user can interact with them, for example
- by zooming into a crowded series to display more details,
- by panning the visible viewport to other features,
- by reading values from the series or by measuring characteristic parameters of the plot.
The TAChart package is equipped with a powerful collection of tools to help creating interactive charts. In this tutorial, we want to show how to apply zooming and panning in your own programs, in another tutorial we will focus on data tools to analyze data interactively.
If you are not familiar with TAChart we recommend that you have a look at the Getting started tutorial. Of course, you must have some experience with Lazarus and FPC.
ChartTools have made some progress after the official version 1.0 of Lazarus has been released. Therefore, it may be advantageous to use the current trunk version from svn, otherwise some topics of this tutorial will not be available.
We need a chart to play with. You can use any chart that you ever created for our exercises, but I'll be using here the simple chart of the Getting started tutorial.
Zooming and Panning - the easy way
Zooming is very easy - you don't have to do anything to implement it since it is built into the heart of the chart component. You just have to drag a rectangle with the left mouse button down around the feature of interest that you want to see in detail. However, you should keep in mind that you have to drag the mouse from the top-left to the right-bottom corner of the rectangle to get the zoom effect. After you release the mouse button, the region is blown up to fill the entire chart. Easy.
You can repeat this procedure to zoom into the chart deeper and deeper.
If you want to restore the original viewport just click into the chart with the left button, or drag a rectangle in any -- except for the top-left to right-bottom -- direction.
Maybe, for some reason, you do not like this behavior.
TChart has a property
AllowZoom with which you can turn off the zooming capability. Beyond that, there is nothing else to control the default zooming action. But don't give up: A bit further down we will discuss the ChartTools, and they give you almost unlimited access to any interactive feature.
After you have zoomed into a chart you may want to shift the viewport a bit to find a better view. This operation is called "panning". Unfortunately, the released version of Lazarus (v1.0) does not have built-in panning capabilities. Only recently a default panning operation has been introduced into the trunk version of Lazarus. For this purpose, you hold down the right mouse button, the cursor transforms into a 4-arrow-shape to indicate that you can move the viewport around by dragging.
As with zooming you can restore the original viewport by clicking into the chart, or by dragging a rectangle in any direction, except from top-left to bottom-right. Note that you use the left mouse button for "un-panning" although you had used the right button for panning.
TChart currently does not have a property
AllowPan to turn off panning. As we will see shortly, panning can be turned off also with ChartTools, so this is no real disadvantage.
Using chart tools
As you have seen, to add zooming and panning capabilities to your program can't be easier -- there's nothing to do, they are already there. But on the other hand, usage of the built-in routines is quite limited: zooming is only possible by using the left mouse button, panning by using the right mouse button; no mouse-wheel support; no vertical-only or horizontal-only zooming; no reading of data values, etc.
For more versatile user interaction a set of chart tools has been added to TAChart. All these tools inherit from
TChartTool (there is another ancestor down along the hierarchy,
TBasicChartTools, but this is not important here). Each tool is specialized to some specific action. There are two types of tools: Extent tools and data tools.
- Extent tools modifiy the extent of the chart -- this is the visible viewport -- i.e. they are responsible for zooming and panning.
- for zooming:
- for unzooming:
- there are no dedicated tools for unzooming, this feature is built into the tools used for zooming. For unzooming, you usually invert the parameter that defines the amount of zooming.
- for panning:
- Data tools give interactive access to data displayed in the series, or even allow to modify the data:
- TDataPointClickTool identifies the data point and series onto which the mouse is clicked.
- TDataPointDragTool allows to drag a data point by means of the mouse
- TDataPointHintTool displays a hovering hint windows when the mouse is close enough to a data point
- TDataPointCrosshairTool displays a crosshair when the mouse is sufficiently close to a data point:
- TDataPointDistanceTool allows to measure the distance between data points.
- and, if nothing else fits your needs, TUserDefinedTool allows you to build your own tool according to your specific needs.
Alle these tools have a property called
Shift containing a set of shift states that activate the tool.
TShiftState is a fundamental LCL property and declared as follows:
TShiftState = (ssShift, ssAlt, ssCtrl, ssLeft, ssRight, ssMiddle, ssDouble, // Extra additions ssMeta, ssSuper, ssHyper, ssAltGr, ssCaps, ssNum, ssScroll,ssTriple,ssQuad,ssExtra1,ssExtra2);
An example: If the tool's
ssLeft then the tool becomes active when the user presses the CTRL key and left mouse button. Note that all conditions must be met, the tool does not activate if the left mouse button is pressed alone. Therefore, each tool must have its own set of unique
There is another condition that must be met for the tool to become active: Its property
Enabled must be set to
true, but this is the default setting. This property is useful when several tools share the same
Shift combinations and are activated by some other code, for example by toolbar buttons.
Finally, all tools have in common various events that are fired before or after a key or mouse button has been pressed or released; in case of the mouse, there are also events that fire before or after the mouse is moved. Usually, chart tools work "out of the box", and these events are not needed. But they are necessary when some functionality is to be added. We will see examples in the course of this tutorial.
But now, enough of theory! There would be more to say, but it would be a duplication of the |TAChart documentation. Let's practice instead.
Let us add tools to our demo chart. How to do that?
Chart tools are collected in a special component,
TChartToolset. You can find it somewhere in the center of the "chart" component palette of Lazarus, it is the one with the screw driver.
Drop the component onto your form. The
TChartToolset manages the communication between tools and chart. Therefore, the chart has a property
Tools which as to be assigned to the
ChartToolset instance. Note that this is a 1:n relationship, i.e. one toolset can handle multiple charts, a very nice feature! I should also mention that I usually forget to make that assignment -- and spend time in seeking for the reason why the tools do not work...
Although we did not yet add any tools to the toolset, maybe this is a good point to compile and run our program. Try to zoom or pan, as we did above. Oh -- no longer working! This is an important observation: by adding a
TChartToolset to the form we replaced the default toolset built into the chart. So, the built-in tools for zooming and panning are not operational any more.
In order to re-establish zooming and panning for our application we have to add one (ore more) zooming or panning tools to the toolset.
What do want to achieve? Maybe, instead of using the left and right mouse buttons we could take advantage of the mouse wheel for zooming and panning. And we could assign the left mouse button to a tool which allows us to read data values from the series while dragging the mouse along the series.
Zooming by the mouse-wheel
To add a zoom mouse-wheel tool to the chart you double-click on the
ChartToolset1 on the form, or double-click on it on the object tree of the object inspector, or right-click and select "Edit tools", as you do with all collection editors of TAChart. Select "Zoom mouse-wheel" from the dropdown list, and a new item
ChartToolset1ZoomMousewheelTool1 appears in the object tree as a child of
If you would run the program at this time, zooming still would not work because the tool has properties
ZoomRatio that define the amount of zooming per mouse-wheel step, and they are still at their default of 1. So, set
ZoomFactor to 1.1, and run the program. Now zooming works fine.
Let's have a look at the properties of the zoom mousewheel tool. There are
AnimationsSteps which can be used to achieve an animation of the zooming effect.
true the position of the mouse cursor remains fixed while the mouse wheel is rotated. If
false the position of the mouse cursor is moved into the chart center with each step of the mouse wheel. In this case you have to be very careful because it is extremely easy to lose orientation in the chart.
Using the ExtentSizeLimit
When you zoom out you will expose empty regions of the x axis. Because the user can zoom out further and further the chart may eventually disappear. To prevent this we should restrict the x range such that always data are displayed along the x axis.
For this purpose, TChart has a property
ExtentSizeLimit. Our chart has an x extent from -10 to 10. So we set
ExtentSizeLimit.XMax to the difference, 20, and set
true. Now, we cannot zoom out any more beyond the original exent.
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
Correspondingly, the same can be done with the y axis.
Horizontal and vertical zooming
Why are there two parameters that control the amount of zooming,
ZoomRatio? This is because it allows non-proportional zooming: the x zoom factor is given by
ZoomFactor alone, but the y zoom factor is determined by the product
ZoomFactor*ZoomRatio. As long as
ZoomRatio=1, zooming occurs isotropically in all directions. When you set
ZoomRatio=1.1 we leave the x direction unchanged, but zoom only along the y direction. Or, if we set
ZoomRatio = 1/ZoomFactor zooming occurs only horizontally along the x axis.
Hey - these would be nice features for our program! When we look at the keyboard the SHIFT, CTRL and ALT keys form some kind of coordinate system: CTRL is the origin, SHIFT is the y direction, ALT is the x direction. So we could assign the SHIFT key to vertical zooming, the ALT key to horizontal zooming, and "no key" to isotropic zooming -- that's easy to remember.
How can we implement this feature? Add two more ZoomMouseWheelTools to the form. The first one will do the vertical zoom, so name it
ssShift in the
Shift property, and set
ZoomRatio = 0.909 (which is approximately equal to 1/1.1). The other tool will be for the horizontal zoom, name it
ssAlt in the
Shift property, and set
ZoomFactor = 1.1 and
ZoomRatio = 0.909.
Panning by the mouse-wheel
Let's move on the panning. Why don't we use the mousewheel also for panning? Here, panning can go only either in x or y direction. So let us add two PanMouseWheelTools and replace the number at the end of their name by "_vert" and "_hor". The tool for vertical panning should be activated by the SHIFT key again, the tool for horizontal panning by the ALT key.
There is a property
WheelUpDirection which defaults to
pdUp. This setting is not correct for the horizontal panning tool, select
Run the program. When you rotate the mouse wheel with SHIFT of ALT down the chart zooms, but does not pan. What's wrong?
The reason is that we are using the same
Shift settings for the zooming and panning tools. Well, we could use the CTRL key and assign it to the panning tools. Then vertical panning, for example, would occur by pressing the CTRL and SHIFT keys when rotating the mouse wheel, vertical zooming would occur with the SHIFT key alone.
Let's go another way here to demonstrate usage of the
Enabled property. Add a toolbar to the form with two buttons to activate either zooming or panning. Rename the first button to
ZoomToolbutton and set
true. Rename the second button to
PanToolbutton. For both buttons, set the following properties:
Grouped = true
Style = tbsCheck
and assign their
OnClick to the following event handler:
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;
This enables either the zooming tools or the panning tools, depending on which toolbutton is down. To synchronize the chart tools'
Enabled with the buttons'
Down you should call
ZoomPanToolbuttonClick(nil) in the
OnCreate event of the form.
Some observations when you run the program:
- The vertical zoom direction is opposite to the direction the mouse wheel is rotated. You can fix this by setting the
WheelUpDirectionof the vertical panning tool to
- The empty x regions beyond -10 and +10 can become visible again. This is because the
ExtentSizeLimitchecks only the width of the extent. To take care of this the PanMousewheelTool has a set of properties
LimitToExtent. To disallow horizontal panning beyond the x extent with data you have to activate the options
- Maybe you want to change the speed of panning. This can be done by adapting the property
Reading data from the series
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.
Our task is to read the x,y coordinates of the series underneath the mouse cursor. A neat tool to achieve this is the
TDatapointClickTool. Add it to the toolset. Set
ssLeft to activate it by a left button click. And add a
TStatusbar to the form where we will display the requested information.
Whenever a click occurs on (or sufficiently near) a data point the relevant data are stored in the tool. For example, there is a
Series property which identifies onto which series the click occured. Similarly, there is a property
PointIndex which tells the index of the hit point within its series. And the tool provides an
OnPointClick 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:
procedure TForm1.ChartToolset1DataPointClickTool1PointClick(ATool: TChartTool; APoint: TPoint); var x,y: Double; begin with TDatapointClickTool(ATool) do begin if Series <> nil then begin x := TLineSeries(Series).GetXValue(PointIndex); y := TLineSeries(Series).GetYValue(PointIndex); Statusbar1.SimpleText := Format('%s: x = %f, y = %f', [TLineSeries(Series).Title, x, y]); end else Statusbar1.SimpleText := ''; end; end;
Unfortunately the parameter
ATool passed to the event is rather general, we have to type-cast it to
TDatapointClickTool to get access to the mentioned properties. Similarly, the
Series is of a very basic type, another type-cast is needed. Be careful with type-casts, make sure that the class types are correct.
That's all. 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.