Gauge
05/03/2020
Autor del proyecto: Doug Hennig
Introducción
A la gente le gustan las imágenes visuales. La mayoría de las personas preferiría ver un gráfico que columnas de números sin procesar porque es más fácil ver las relaciones entre los elementos visualmente. Agregar herramientas de análisis como gráficos y medidores a sus aplicaciones las hace mucho más valiosas para sus usuarios.
¿Qué es un medidor? Un indicador es una imagen que muestra cómo un valor único se compara con un valor máximo o objetivo. Los dos valores pueden ser cualquier cosa: ventas actuales en comparación con el presupuesto, volumen hasta la fecha en comparación con el volumen máximo permitido, y así sucesivamente. Por ejemplo, muchas organizaciones de caridad muestran el estado actual de sus campañas de recaudación de fondos con un termómetro. La parte superior del termómetro representa el valor de la meta de recaudación de fondos y la altura de la barra dentro del termómetro representa cuánto dinero se ha recaudado hasta el momento.
El tipo más común de medidor se parece a un velocímetro en un automóvil; por ejemplo -> imagen a la derecha.
El final del medidor representa el valor máximo y la posición de la aguja representa el valor actual. Las bandas de color alrededor del borde exterior del indicador muestran los rangos de ciertas categorías. Por ejemplo, en la figura a la derecha, la banda roja indica dónde las ventas son demasiado bajas, la banda amarilla donde son aceptables pero no excelentes, y la banda verde donde las ventas deberían ser para hacer feliz al jefe.
La clase Gauge
Solo se usa una sola clase para dibujar indicadores: Gauge en Gauge.VCX (también se requieren componentes adicionales, como hablaré más adelante). Es una subclase de Custom, por lo que no tiene una apariencia visible en tiempo de ejecución. ¿Cómo aparece el medidor entonces? Después de llamar al método DrawGauge, la propiedad cImage se establece en los bytes para la imagen del medidor. Puede usar FILETOSTR () para escribir el contenido de cImage en un archivo, como si desea usar el indicador en un informe, o establecer la propiedad PictureVal de un objeto Image si desea que aparezca en un formulario. Por ejemplo, SampleGauge.SCX, uno de los formularios incluidos en las descargas, utiliza este código para que el objeto Gauge al que se hace referencia en la propiedad oGauge dibuje un medidor en una imagen llamada imgGauge:
with This .oGauge.nSize = .imgGauge.Width .oGauge.DrawGauge() .imgGauge.PictureVal = .oGauge.cImage endwith
Propiedad
Descripción
cDialText
El texto para el dial
cDialTextFontName
La fuente para el texto de marcado
cErrorMessage
El texto de cualquier error que ocurra
cFormat
El formato de las etiquetas en sintaxis .NET: {0:#,##0} por defecto
cImage
La imagen del medidor
cLabelFontName
La fuente de las etiquetas
lAdjustLabelSize
.T. para ajustar el tamaño de la etiqueta según el tamaño del medidor, .F. usar el tamaño especificado en nLabelFontSize
lDialTextFontBold
.T. si el texto marcado está en negrita
lDialTextFontItalic
.T. if the dial text is italics
lDisplayDigitalValue
.T. to display the value in a digital display
lLabelFontBold
.T. si el texto de la etiqueta está en negrita
lLabelFontItalic
.T. si el texto marcado es cursiva
lShowGoalMarker
.T. para mostrar un marcador para el valor del objetivo
lValuesAsPercentages
.T. para usar porcentajes para valores, .F. usar cantidades para valores
nBackColor
El color de fondo o el color inicial para un degradado de fondo
nBackColor2
El color final para un degradado de fondo; si es lo mismo que nBackColor, no hay gradiente
nBackColorAlpha
El alfa para el color de fondo
nBackGradientMode
El modo para un gradiente de fondo: 0 = de izquierda a derecha, 1 = de arriba a abajo, 2 = de arriba a la izquierda, 3 = de arriba a la derecha
nBand1Color
El color para la banda 1
nBand1End
La posición final para la banda 1
nBand2Color
El color para la banda 2
nBand2End
La posición final para la banda 2
nBand3Color
El color para la banda 3
nDialAlpha
El alfa para el color del dial
nDialColor
El color a usar para el dial
nDialTextColor
El color del texto de marcado
nDialTextFontSize
El tamaño de fuente para el texto marcado
nDigitsColor
El color de los dígitos digitales.
nGlossiness
El valor de brillo (0 - 100)
nGoalMarkerColor
El color a usar para el valor objetivo
nGoalPosition
El valor objetivo para el medidor
nLabelColor
El color a usar para las etiquetas
nLabelDistance
La distancia entre etiquetas y marcas principales
nLabelFactor
El factor a utilizar para las etiquetas: 1, 1000, 10000, etc.
nLabelFontSize
El tamaño de fuente para las etiquetas.
nMajorTickColor
El color de las marcas principales
nMajorTickCount
El número de marcas principales
nMaxValue
El valor máximo para el medidor
nMinorTickColor
El color de las marcas pequeñas
nMinorTickCount
El número de las marcas pequeñas
nMinValue
El valor mínimo para el medidor
nSize
La altura y el ancho del medidor (es un cuadrado, por lo que son iguales)
nValue
El valor actual para el medidor
oBridge
Una referencia a un objeto wwDotNetBridge
oGauge
Una referencia a un objeto .NET GaugeControl
Como puede ver, hay bastantes. La mayoría de ellas afectan la apariencia del medidor. La mejor manera de comprobar cómo funcionan las distintas propiedades es ejecutando SampleGauge.SCX:
Cada control tiene una información sobre herramientas que especifica qué propiedad controla. Cambiar cualquier configuración vuelve a dibujar inmediatamente el indicador para que pueda ver el efecto instantáneamente.
Aquí hay comentarios sobre algunas de las propiedades:
- nGlossiness controla qué tan brillante es la elipse "brillante" que aparece en la parte superior del indicador. Esta elipse da la ilusión de la luz reflejada, como si el medidor estuviera hecho de vidrio o plástico.
- El tamaño del medidor está determinado por la propiedad nSize; Como un indicador se dibuja como un cuadrado, la altura y el ancho de la imagen están configurados en nSize.
- De forma predeterminada, las etiquetas que indican los valores alrededor del indicador se dimensionan en función del tamaño del indicador: cuanto mayor es el indicador, mayores son las etiquetas. Establezca lAdjustLabelSize en .F. si desea utilizar el tamaño de fuente especificado en nLabelFontSize en su lugar.
- cFormat indica cómo se formatean las etiquetas. Tiene que usar la sintaxis .NET para formatos de números por razones que serán obvias más adelante. La sintaxis de .NET usa "#" como un marcador de posición de dígitos opcional, "0" como un marcador de posición de dígitos que muestra 0 si no hay ningún dígito, "." para un separador decimal, y "," para usar un separador de miles (a diferencia de VFP, solo se necesita uno en el formato incluso cuando los números superan el millón). La cadena de formato está rodeada por "{0:" y "}", por lo que el valor predeterminado de "{0: #, ## 0}" no especifica ceros a la izquierda, ni decimales, y miles de separadores.
- nBand1End indica en qué parte del borde exterior del indicador aparece la primera banda de color, como la banda roja en la Figura 1. Solo se necesita el valor final, ya que la banda comienza en 0. De manera similar, nBand2End y nBand3End indican las posiciones finales de bandas de segundo y tercer color, siendo las posiciones iniciales el final de la banda anterior.
- De forma predeterminada, se supone que nBand1End, nBand2End y nBand3End son porcentajes, por lo que un valor de 35 indica una posición final del 35% del arco del medidor. Si desea utilizar cantidades en su lugar, como 25,000, establezca lValuesAsPercentages en .F.
- Como el indicador no puede superar el 100% o menos del 0%, la aguja está vinculada a esos valores cuando nValue es mayor que nMaxValue o menor que nMinValue. Debido a que es posible que desee que el valor máximo sea un objetivo que podría superarse (por ejemplo, la cuota mensual de un vendedor puede ser de $ 10,000 pero ciertamente podrían vender más que eso), puede establecer nGoalPosition en un valor inferior a 100. Por ejemplo, si lo establece en 75, el valor nMaxValue aparece en 75%.
- nMinValue puede ser mayor que nMaxValue; por ejemplo, es posible que desee un indicador que se ejecute de 100 a 0.
- Para números más grandes (más de 1,000), las etiquetas pueden superponerse a las principales marcas de verificación, por lo tanto, aumente el valor de nLabelDistance en consecuencia. Alternativamente, puede establecer nLabelFactor en un valor para dividir las etiquetas por números más pequeños. Por ejemplo, si establece nLabelFactor en 10000, la posición de 5,000 en el indicador aparece como "5".
Crear un tablero
Los paneles de control están de moda en estos días. Un tablero de instrumentos es un formulario que muestra múltiples paneles de información, como gráficos, informes y, por supuesto, indicadores. Otro formulario de muestra que viene con las descargas, Dashboard.SCX, es una demostración simple de cómo podría funcionar un tablero.
Este formulario es realmente bastante simple. Su método Init agrega ocho controles y tamaños de imagen y los coloca para que ocupen dos filas de cuatro imágenes. Un temporizador en el formulario llama al método DrawGauges del formulario, que utiliza un solo control Gauge para hacer el dibujo. DrawGauges establece las propiedades del control Gauge en diferentes valores para cada indicador (diferentes colores del dial y diferentes valores actuales y máximos), dibuja el indicador y establece la propiedad PictureVal de cada control de imagen en la imagen resultante. Solo por diversión, el temporizador dispara cada 2 segundos y muestra un valor aleatorio para que pueda ver cómo se mueven las agujas.
Cómo funciona Gauge
La clase Gauge es en realidad un contenedor para una DLL .NET que hace todo el trabajo. Discutiré la clase .NET más tarde.
Para evitar el registro de COM y otros problemas, utilizo la utilidad wwDotNetBridge de Rick Strahl (https://west-wind.com/wwDotnetBridge.aspx). Como puede ver en el siguiente código, el método Init de Gauge crea una instancia de wwDotNetBridge en la propiedad oBridge. Como generalmente solo desea una única instancia de wwDotNetBridge en una aplicación, puede pasar una instancia existente a Init en su lugar. Init también carga el ensamblado Gauge.DLL .NET y crea instancias de la clase Gauge.GaugeControl en la propiedad oGauge.
Dado que la DLL .NET hace todo el trabajo, todo el método DrawGauge de la clase Gauge tiene que hacer es llenar las propiedades del objeto .NET con los valores de sus propias propiedades, llamar al método DrawGauge del objeto .NET y poner el retorno valor, que son los bytes de la imagen del medidor, en cImage.
La única complicación en DrawGauge es que los valores de color VFP no coinciden con los valores de color .NET: los valores .NET tienen los componentes rojo, verde y azul invertidos, y también admiten un valor alfa o de transparencia. Entonces, DrawGauge llama a un método auxiliar llamado GetColor, que extrae los componentes de color y los pone en el orden necesario para .NET.
El componente .NET
GaugeControl.cs, incluido con las descargas, es el código fuente del componente de calibre .NET en Gauge.DLL. Comencé con el código creado por Ambalavanar Thirugnanam, disponible en http://www.ucancode.net/Visual_C_MFC_Samples/CSharp_Example_Free_DOTNET_Gauge_Control_Draw_Source_Code.htm, e hice varios cambios en él:
Lo modifiqué para que sea una clase .NET simple que devuelve una imagen como una cadena en lugar de un Control de usuario de formularios Windows Forms que muestra el indicador. Esto permite que la imagen se escriba en un archivo o se muestre en un control de imagen VFP sin tener que preocuparse por registrar el control .NET como un control ActiveX y agregarlo a un formulario VFP.
- Agregué propiedades para varios colores, como el color de fondo, en lugar de usar valores codificados.
- Agregué soporte para un degradado de fondo además de un color sólido.
- Debido a que es difícil crear un objeto de fuente .NET en VFP, incluso con wwDotNetBridge, agregué propiedades para las configuraciones de nombre, tamaño, negrita y cursiva de las fuentes utilizadas para el texto de marcado y las etiquetas. GaugeControl utiliza estas propiedades para crear instancias de un objeto Font con la configuración especificada.
- Si está interesado en cómo funciona el componente .NET, le recomiendo leer el artículo mencionado anteriormente, ya que analiza la lógica y las matemáticas involucradas en el dibujo del indicador. Luego examine el código fuente de C # en GaugeControl.cs para ver cómo se implementa.
Si compila la solución Gauge que incluye GaugeControl.cs, encontrará que tiene un evento posterior a la compilación que copia la DLL en la carpeta principal de la solución, que es la misma carpeta donde se encuentra Gauge.VCX. Tenga en cuenta que si ha utilizado el control VFP Gauge y VFP sigue abierto, debe cerrar VFP antes de crear la solución .NET porque la DLL .NET todavía está abierta en VFP.
Como GaugeControl.cs usa GDI + para hacer todo el dibujo, ¿por qué no convertí el código C # a código VFP usando el proyecto VFPX GDIPlusX? Después de todo, eso nos daría una solución 100% VFP sin necesidad de wwDotNetBridge o Gauge.DLL. La razón por la que no lo hice es doble:
- ¿Por qué reinventar la rueda? Hacía falta varias horas para convertir el código C # en el código VFP equivalente y habría mucha depuración para asegurarse de que funciona igual.
- Me he encontrado con algunos problemas de rendimiento con GDIPlusX en algunas máquinas. De hecho, esto es lo que me llevó a mirar esta solución en primer lugar. Estaba usando el proyecto VFPX FoxCharts para dibujar indicadores, pero descubrí que en algunos sistemas, me estaba tomando un minuto o más dibujar el indicador. Al rastrear el problema, descubrí que en esos sistemas, algunas de las llamadas a funciones GDI + tardaban más en ejecutarse, y estas funciones se llamaban miles de veces para cada indicador. No sé por qué algunos sistemas tienen este problema de rendimiento con GDIPlusX, pero el componente .NET no tiene tales problemas en esos sistemas.
Medidor de implementación
Implementar Gauge es sencillo:
- Agregue Gauge.VCX y wwDotNetBridge.PRG a su proyecto.
- Use la clase Gauge como mejor le parezca: para crear archivos de imagen para informes (cImage está en formato PNG) o para la fuente de imágenes en formularios.
- Incluya wwDotNetBridge.DLL, ClrHost.DLL y Gauge.DLL en su instalador o copie esos archivos en el sistema del cliente. No se requiere registro para ninguno de estos componentes.
Gauge.DLL requiere la versión 2.0 del marco .NET. Windows Vista y versiones posteriores vienen con .NET 2.0, por lo que este es solo un problema para Windows XP y versiones anteriores. Si usa Inno Setup como su instalador de aplicaciones, puede hacer que su instalador detecte si falta .NET 2.0 y lo descargue e instale automáticamente agregando #INCLUDE DotNet2Install.iss a su archivo de script Inno. DotNet2Install.iss se incluye en las descargas, al igual que Isxdl.DLL, un componente utilizado por DotNet2Install.iss.
Referencias
Autor del proyecto: Doug Hennig
Github: Gauge - Link 1
Mega: Gauge - Link 2
Mega: Gauge - Link 3 -> una versión más antigua que a mi si me funciona.