Notes on Using TeeChart

June 2003 - Phil Sheppard - Creative Analytics Pty Ltd

The following notes have been compiled on the TeeChart configurations Iíve found useful in my applications.† TeeChart is very comprehensive which can tend to make it a little complex to understand and use. I find that in most applications I need to configure the chart and seriesí in run-time, and so these examples focus on that approach. The following examples are based on TeeChart Pro version 6 and I commonly use the TChart and TChartGrid components.

The information contained in this document is believed to be reliable however Creative Analytics Pty Ltd does not guarantee its completeness or accuracy.

Configuring the Chart

By default TeeChart will place the values of the series in the legend, however I prefer the series names so I generally set the following two properties.

Chart.Legend.LegendStyle := lsSeries;† //to display seriesí names in legend
Chart.Legend.ShadowSize := 0;†† //to remove shadow in legend
Chart.Legend.Visible := True;

I find the following method useful to undo a user-invoked zoom.

Chart.UndoZoom;

Configuring an Axis

It is possible to set the range of any axis (rather than have it automatic).† The labels can also be set at an angle by specifying the angle in degrees.

Chart.BottomAxis.Automatic := False;
Chart.BottomAxis.SetMinMax(0, 1);
Chart.BottomAxis.LabelsAngle := 90;

Adding and Configuring a Series

When you add a series to a chart in run-time, there are quite a few properties that need setting, so I often use a local procedure for this and MyLine or MyBar are local variables.

Most of the properties are self explanatory, however:

procedure AddLineSeries(const Title: string);
begin
  MyLine := TLineSeries.Create(Chart);
  Chart.AddSeries(MyLine);
  MyLine.Title := Title;
  MyLine.ShowInLegend := True;
  MyLine.Marks.Visible := False;
  MyLine.Pointer.Visible := True;
  MyLine.XValues.DateTime := True;
  MyLine.LinePen.Width := 2;
end;


procedure AddBarSeries(const Title: string);
begin
  MyBar := TBarSeries.Create(Chart);
  Chart.AddSeries(MyBar);
  MyBar.Title := Title;
  MyBar.ShowInLegend := True;
  MyBar.Marks.Visible := False;
  MyBar.MultiBar := mbStacked;
  MyBar.BarPen.Visible := False;
end;

The Pointer property relates to boxes or circles etc at each data point in addition to the series line.

MyLine.Pointer.Style is one of psRectangle, psCircle, psTriangle, psDownTriangle, psCross, psDiagCross, psStar, psDiamond, or psSmallDot

By default each series has the same pointer (!) so I use the following to set a different pointer styles for each series.† I set p to zero for the first series, and then incrementing it for each series, checking that p is valid.

MyLine.Pointer.Style := TSeriesPointerStyle(p);
if p > Ord(High(TSeriesPointerStyle)) then p := 0; //Check for valid p

The Y values can be sorted using the following:

MySeries.YValues.Order := loDescending;
MySeries.YValues.Sort;
MySeries.XValues.FillSequence;

Adding Data to a Series

The easiest way to add data is to using the Add method, specifying the Y value and its associated label.† For example:

for i := 0 to n-1 do
  MyBar.Add(YData[i], MyLabel[i]);

Y values can be modified in a series as follows:

for i := 0 to n-1 do
  MyLine.YValue[i] := YData[i];

X labels can be modified in a series as follows:

for i := 0 to n-1 do
  MyLine.XLabel[i] := MyLabel[i];

Alternatively, an array of data can also be added in one hit as follows (YData is an array of Real):

for s := 0 to Chart.SeriesCount-1 do
  Chart.Series[s].AddArray(YData);

Copying to the Clipboard

Copying the chart as a picture is straightforward, however if we just use a standard method such as Chart.CopyToClipboardMetafile(True) we may get a border on the top and left side (depending on the Bevel settings). So I tend to set the Bevelís to bvNone before calling the copy to remove the partial-border.

procedure TMainForm.BtnCopyChartClick(Sender: TObject);
begin
  Chart.BevelInner := bvNone;
  Chart.CopyToClipboardMetafile(True);
  Chart.BevelInner := bvLowered;
end;

