Otra opción de una clase 100% VFP que permite incluir fácilmente gráficas de barras, torta, anillo y conos a nuestras aplicaciones, con la creación de objetos Shapes en un contenedor.
Gráficas con objetos 100% VFP
Por Luis María Guayán, Tucumán, Argentina
En estos últimos días he estado atento a los artículos de Cesar Chalom (San
Pablo, Brasil), referidos a los gráficos de barras y de torta generados con
código VFP puro. Estos artículos publicados en su
Blog y traducidos por Ana María Bisbé York para PortalFox son los
siguientes:
y
La técnica utilizada para crear estas gráficas, es mediante el uso de objetos Labels y
Lines respectivamente.
El objetivo de este artículo es tener otra opción (siempre VFP nos da esta
posibilidad) de generar gráficas con solo el uso del objeto Shape, sin depender del uso de ninguna
otra herramienta de
terceras partes.
La idea
Con el uso del objeto Shape es fácil crear barras rectangulares configurando
las propiedades Width (ancho) y Height (alto) con las dimensiones
correspondientes, según la cantidad de barras del gráfico y el valor a graficar.
El tema aquí es como graficar las distintas porciones de una torta (sección
circular) o las secciones de un anillo con este objeto.
A partir de Visual FoxPro 9.0, disponemos de la nueva propiedad PolyPoints en
el objeto Shape (también en el objeto Line), que nos permiten graficar formas poligonales (ver el
artículo
Dibujando polígonos con VFP 9.0). Con esta técnica, graficaremos cada
porción como un polígono con sus respectivas dimensiones, e iremos añadiendo las
restantes porciones hasta completar la gráfica como se observa en las figuras
siguientes.

Nota: Las opciones del tipo de gráfica torta, anillo y cono, solo se podrá ejecutar en la
versión 9.0 de Visual FoxPro, por el uso de la propiedad PolyPoints de los
objetos Shapes.
La clase
Como mencioné al principio de estas líneas, mi objetivo es generar gráficas
con código VFP puro, para ello he creado una clase a partir de un objeto
Container, al que se irán agregando en tiempo de ejecución los objetos Shape para formar
la gráfica y también se agregaran las correspondientes leyendas. Su uso es muy
fácil, y solo de deben configurar 5 propiedades y ejecutar el método
GenerarGrafica() como se expone en las siguientes líneas
de código:
WITH THISFORM.lmGraph
.TipoGrafica = 1 && Anillo
.TipoLeyenda = 3 && Rótulos
.TipoColor = 0 && Aleatorios
.TituloGrafica = "Consumos por mes"
.Alias = "MiTabla"
.GenerarGrafica()
ENDWITH
Las propiedades de la clase y sus valores son los siguientes:
- TipoGrafica: 0=Torta, 1=Anillo, 2=Barras verticales, 3=Barras horizontales,
4=Conos verticales, y 5=Conos horizontales
- TipoLeyenda: 0=Sin leyendas, 1=Valores, 2=Porcentajes, 3=Rótulos,
4=Rótulos y valores, y 5=Porcentajes y rótulos
- TipoColor: 0=Aleatorios y 1=Colores básicos (28 colores definidos)
- TituloGrafica: Cadena con el título superior de la gráfica
- Alias: Alias de la tabla o cursor que contiene los rótulos y valores a
graficar
Sobre la tabla o cursor que contiene los datos a graficar, ésta debe estar
abierta al momento de ejecutar el método GenerarGrafica(), y debe
tener al menos dos campos. El contenido y la estructura de los dos primeros
campos debe ser:
- 1° Campo: Este campo contiene los valores y debe ser Numérico.
- 2° Campo: Este campo contiene los rótulos y puede ser de tipo Caracter, Date,
DateTime o Numérico.
Los ejemplos
Ejecutando el formulario de ejemplo con la clase incluida, podemos lograr
gráficas simples y agradables como lo muestran las figuras siguientes:
Gráfica de torta, con leyenda de porcentajes y rótulos, y colores
aleatorios

Gráfica de anillo, con leyenda de rótulos y valores, y colores básicos

Gráfica de barras verticales, con leyenda de rótulos, y colores aleatorios

