- Tipos de objetos y características de la especificación de sus coordenadas
- Objetos vinculados a tiempo y precio
- Objetos vinculados a coordenadas de pantalla
- Crear objetos
- Borrar objetos
- Encontrar objetos
- Visión general de las funciones de acceso a las propiedades de los objetos
- Propiedades principales de los objetos
- Coordenadas de tiempo y precio
- Anclar la esquina de la ventana y las coordenadas de la pantalla
- Definir el punto de anclaje en el objeto
- Gestión del estado de los objetos
- Prioridad de los objetos (orden Z)
- Ajustes de visualización de objetos: color, estilo y marco
- Ajustes de fuente
- Rotar un texto en un ángulo arbitrario
- Determinar ancho y alto del objeto
- Visibilidad de los objetos en el contexto de marcos temporales
- Asignar un código de carácter a una etiqueta
- Propiedades de los rayos para objetos con líneas rectas
- Gestionar el estado pulsado de los objetos
- Ajustar imágenes en objetos bitmap
- Recortar (dar salida a parte) de una imagen
- Propiedades de los campos de entrada: alineación y sólo lectura
- Anchura del canal de desviación estándar
- Establecer niveles en objetos de nivel
- Propiedades adicionales de los objetos de Gann, Fibonacci y Elliot
- Objeto gráfico
- Mover objetos
- Obtener hora o precio en puntos de línea especificados
Determinar ancho y alto del objeto
Algunos tipos de objetos permiten establecer sus dimensiones en píxeles. Entre ellos se incluyen OBJ_BUTTON, OBJ_CHART, OBJ_BITMAP, OBJ_BITMAP_LABEL, OBJ_EDIT y OBJ_RECTANGLE_LABEL. Además, los objetos OBJ_LABEL admiten la lectura (pero no la configuración) de tamaños porque las etiquetas se expanden o contraen automáticamente para ajustarse al texto que contienen. Si se intenta acceder a propiedades de otros tipos de objetos, se producirá un error OBJECT_WRONG_PROPERTY (4203).
Identificador |
Descripción |
---|---|
OBJPROP_XSIZE |
Anchura del objeto a lo largo del eje X en píxeles |
OBJPROP_YSIZE |
Altura del objeto a lo largo del eje Y en píxeles |
Ambos tamaños son enteros y, por lo tanto, se manejan con las funciones ObjectGetInteger/ObjectSetInteger.
Para los objetos OBJ_BITMAP y OBJ_BITMAP_LABEL se realiza un tratamiento especial de las dimensiones.
Sin asignar una imagen, estos objetos permiten establecer un tamaño arbitrario. Al mismo tiempo, se dibujan transparentes (sólo el marco es visible si no se «oculta» también configurando el color clrNone), pero reciben todos los eventos, en concreto, sobre los movimientos del ratón (con una descripción de texto, si la hay, en una información sobre herramientas) y los clics de sus botones sobre el objeto.
Cuando se asigna una imagen, ésta tiene por defecto la altura y la anchura del objeto. Sin embargo, un programa MQL puede establecer tamaños más pequeños y seleccionar un fragmento de una imagen para mostrar; encontrará más información al respecto en la sección sobre sincronización de tramas o frames. Si intenta establecer la altura o la anchura mayores que el tamaño de la imagen, ésta deja de mostrarse y las dimensiones del objeto no cambian.
Como ejemplo, vamos a desarrollar una versión mejorada del script ObjectAnchorLabel.mq5 de la sección titulada Definir el punto de anclaje en el objeto. En esa sección, movíamos la etiqueta de texto alrededor de la ventana y la invertíamos cuando alcanzaba alguno de los bordes de la misma, pero lo hacíamos teniendo en cuenta sólo el punto de anclaje. Debido a esto, dependiendo de la ubicación del punto de anclaje en el objeto, podría darse la situación de que la etiqueta se desplazara casi por completo más allá de la ventana. Por ejemplo, si el punto de anclaje estuviera en el lado derecho del objeto, el desplazamiento hacia la izquierda provocaría que casi todo el texto sobrepasara el borde izquierdo de la ventana antes de que el punto de anclaje tocara el borde.
En el nuevo script ObjectSizeLabel.mq5, tendremos en cuenta el tamaño del objeto y cambiaremos la dirección del movimiento en cuanto toque el borde de la ventana con cualquiera de sus lados.
Para la correcta implementación de este modo, debe tenerse en cuenta que cada esquina de ventana utilizada como centro de referencia de coordenadas al punto de anclaje sobre el objeto determina la dirección característica de los ejes X e Y. Por ejemplo, si el usuario selecciona la esquina superior izquierda en la variable de entrada ENUM_BASE_CORNER, entonces X aumenta de izquierda a derecha e Y aumenta de arriba abajo. Si se considera que el centro es la esquina inferior derecha, entonces X aumenta de derecha a izquierda de la misma, e Y aumenta de abajo a arriba.
Una combinación mutua diferente de la esquina de anclaje en la ventana y el punto de anclaje en el objeto requiere diferentes ajustes de las distancias entre los bordes del objeto y los bordes de la ventana. En concreto, cuando se selecciona una de las esquinas de la derecha y uno de los puntos de anclaje del lado derecho del objeto, entonces no es necesaria la corrección en el borde derecho de la ventana, y en el lado opuesto, el izquierdo, hay que tener en cuenta la anchura del objeto (de manera que sus dimensiones no se salgan de la ventana hacia la izquierda).
Esta regla sobre la corrección del tamaño de un objeto puede generalizarse:
- En el borde de la ventana adyacente a la esquina de anclaje, la corrección es necesaria cuando el punto de anclaje se encuentra en el lado más alejado del objeto con respecto a esta esquina;
- En el borde de la ventana opuesto a la esquina de anclaje, la corrección es necesaria cuando el punto de anclaje se encuentra en el lado cercano del objeto con respecto a esta esquina.
En otras palabras: si el nombre de la esquina (en el elemento ENUM_BASE_CORNER) y el punto de anclaje (en el elemento ENUM_ANCHOR_POINT) contienen una palabra común (por ejemplo, DERECHA), la corrección es necesaria en el lado más alejado de la ventana (es decir, lejos de la esquina seleccionada). Si se encuentran direcciones opuestas en la combinación de los lados ENUM_BASE_CORNER y ENUM_ANCHOR_POINT (por ejemplo, IZQUIERDA y DERECHA), la corrección es necesaria en el lado más cercano de la ventana. Estas reglas funcionan igual para los ejes horizontal y vertical.
Además, hay que tener en cuenta que el punto de anclaje puede estar en el centro de cualquier lado del objeto. A continuación, en la dirección perpendicular se requiere una sangría desde los bordes de la ventana, igual a la mitad del tamaño del objeto.
Un caso especial es el punto de anclaje en el centro del objeto. Para ello, debe tener siempre un margen de distancia en cualquier dirección, igual a la mitad del tamaño del objeto.
La lógica descrita se implementa en una función especial denominada GetMargins, la cual toma como entradas la esquina y el punto de anclaje seleccionados, así como las dimensiones del objeto (dx y dy). La función devuelve una estructura con 4 campos que contienen los tamaños de las sangrías adicionales que deben apartarse del punto de anclaje en la dirección de los bordes cercano y lejano de la ventana para que el objeto no se pierda de vista. Las sangrías reservan la distancia en función de las dimensiones y la posición relativa del propio objeto.
struct Margins
|
Para unificar el algoritmo se introducen las siguientes definiciones macro de direcciones (lados):
#define LEFT 0x1
|
Con su ayuda se definen máscaras de bits (combinaciones) que describen los elementos de las enumeraciones ENUM_BASE_CORNER y ENUM_ANCHOR_POINT.
const int corner_flags[] = // flags for ENUM_BASE_CORNER elements
|
Cada uno de los arrays, corner_flags y anchor_flags, contiene exactamente tantos elementos como existen en la enumeración correspondiente.
A continuación viene el código de la función principal. En primer lugar, tratemos la opción más sencilla: el punto de anclaje central.
if(anchor == ANCHOR_CENTER)
|
Para analizar el resto de situaciones, utilizaremos las máscaras de bits de los arrays anteriores direccionándolos directamente por los valores recibidos corner y anchor.
const int mask = corner_flags[corner] & anchor_flags[anchor];
|
Si la esquina y el punto de anclaje están en el mismo lado horizontal, funcionará la siguiente condición y se ajustará la anchura del objeto en el borde más alejado de la ventana.
if((mask & (LEFT | RIGHT)) != 0)
|
Si no están en el mismo lado, pueden estar en lados opuestos, o puede darse el caso de que el punto de anclaje esté en medio del lado horizontal (arriba o abajo). La comprobación de un punto de anclaje en el centro se realiza mediante la expresión (anchor_flags[anchor] & (LEFT | RIGHT)) == 0; entonces, la corrección es igual a la mitad de la anchura del objeto.
else
|
En caso contrario, con la orientación opuesta de la esquina y el punto de anclaje, realizamos un ajuste de la anchura del objeto en el borde cercano de la ventana.
Se realizan comprobaciones similares para el eje Y.
if((mask & (UPPER | LOWER)) != 0)
|
Ahora la función GetMargins está lista, y podemos proceder al código principal del script en la función OnStart. Como antes, determinamos el tamaño de la ventana, calculamos las coordenadas iniciales en el centro, creamos un objeto OBJ_LABEL y lo seleccionamos.
void OnStart()
|
Para la animación, un bucle infinito proporciona las variables pass (contador de iteraciones) y anchor (el punto de anclaje, que se elegirá periódicamente de forma aleatoria).
int pass = 0;
|
Pero hay algunos cambios en comparación con ObjectAnchorLabel.mq5.
No generaremos movimientos aleatorios del objeto; en lugar de ello, vamos a establecer una velocidad constante de 5 píxeles en diagonal.
int px = 5, py = 5; |
Para almacenar el tamaño de la etiqueta de texto, reservaremos dos nuevas variables.
int dx = 0, dy = 0; |
El resultado del recuento de sangrías adicionales se almacenará en una variable m de tipo Margins.
Margins m = {}; |
A esto le sigue directamente el bucle de mover y modificar el objeto. En él, cada 75 iteraciones (una iteración de 100 ms, véase más adelante), seleccionamos aleatoriamente un nuevo punto de anclaje, formamos un nuevo texto (el contenido del objeto) a partir de él y esperamos a que los cambios se apliquen al objeto (llamando a ChartRedraw). Esto último es necesario porque el tamaño de la inscripción se ajusta automáticamente al contenido, y el nuevo tamaño es importante para que podamos calcular correctamente las sangrías en la llamada a GetMargins.
Obtenemos las dimensiones utilizando las llamadas ObjectGetInteger con las propiedades OBJPROP_XSIZE y OBJPROP_YSIZE.
for( ;!IsStopped(); ++pass)
|
Una vez que conocemos el punto de anclaje y todas las distancias, movemos el objeto. Si este «choca» contra la pared, cambiamos el sentido del movimiento al opuesto (px a -px o py a -py, según el lado).
// bounce off window borders, object fully visible
|
Queda por actualizar el estado del propio objeto: mostrar las coordenadas actuales en la etiqueta de texto y asignarlas a las propiedades OBJPROP_XDISTANCE y OBJPROP_YDISTANCE.
ObjectSetString(0, name, OBJPROP_TEXT, " " + EnumToString(anchor)
|
Después de cambiar el objeto, llamamos a ChartRedraw y esperamos 100 ms para garantizar una animación razonablemente suave.
ChartRedraw();
|
Al final del bucle, volvemos a comprobar el tamaño de la ventana, ya que el usuario puede cambiarlo mientras se ejecuta el script, y también repetimos la petición de tamaño.
h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, t) - 1;
|
Hemos omitido algunas otras innovaciones del script ObjectSizeLabel.mq5 para que la explicación sea concisa. Quienes lo deseen pueden consultar el código. En concreto, se han utilizado colores distintivos para la inscripción: cada color específico corresponde a su propio punto de anclaje, lo que hace que los puntos de cambio sean más perceptibles. También puede hacer clic en Delete mientras se ejecuta el script: esto eliminará el objeto seleccionado del gráfico y el script finalizará automáticamente.