Gráficas con Áreas Activas


Un Método para Programar Áreas Activas en Una Gráfica

Por Harvey Triana

Una pregunta frecuente que he visto en los News y Mailing List de Visual Basic es: ¿hay alguna forma de construir un mapa que dependiendo donde se haga clic, haga diferentes procesos?. Este articulo presenta una técnica bastante práctica y efectiva para hacer esto.



Principio Básico

Es simple, se escribe código en los eventos MouseMove, para indicar al usuario con un puntero que la área es activa, y MouseDown, donde escribiremos el código de la acción, todo limitado a un rectángulo definido por un área de coordenadas (x1, y1)-(x2, y2).

El siguiente ejemplo describe el principio con un área entre (100, 100)-(140-140) en pixeles.

Cree un Proyecto, con un Form, un PictureBox (llámalo P) y un Label. Pegue el siguiente código. Puede adicionar una imagen al Picture para que se vea mejor. En las dos áreas especificadas el puntero del Mouse cambia a una cruz. Al dar clic nos dirá algo.

Private Sub Form_Load()
    P.ScaleMode = vbPixels
End Sub

Private Sub P_MouseDown( _
            Button As Integer, Shift As Integer, X As Single, Y As Single _
            )
    P.MousePointer = vbDefault
    If (X >= 100 And X <= 140) And (Y >= 100 And Y <= 140) Then
       MsgBox "Clic sobre área (10, 10)-(40, 40)..."
    Else
       MsgBox "Clic fuera del área activa..."
    End If
End Sub

Private Sub P_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
    
    If (X >= 101 And X <= 140) And (Y >= 100 And Y <= 140) Then
       If Not P.MousePointer = vbCrosshair Then
          P.MousePointer = vbCrosshair
       End If
    Else
       If Not P.MousePointer = vbDefault Then
          P.MousePointer = vbDefault
       End If
    End If
    Label1 = X & ", " & Y
End Sub

El Control Label es útil para que pueda observar los valores (x, y) en pixeles por donde pasa el Mouse. Cuando el Mouse pase por el área comprendida entre (100, 100)-(140, 140), cambiará su puntero a una cruz; si da clic sobre esta área se dará un mensaje. Para más de un área activa usaría bloques ElseIf dentro de los eventos y el mismo modelo de código.

NOTA
Para usar un puntero de Mouse personalizado, use la constante vbCustom en MousePointer, agregue un Control Image con el Icono que desee, y el bloque de código sería:

If Not pic_Panorama.MousePointer = vbCustom Then
   pic_Panorama.MousePointer = vbCustom
   pic_Panorama.MouseIcon = Image1
End If


Método Avanzado

Bien, la técnica explicada anteriormente es efectiva para unas pocas áreas activas. Es seguida explicare como crear un procedimiento generalizado y práctico para muchas áreas activas en una aplicación dedicada.

  1. Variables Requeridas. El siguiente bloque de variables es el modelo básico. La estructura puede ser modificada para agregar nuevas propiedades a cada área, p.e. un Icono de Mouse diferente para cada área, un sonido asociado, etc.
Private Type AreaActiva
    x1 As Integer
    x2 As Integer
    y1 As Integer
    y2 As Integer
    Descripción As String * 64
End Type
Private AreasActivas As Integer
Private Area() As AreaActiva
Private IndexActivo As Integer
  1. Código Base. El código para los eventos MouseMove y MouseDown seguirán el siguiente modelo.
Private Sub pic_Panorama_MouseMove( _
            Button As Integer, Shift As Integer, X As Single, Y As Single _
            )
    Static i
    i = 1
    IndexActivo = 0
    Do Until Not (IndexActivo = 0) Or i > AreasActivas
       If (X >= Area(i).x1 And X <= Area(i).x2) _
          And _
          (Y >= Area(i).y1 And Y <= Area(i).y2) Then
          IndexActivo = i
       End If
       i = i + 1
    Loop
    If IndexActivo Then
       If Not pic_Panorama.MousePointer = vbCustom Then
         'Implementa un cambio de Icono de Mouse personalizado
          pic_Panorama.MousePointer = vbCustom
          pic_Panorama.MouseIcon = Image1
         'Implementa el cambio del ToolTipText
          LetToolTipTex Area(IndexActivo).Descripción
       End If
    Else
       If Not pic_Panorama.MousePointer = vbDefault Then
          pic_Panorama.MousePointer = vbDefault
          pic_Panorama.ToolTipText = ""
       End If
    End If

   'Label que indica las coordenadas del puntero del mouse 
    lbl_Guia = X & ", " & Y
