TQSpiderGraph HowTo.
TQSpiderGraph (v 1.0) is a Delphi visual component, designed and tested
using Delphi 6 PE. It comes under the form of a Delphi unit and a
"ready to install" component.
It can be found on the web at http://mapage.noos.fr/qnno/pages/delphi_en.htm.
- Licence.
- Instaling as a component.
- Lines values have to be of type Single;
- Properties and methods.
- Properties and methods
list
- Directly reaching Axes and
Lines properties
- Example of defining axes
- Example of defining lines,
and how to pass them some values to display.
- "How can I show, say, axes' max and min
to my
user ?"
Note
: The manipulation of most of the properties of SpiderGraph's
instances is illustrated in
the demo source provided, to which you
should have a look.
1. Licence
// This component is freeware, but under
copyrights that remain mine for
// my parts of the code, and original writters
for their parts of the code.
// This is mainly the case with the
computation of a polygon area (which
// can be found anywhere on the web...).
// This unit can be freely used in any
application, freeware, shareware
// or commercial. However, I would apreciate
your sending me an email if
// you decide to use it. Of course, you use it
under your own and single
// responsability. Neither me, nor
contributors, could be held responsible
// for any problem resulting from the use of
this unit. ;-)
// It can also be freely distributed, provided
this licence and the copyright
// notice remains within it unchanged.
// copyright © 2004 by Olivier Touzot.
2. Install
Instances of TQSpiderGraph can be dynamically created at run-time,
through its constructor, like any other visual component. In this case,
you'll need to to assign its "parent" property in order for it to
appear somewhere in its parent ClientRect.
Otherwise, to manipulate SpiderGraphs into Delphi's IDE ("at design
time"), you'll have to install it like any other component :
-1- Where will it go ?
If you look at the Register(); procedure
into the source code, you'll find the following lines :
Procedure Register;
Begin
RegisterComponents('Samples',
[TQSpiderGraph]);
End;
The 'Samples' string indicates where the
component icone will go.
In the present case, it will appear upon
the 'Sample' palette. You can change this destination if you want.
-2a- Installing (D6PE):
The three files provided :
- QSpiderGraph.pas
(the source)
- QSpiderGraph.dcu (the Delphi
Compiled
Unit)
- QSpiderGraph.dcr
(the component
ressources file = the icon)
must have been unzipped in a directory known by the IDE, for example,
your '.\delphiN\lib' one.
If you want to install it elsewhere,
make sure to add the path of this directory in Delphi's Path list.
Once unzipped :
- Open Delphi's IDE. Then, use the 'close everything' menu item to
close openned files (if, for example, a project has been opened.)
- Select 'Install
Component'
from the 'Component'
menu ;
- The dialog now openning proposes two tabs : You can choose to
install the component in an existing package, or
in a new one.If you
choose to create a new package, you simply will have to give it a
name.
- Fill the 'unit
name' field, using the button to select QSpiderGraph.pas,
the file you've just unzipped in your .\lib\
directory.
- In the 'packet' window that has appeared, use the 'compile'
button.Delphi should display a message saying that the 'samples'
palette (or any other you choosed) has been updated to reflect
changes.
You're done, and can start using it like any other visual component,
changing its properties, aso.
-2b- Uninstalling (D6PE):
This a bit quicker than installing, but
the path is almost the same :
- Use 'instal packet' menu, to have the installed packages dialog
appear.
- Select the one you installed QSpiderGraph into ;
- Use the 'modify' button. Answer 'Yes' to the warning screen about
the window change ;
- In the dialog box that you now know, select anything linked to
QSpiderGraph, and use the 'remove' button to remove it.
- Don't forget to compile this package, when done. That's all.
3. Lines values have to be of type
"Single".
TQSpiderGraph displays lines
representing values. These values are bound in a range, corresponding
to each axe maxValue and minValue (each axe max and min can be set
independently);
The graph knows what to display once you have passed to it as many
values per line as the graph has of axes. (Adding lines (and their
values) to a graph can be done in two different ways, both shown below).
The graph waits for a particular type of datas :
The lines' values have to be Single.
This was the easiest type to handle : Converting integers to single can
be easilly made throught something like :
mySingleValue := myOriginalInteger /
1;
Whereas converting single to integers would have imply rounding
resulting in a loss of precision.
Siblingly, the Real Type seemed to be much above what should be needed.
If your data are of type real, (and are above the max value of a
single...) you'll have to convert them to singles, but losing some
precision.
3. Properties and methods
Types used in public and published properties (see below) :
TQSingleArray
= Array of Single;
TQBackGround
= (bgTransparent, bgColored, bgTopBottom, bgBottomTop,
bgLeftToRight, bgRightToLeft);
TQTitlePosition =
(tpTop, tpBottom);
TQBorderStyle
= (bsNone, bsFlat, bs3D);
TQHighlightMode =
(hmShowPoints, hmWidened, hmColorLine, hmFlashLine);
TQSpiderGraph
public & published properties :
At the graph object instance level, properties and method dealing with
lines and axes do apply globally to all
the lines and/or axes.
Each line and each axe can obviously be reached independantly too,
using the specific properties of the wanted object instance (see
axes and lines properties.)
For example, the boolean property aGraph.AxesAutoSized := True would set the
autoSized property of both Max and
min of each axe of the graph to True.
To notify the graph that it's second axe (zero based-> [1]) min
property has to be true, and this one only, you would have to reach it
explicitely :
aGraph.Axes[1].autoSizeMin := True;
Published properties.
Component layout :
property Anchors;
// Usual anchors property.
property borderStyle : TQBorderStyle;
// TQBorderStyle
=
(bsNone,bsFlat,bs3D);
// The shape of the component;
property backGround
: TQBackGround;
// TQBackGround
=
(bgTransparent, bgColored, bgTopBottom, bgBottomTop, bgLeftToRight,
bgRightToLeft);
// The whole component background.
// . bgTransparent makes it
transparent ;
// . bgColored will draw the
color given in the "backGroundColor"
property (bellow) ;
// . bgTopBottom,
bgBottomTop, bgLeftToRight, bgRightToLeft will draw a gradient,
// using the
"backGStartColor" and "backGFinalColor" propeprties.
Note
that the component background will be drawn everywhere (either
transparent, gradient or single color) if the graph property polygonFill (bellow) is set to False. Otherwise, the
polygone area of the graph (defined by the line linking it's summits)
will be filled independantly, using the graph's polygonColor property.
property backGroundColor
: TColor;
// The color to be used for the graph's
background, if TQBackGround (above)
is set to "bgColored";
property backGStartColor
: TColor;
// Gradient first color to be used for
the graph's background, if TQBackGround
(above) is set to one of the 4
gradient values ;
property backGFinalColor
: TColor;
// Gradient last color to be used for
the graph's background, if TQBackGround
(above) is set to one of the 4
gradient values ;
property titleCaption
: String;
// The title of the graph.
// It's color, size, aso will depend on
the PUBLIC property "titleFont :
TFont" (public properties, bellow) ;
property titlePosition
: TQTitlePosition;
//
TQTitlePosition = (tpTop,tpBottom);
// If tpTop, the title is drawn at the
top of the graph, and the lines captions box at the bottom ;
// If tpBottom, the title is drawn at
the bottom of the graph, and the lines captions box at the top ;
Axes
(Generic accesses):
property polygonFill
:
Boolean;
// Default := False;
// If true, the polygon linking
the axes' summits will be filled with the polygonColor below.
// If False, it will be
transparent (ie. the graph's background will show).
property polygonColor
: TColor;
// Default := clWhite;
// See above.
property
axesColor
: TColor;
// The color of the axes and the polygon
joining their summits.
property
axesCount
: Integer;
// The number of axes of a graph.
// Can't be bellow 3.
// Resetting
this property resets the
lines collection too.
// In other words : Set the axes
count,
//
Then set the lines count
//
Then populate the lines values.
property axesCaptionsFramed
: Boolean;
// If True, the captions near each axe
summit are surrounded by a frame.
// Apply to all axes;
property axesAutoSized
: Boolean;
// This is a global setter/getter.
// Setting it (to TRUE or FALSE) will
call the autoSizeMin and autoSizeMax method of each
// axe of the graph, passing it the
value received.
// The setter works on both min AND max
properties of each axe ;
// The getter returns True if ALL the
min and max properties of ALL the axes have been set to true using the
global setter;
// Each axe's minValue or maxValue can be reached
independently too (see
axes properties).
// (See also some more detailed
information about axes behaviour when
values to display are
outside their theorical range).
Lines (generic accesses) :
property defaultPenWidth
: Word;
// This is the lines defaultPenWidth. It
defaults to 2 pixels and can range from
1 to 255;
// Each line penWidth can be
changed independantly from others too, accessing it directly
// (myGraph.Line[i].penWidth:= some_value_between_1
&_255;)
property showLinesPoints
: Boolean;
// Write
only property.
// If TRUE, the points of all the lines
are drawn
(little circles).
// To have only the points of a
particular line be drawn, either access the showPoints property of
// this particular line directly (myGraph.Line[i].showPoints := True;), or
use the HighlightLineByIndex(index :
Integer);
// public procedure of your graph,
after having set the graph's highlightMode
property to hmShowPoints (see
public properties, bellow) ;
property showLinesCaption:
Boolean;
// If TRUE, a box is drawn on the graph,
containing
each line's caption and (at the left of the caption) a little rectangle
filled with it's color.
// This box will
be drawn at the opposite side of the title :
// -> at the
bottom of the graph if the titlePosition
is set to tpTop
// -> at the
top of the graph if the titlePosition
is set to tpBottom;
property linesBoxTransparent
: Boolean;
// If True the graph background appears
bellow this box, otherwise, the background of the box will be drawn
using the linesBoxColor.
property linesBoxColor.: TColor;
// The color to use to draw the
background of the box above, if linesBoxTransparent is set to
True;
property highlightColor
: TColor;
// The color to be used when
highlighting or flashing a line.
MouseMoves :
property trackMouseMoves
:
Boolean;
// Default := True;
// If False, mouse moves are not tracked
at all :
// -> The "mouseBox" won't ever
appear ;
// -> The events "MouseEnterLine" and
"MouseExitLine" are not
emitted;
// Then, if you write some code in the
two corresponding published event handler, but
// trackMouseMoves
is set to False, this
code won't ever execute.
property showMouseBox
: Boolean;
// The box that is displayed by the
graph when the
mouse is above a line.
// This box shows the line's caption,
and the values of each of
the axes for this line.
// If set to False, this box will no
longer appear. However, the "MouseEnterLine"
and
// "MouseExitLine"
events (bellow) )will still be
emitted.
property mBoxForColor
: TColor;
// The font color of EACH MouseBox above.
// SEE BELOW "mBoxUsesLnColor"
property mBoxBackColor
: TColor;
// The background color of EACH MouseBox
above.
// SEE BELOW "mBoxUsesLnColor"
property mBoxUsesLnColor
:
Boolean;
// Default := False;
// If TRUE, EACH "mouse box" will use
it's corresponding line's color as
font color;
// Otherwise, all these boxes font color
will be set to "mBoxForColor";
property mBoxParent
: TWinControl;
// Defaults to the graph instance itself.
// It can be set either to your Form
(using, for example "self" in
the constructor
// of your form), or "Nil." (see the demo source code).
// Assigning the form as parent to the
boxes showing the lines values allows them
// to be drawn on the whole form.
Otherwise, (if Nil is passed),
the box won't go
// beyond the limits of the graph.
property OnMouseEnterLine:
TQMouseLineEvt;
property OnMouseExitLine
: TQMouseLineEvt;
// TQMouseLineEvt
= procedure(Sender:
TObject; const lineIndex: integer)
of object;
// In both these event handlers,
lineIndex is the index of
the line the mouse has just entered or leaved.
// It can be used to reach specific
properties of this line, like it's color,
caption, values...
Other public properties :
constructor
Create(aOwner :
TComponent); override;
// Allow to dynamically create graph
instances at run-time ;
destructor Destroy; override;
// Allow to Free instances of
TQSpiderGraph, if you didn't set their "owner" property.
axesFont
: TFont;
// Font used to draw the axes captions
(near to axes summits);
titleFont : TFont;
// The graph title Font ;
// Default properties set are :
// .Size := 12;
// .Color := clBlue;
// .Style := [fsBold];
linesCpnFont : TFont;
// Font used to draw the box showing
the lines names and their associated colors.
property linesCount
: Integer;
// To be set BEFORE working with lines. Tells
the graph how many lines it will manage.
// When this property is set, the graph
initialises as many lines objet instances as needed.
// and they become ready to use.
property axes[ix:Integer] : TQSpiderAxe;
// Read
only property
// Gives access to each axe's public
properties.
// As long as TQSpiderGraph property "axes" is
implemented as an array of pointers to TQAxes instances,
// accessing this array with a bad index
will return NIL
(like in any other similar object, for example,
// a StringList), and an EInvalidPointer exception
will occur.
property lines[ix:Integer] : TQSGLine;
// Read
only property
// Gives access to each axe's public
properties.
// As long as TQSpiderGraph property "lines" is
implemented as an array of pointers to TQLines instances,
// accessing this array with a bad index
will return NIL
(like in any other similar object, for example,
// a StringList), and an EInvalidPointer exception
will occur.
property line :
TQSGLine;
// Read
only property
// Gives access to the "aGraphInstance.lines[0]".
Handy access to the first line object of a graph,
// mainly usefull if a graph has only
one line.
// (a graph has allways at least one
line (a "line[0]"), even if this line has no values.)
Sorting & Highlighting lines
Spider graphs are usefull to allow your user to visually compare sets
of values. To go further with this idea, TQSpiderGraph provides means
to "highlight" lines as needed : You may want to temporarily highlight
the line which area is the widest, or the smallest, or the line which
has the highest value on a particular axe, aso.
In order to do that, some methods are
available. They all rely on the graph's highlightMode which is a
combination of several states :
highlightMode : Set of TQHighlightMode;
//
TQHighlightMode = (hmShowPoints,
hmWidened, hmColorLine, hmFlashLine);
// Decide how selected lines will be
highlighted.
// As long as it is a "Set of", it can
be a combination of any of the following :
// hmShowPoints : The points
of the selected line(s) will be drawn. Other won't.
//
hmWidened :
The selected line(s)' pen width will be
the double of its default one (this_particular_Line.penWidth);
// hmColorLine : The
line color will be temporarilly overrided using the graph's
highlightColor;
// hmFlashLine :
The
line will "Flash" some momments, alternating it's color and the graph's
highlightColor .
// Any line or group of lines can be
highlighted, either directly (by index) or to
// show the result of a kind of "sort to
higlight"
request (See procs
just below).
Here follows a list of the
different procedures and
functions available :
a- Highlighting lines :
procedure HighlightLineByIndex(index :
Integer); overload;
// request the graph to highlight the
line whose index is "index".
procedure
HighlightLineByIndex(indexArray:TIntegerDynArray); overload;
// request the graph to highlight the
lines whose indexes are in the array "indexArray".
procedure HighlightLineByCrit
(criteria:Integer;
maxWanted: Boolean = True);
// Request the graph to highlight
the
"best"
line, for such or such criteria.
// If maxWanted is set to True, "best" will
mean "Highest",
otherwise it will mean "Lowest".
// criteria
can be :
// " -2" : "Sort
by Area" : The line(s)
with the "best"
area will be highlighted ;
// " -1" : "Don't
highlight any line" :
Resets all the lines to their "standard" mode ;
// " 0 .. axesCount-1"
: Highlight the
line(s) with the "best" value
on this particular axe.
// Note : TQSPiderGraph defines
two
constants that can be used with this function :
// Const HC_NONE = -1;
// Const HC_AREA = -2;
// So that calls like "myGraph.HighlightLineByCrit
(HC_NONE);" or
"myGraph.HighlightLineByCrit
(HC_AREA);"
// are fully ok (the first one
will
simply reset all lines to their "usual" state).
// Any call to this procedure with
a criteria outside the
range [HC_AREA .. axesCount-1] will set
// criteria's value to HC_NONE thus
resetting the lines' state.
The "highlight" state of
a line will stay on the graph until you reset it, using for example a
call to :
"myGraph.HighlightLineByCrit (HC_NONE);"
|
|
b- Retrieving lines indexes, without
highlighting :
function GetBestLineByArea (maxWanted: Boolean = True) :
TIntegerDynArray;
// Returns an array containing the
indexes of the lines with the widest or smallest area,
// depending on maxWanted.
// Usually contains one index
only. However, in the case where several lines had the exact same area,
// all their indexes would be
returned (and Length(result_of_the_function_call) would
tell you how many they are) .
// Returns [-1] if nothing could be
found (would occur for example if no lines had values, yet).
function
GetBestLineByAxe(axeIx:Integer;
maxWanted: Boolean = True) : TIntegerDynArray;
// Returns an array containing the
indexes of the lines with the greatest or smallest value for
// the axe axeIx,
depending on maxWanted.
// Usually contains one index
only. However, in the case where several lines had the exact same value
// for this particular axe, all
their indexes would be returned (and
Length(result_of_the_function_call)
// would tell you how many they
are) .
// Returns [-1] if nothing could be
found (would occur for example if no lines had values, yet).
Quickly adding or deletting lines :
TQSpiderGraph provides two means to add lines. The first one is to
first declare the number of lines a graph will manage, then access in a
loop the Values[i] property of each line, as detailed bellow.
Setting the linesCount property will have the graph create all the
needed lines, aso.
The other way is to use it's "AddLine();"
methode, which is a function which returns the index of this new line.
function
AddLine(Const vals:TQSingleArray) : Integer;
// (Note : TQSingleArray = Array of Single;)
// Adds a line to the graph,
passing to
it it's values directly ; (see bellow an example
using AddLine);
// If you look at the source,
you'll see that each time a graph is created, an empty line is added
// to its line collection (the
".Lines[0]"). If this "line[0]" has not yet values when you
first call
// "AddLine();", this new one will
become
Line[0]. Otherwise
the new one will be
basically
// added to the actual collection.
// The function returns the index of
this new line (and -1 if a
problem occured...)
// This index is to be used to set up
particular properties of this line,
// (like its color, its caption, aso.).
function RemoveLine(lineIndex
:Integer) : Boolean;
// Removes the line[lineIndex] from the
graphCollection.
// If lineIndex is below zero or above
linesCount -1, the function returns False (and does nothing...);
// otherwise, it returns True;
TQSpiderGraph
stores two kind of objects instances, that you'll have to manipulate at
run-time : It's axes and
the lines.
Both are arrays of pointers to instances of... (TQSGLines and
TQSpiderAxes)
Both will raise Exceptions if you try to acces an element out of the
bounds of the corresponding array ;
Both array's lengths can be known through a reading of the
corresponding
Count property :
aGraph.AxesCount;
(Integer)
aGraph.LinesCount;
(Integer)
Accesses are then of the kind :
aGraph.Lines[2].Color
:= clGreen;
aGraph.Axes[5].autoSized := True;
Or, if for example you want to give your user the possibility to see only the lines whose values are
above a certain limit (say 50)
for one of its axes (the fourth one) :
For i := 0 To myGraph.linesCount
-1
Do
If
myGraph.Lines[i].Values[3] < 50
Then myGraph.Lines[i].visible
:= False;
myGraph.Invalidate;
// Visible doesn't call it's
parent invalidate, so, do it
yourself...
( which is - obviously - equivalent to :
For i := 0 To myGraph.linesCount
-1
Do
myGraph.Lines[i].visible
:= ( myGraph.Lines[i].Values[3] < 50 ) ;
myGraph.Invalidate;
// Visible doesn't call it's
parent invalidate, so, do it
yourself... )
Axes and
lines public properties and
methodes are :
Axes ( myGraph.Axes[i].XXX ):
caption :
String;
// The caption to be drawn beside this
axe summits;
// It defaults to this axe index into
the constructor.
property autoSizeMin
: Boolean;
// Default : False;
// If set to True, This axe's min will
be set to
// (actual min - one tweniest of its
actual min and max values).
property autoSizeMax
: Boolean;
// Default : False;
// If set to True, This axe's max will
be set to
// (actual max + one tweniest of its
actual min and max values).
property autoSized:
Boolean;
// At reading, returns TRUE if AT LEAST
ONE of the two properties (autoSizeMax or autoSizeMin)
// is set to TRUE.
// In Write access, will set BOTH min
and max to Autosized.
property minValue
: Single;
// Allows to read or set this axe's
actual min value;
// Setting it will reset the
autoSizeMin property of this axe to False. However, if you soon
// passed some values for this axe
(by setting some lines values), and the minValue you are
// trying to define is greater
than the actual lowest existing value for this axe, the the new minValue
// will be discarded.
property maxValue
: Single;
// Allows to read or set this axe's
actual max value;
// Similarly to above, it is not
possible to set as maxValue a value lower than the actual max value
// received by this axe (if it soon has
some).
About
axes maxValue, minValue, and autosizing :
Please note that the axes allways change their
minValue and
maxValue when you send
values outside the actual range of an axe whatever
is the state of their autoSized
properties.
For example, if you change nothing to the axes default max and min of a
graph, they are set to 0 and 100. But if one of your lines has the
following values :
[10, 180, 40, 32],
then the maxValue of the
seconf axe ("axe[1]") will be changed to adapt to this "180" value
you sent, and which is far above it's actual maxValue. This is needed because :
- Either the graph
changed nothing, and lines could have been drawn anywhere outside the
graph's area...
- Or it would have had
to change the received values, to have them fit
into each axe. Changing your values (to set them to the axe's max or
min
didn't seem a good idea either, nor did simply drawing them at the max
or min position, because they are not at "max" but above or bellow.)
- Or stayed the
possibility to change axes max or min whenever needed, in order to
adapt
them to what you send. This is what the graph does.
This dynamic changing of the max and
min of an axe are a different thing from the axesAutoSized property seen above. The autoSized property of axes
or of an axe tell this axe to change its
extrems to narrow them from
the values you sent, whatever they are.
Thus, if an axe had only these values to display :
[0.43,
0.45,
0.4, 0.32]
but its actual min and max
where, say, 0..100, then autosizing this axe would result in the axe
defining its new max and min so that they would be :
- minValue := the
lowest value received - 1/20st of the total
range -> 0.32 - ((
0.43-0.32) / 20) = 0,3145
- maxValue := the
highest value received + 1/20st of the total
range
-> 0.43 + ((0.43-0.32) / 20) = 0,4355
In a way, the behaviour
first described only deals with datas OUTSIDE the theorical range
whereas the last one deals deals with both narrow range / wide range
datas.
|
Lines ( myGraph.Lines[i].XXX ):
caption :
String;
// The caption of this line. It is the
one to be drawn in the
// Lines captions box (beside it's
color), if this one is displayed.
// It defaults to this axe index into
the constructor.
property
color
:
TColor;
// This line color;
// Default : clRed;
property penWidth :
Word;
// This line penWidth;
// Default : TRUE;
property
values :
TQSingleArray ;
// (Note : TQSingleArray = Array of Single;)
// The values of this line. One by axe.
property showPoints :
Boolean ;
// If True, this line's points (little
circles) will be drawn.
property visible
: Boolean;
// Allows to temporailly hide a specific
line.
// Visible DOESN'T call the invalidate
methode of the graph, so that nothing will change untill :
// -> some other property is changed,
// -> you explicitely call the graph
"invalidate" methode.
procedure Hide;
// Hides a line.
// Opposite to "visible" behaviour",
calls the graph "invalidate" property.
procedure Show;
// Shows back a previously hidded line.
// Opposite to "visible" behaviour",
calls the graph "invalidate" property.
Defining axes example :
As long as SpiderGraph instances should most often be defined
once only, and not evolve at run-time, the published properties of the
Axes collection should be used at design-time to set-up this part of
the graph (axesCount, axesColor, polygonFill or not, aso.).
Then, some properties will stay to be defined at run-time.
This is the case with the axes maxValue and minValue, their captions,
aso. :
To have all axes autosize themselves, a good idea is to set the
axesAutoSized property before adding lines values.
aGraph.axesAutoSized := True;
The Following graph's axes have been defined partly in the IDE, then in
the code, when the time comes to show the graph :