Copying the data underpinning a chart is a bit more complex, however the following seems to work well.† (This requires TeeStore in uses clause).

procedure TMainForm.BtnCopyDataClick(Sender: Object);
var
  Data: TSeriesDataText;
begin
  Data := TSeriesDataText.Create(Chart, nil);
  try
    Data.IncludeLabels := True;
    Data.IncludeHeader := True;
    Data.IncludeIndex := False;
    Data.IncludeColors := False;
    Data.CopyToClipboard;
  finally
    Data.Free;
  end;
end;

Saving and Loading tee Files

Saving and loading charts as TeeChart files has a couple of complications. The procedures are globals, and not methods of the Chart object.† They require both TeeStore and TeeEdiSeri (!) in the uses clause.† The application will compile with just TeeStore but will generate run-time errors if you donít include TeeEdiSeri.

LoadChartFromFile(TCustomChart(Chart), FileName);
SaveChartToFile(Chart, FileName, True);

Events and linked components such as a popup menu cannot be saved, and you must set all events to nil before saving the chart and re-assign them after the chart is loaded.

Functions

There are many things you can do with functions. What Iíve used here relates to creating a series that is the total of the other series.† This requires TeeFunci in uses clause.

Create the TLineSeries as above then

MyLine.SetFunction(TAddTeeFunction.Create(Self));
for i := 0 to Chart.SeriesCount-1 do
  MyLine.DataSources.Add(Chart.Series[i]);
MyLine.CheckDataSource;† //required to update function-series!

Apparently this is the correct way of using a TeeFunction, however it does create an access violation when exiting the application.† This is a bug and the work-around is to include a Chart.FreeAllSeries in the FormClose event.

Timeseries Charts

Datetime based series can be plotted in TChart by setting the DateTime property of the XValues property.† The datetime format of the bottom axis can be specified and then TChart will create x-axis labels according to your format with having to expressly define labels. I tend to use the AddXY() method when adding data to a timeseries chart, where X is your timestamp and Y is the value at that timestamp.

MyLine.XValues.DateTime := True;
Chart.BottomAxis.DateTimeFormat := 'hh:mm';
Chart.BottomAxis.Increment := DateTimeStep[dtOneMonth];
Chart.BottomAxis.Increment := DateTimeStep[dtThirtyMinutes];

Gantt Charts

I set the Order properties because I didn't want the chart to automatically sort each series. I wasn't sure how many series' I should have, but I just used one series for 50 plus bars in the Gantt chart.† You can then superimpose other series (such as a line) over the Gantt if your require this.

MyGantt := TGanttSeries.Create(Chart);
Chart.AddSeries(MyGantt);
MyGantt.ShowInLegend := False;
MyGantt.XValues.Order := loNone;
MyGantt.YValues.Order := loNone;
MyGantt.ConnectingPen.Width := 2;
For i := 0 to n-1 do
  MyGantt.AddGanttColor(StartDate[i], EndDate[i], i, Name[i], MyColour[i]);

3-D Charts

While 3D charts look cool, they are harder to read than a 2D chart so I donít tend to use them too much. They are a little more complex to configure (and populate with data) than a 2D series, but this is how Iíve implemented a 3D surface.

MySurface := TSurfaceSeries.Create(Chart);
Chart.AddSeries(MySurface);
Chart.DepthAxis.Visible := True;
Chart.View3DOptions.Orthogonal := False;
Chart.View3DOptions.Zoom := 90;
Chart.Chart3Dpercent := 40;
MySurface.IrregularGrid := True;
MySurface.UseColorRange := False;
MySurface.UsePalette := True;
MySurface.PaletteStyle := psPale;
MySurface.Pen.Visible := True;
MySurface.ShowInLegend := False;
for i := 0 to n-1 do
  for j := 0 to m-1 do
    MySurface.AddXYZ(j, MyData[i,j], i);

TChartGrid

I have found that the grid doesnít always populate properly and so I always call ChartGrid.RecalcDimensions once Iíve finished adding series and data.

The top left cell in the ChartGrid contains the word ďTextĒ by default, and this can be changed by setting the global variable: TeeMsg_Text := Ďmy textí.† This requires the TeeConst in the uses clause.