End Sub

Private Sub pic_Panorama_MouseDown( _
            Button As Integer, Shift As Integer, X As Single, Y As Single _
            )
    If IndexActivo Then
       pic_Panorama.MousePointer = vbDefault
      
      'En un aplicación usaría Select Case IndexActivo...
      
       MsgBox "Clic sobre: " & Area(IndexActivo).Descripción
    End If
End Sub

Por supuesto, la implementación del mensaje de ToolTipText es opcional, solo se uso para demostrar la funcionalidad.

NOTA
Uso un procedimiento particular para asignar el ToolTipText. Esto se hace para que no se reasigne continuamente al mover el Mouse sobre el área activa. Una buena práctica de programación:

Private Sub LetToolTipTex(ByVal s As String)
    s = RTrim(s)
    If Not pic_Panorama.ToolTipText = s Then
       pic_Panorama.ToolTipText = s
    End If
End Sub
  1. Asignando las Areas Activas. Este es un paso bastante simple, valiéndose del control Label que me indica la posición del puntero (note la línea lbl_Guia = X & ", " & Y), tóme una hoja y un lápiz, corra la aplicación, y mueva el Mouse por donde quiera. Seleccione los contornos de las áreas que desea serán activas (señale una esquina superior y una esquina inferior aproximadas) y anote su descripción. Algunas figuras pueden tener dificultad para encasillarlas en un rectángulo, en este caso puede usar varios rectángulos para dar el contorno, y el caso será programado como único según su organización. Procure no cruzar los rectángulos. Despues escriba un procedimiento para asignar los datos. La forma más simple, es como el ejemplo que suministro:
Private Sub AsigneAreas()
    AreasActivas = 3
    ReDim Area(1 To AreasActivas)
    Area(1).Descripción = "Torre de Perforación"
    Area(1).x1 = 199
    Area(1).y1 = 11
    Area(1).x2 = 214
    Area(1).y2 = 132
    Area(2).Descripción = "Caseta de Químicos"
    Area(2).x1 = 104
    Area(2).y1 = 109
    Area(2).x2 = 141
    Area(2).y2 = 125
    Area(3).Descripción = "Laboratorio de Geología"
    Area(3).x1 = 60
    Area(3).y1 = 151
    Area(3).x2 = 117
    Area(3).y2 = 183
End Sub

Por ultimo, si lo desea puede eliminar el Control Label (y su línea en el evento MouseMove del PictureBox) que sirvió de guía para apuntar las áreas, ya no será necesario en la aplicación.


Perspectiva

Esta implementación es una fuente de ideas para diversas aplicaciones:

  • Una aplicación que al dar clic muestre otra fotografía referente al área que se señalo, y así sucesivamente (una visita virtual). Un ejemplo de esto son aquellos programas multimedia para niños que les enseñan algún idioma y ellos simplemente van navegando por donde quieran y señalando objetos, los cuales indican y pronuncian el nombre del objeto.
  • Escribir una aplicación que muestre el funcionamiento de una fabrica o empresa. Nuevamente las áreas activas llevaran a otras fotografías. Seria elegante incluir sonido tanbién.
  • Crear una interfaz muy amigable y simple, para usuarios con escasos conocimientos en manejo de software.
  • Escribir un tutor de su aplicación.

Etcétera.


El ejemplo que incluye este articulo contiene todo el código descrito de manera que puede observar los resultados y juzgar. Este ejemplo tiene tres áreas activas como decribe el procedimiento AsigneAreas(). Si quiere pase el Mouse por la las casetas y torre que se ven en el fotografia. La imagen inicial que se muestra en este documento corresponde a este ejemplo en ejecución. vbAreas.zip (97k).