Gráfica de barras horizontales, sin leyenda, y colores básicos

Gráfica de conos verticales, con leyenda de porcentajes, y colores
aleatorios

Gráfica de conos verticales, con leyenda de valores, y colores
aleatorios

El formulario del ejemplo tiene una casilla de verificación (DEMO) que
marcada, generará aleatoria y automáticamente cada 2 segundos, las distintas
combinaciones posibles para cada una de las propiedades de la clase.
El código
El siguiente es el código del formulario de ejemplo y la clase para generar
las gráficas:
PUBLIC goForm
goForm = CREATEOBJECT("frmEjemplo")
goForm.SHOW
RETURN
*--
*-- Definición del formulario de ejemplo
*--
DEFINE CLASS frmejemplo AS FORM
HEIGHT = 431
WIDTH = 496
SHOWWINDOW = 2
AUTOCENTER = .T.
CAPTION = "Gráficas con VFP9"
ICON = (HOME(4) + "icons\office\graph11.ico")
NAME = "frmEjemplo"
ADD OBJECT tmr AS TIMER WITH ;
ENABLED = .F., INTERVAL = 2000, NAME = "tmr"
ADD OBJECT opgColores AS OPTIONGROUP WITH ;
BUTTONCOUNT = 2, ANCHOR = 6, VALUE = 1, ;
HEIGHT = 48, LEFT = 344, TOP = 280, WIDTH = 136, ;
TABINDEX = 4, NAME = "opgColores", ;
Option1.BACKSTYLE = 0, Option1.CAPTION = "Colores aleatorios", ;
Option1.VALUE = 1, Option1.HEIGHT = 17, Option1.LEFT = 8, Option1.TOP = 8, ;
Option1.WIDTH = 120, Option1.AUTOSIZE = .T., Option1.NAME = "Option1", ;
Option2.BACKSTYLE = 0, Option2.CAPTION = "Colores básicos", ;
Option2.VALUE = 0, Option2.HEIGHT = 17, Option2.LEFT = 8, Option2.TOP = 24, ;
Option2.WIDTH = 109, Option2.AUTOSIZE = .T., Option2.NAME = "Option2"
ADD OBJECT cmdGenerar AS COMMANDBUTTON WITH ;
TOP = 392, LEFT = 376, HEIGHT = 32, WIDTH = 104, ;
ANCHOR = 6, WORDWRAP = .T., CAPTION = "Generar gráfica", ;
TABINDEX = 9, NAME = "cmdGenerar"
ADD OBJECT opgGraficas AS OPTIONGROUP WITH ;
BUTTONCOUNT = 6, ANCHOR = 6, VALUE = 1, ;
HEIGHT = 112, LEFT = 16, TOP = 280, WIDTH = 144, ;
TABINDEX = 2, NAME = "opgGraficas", ;
Option1.BACKSTYLE = 0, Option1.CAPTION = "Torta", ;
Option1.VALUE = 1, Option1.HEIGHT = 17, Option1.LEFT = 8, Option1.TOP = 8, ;
Option1.WIDTH = 46, Option1.AUTOSIZE = .T., Option1.NAME = "Option1", ;
Option2.BACKSTYLE = 0, Option2.CAPTION = "Anillo", ;
Option2.VALUE = 0, Option2.HEIGHT = 17, Option2.LEFT = 8, Option2.TOP = 24, ;
Option2.WIDTH = 48, Option2.AUTOSIZE = .T., Option2.NAME = "Option2", ;
Option3.BACKSTYLE = 0, Option3.CAPTION = "Barras verticales", ;
Option3.HEIGHT = 17, Option3.LEFT = 8, Option3.TOP = 40, Option3.WIDTH = 110, ;
Option3.AUTOSIZE = .T., Option3.NAME = "Option3", ;
Option4.BACKSTYLE = 0, Option4.CAPTION = "Barras horizontales", ;
Option4.HEIGHT = 17, Option4.LEFT = 8, Option4.TOP = 56, Option4.WIDTH = 125, ;
Option4.AUTOSIZE = .T., Option4.NAME = "Option4", ;
Option5.BACKSTYLE = 0, Option5.CAPTION = "Conos verticales", ;
Option5.HEIGHT = 17, Option5.LEFT = 8, Option5.TOP = 72, Option5.WIDTH = 110, ;
Option5.AUTOSIZE = .T., Option5.NAME = "Option5", ;
Option6.BACKSTYLE = 0, Option6.CAPTION = "Conos horizontales", ;
Option6.HEIGHT = 17, Option6.LEFT = 8, Option6.TOP = 88, Option6.WIDTH = 125, ;
Option6.AUTOSIZE = .T., Option6.NAME = "Option6"
ADD OBJECT opgLeyendas AS OPTIONGROUP WITH ;
BUTTONCOUNT = 6, ANCHOR = 6, VALUE = 6, ;
HEIGHT = 112, LEFT = 176, TOP = 280, WIDTH = 152, ;
TABINDEX = 3, NAME = "opgLeyendas", ;
Option1.BACKSTYLE = 0, Option1.CAPTION = "Sin leyendas", ;
Option1.HEIGHT = 17, Option1.LEFT = 8, Option1.TOP = 8, Option1.WIDTH = 89, ;
Option1.AUTOSIZE = .T., Option1.NAME = "Option1", ;
Option2.BACKSTYLE = 0, Option2.CAPTION = "Valores", ;
Option2.HEIGHT = 17, Option2.LEFT = 8, Option2.TOP = 24, Option2.WIDTH = 60, ;
Option2.AUTOSIZE = .T., Option2.NAME = "Option2", ;
Option3.BACKSTYLE = 0, Option3.CAPTION = "Porcentajes", ;
Option3.HEIGHT = 17, Option3.LEFT = 8, Option3.TOP = 40, Option3.WIDTH = 84, ;
Option3.AUTOSIZE = .T., Option3.NAME = "Option3", ;
Option4.BACKSTYLE = 0, Option4.CAPTION = "Rótulos", ;
Option4.HEIGHT = 17, Option4.LEFT = 8, Option4.TOP = 56, Option4.WIDTH = 61, ;
Option4.AUTOSIZE = .T., Option4.NAME = "Option4", ;
Option5.BACKSTYLE = 0, Option5.CAPTION = "Rótulos y valores", ;
Option5.VALUE = 0, Option5.HEIGHT = 17, Option5.LEFT = 8, Option5.TOP = 72, ;
Option5.WIDTH = 112, Option5.AUTOSIZE = .T., Option5.NAME = "Option5", ;
Option6.BACKSTYLE = 0, Option6.CAPTION = "Porcentajes y rótulos", ;
Option6.VALUE = 1, Option6.HEIGHT = 17, Option6.LEFT = 8, Option6.TOP = 88, ;
Option6.WIDTH = 133, Option6.AUTOSIZE = .T., Option6.NAME = "Option6"
ADD OBJECT lmGraph AS lmgraph WITH ;
ANCHOR = 15, TOP = 8, LEFT = 8, WIDTH = 480, HEIGHT = 264, ;
TABINDEX = 1, NAME = "lmGraph", lbl.NAME = "lbl"
ADD OBJECT chk AS CHECKBOX WITH ;
TOP = 404, LEFT = 304, HEIGHT = 17, WIDTH = 53, ANCHOR = 6, ;
WORDWRAP = .T., AUTOSIZE = .T., ALIGNMENT = 0, BACKSTYLE = 0, ;
CAPTION = "DEMO", VALUE = .F., TABINDEX = 8, NAME = "chk"
ADD OBJECT opgDatos AS OPTIONGROUP WITH ;
BUTTONCOUNT = 2, ANCHOR = 6, VALUE = 1, HEIGHT = 48, ;
LEFT = 344, TOP = 336, WIDTH = 136, TABINDEX = 5, NAME = "opgDatos", ;
Option1.BACKSTYLE = 0, Option1.CAPTION = "Ejemplo semanal", ;
Option1.VALUE = 1, Option1.HEIGHT = 17, Option1.LEFT = 8, Option1.TOP = 8, ;
Option1.WIDTH = 116, Option1.AUTOSIZE = .T., Option1.NAME = "Option1", ;
Option2.BACKSTYLE = 0, Option2.CAPTION = "Ejemplo anual", ;
Option2.VALUE = 0, Option2.HEIGHT = 17, Option2.LEFT = 8, Option2.TOP = 24, ;
Option2.WIDTH = 98, Option2.AUTOSIZE = .T., Option2.NAME = "Option2"
ADD OBJECT lblTitulo AS LABEL WITH ;
AUTOSIZE = .T., ANCHOR = 6, BACKSTYLE = 0, CAPTION = "Título", ;
HEIGHT = 17, LEFT = 16, TOP = 404, WIDTH = 32, TABINDEX = 6, NAME = "lblTitulo"
ADD OBJECT txtTitulo AS TEXTBOX WITH ;
ANCHOR = 6, VALUE = "TOTAL DE VENTAS", HEIGHT = 23, LEFT = 56, ;
TABINDEX = 7, TOP = 400, WIDTH = 232, NAME = "txtTitulo"
PROCEDURE GenerarCursor
LPARAMETERS tnDatos
CREATE CURSOR MiCursor (Valor N(10,2), Rotulo C(20))
IF tnDatos = 1
INSERT INTO MiCursor VALUES (RAND() * 1000 + 250, "Lunes")
INSERT INTO MiCursor VALUES (RAND() * 1000 + 250, "Martes")
INSERT INTO MiCursor VALUES (RAND() * 1000 + 250, "Miércoles")
INSERT INTO MiCursor VALUES (RAND() * 1000 + 250, "Jueves")
INSERT INTO MiCursor VALUES (RAND() * 1000 + 250, "Viernes")
INSERT INTO MiCursor VALUES (RAND() * 1000 + 250, "Sábado")
INSERT INTO MiCursor VALUES (RAND() * 1000 + 250, "Domingo")
ELSE
INSERT INTO MiCursor VALUES (RAND() * 1000 + 250, "Enero")
INSERT INTO MiCursor VALUES (RAND() * 1000 + 250, "Febrero")
INSERT INTO MiCursor VALUES (RAND() * 1000 + 250, "Marzo")
INSERT INTO MiCursor VALUES (RAND() * 1000 + 250, "Abril")
INSERT INTO MiCursor VALUES (RAND() * 1000 + 250, "Mayo")
INSERT INTO MiCursor VALUES (RAND() * 1000 + 250, "Junio")
INSERT INTO MiCursor VALUES (RAND() * 1000 + 250, "Julio")
INSERT INTO MiCursor VALUES (RAND() * 1000 + 250, "Agosto")
INSERT INTO MiCursor VALUES (RAND() * 1000 + 250, "Setiembre")
INSERT INTO MiCursor VALUES (RAND() * 1000 + 250, "Octubre")
INSERT INTO MiCursor VALUES (RAND() * 1000 + 250, "Noviembre")
INSERT INTO MiCursor VALUES (RAND() * 1000 + 250, "Diciembre")
ENDIF
ENDPROC
PROCEDURE tmr.TIMER
THISFORM.opgGraficas.VALUE = CEILING(RAND()*6)
THISFORM.opgLeyendas.VALUE = CEILING(RAND()*6)
THISFORM.opgColores.VALUE = CEILING(RAND()*2)
THISFORM.opgDatos.VALUE = CEILING(RAND()*2)
THISFORM.txtTitulo.VALUE = IIF(THISFORM.opgDatos.VALUE = 1, ;
"Total de ventas por día", "Total de ventas por mes")
THISFORM.cmdGenerar.CLICK
ENDPROC
PROCEDURE cmdGenerar.CLICK
WITH THISFORM.lmGraph
.TipoGrafica = THISFORM.opgGraficas.VALUE - 1
.TipoLeyenda = THISFORM.opgLeyendas.VALUE - 1
.TipoColor = THISFORM.opgColores.VALUE - 1
.ALIAS = "MiCursor"
.TituloGrafica = ALLTRIM(THISFORM.txtTitulo.VALUE)
THISFORM.GenerarCursor(THISFORM.opgDatos.VALUE)
.GenerarGrafica()
ENDWITH
ENDPROC
PROCEDURE chk.VALID
IF THIS.VALUE
THISFORM.tmr.TIMER
ENDIF
THISFORM.SETALL("Enabled", NOT THIS.VALUE, "CommandButton")
THISFORM.SETALL("Enabled", NOT THIS.VALUE, "OptionGroup")
THISFORM.SETALL("Enabled", NOT THIS.VALUE, "TextBox")
THISFORM.tmr.ENABLED = THIS.VALUE
ENDPROC
ENDDEFINE
*--
*-- Definición de la clase lmGraph
*--
DEFINE CLASS lmgraph AS CONTAINER
WIDTH = 200
HEIGHT = 100
SPECIALEFFECT = 1
BACKCOLOR = RGB(255,255,255)
ALIAS = ""
TipoLeyenda = 5
TipoColor = 0
TipoGrafica = 0
TituloGrafica = "Título"
NAME = "lmgraph"
ADD OBJECT lbl AS LABEL WITH ;
AUTOSIZE = .T., FONTBOLD = .T., BACKSTYLE = 0, ;
CAPTION = "lmGraph v.1.0", HEIGHT = 17, LEFT = 8, ;
TOP = 8, VISIBLE = .F., WIDTH = 79, NAME = "lbl"
*-- Genera la Gráfica
PROCEDURE GenerarGrafica
LOCAL lcCampoRotulo, lcCampo, lnSaltoH, lnSaltoV, lnReg, ;
lnTotal, lnCantReg, lnMaximo, lnMaxWidth, lcRotulo, lnValor, lnPorc, ;
lcObjPor, lcObjShp, lcObjLey, lnDim, lnHasta, ;
lnI, lnJ, lnAng, lnCos, lnSen, lcObj1, lcObj2
*--
*-- Limpio los objetos del gráfico
*--
THIS.LimpiarGrafica()
*---
*--- Verifico la versión de VFP y tipo de gráfica
*---
IF VERSION(5) < 900 AND INLIST(THIS.TipoGrafica, 0, 1, 4, 5)
MESSAGEBOX("El tipo de gráfica seleccionada no esta disponible para" + ;
CHR(13) + VERSION(), 48, "lmGraph")
RETURN
ENDIF
*--
*-- Tabla de datos
*--
IF EMPTY(THIS.ALIAS)
MESSAGEBOX("No especificó la propiedad Alias.", 48, "lmGraph")
RETURN
ENDIF
IF NOT USED(THIS.ALIAS)
MESSAGEBOX("La tabla " + PROPER(THIS.ALIAS) + ;
" no está en uso.", 48, "lmGraph")
RETURN
ENDIF
IF AFIELDS(la,THIS.ALIAS) < 2
MESSAGEBOX("La tabla " + PROPER(THIS.ALIAS) + ;
" tiene menos de dos campos.", 48, "lmGraph")
RETURN
ENDIF
IF NOT INLIST(la(1,2), "N", "I")
MESSAGEBOX("El segundo campo de la tabla " + PROPER(THIS.ALIAS) + ;
" no es numérico.", 48, "lmGraph")
RETURN
ENDIF
SELECT (THIS.ALIAS)
lcCampoValor = la(1,1)
lcCampoRotulo = la(2,1)
CALCULATE COUNT() TO lnCantReg
IF lnCantReg = 0
MESSAGEBOX("La tabla " + PROPER(THIS.ALIAS) + ;
" no contiene datos.", 48, "lmGraph")
RETURN
ENDIF
CALCULATE SUM(EVALUATE(lcCampoValor)) TO lnTotal
CALCULATE MAX(EVALUATE(lcCampoValor)) TO lnMaximo
*--
*-- Variables y área del gráfico
*--
#DEFINE AnguloPrimerSector 270
#DEFINE AngulosParaGraficar 360
lnAnguloSector = AnguloPrimerSector
lnLeft = 10
lnTop = IIF(EMPTY(THIS.TituloGrafica),10,30)
lnWidth = THIS.WIDTH - lnLeft * 2
lnHeight = THIS.HEIGHT - lnTop - lnLeft
lnSaltoH = FLOOR(lnHeight / lnCantReg)
*--
*-- Titulo del gráfico
*--
IF NOT EMPTY(THIS.TituloGrafica) && Con título
THIS.ADDOBJECT("lblTitulo","Label")
WITH THIS.lblTitulo
.BACKSTYLE = 0
.ALIGNMENT = 2
.FONTSIZE = 12
.FONTBOLD = .T.
.CAPTION = THIS.TituloGrafica
.TOP = 5
.LEFT = lnLeft
.WIDTH = lnWidth
.HEIGHT = 30
ENDWITH
ENDIF
*--
*-- Armo leyenda y tomo el ancho
*--
IF THIS.TipoLeyenda # 0 && Con leyenda
lnMaxWidth = 0
lnReg = 1
SCAN ALL
lcRotulo = ALLTRIM(TRANSFORM(EVALUATE(lcCampoRotulo)))
lnValor = EVALUATE(lcCampoValor)
lnPorc = lnValor / lnTotal * 100
lcObjLey = "oLey" + TRANSFORM(lnReg)
THIS.ADDOBJECT(lcObjLey,"Label")
WITH THIS.&lcObjLey
.TOP = lnSaltoH * lnReg - lnSaltoH + lnTop
DO CASE
CASE THIS.TipoLeyenda = 1
.CAPTION = TRANSFORM(lnValor)
CASE THIS.TipoLeyenda = 2
.CAPTION = TRANSFORM(ROUND(lnPorc,2)) + "%"
CASE THIS.TipoLeyenda = 3
.CAPTION = lcRotulo
CASE THIS.TipoLeyenda = 4
.CAPTION = lcRotulo + " - " + TRANSFORM(lnValor)
OTHERWISE
.CAPTION = TRANSFORM(ROUND(lnPorc,2)) + "% - " + lcRotulo
ENDCASE
.FONTSIZE = 8
.BACKSTYLE = 0
.LEFT = lnWidth + 100
.AUTOSIZE = .T.
.VISIBLE = .T.
lnMaxWidth = MAX(lnMaxWidth,.WIDTH)
ENDWITH
lnReg = lnReg + 1
ENDSCAN
lnLeftLeyenda = MAX(lnWidth * .60, lnWidth - lnMaxWidth - 40)
ENDIF
*--
*-- Armo el resto del gráfico
*--
lnReg = 1
SCAN ALL
lnValor = EVALUATE(lcCampoValor)
lnPorc = lnValor / lnTotal * 100
*--
*-- Armo cada porcion
*--
lcObjPor = "oPor" + TRANSFORM(lnReg)
THIS.ADDOBJECT(lcObjPor,"Shape")
WITH THIS.&lcObjPor
DO CASE
CASE THIS.TipoGrafica = 0 OR THIS.TipoGrafica = 1 && Torta/Anillo
IF THIS.TipoLeyenda = 0 && Sin leyenda
STORE MIN(lnWidth ,lnHeight) TO .WIDTH, .HEIGHT
.TOP = FLOOR((lnHeight - .HEIGHT) / 2 + lnTop)
.LEFT = FLOOR((lnWidth - .WIDTH) / 2 + lnLeft)
ELSE
STORE MIN(lnLeftLeyenda, lnHeight) TO .WIDTH, .HEIGHT
.TOP = FLOOR((lnHeight - .HEIGHT) / 2 + lnTop)
.LEFT = FLOOR((lnLeftLeyenda - .WIDTH) / 2 + lnLeft)
ENDIF
.POLYPOINTS = "This.aPoly"
lnDim = AngulosParaGraficar * lnPorc / 100
lnHasta = CEILING(lnDim) + 1
.ADDPROPERTY("aPoly[" + TRANSFORM(lnHasta) + ",2]")
STORE 50 TO .aPoly[1,1], .aPoly[1,2]
FOR lnI = 2 TO lnHasta
lnAng = (360 / AngulosParaGraficar) * (lnI - 2)
lnCos = COS(DTOR(lnAng + lnAnguloSector))
lnSen = SIN(DTOR(lnAng + lnAnguloSector))
.aPoly(lnI,1) = 50 * lnCos + 50
.aPoly(lnI,2) = 50 * lnSen + 50
ENDFOR
lnAnguloSector = lnAnguloSector + lnDim * 360 / AngulosParaGraficar
CASE THIS.TipoGrafica = 2 OR THIS.TipoGrafica = 4 && Barras/Conos Verticales
IF THIS.TipoLeyenda = 0 && Sin leyenda
lnSaltoV = FLOOR(lnWidth / lnCantReg)
ELSE
lnSaltoV = FLOOR(lnLeftLeyenda / lnCantReg)
ENDIF
.WIDTH = lnSaltoV + 1
.LEFT = lnSaltoV * lnReg - lnSaltoV + lnLeft
.HEIGHT = lnValor / lnMaximo * lnHeight
.TOP = lnHeight - .HEIGHT + lnTop
IF THIS.TipoGrafica = 4 && Conos
.POLYPOINTS = "This.aPoly"
.ADDPROPERTY("aPoly[" + TRANSFORM(4) + ",2]")
STORE 0 TO .aPoly[1,1], .aPoly[2,2], .aPoly[3,2]
STORE 100 TO .aPoly[1,2], .aPoly[4,1], .aPoly[4,2]
.aPoly[2,1] = 30
.aPoly[3,1] = 70
ENDIF
CASE THIS.TipoGrafica = 3 OR THIS.TipoGrafica = 5 && Barras/Conos Horizontales
IF THIS.TipoLeyenda = 0 && Sin leyenda
.WIDTH = lnValor / lnMaximo * lnWidth
ELSE
.WIDTH = lnValor / lnMaximo * lnLeftLeyenda
ENDIF
.LEFT = lnLeft
.HEIGHT = lnSaltoH + 1
.TOP = lnSaltoH * lnReg - lnSaltoH + lnTop
IF THIS.TipoGrafica = 5 && Conos
.POLYPOINTS = "This.aPoly"
.ADDPROPERTY("aPoly[" + TRANSFORM(4) + ",2]")
STORE 0 TO .aPoly[1,1], .aPoly[2,1], .aPoly[2,2]
STORE 100 TO .aPoly[1,2], .aPoly[3,1], .aPoly[4,1]
.aPoly[3,2] = 25
.aPoly[4,2] = 75
ENDIF
OTHERWISE
MESSAGEBOX("Tipo de gráfica no definida.", 48, "lmGraph")
RETURN
ENDCASE
*--
*-- Color de la porción
*--
IF THIS.TipoColor = 0
.BACKCOLOR = FLOOR(RAND() * 16777216) && Aleatorio
ELSE
.BACKCOLOR = THIS.ColoresBasicos(lnReg)
ENDIF
ENDWITH
*--
*-- Armo leyendas
*--
IF THIS.TipoLeyenda # 0 && Con leyenda
lcObjShp = "oShp" + TRANSFORM(lnReg)
THIS.ADDOBJECT(lcObjShp,"Shape")
WITH THIS.&lcObjShp
.HEIGHT = 12
.WIDTH = 12
.BACKCOLOR = EVALUATE("THIS.oPor" + TRANSFORM(lnReg) + ".BACKCOLOR")
.TOP = lnSaltoH * lnReg - lnSaltoH + lnTop
.LEFT = lnLeftLeyenda + lnLeft + 10
ENDWITH
lcObjLey = "oLey" + TRANSFORM(lnReg)
WITH THIS.&lcObjLey
.LEFT = lnLeftLeyenda + lnLeft + 30
ENDWITH
ENDIF
lnReg = lnReg + 1
ENDSCAN
*--
*-- Anillo
*--
IF THIS.TipoGrafica = 1 && Anillo
THIS.ADDOBJECT("oShpMed","Shape")
WITH THIS.oShpMed
IF THIS.TipoLeyenda = 0 && Sin leyenda
STORE MIN(lnWidth ,lnHeight) * .45 TO .WIDTH, .HEIGHT
.TOP = FLOOR((lnHeight - .HEIGHT) / 2 + lnTop)
.LEFT = FLOOR((lnWidth - .WIDTH) / 2 + lnLeft)
ELSE
STORE MIN(lnLeftLeyenda, lnHeight) * .45 TO .WIDTH, .HEIGHT
.TOP = FLOOR((lnHeight - .HEIGHT) / 2 + lnTop)
.LEFT = FLOOR((lnLeftLeyenda - .WIDTH) / 2 + lnLeft)
ENDIF
.BACKCOLOR = THIS.BACKCOLOR
.CURVATURE = 99
ENDWITH
ENDIF
*--
*-- Uno porciones en Torta/Anillo
*--
IF THIS.TipoGrafica = 0 OR THIS.TipoGrafica = 1 && Torta/Anillo
FOR lnI = 1 TO lnCantReg - 1
lcObj1 = "This.oPor" + TRANSFORM(lnI)
lcObj2 = "This.oPor" + TRANSFORM(lnI+1)
lnJ = ALEN(&lcObj1..aPoly,1)
&lcObj1..aPoly(lnJ,1) = &lcObj2..aPoly(2,1)
&lcObj1..aPoly(lnJ,2) = &lcObj2..aPoly(2,2)
ENDFOR
lcObj1 = "This.oPor" + TRANSFORM(1)
lnJ = ALEN(&lcObj2..aPoly,1)
&lcObj2..aPoly(lnJ,1) = &lcObj1..aPoly(2,1)
&lcObj2..aPoly(lnJ,2) = &lcObj1..aPoly(2,2)
ENDIF
*--
*-- Hago visible los objetos creados
*--
THIS.SETALL("Visible",.T., "Shape")
THIS.SETALL("Visible",.T., "Label")
ENDPROC
PROCEDURE ColoresBasicos
LPARAMETERS tn
LOCAL la(28)
tn = MOD(tn-1,28)+1
la(1) = RGB(255,0,0) && Rojo
la(2) = RGB(255,255, 0) && Amarillo
la(3) = RGB(0,0,255) && Azul
la(4) = RGB(0,128,0) && Verde Oscuro
la(5) = RGB(255,128,0) && Anaranjado
la(6) = RGB(128,64,0) && Marrón
la(7) = RGB(255,0,255) && Magenta
la(8) = RGB(128,0,255) && Violeta
la(9) = RGB(0,255,255) && Cyan
la(10) = RGB(192,192,0) && Amarillo Oscuro
la(11) = RGB(192,0,0) && Rojo Oscuro
la(12) = RGB(0,255,0) && Verde
la(13) = RGB(0,0,128) && Azul Oscuro
la(14) = RGB(255,192,0) && Anaranjado Claro
la(15) = RGB(0,192,255) && Azul claro
la(16) = RGB(128,128,0) && Marrón Claro
la(17) = RGB(255,192,255) && Magenta Claro
la(18) = RGB( 64,128,128) && Verde Azulado
la(19) = RGB(255,0,128) && Fucsia
la(20) = RGB(255,255,192) && Amarillo Claro
la(21) = RGB(192,0,255) && Violeta Claro
la(22) = RGB(192,255,192) && Verde Claro
la(23) = RGB(128,0,128) && Violeta Oscuro
la(24) = RGB(192,255,255) && Cyan Claro
la(25) = RGB(128,128,128) && Gris Oscuro
la(26) = RGB(255,255,255) && Blanco
la(27) = RGB(192,192,192) && Gris
la(28) = RGB(0,0,0) && Negro
RETURN la(tn)
ENDPROC
PROCEDURE LimpiarGrafica
LOCAL lnI
FOR lnI = THIS.CONTROLCOUNT TO 1 STEP -1
THIS.REMOVEOBJECT(THIS.CONTROLS(lnI).NAME)
ENDFOR
ENDPROC
PROCEDURE INIT
SET TALK OFF
RAND(-1)
ENDPROC
ENDDEFINE
La descarga
Pueden descargar el proyecto con el formulario de ejemplo y la clase lmGraph
desde las descargas de PortalFox siguiendo el enlace siguiente:
El final
Con la clase presentada en este artículo, ustedes podrán generar gráficas
simples y muy fáciles de incorporar en formularios de VFP. Si necesitan
gráficas mas avanzadas utilizando solo Visual FoxPro, los invito a que lean el
excelente artículo de Cesar Chalom publicado en la revista UTMag y se sorprenderán de todo
lo que se puede realizar con GDI+
Los comentarios
Cualquier comentario que quieran realizar sobre este artículo y esta
clase, será bienvenido. Solo envien sus comentario haciendo clic en el botón
"Enviar comentario" al final de este artículo.
Hasta la próxima.
Luis María
|