|
With QSPiderGraph1 Do Begin // Caption could have been set in the IDE too. titleCaption := '- Cars benchmark -'; linesCpnFont.Color := RGB(128,128,128); axesFont.Color := RGB(128,128,128); linesBoxColor := QSPiderGraph1.polygonColor; axesAutoSized := True; End;
// Now dealing with axes : QSpiderGraph1.axesCount := 6; QSpiderGraph1.axes[0].caption := 'Horse power'; QSpiderGraph1.axes[1].caption := 'Weight (kg)'; QSpiderGraph1.axes[2].caption := 'Cylinders'; QSpiderGraph1.axes[3].caption := 'Accel. : 0..100 km/h (in ")'; QSpiderGraph1.axes[4].caption := 'Top speed'; QSpiderGraph1.axes[5].caption := 'Price (USD)';
// ---
|
(The datas shown on this
graph are for demonstration only, come from several sources on the net
and may be totaly wrong...)
Defining lines example :
Here again, some properties should be defined if possible at
design-time. But (at least) the lines values will have to be set at
run-time.
As said earlier, there are several ways to add lines to a graph.
The first one is to add all the lines at once, in a loop.
This is done through accessing the Lines[i].Values property.
For example, in the graph above, the lines values can be added this way
(an other way is illustrated bellow):
The thin way :
You've got an array of singles containing some lines of values ( the
values corresponding to the graph's axes ).
Each line contains the datas of a line of the graph.
Eg : ("source" is the array where your datas are actually stored : ).
source := [
52
|
2303
|
12
|
4.8
|
290
|
149900
|
580
|
1650
|
12
|
3.5
|
330
|
279900
|
aso.
|
|
|
|
|
|
]
The code could look like :
|
The fat way.
The code of the left column corresponds to the following :
|
// vars : var aQSArray : TQSingleArray; // TQSingleArray = array of single; // is defined in QSpiderGraph.pas ; Begin {...} QSpiderGraph1.linesCount := numOfLinesToAdd; SetLenght(aQSArray,numOfcolsOfDatas); For lineCount := 0 To numOfLinesToAdd -1 Do // Do as much times as there are lines to add : Begin // Fills the array to send with the values of one line. For columnIndex := 0 To numOfcolsOfDatas -1 Do aQSArray[columnIndex] := source[lineCount,columnIndex]; // Adds these values to the correspondinf line mySGraph.Lines[lineCount].Values := aQSArray; mySGraph.Lines[lineCount].caption := 'Line number ' + IntToStr(lineCount); End; // Once done, you will have to define a color // for each of your lines, and the job is done. End;
|
// Adding lines to the graph :
// vars : var anArray : TQSingleArray; // TQSingleArray = array of single; // is defined in QSpiderGraph.pas ; Begin {...} // -1- Setting the lines count to have the graph prepare evthg QSpiderGraph1.linesCount := 5; // Now sizing a TQSingleArray, and populate it SetLength(anArray,6); anArray[0] := 552; // HP anArray[1] := 2303; // Weight anArray[2] := 12; // Cyls anArray[3] := 4.8; // Accel anArray[4] := 290; // TopSpeed anArray[5] := 149900; // $ // Adds these values to this line QSpiderGraph1.lines[0].values := anArray; // And sets this line's particular properties QSpiderGraph1.lines[0].color := RGB(145,105,126); QSpiderGraph1.lines[0].caption := 'Bentley Continental GT';
// And do the same again, with all our lines. anArray[0] := 580; anArray[1] := 1650; anArray[2] := 12; anArray[3] := 3.5; anArray[4] := 330; anArray[5] := 279900; QSpiderGraph1.lines[1].values := anArray; QSpiderGraph1.lines[1].color := RGB(0,147,195); QSpiderGraph1.lines[1].caption := 'Lamborghini Murcielago (coupe)';
anArray[0] := 381; anArray[1] := 1360; anArray[2] := 6; anArray[3] := 4.4; anArray[4] := 306; anArray[5] := 143900; QSpiderGraph1.lines[2].values := anArray; QSpiderGraph1.lines[2].color := RGB(255,183,209); QSpiderGraph1.lines[2].caption := 'Porsche 911 (GT3 RS)';
anArray[0] := 655; anArray[1] := 1170; anArray[2] := 8; anArray[3] := 3.5; anArray[4] := 390; anArray[5] := 390000; QSpiderGraph1.lines[3].values := anArray; QSpiderGraph1.lines[3].color := RGB(215,164,45); QSpiderGraph1.lines[3].caption := 'Koenigsegg CC';
anArray[0] := 500; anArray[1] := 1950; anArray[2] := 12; anArray[3] := 4.7; anArray[4] := 250; anArray[5] := 126000; QSpiderGraph1.lines[4].values := anArray; QSpiderGraph1.lines[4].color := RGB(154,186,144); QSpiderGraph1.lines[4].caption := 'Mercedes-Benz SL 600';
// And that's done for the Graph
|
An other way to add lines to a
graph is the AddLine(); function.
This function will add one line to a
graph each time it is called ;
It's prototype is :
function AddLine (Const vals:TQSingleArray) : Integer;
> It waits for a pointer at an array of single, as above. This Type
is defined in TQSpiderGraph.pas unit
TQSingleArray
= Array of Single;
> It returns an integer, which is the index of the line that has
just been added. This index allows then to set up the specific
properties of this line, like it's color, it's particular penWidth, aso.
The above example could then have been rewrited the following way
:
// vars :
var ix :
Integer;
// indexes the AddLine(); function will return.
aQSArray :
TQSingleArray;
// TQSingleArray = array of single;
// is defined in QSpiderGraph.pas ;
Begin
{...}
SetLength(aQSArray, numOfColsOfDatas);
For lnCount := 0 To
numOfLinesToAdd -1 Do
Begin
// -1- Fills the array
to send to the line
For dataCount :=
0 To
numOfcolsOfDatas-1 Do
aQSArray[dataCount] := source[dataCount];
// -2- Then Add a line with these
datas to the collection
// Note : If a problem
occured, the returned index = "-1";
ix := mySGraph.AddLine(aQSArray);
If ix < 0 Then
ShowMessage ('Unable to add this
line...')
Else mySGraph.Lines[ix].caption
:=
'Line number ' +
IntToStr(lnCount +1);
End;
End;
The demo provided contains examples of accessing almost all the
properties of a QSpiderGarph instance. Have a look at it's source.
Displaying
axes' max and
min properties (and others, too) :
The component does a lot, but not everything, and sometimes you'll be
alone with yourself to do some things.
Displaying the component's axes'
max and min values is an example of this :
As long as each axe max and / or min value can be set independantly,
displaying their values on the graph would have result in something
impossible to read, specialy when minValues are considered, cause they
would have been drawn so close to the center of the graph, that they
would have overlap each other, resulting in something hugly.
However, it is easy (and I do agree : a little boring...)
to display them "by hand" near the component itself :
You could put a graph on a panel, leaving enough space on one of
the sides of the panel to put there as many labelss as there is axes
in the graph, and then somewhere in your code, where you define the
component (for example in the procedure adding lines to this graph) add
some lines of the kind :
label1.caption : 'Axe_0 Minimum = ' + FloatToStStr(myGraph.Axes[0].minValue);
label2.caption : 'Axe_0 Maximum = ' + FloatToStStr(myGraph.Axes[0].maxValue);
aso.
You could display them in anything else too, like a TListView, a
TStringGrid, a little memo, aso.
Displaying line's values, when
there is a lot of axes :
As you may have see in the demo, the "yellow box" poping-up when the
mouse is over a line is sometines so big that it is not possible to
read everything.
This shouldn't be so frequent in real usage, because it would certainly
not be a good idea to provide to your user graphs with tons of axes at
once : Readibility would suffer a lot...
However, the same problem will appear if the graph is relatively
little.
In such cases, you could set the graph's showMouseBox property to
False, and react yourself to the "MouseEnterLine"
and "MouseExitLine"
events.
Each time one of this events is fired, it gives you the index of the
line in question, allowing you to read (and then display wherever you
want) it's properties (including values, that you should in fact
already know).
( Top )