Charts help to understand long series of numerical values better. To see what this means just Copy & Paste PROGRAM2 at the end of this article into the Command window and run it (for newbies only: select all lines of code in command window before you press Enter).
That's why VFP has its own tools for creating graphs. If you need a chart from table values occasionally or just need to make some preliminary analyze of numeric data and you need it quickly and efficiently, follow this way.
The idea is pretty simple and admonishes of about good old days of text orientated computing. Form the bars of the chart as a string of characters in one column of grid such a way, that the string length as seen on the screen correspondents to given numeric value. Some conditions must be met to obtain acceptable results.
Let's suppose we have a table with numeric field numField and with one extra field of character type tmp to be filled with bars. This field can be added temporarily just for displaying chart or calculated field can be used as well.
The "key operation" is to fill column with bars by putting the string with appropriate length into the field tmp for every record and it is in fact the only thing you must do.
REPLACE tmp WITH REPLICATE("I", lnMaxSize * numField / lnMaxValue) FOR ALL
* or using SQL
SELECT numField, PADR(REPLICATE("I", lnMaxSize * numField / lnMaxValue), lnMaxSize," ") AS tmp FROM table
"I" character as a basic element for creating bars of chart. Here uppercase letter I is used. Especially suitable if taken from some Sans Serif proportional bold font, e.g. ARIAL. Other candidates are characters from Wingdings fonts. Some of them even have no gap between each other.
lnMaxSize Number of elements to form maximum usable width of graph. The bigger is lnMaxSize, the higher is a resolution and less is the granularity. Big values are better but lead to wide graph of course and that's why the narrow proportional letter "I" is so useful here.
lnMaxValue expected maximal numeric value to be displayed. To keep control over the graph and avoid error messages we must set this value thoroughly. If we want the highest value to occupy full width of graph, we simply set lnMaxValue to maximal value from series by means of command CALCULATE MAX(numField) TO lnMaxValue. This is the safest method. In other situations we may want set lnMaxValue higher. For example if our values are in percents (but surely less then 100%) it might be advisable set lnMaxValue = 100
Don't forget: bars are still characters in TextBoxes and can be colored (even conditionally) at your will using SETALL command in conjunction with column properties DYNAMIC... It was described here many times (Thread184-343788 and so on). See also FAQ184-3098 with different approach to this problem.
Let's summarize this method:
PROS - Absolute simplicity - Very quick painting and refreshing - Universal - works with grids, browse windows and others controls with text property (not mentioned obsolete LIST, DISPLAY) - No DLLs, Classes, Libraries or third party elements - No place on disk - Easy to implement in existing projects - VFP version independent (in fact it is even VFP independent)
CONS - There is no easy way to add axis and scales to these charts. If you really insist on having some, you can construct those using abscissas from Shape base class. While lnMaxValue remains the same theirs positions need not to be changed.
- Unable to work with more series although quasi two series are possible with combination of letters like iiiiiiiiiiIIIIIIII
- It is neither 3-D nor 4-D. Graphical quality is low, if any. Happily there are other tools to use (mainly outside of VFP), if graphical effects are on the first place.
Here are two sample codes to play with
* PROGRAM1 * Sample program to illustrate * placing a quasi chart into a grid column
PUBLIC oform1, lnMaxSize, lnMaxValue lnMaxSize = 100
oform1=CREATEOBJECT("form1") oform1.Show RETURN
DEFINE CLASS form1 AS form DoCreate = .T. Caption = "Chart inside a grid - try change a field Area" Name = "Form1" Width = 530 Height = 220
ADD OBJECT grid1 AS Grid WITH ; Height = 182, ; Left = 20, ; Top = 20, ; Width = 485, ; Name = "Grid1", ; Font = "Arial"
PROCEDURE Init CREATE table tblTable (state c(20),numField n(6),tmp c(100)) INSERT INTO tblTable VALUES ("TEXAS",692407,"") INSERT INTO tblTable VALUES ("UTAH",219932,"") INSERT INTO tblTable VALUES ("VERMONT",24887,"") INSERT INTO tblTable VALUES ("VIRGINI",105711,"") INSERT INTO tblTable VALUES ("WASHINGTON",176617,"") INSERT INTO tblTable VALUES ("WEST VIRGINIA",62629,"") INSERT INTO tblTable VALUES ("WISCONSIN",145439,"") INSERT INTO tblTable VALUES ("WYOMING",253597,"") * and few more fictional states FOR n = 1 TO 1000 lcState = "" FOR m = 1 TO 3 + 9 * RAND() lcState = lcState + CHR(65 + 25 * RAND()) ENDFOR INSERT INTO tblTable VALUES (lcState,50000 + 600000 * RAND(),"") ENDFOR GO TOP WITH thisform.grid1 .columncount = 3 .DeleteMark = .F. .RecordMark = .F. .recordsourcetype = 1 .recordsource = [tblTable] .GridLineColor = RGB(192,192,192) .column1.width = 100 .column1.header1.caption = "State" .column1.header1.FontBold = .T. .column2.width = 55 .column2.header1.caption = "Area" .column2.header1.FontBold = .T. .column3.width = 360 .column3.fontbold = .T. .column3.forecolor = RGB(255,0,0) .column3.header1.caption = " Areas are expressed graphically here" .column3.header1.FontBold = .T. .column3.enabled = .F. ENDWITH ENDPROC
PROCEDURE grid1.AfterRowColChange * This code should better go to * ThisForm.Grid1.column2.text1.InteractiveChange proc, * but I don't know how to write it down in this program * (Column2 not found Error) LPARAMETERS nColIndex lnRecNo = RECNO() CALCULATE MAX(numField) to lnMaxValue REPLACE tmp WITH " " + REPLICATE("I", lnMaxSize * numField / lnMaxValue) ALL GOTO lnRecNo ENDPROC ENDDEFINE
* PROGRAM2 * Sample program to demonstrate strength (and beauty) * of graphical presentations of numbers
* need some table with numeric values CREATE CURSOR tmp (numbers N(4))
* let numbers follow elegant sine curve, * but use "error" values sometimes FOR n = 1 TO 1000 IF MOD(n,5) = 1 * random error value INSERT INTO tmp (numbers) VALUES (100 + 899 * RAND()) ELSE * pure sine INSERT INTO tmp (numbers) VALUES (100 + 449 * (1+SIN(n/8))) ENDIF ENDFOR GO TOP
* see numbers only first SELECT * FROM tmp INTO CURSOR WithOutGraph BROWSE TITLE "Do you think that these numbers a r e r a n d o m ? ... close to see"
* see both numbers and its graphical presentation - what a surprise SELECT numbers, PADR(REPLICATE("I", 100 * numbers / 1000),100," ") AS Graph FROM tmp INTO CURSOR WithGraph BROWSE TITLE "With graphical presentation you can see m o r e ... close to end" NOWAIT