
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.
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.
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:
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:
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 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
Pueden descargar el proyecto con el formulario de ejemplo y la clase lmGraph desde las descargas de PortalFox siguiendo el enlace siguiente:
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+
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
| Gráficas con objetos 100% VFP | Entrar/Crear una cuenta [7] | 8 Comentarios | |
|
| |
| Los comentarios son propiedad de sus respectivos autores. No somos responsables de su contenido. |
Re: Gráficas con objetos 100% VFPpor amby en 25 Ago, 2006 - 06:15 (Información del usuario [8] | Enviar un mensaje [9] http://www.amby.net [10]) |
|
Enhorabuena ¡¡¡¡¡¡¡¡¡¡¡ Excelente artículo !!!!!!!!!!!!!! ¡¡¡¡¡¡¡¡¡¡¡ Excelente explicación !!!!!!!!! Utilísimo !!!!! .... y por si fuera poco ... didáctico, práctico, sencillo.... Tiene todos los ingredientes. Muchas Gracias Luis María, Un abrazo, Ana |
Re: Gráficas con objetos 100% VFPpor pnarvaez678 en 25 Ago, 2006 - 10:49 (Información del usuario [11] | Enviar un mensaje [12] http:// [13]) |
|
Luis Maria: Este es un artículo supremamente útil. Mil gracias por compartir toda esa camada de conocimientos con toda la comunidad Foxera. Pablo Barranquilla - Colombia |
Re: Gráficas con objetos 100% VFPpor julgarcia en 25 Ago, 2006 - 11:13 (Información del usuario [14] | Enviar un mensaje [15] http:// [16]) |
|
Enhorabuena por el excelente trabajo. Será de gran utilidad. Por fin podemos olvidarnos de los controles Axtivex para gráficos. Tengo una duda y tal vez sea osado en la pregunta: ¿Como se traslada a un report para la visualización en papel? Saludos |
Re: Gráficas con objetos 100% VFPpor efraim_ml en 25 Ago, 2006 - 12:37 (Información del usuario [17] | Enviar un mensaje [18] |
| Gracias Luis María, por dar a conocer de lo que VFP es capaz. |
Re: Gráficas con objetos 100% VFPpor jtaz en 05 Sep, 2006 - 06:38 (Información del usuario [19] | Enviar un mensaje [20] http:// [21]) |
|
Muy bien explicado, sumamente útil, ya que puesto de esta manera nos permite explorar por nuestra cuenta para ver los alcances de lo expuesto... Se agradece a Luís María, quien siempre brinda su colaboración de una manera por demás valiosa... |
Re: Gráficas con objetos 100% VFPpor CesarCh en 17 Sep, 2006 - 11:23 (Información del usuario [22] | Enviar un mensaje [23] |
|
Excelente articulo ! Y certamente muy inspirador. Lo que mas me gusta en publicar mis ideas o trucos a la comunidad es que casi siempre soy premiado recibindo nuevas ideas que me muestran nuevos caminos. Considero tu articulo como uno de estos premios. Como respuesta a la solicitacion de Amby, pretendo publicar en brebe como guardar los resultados creados por tu clase utilizando las nuevas clases Gdiplus-X. Tu articulo "Dibujando polígonos con VFP 9.0" es tambien excelente. Gracias !!! Y perdon por mi portuñol ! Cesar Ch Sao Paulo - Brasil |
Re: Gráficas con objetos 100% VFPpor LuisMaria [24] (luismaria@portalfoxxx.com) en 19 Sep, 2006 - 08:41 (Información del usuario [25] | Enviar un mensaje [26] http://www.luismariaguayan.com.ar [27]) |
|
Muchas gracias a todos por sus palabras, que indican que con VFP tenemos una excelente herramienta. Sigamos apoyando al zorro difundiendo nuestros trabajos a toda la comunidad, y colaborando con este Portal enviando mas material para su publicación Saludos a todos, Luis María |
Re: Gráficas con objetos 100% VFPpor salsaemg en 24 Oct, 2006 - 10:30 (Información del usuario [28] | Enviar un mensaje [29] |
|
definitivamente he aprendido mas en la generacion de grafica, deseo saber como las puedo insetar en mis reportes. gracias |