- 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
Ajustes de visualización de objetos: color, estilo y marco
La apariencia de los objetos puede modificarse mediante diversas propiedades, que exploraremos en esta sección, empezando por el color, el estilo, el ancho de línea y los bordes. Otros aspectos del formato, como el tipo de letra, la inclinación y la alineación del texto, se abordarán en las secciones siguientes.
Todas las propiedades de la siguiente tabla tienen tipos compatibles con enteros y, por tanto, son gestionadas por las funciones ObjectGetInteger y ObjectSetInteger.
Identificador |
Descripción |
Tipo de propiedad |
---|---|---|
OBJPROP_COLOR |
El color de la línea y del elemento principal del objeto (por ejemplo, fuente o relleno) |
color |
OBJPROP_STYLE |
Estilo de línea |
ENUM_LINE_STYLE |
OBJPROP_WIDTH |
Grosor de la línea en píxeles |
int |
OBJPROP_FILL |
Rellenar un objeto con color (para OBJ_RECTANGLE, OBJ_TRIANGLE, OBJ_ELLIPSE, OBJ_CHANNEL, OBJ_STDDEVCHANNEL, OBJ_REGRESSION) |
bool |
OBJPROP_BACK |
Objeto en segundo plano |
bool |
OBJPROP_BGCOLOR |
Color de fondo para OBJ_EDIT, OBJ_BUTTON, OBJ_RECTANGLE_LABEL |
color |
OBJPROP_BORDER_TYPE |
Tipo de marco para panel rectangular OBJ_RECTANGLE_LABEL |
ENUM_BORDER_TYPE |
OBJPROP_BORDER_COLOR |
Color del marco para el campo de entrada OBJ_EDIT y el botón OBJ_BUTTON |
color |
A diferencia de la mayoría de los objetos con líneas (verticales y horizontales separadas, de tendencia, cíclicas, canales, etc.), donde la propiedad OBJPROP_COLOR define el color de la línea, para las imágenes OBJ_BITMAP_LABEL y OBJ_BITMAP define el color del marco, y OBJPROP_STYLE define el tipo de dibujo del marco.
Ya hemos conocido la enumeración ENUM_LINE_STYLE, utilizada para OBJPROP_STYLE, en el capítulo sobre indicadores, en la sección sobre Configuración de trazado.
Es necesario distinguir el relleno realizado por el color de primer plano OBJPROP_COLOR del color de fondo OBJPROP_BGCOLOR. Ambos son compatibles con diferentes grupos de tipos de objetos, que se enumeran en la tabla.
La propiedad OBJPROP_BACK requiere una explicación aparte. El hecho es que los objetos y los indicadores se muestran por defecto en la parte superior del gráfico de precios. El usuario puede cambiar este comportamiento para todo el gráfico yendo al cuadro de diálogo Setting del gráfico, y más allá del marcador Shared, la opción Chart on top. Este indicador también tiene un equivalente en el software, la propiedad CHART_FOREGROUND (véase Modos de visualización de gráficos). Sin embargo, a veces es conveniente no eliminar todos los objetos, sino sólo los seleccionados, en el fondo. A continuación, para ellos, puede establecer OBJPROP_BACK en true. En este caso, el objeto se solapará incluso con los separadores de cuadrícula y de punto, si están activados en el gráfico.
Cuando el modo de relleno OBJPROP_FILL está activado, el color de las barras que caen dentro de la forma depende de la propiedad OBJPROP_BACK. Por defecto, con OBJPROP_BACK igual a false, las barras que se superponen al objeto se dibujan en color invertido con respecto a OBJPROP_COLOR (el color invertido se obtiene cambiando todos los bits del valor de color por los opuestos, por ejemplo, se obtiene 0x00FF7F para 0xFF0080). Con OBJPROP_BACK igual a true, las barras se dibujan de la forma habitual, ya que el objeto se muestra en segundo plano, «debajo» del gráfico (véase un ejemplo más abajo).
La enumeración ENUM_BORDER_TYPE contiene los siguientes elementos:
Identificador |
Apariencia |
---|---|
BORDER_FLAT |
Plano |
BORDER_RAISED |
Convexo |
BORDER_SUNKEN |
Cóncavo |
Cuando el borde es plano (BORDER_FLAT), se dibuja como una línea con color, estilo y anchura según las propiedades OBJPROP_COLOR, OBJPROP_STYLE, OBJPROP_WIDTH. Las versiones convexa y cóncava imitan chaflanes de volumen alrededor del perímetro en tonos de OBJPROP_BGCOLOR.
Cuando no se establece el color del borde OBJPROP_BORDER_COLOR (por defecto, que corresponde a clrNone), el campo de entrada queda enmarcado por una línea del color principal OBJPROP_COLOR, y alrededor del botón se dibuja un marco tridimensional con chaflanes en los tonos de OBJPROP_BGCOLOR.
Para probar las nuevas propiedades, vea el script ObjectStyle.mq5. En él, crearemos 5 rectángulos del tipo OBJ_RECTANGLE, es decir, con referencia al tiempo y a los precios. Estarán espaciados uniformemente por todo el ancho de la ventana, resaltando el rango entre el precio máximo High y el precio mínimo Low en cada uno de los cinco periodos de tiempo. Para todos los objetos ajustaremos y cambiaremos periódicamente el color, el estilo y el grosor de las líneas, así como la opción de relleno y visualización detrás del gráfico.
Vamos a utilizar de nuevo la clase auxiliar ObjectBuilder, derivada de Object Selector. A diferencia de lo que vimos en la sección anterior, añadimos a ObjectBuilder un destructor en el que llamaremos a ObjectDelete.
#include <MQL5Book/ObjectMonitor.mqh>
|
Esto permitirá asignar a esta clase no sólo la configuración de objetos, sino también su eliminación automática al finalizar el script.
En la función OnStart averiguamos el número de barras visibles y el índice de la primera barra, y también calculamos la anchura de un rectángulo en barras.
#define OBJECT_NUMBER 5
|
Reservemos un array de punteros inteligentes para los objetos con el fin de garantizar la llamada de los destructores ObjectBuilder.
AutoPtr<ObjectBuilder> objects[OBJECT_NUMBER]; |
Defina una paleta de colores y cree 5 objetos de rectángulo.
color colors[OBJECT_NUMBER] = {clrRed, clrGreen, clrBlue, clrMagenta, clrOrange};
|
Aquí, para cada objeto, se calculan las coordenadas de dos puntos de anclaje; se establecen el color, el estilo y el ancho de línea iniciales.
A continuación, en un bucle infinito, cambiamos las propiedades de los objetos. Cuando ScrollLock está activado, la animación puede pausarse.
const int key = TerminalInfoInteger(TERMINAL_KEYSTATE_SCRLOCK);
|
Este es el aspecto que tiene en un gráfico:
Rectángulos OBJ_RECTANGLE con diferentes configuraciones de visualización
El rectángulo rojo situado más a la izquierda tiene activado el modo de relleno y está en primer plano. Así, las barras de su interior se muestran en azul brillante contrastado (clrAqua, también conocido comúnmente como cyan, que es clrRed invertido ). El rectángulo púrpura también tiene un relleno, pero con una opción de fondo, por lo que las barras en él se muestran de una manera estándar.
Tenga en cuenta que el rectángulo naranja cubre completamente las barras al principio y al final de su subrango debido a la gran anchura de las líneas y a la visualización en la parte superior del gráfico.
Cuando el relleno está activado, no se tiene en cuenta la anchura de la línea. Cuando la anchura del borde es superior a 1, no se aplican algunos estilos de línea discontinua.
ObjectShapesDraw
Para el segundo ejemplo de esta sección, recuerde el hipotético programa de dibujo de formas que esbozamos en la Parte 3 cuando descubrimos POO. Nuestro progreso se detuvo en el hecho de que en el método de dibujo virtual (y se llamaba dibujar) sólo podíamos imprimir un mensaje en el registro de que estábamos dibujando una forma específica. Ahora, tras familiarizarnos con los objetos gráficos, tenemos la oportunidad de poner en práctica el dibujo.
Tomemos el script Shapes5stats.mq5 como punto de partida. La versión actualizada se llamará ObjectShapesDraw.mq5.
Recordemos que, además de la clase base Shape, hemos descrito varias clases de formas: Rectangle, Ellipse, Triangle, Square, Circle. Todas ellas superponen con éxito objetos gráficos de los tipos OBJ_RECTANGLE, OBJ_ELLIPSE, OBJ_TRIANGLE. Pero hay algunos matices.
Todos los objetos especificados están vinculados a coordenadas de tiempo y precio, mientras que nuestro programa de dibujo asume ejes X e Y unificados con posicionamiento puntual. A este respecto, necesitaremos configurar un gráfico para dibujar de forma especial y utilizar la función ChartXYToTimePrice para recalcular los puntos de la pantalla en tiempo y precio.
Además, los objetos OBJ_ELLIPSE y OBJ_TRIANGLE permiten rotaciones arbitrarias (en concreto, los radios pequeño y grande de una elipse pueden rotarse), mientras que OBJ_RECTANGLE siempre tiene sus lados orientados horizontal y verticalmente. Para simplificar el ejemplo, nos limitamos a la posición estándar de todas las formas.
En teoría, la nueva aplicación debería considerarse una demostración de objetos gráficos, y no un programa de dibujo. Un enfoque más correcto para el dibujo completo, desprovisto de las restricciones que imponen los objetos gráficos (ya que están destinados a otros fines en general, como el marcado de gráficos), es utilizar recursos gráficos. Por ello, volveremos a replantearnos el programa de dibujo en el capítulo dedicado a los recursos.
En la nueva clase Shape vamos a deshacernos de la estructura anidada Pair con coordenadas de objetos: esta estructura servía para demostrar varios principios de la programación orientada a objetos (POO), pero ahora es más fácil devolver la descripción original de los campos int x, y directamente a la clase Shape. También añadiremos un campo con el nombre del objeto.
class Shape
|
El campo name será necesario para establecer las propiedades de un objeto gráfico, así como para eliminarlo del gráfico, lo que es lógico hacer en el destructor.
Dado que los distintos tipos de formas requieren un número diferente de puntos o tamaños característicos, añadiremos el método setup, además del método virtual draw, en la interfaz Shape:
virtual void setup(const int ¶meters[]) = 0; |
Recordemos que en el script hemos implementado una clase anidada Shape::Registrator, que se encargaba de contar el número de formas por tipo. Ha llegado el momento de confiarle algo más de responsabilidad para que funcione como una fábrica de formas. Las clases o métodos «fábrica» son buenos porque permiten crear objetos de diferentes clases de forma unificada.
Para ello, añadimos a Registrator un método para crear una forma con los parámetros que incluyen las coordenadas obligatorias del primer punto, un color, y un array de parámetros adicionales (cada forma será capaz de interpretarlo de acuerdo con sus propias reglas, y en el futuro, leer o escribir en un archivo).
virtual Shape *create(const int px, const int py, const color back,
|
El método es virtual abstracto porque ciertos tipos de formas sólo pueden ser creados por clases registradoras derivadas descritas en clases descendientes de Shape. Para simplificar la escritura de clases de generación de registro derivadas, introducimos una clase de plantilla MyRegistrator con una implementación del método create adecuada para todos los casos.
template<typename T>
|
Aquí llamamos al constructor de alguna forma T previamente desconocida, la ajustamos llamando a setup y devolvemos una instancia al código de llamada.
Así es como se utiliza en la clase Rectangle, que tiene dos parámetros adicionales para la anchura y la altura.
class Rectangle : public Shape
|
Al crear una forma, su nombre contendrá no sólo el nombre de la clase (typename), sino también el número ordinal de la instancia, calculado en la llamada a r.increment().
Otras clases de formas se describen de forma similar.
Ahora es el momento de examinar el método draw para Rectangle. En él, traducimos un par de puntos (x,y) y (x + dx, y + dy) a coordenadas tiempo/precio utilizando ChartXYToTimePrice y creamos un objeto OBJ_RECTANGLE.
void draw() override
|
Por supuesto, no olvide establecer el color en OBJPROP_COLOR y el relleno en OBJPROP_FILL.
En el caso de la clase Square, no es necesario modificar nada en sí: basta con igualar dx y dy.
Para la clase Ellipse, dos opciones adicionales, dx y dy, determinan los radios pequeño y grande trazados en relación con el centro (x,y). En consecuencia, en el método draw calculamos 3 puntos de anclaje y creamos un objeto OBJ_ELLIPSE.
class Ellipse : public Shape
|
Circle es un caso especial de elipse con radios iguales.
Por último, en esta fase sólo se admiten los triángulos equiláteros: el tamaño del lado está contenido en un campo adicional dx. Le invitamos a conocer su métododraw en el código fuente de forma independiente.
El nuevo script generará, como antes, un número determinado de formas aleatorias. Se crean mediante la función addRandomShape.
Shape *addRandomShape()
|
Aquí es donde vemos el uso del método de fábrica create, llamado sobre un objeto registrador seleccionado aleatoriamente con el número n. Si más adelante decidimos añadir otras clases de formas, no tendremos que cambiar nada en la lógica de generación.
Todas las formas se colocan en la parte central de la ventana y tienen dimensiones no superiores a un cuarto de la ventana.
Queda por considerar directamente las llamadas a la función addRandomShape, y la configuración especial del horario que ya hemos mencionado.
Para obtener una representación «cuadrada» de los puntos en la pantalla, ajuste el modo CHART_SCALEFIX_11. Además, elegiremos la escala más densa (comprimida) a lo largo del eje temporal CHART_SCALE (0), porque en ella una barra ocupa 1 píxel horizontal (máxima precisión). Por último, desactive la visualización del propio gráfico estableciendo CHART_SHOW en false.
void OnStart()
|
Para almacenar las formas, reservemos un array de punteros inteligentes y llenémoslo de formas aleatorias.
#define FIGURES 21
|
A continuación, ejecutamos un bucle infinito hasta que el usuario detiene el script, en el que movemos ligeramente las formas utilizando el método move.
while(!IsStopped())
|
Al final, restauramos la configuración del gráfico.
// it's not enough to disable CHART_SCALEFIX_11, you need CHART_SCALEFIX
|
En la siguiente captura de pantalla se muestra el aspecto que podría tener un gráfico con las formas dibujadas.
Objetos de forma del gráfico
La particularidad de dibujar objetos es la «multiplicación» de los colores en los lugares donde se superponen.
Debido a que el eje Y sube y baja, todos los triángulos están al revés, pero eso no es crítico, porque vamos a rehacer el programa de pintura basado en recursos de todos modos.