English Русский 中文 Deutsch 日本語 Português
Gráficos en la biblioteca DoEasy (Parte 74): Elemento gráfico básico sobre la clase CCanvas

Gráficos en la biblioteca DoEasy (Parte 74): Elemento gráfico básico sobre la clase CCanvas

MetaTrader 5Ejemplos | 22 julio 2021, 17:11
600 0
Artyom Trishkin
Artyom Trishkin

Contenido


Concepto

En el artículo anterior, iniciamos un gran apartado de la biblioteca dedicado al trabajo con gráficos, y comenzamos a crear el objeto de formulario como el objeto principal de todos los objetos gráficos en la biblioteca creados sobre la base de la clase de la Biblioteca Estándar CCanvas. Asimismo, pusimos a prueba algunas mecánicas y continuamos el desarrollo del objeto creado. Pero un análisis cuidadoso mostró que el concepto seleccionado, en primer lugar, se distingue del concepto de construcción de los objetos de biblioteca y que, en segundo lugar, el objeto de formulario es ya algo más complejo que el objeto básico.

Para el objeto gráfico básico en el lienzo, introduciremos el concepto de "elemento", a partir del cual construiremos el resto de los objetos gráficos. Por ejemplo, un objeto de formulario también es prácticamente un objeto mínimo suficiente para dibujar cualquier construcción gráfica en el programa, pero también puede ser un objeto independiente para realizar un diseño, pues dispondrá de la capacidad de dibujar el marco de un objeto, diversas figuras y texto, mientras que el objeto de elemento servirá como base para crear todos los objetos posteriores en la jerarquía "gráfica" de la biblioteca, por ejemplo:

  • Objeto gráfico básico: es el sucesor de CObject y contiene las propiedades inherentes a los objetos gráficos que se pueden construir en el terminal;
  • Objeto de elemento en el lienzo: tiene las propiedades de un objeto construido sobre la base de un objeto de lienzo;
  • Objeto de formulario: tiene las propiedades y funciones adicionales para diseñar la apariencia del objeto de elemento;
  • Objeto de ventana: es un objeto compuesto basado en objetos de elemento y objetos de formulario;
  • y así sucesivamente,

Usando este nuevo concepto como base, hoy reelaboraremos la clase básica de los objetos gráficos de la biblioteca CGBaseObj y crearemos el nuevo objeto "elemento gráfico", que repetirá al completo el concepto de construcción de los objetos principales de la biblioteca. En el futuro, este enfoque nos permitirá buscar rápidamente los objetos gráficos que necesitamos, así como ordenarlos y controlar su comportamiento y dibujado.


Mejorando las clases de la biblioteca

En el archivo MQL5\Include\DoEasy\Data.mqh, añadimos los índices del nuevo mensaje:

   MSG_LIB_SYS_FAILED_CREATE_STORAGE_FOLDER,          // Failed to create folder for storing files. Error: 
   MSG_LIB_SYS_FAILED_ADD_ACC_OBJ_TO_LIST,            // Error. Failed to add current account object to collection list
   MSG_LIB_SYS_FAILED_CREATE_CURR_ACC_OBJ,            // Error. Failed to create account object with current account data
   MSG_LIB_SYS_FAILED_OPEN_FILE_FOR_WRITE,            // Could not open file for writing
   MSG_LIB_SYS_INPUT_ERROR_NO_SYMBOL,                 // Input error: no symbol
   MSG_LIB_SYS_FAILED_CREATE_SYM_OBJ,                 // Failed to create symbol object
   MSG_LIB_SYS_FAILED_ADD_SYM_OBJ,                    // Failed to add symbol
   MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ,                 // Failed to create the graphical element object

y el texto que se corresponde con el índice nuevamente añadido:

   {"Не удалось создать папку хранения файлов. Ошибка: ","Could not create file storage folder. Error: "},
   {"Ошибка. Не удалось добавить текущий объект-аккаунт в список-коллекцию","Error. Failed to add current account object to collection list"},
   {"Ошибка. Не удалось создать объект-аккаунт с данными текущего счёта","Error. Failed to create account object with current account data"},
   {"Не удалось открыть для записи файл ","Could not open file for writing: "},
   {"Ошибка входных данных: нет символа ","Input error: no "},
   {"Не удалось создать объект-символ ","Failed to create symbol object "},
   {"Не удалось добавить символ ","Failed to add "},
   {"Не удалось создать объект-графический элемент ","Failed to create graphic element object "},

Para el nuevo objeto "elemento gráfico", añadimos su tipo al archivo \MQL5\Include\DoEasy\Defines.mqh, a la lista de enumeración de los tipos de objetos gráficos, así como sus propiedades de tipo entero y string:

//+------------------------------------------------------------------+
//| The list of graphical element types                              |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_ELEMENT_TYPE
  {
   GRAPH_ELEMENT_TYPE_ELEMENT,                        // Element
   GRAPH_ELEMENT_TYPE_FORM,                           // Form
   GRAPH_ELEMENT_TYPE_WINDOW,                         // Window
  };
//+------------------------------------------------------------------+
//| Integer properties of the graphical element on the canvas        |
//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_PROP_INTEGER
  {
   CANV_ELEMENT_PROP_ID = 0,                          // Form ID
   CANV_ELEMENT_PROP_TYPE,                            // Graphical element type
   CANV_ELEMENT_PROP_NUM,                             // Element index in the list
   CANV_ELEMENT_PROP_CHART_ID,                        // Chart ID
   CANV_ELEMENT_PROP_WND_NUM,                         // Chart subwindow index
   CANV_ELEMENT_PROP_COORD_X,                         // Form's X coordinate on the chart
   CANV_ELEMENT_PROP_COORD_Y,                         // Form's Y coordinate on the chart
   CANV_ELEMENT_PROP_WIDTH,                           // Form width
   CANV_ELEMENT_PROP_HEIGHT,                          // Form height
   CANV_ELEMENT_PROP_RIGHT,                           // Form right border
   CANV_ELEMENT_PROP_BOTTOM,                          // Form bottom border
   CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,                  // Active area offset from the left edge of the form
   CANV_ELEMENT_PROP_ACT_SHIFT_TOP,                   // Active area offset from the top edge of the form
   CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,                 // Active area offset from the right edge of the form
   CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,                // Active area offset from the bottom edge of the form
   CANV_ELEMENT_PROP_OPACITY,                         // Form opacity
   CANV_ELEMENT_PROP_COLOR_BG,                        // Form background color
   CANV_ELEMENT_PROP_MOVABLE,                         // Form moveability flag
   CANV_ELEMENT_PROP_ACTIVE,                          // Form activity flag
   CANV_ELEMENT_PROP_COORD_ACT_X,                     // X coordinate of the form's active area
   CANV_ELEMENT_PROP_COORD_ACT_Y,                     // Y coordinate of the form's active area
   CANV_ELEMENT_PROP_ACT_RIGHT,                       // Right border of the form's active area
   CANV_ELEMENT_PROP_ACT_BOTTOM,                      // Bottom border of the form's active area
  };
#define CANV_ELEMENT_PROP_INTEGER_TOTAL (23)          // Total number of integer properties
#define CANV_ELEMENT_PROP_INTEGER_SKIP  (0)           // Number of integer properties not used in sorting
//+------------------------------------------------------------------+
//| Real properties of the graphical element on the canvas           |
//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_PROP_DOUBLE
  {
   CANV_ELEMENT_PROP_DUMMY = CANV_ELEMENT_PROP_INTEGER_TOTAL, // DBL stub
  };
#define CANV_ELEMENT_PROP_DOUBLE_TOTAL  (1)           // Total number of real properties
#define CANV_ELEMENT_PROP_DOUBLE_SKIP   (1)           // Number of real properties not used in sorting
//+------------------------------------------------------------------+
//| String properties of the graphical element on the canvas         |
//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_PROP_STRING
  {
   CANV_ELEMENT_PROP_NAME_OBJ = (CANV_ELEMENT_PROP_INTEGER_TOTAL+CANV_ELEMENT_PROP_DOUBLE_TOTAL), // Form object name
   CANV_ELEMENT_PROP_NAME_RES,                        // Graphical resource name
  };
#define CANV_ELEMENT_PROP_STRING_TOTAL  (2)           // Total number of string properties
//+------------------------------------------------------------------+

Como los objetos basados ​​en el lienzo aún no tienen propiedades reales, pero el concepto de construcción de los objetos de la biblioteca requiere su presencia, simplemente añadiremos como la única propiedad real una propiedad stub de tipo real.

Para poder clasificar los objetos de elemento gráfico según sus propiedades, añadiremos una enumeración con sus posibles criterios de clasificación:

//+------------------------------------------------------------------+
//| Possible sorting criteria of graphical elements on the canvas    |
//+------------------------------------------------------------------+
#define FIRST_CANV_ELEMENT_DBL_PROP  (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP)
#define FIRST_CANV_ELEMENT_STR_PROP  (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP+CANV_ELEMENT_PROP_DOUBLE_TOTAL-CANV_ELEMENT_PROP_DOUBLE_SKIP)
enum ENUM_SORT_CANV_ELEMENT_MODE
  {
//--- Sort by integer properties
   SORT_BY_CANV_ELEMENT_ID = 0,                       // Sort by form ID
   SORT_BY_CANV_ELEMENT_TYPE,                         // Sort by graphical element type
   SORT_BY_CANV_ELEMENT_NUM,                          // Sort by form index in the list
   SORT_BY_CANV_ELEMENT_CHART_ID,                     // Sort by chart ID
   SORT_BY_CANV_ELEMENT_WND_NUM,                      // Sort by chart window index
   SORT_BY_CANV_ELEMENT_COORD_X,                      // Sort by the form X coordinate on the chart
   SORT_BY_CANV_ELEMENT_COORD_Y,                      // Sort by the form Y coordinate on the chart
   SORT_BY_CANV_ELEMENT_WIDTH,                        // Sort by the form width
   SORT_BY_CANV_ELEMENT_HEIGHT,                       // Sort by the form height
   SORT_BY_CANV_ELEMENT_RIGHT,                        // Sort by the form right border
   SORT_BY_CANV_ELEMENT_BOTTOM,                       // Sort by the form bottom border
   SORT_BY_CANV_ELEMENT_ACT_SHIFT_LEFT,               // Sort by the active area offset from the left edge of the form
   SORT_BY_CANV_ELEMENT_ACT_SHIFT_TOP,                // Sort by the active area offset from the top edge of the form
   SORT_BY_CANV_ELEMENT_ACT_SHIFT_RIGHT,              // Sort by the active area offset from the right edge of the form
   SORT_BY_CANV_ELEMENT_ACT_SHIFT_BOTTOM,             // Sort by the active area offset from the bottom edge of the form
   SORT_BY_CANV_ELEMENT_OPACITY,                      // Sort by the form opacity
   SORT_BY_CANV_ELEMENT_COLOR_BG,                     // Sort by the form background color
   SORT_BY_CANV_ELEMENT_MOVABLE,                      // Sort by the form moveability flag
   SORT_BY_CANV_ELEMENT_ACTIVE,                       // Sort by the form activity flag
   SORT_BY_CANV_ELEMENT_COORD_ACT_X,                  // Sort by X coordinate of the form active area
   SORT_BY_CANV_ELEMENT_COORD_ACT_Y,                  // Sort by Y coordinate of the form active area
   SORT_BY_CANV_ELEMENT_ACT_RIGHT,                    // Sort by the right border of the form active area
   SORT_BY_CANV_ELEMENT_ACT_BOTTOM,                   // Sort by the bottom border of the form active area
//--- Sort by real properties

//--- Sort by string properties
   SORT_BY_CANV_ELEMENT_NAME_OBJ = FIRST_CANV_ELEMENT_STR_PROP,// Sort by the form object name
   SORT_BY_CANV_ELEMENT_NAME_RES,                     // Sort by the graphical resource name
  };
//+------------------------------------------------------------------+

Ya describimos todas estas enumeraciones en el primer artículo; también las hemos discutido muchas veces, por lo que no vamos a repetirnos aquí.

Antes de comenzar a crear el objeto "elemento gráfico", reelaboraremos la clase de objeto básico de todos los objetos gráficos de la biblioteca en el archivo MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh..

En este objeto guardaremos todas las propiedades generales de cualquier objeto gráfico, como el tipo de objeto creado, el identificador del gráfico y el número de la subventana donde se construye el objeto gráfico, su nombre y el prefijo del nombre. Cualquier objeto gráfico de la biblioteca heredará de esta clase.

Para nosotros, resultará más cómodo recrear al completo esta clase que arreglar la existente. Por consiguiente, solo tendremos que eliminar todo en este archivo y añadir el nuevo:

//+------------------------------------------------------------------+
//|                                                     GBaseObj.mqh |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\..\Services\DELib.mqh"
#include <Graphics\Graphic.mqh>
//+------------------------------------------------------------------+
//| Class of the base object of the library graphical objects        |
//+------------------------------------------------------------------+
class CGBaseObj : public CObject
  {
private:
   int               m_type;                             // Object type
protected:
   string            m_name_prefix;                      // Object name prefix
   string            m_name;                             // Object name
   long              m_chart_id;                         // Chart ID
   int               m_subwindow;                        // Subwindow index
   int               m_shift_y;                          // Subwindow Y coordinate shift
   
public:
//--- Return the values of class variables
   string            Name(void)                          const { return this.m_name;      }
   long              ChartID(void)                       const { return this.m_chart_id;  }
   int               SubWindow(void)                     const { return this.m_subwindow; }
//--- The virtual method returning the object type
   virtual int       Type(void)                          const { return this.m_type;      }

//--- Constructor/destructor
                     CGBaseObj();
                    ~CGBaseObj();
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CGBaseObj::CGBaseObj() : m_shift_y(0), m_type(0), m_name_prefix(::MQLInfoString(MQL_PROGRAM_NAME)+"_")
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CGBaseObj::~CGBaseObj()
  {
  }
//+------------------------------------------------------------------+

Bien. Hemos añadido directamente al archivo un archivo de funciones de servicio y un archivo de la clase CGraphic de la Biblioteca Estándar, al que ya se había añadido un archivo de la clase CCanvas. Al mismo tiempo, la clase CGraphic dispone de un amplio espectro de métodos para dibujar diferentes gráficos, cosa que necesitaremos más tarde.

La clase hereda de la clase básica de la Biblioteca Estándar, lo cual nos permitirá crear elementos gráficos como objetos de la clase CObject y guardar listas de objetos gráficos en la biblioteca de la misma forma que ya almacenamos todos nuestros objetos en sus respectivas colecciones.

En la variable privada m_type, guardaremos el tipo de objeto de la enumeración ENUM_GRAPH_ELEMENT_TYPE, que hemos analizado anteriormente.
Por defecto, el tipo del objeto es igual a cero, y se retorna mediante el método virtual Type() de la clase básica de la Biblioteca Estándar:

   //--- method of identifying the object
   virtual int       Type(void)                                    const { return(0);      }

Aquí, hemos redefinido este método; ahora retornará el valor de la variable m_type, que dependerá del tipo de objeto gráfico que creemos.

Variables de clase protegidas:

  • m_name_prefix — aquí almacenaremos el prefijo de los nombres de los objetos para identificar los objetos gráficos según su pertenencia al programa. En consecuencia, aquí escribiremos el nombre del programa que se ha creado usando como base esta biblioteca.
  • m_name — guarda el nombre del objeto gráfico. El nombre completo del objeto se creará sumando el prefijo y el nombre. Por lo tanto, al crear objetos, solo necesitaremos especificar un nombre único para el objeto recién creado, y la clase de objeto "elemento gráfico" añadirá al nombre un prefijo que servirá para identificar el objeto con el programa que lo ha creado.
  • m_chart_id — aquí escribiremos el identificador del gráfico en el que se creará el objeto gráfico.
  • m_subwindow — subventana del gráfico en la que se construirá el objeto gráfico.
  • m_shift_y — valor de desplazamiento de la coordenada Y del objeto creado en la subventana del gráfico.

Los métodos públicos simplemente retornan los valores de las variables de clase correspondientes:

public:
//--- Return the values of class variables
   string            Name(void)                          const { return this.m_name;      }
   long              ChartID(void)                       const { return this.m_chart_id;  }
   int               SubWindow(void)                     const { return this.m_subwindow; }
//--- The virtual method returning the object type
   virtual int       Type(void)                          const { return this.m_type;      }

En el constructor de la clase, en su lista de inicialización, establecemos directamente las coordenadas Y, el tipo de objeto (0, por defecto) y el prefijo del nombre, que consta del nombre del programa y un guión bajo:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CGBaseObj::CGBaseObj() : m_shift_y(0), m_type(0), m_name_prefix(::MQLInfoString(MQL_PROGRAM_NAME)+"_")
  {
  }
//+------------------------------------------------------------------+


Objeto básico de todos los objetos gráficos de la biblioteca basados ​​en el lienzo.

Vamos a crear la clase de objeto "elemento gráfico" usando como base la clase CCanvas.
En la carpeta de la biblioteca \MQL5\Include\DoEasy\Objects\Graph\, creamos el nuevo archivo GCnvElement.mqh de la clase CGCnvElement.

A continuación, añadimos al archivo de la clase el archivo del objeto gráfico básico de la biblioteca del cual deberá heredar la clase:

//+------------------------------------------------------------------+
//|                                                  GCnvElement.mqh |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "GBaseObj.mqh"
//+------------------------------------------------------------------+
//| Class of the base object of the library graphical objects        |
//+------------------------------------------------------------------+
class CGCnvElement : public CGBaseObj
  {
  }

En la sección protegida de la clase, declaramos los objetos de las clases CCanvas y CPause y los dos métodos que retornan la posición de las coordenadas indicadas respecto al elemento y su área activa:

protected:
   CCanvas           m_canvas;                                 // CCanvas class object
   CPause            m_pause;                                  // Pause class object
//--- Return the cursor position relative to the (1) entire element and (2) the element's active area
   bool              CursorInsideElement(const int x,const int y);
   bool              CursorInsideActiveArea(const int x,const int y);

private:

En la sección privada de la clase, declaramos las matrices para guardar las propiedades de los objetos y escribimos los dos métodos que retornan los índices reales de las propiedades indicadas en las matrices correspondientes:

private:
   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];    // Integer properties
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];   // Real properties
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];   // String properties

//--- Return the index of the array the order's (1) double and (2) string properties are located at
   int               IndexProp(ENUM_CANV_ELEMENT_PROP_DOUBLE property)  const { return(int)property-CANV_ELEMENT_PROP_INTEGER_TOTAL;                                 }
   int               IndexProp(ENUM_CANV_ELEMENT_PROP_STRING property)  const { return(int)property-CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_DOUBLE_TOTAL;  }

public:

En la sección pública de la clase, ubicamos los métodos estándar de los objetos de clase de la biblioteca para escribir las propiedades en las matrices y retornar las propiedades de estas, los métodos que retornan las banderas de soporte de una propiedad especificada por parte de un objeto y los métodos para comparar dos objetos:

public:
//--- Set object's (1) integer, (2) real and (3) string properties
   void              SetProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property,long value)   { this.m_long_prop[property]=value;                      }
   void              SetProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE property,double value)  { this.m_double_prop[this.IndexProp(property)]=value;    }
   void              SetProperty(ENUM_CANV_ELEMENT_PROP_STRING property,string value)  { this.m_string_prop[this.IndexProp(property)]=value;    }
//--- Return object’s (1) integer, (2) real and (3) string property from the properties array
   long              GetProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property)        const { return this.m_long_prop[property];                     }
   double            GetProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE property)         const { return this.m_double_prop[this.IndexProp(property)];   }
   string            GetProperty(ENUM_CANV_ELEMENT_PROP_STRING property)         const { return this.m_string_prop[this.IndexProp(property)];   }

//--- Return the flag of the object supporting this property
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property)          { return true; }
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE property)           { return true; }
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_STRING property)           { return true; }

//--- Compare CGCnvElement objects with each other by all possible properties (for sorting the lists by a specified object property)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//--- Compare CGCnvElement objects with each other by all properties (to search equal objects)
   bool              IsEqual(CGCnvElement* compared_obj) const;
//--- Create the element

Todos estos métodos son estándar para los objetos de la biblioteca, consideramos en el primer artículo : siempre puede repetir el material ya cubierto más de una vez.

Además, en la sección pública de la clase, tenemos un método para crear un objeto de "elemento gráfico" en el lienzo, un método que retorna el puntero al objeto de lienzo creado, un método para establecer la frecuencia de actualización del lienzo, un método para cambiar el lienzo en un gráfico y los métodos necesarios para el acceso simplificado a las propiedades del objeto:

//--- Create the element
   bool              Create(const long chart_id,
                            const int wnd_num,
                            const string name,
                            const int x,
                            const int y,
                            const int w,
                            const int h,
                            const color colour,
                            const uchar opacity,
                            const bool redraw=false);
                                
//--- Return the pointer to a canvas object
   CCanvas          *CanvasObj(void)                                                   { return &this.m_canvas;               }
//--- Set the canvas update frequency
   void              SetFrequency(const ulong value)                                   { this.m_pause.SetWaitingMSC(value);   }
//--- Update the coordinates (shift the canvas)
   bool              Move(const int x,const int y,const bool redraw=false);
   
//--- Constructors/Destructor
                     CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                                  const int     element_id,
                                  const int     element_num,
                                  const long    chart_id,
                                  const int     wnd_num,
                                  const string  name,
                                  const int     x,
                                  const int     y,
                                  const int     w,
                                  const int     h,
                                  const color   colour,
                                  const uchar   opacity,
                                  const bool    movable=true,
                                  const bool    activity=true,
                                  const bool    redraw=false);
                     CGCnvElement(){;}
                    ~CGCnvElement();
     
//+------------------------------------------------------------------+
//| Methods of simplified access to object properties                |
//+------------------------------------------------------------------+
//--- Set the (1) X, (2) Y coordinates, (3) element width and (4) height,
   bool              SetCoordX(const int coord_x);
   bool              SetCoordY(const int coord_y);
   bool              SetWidth(const int width);
   bool              SetHeight(const int height);
//--- Set the shift of the (1) left, (2) top, (3) right, (4) bottom edge of the active area relative to the element,
//--- (5) all shifts of the active area edges relative to the element and (6) the element opacity
   void              SetActiveAreaLeftShift(const int value)   { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,fabs(value));    }
   void              SetActiveAreaRightShift(const int value)  { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,fabs(value));   }
   void              SetActiveAreaTopShift(const int value)    { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,fabs(value));     }
   void              SetActiveAreaBottomShift(const int value) { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,fabs(value));  }
   void              SetActiveAreaShift(const int left_shift,const int bottom_shift,const int right_shift,const int top_shift);
   void              SetOpacity(const uchar value,const bool redraw=false);
   
//--- Return the shift (1) of the left, (2) right, (3) top and (4) bottom edge of the element active area
   int               ActiveAreaLeftShift(void)           const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT);    }
   int               ActiveAreaRightShift(void)          const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT);   }
   int               ActiveAreaTopShift(void)            const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP);     }
   int               ActiveAreaBottomShift(void)         const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM);  }
//--- Return the coordinate (1) of the left, (2) right, (3) top and (4) bottom edge of the element active area
   int               ActiveAreaLeft(void)                const { return int(this.CoordX()+this.ActiveAreaLeftShift());              }
   int               ActiveAreaRight(void)               const { return int(this.RightEdge()-this.ActiveAreaRightShift());          }
   int               ActiveAreaTop(void)                 const { return int(this.CoordY()+this.ActiveAreaTopShift());               }
   int               ActiveAreaBottom(void)              const { return int(this.BottomEdge()-this.ActiveAreaBottomShift());        }
//--- Return (1) the opacity, coordinate (2) of the right and (3) bottom element edge
   uchar             Opacity(void)                       const { return (uchar)this.GetProperty(CANV_ELEMENT_PROP_OPACITY);         }
   int               RightEdge(void)                     const { return this.CoordX()+this.m_canvas.Width();                        }
   int               BottomEdge(void)                    const { return this.CoordY()+this.m_canvas.Height();                       }
//--- Return the (1) X, (2) Y coordinates, (3) element width and (4) height,
   int               CoordX(void)                        const { return (int)this.GetProperty(CANV_ELEMENT_PROP_COORD_X);           }
   int               CoordY(void)                        const { return (int)this.GetProperty(CANV_ELEMENT_PROP_COORD_Y);           }
   int               Width(void)                         const { return (int)this.GetProperty(CANV_ELEMENT_PROP_WIDTH);             }
   int               Height(void)                        const { return (int)this.GetProperty(CANV_ELEMENT_PROP_HEIGHT);            }
//--- Return the element (1) moveability and (2) activity flag
   bool              Movable(void)                       const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_MOVABLE);          }
   bool              Active(void)                        const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_ACTIVE);           }
//--- Return (1) the object name, (2) the graphical resource name, (3) the chart ID and (4) the chart subwindow index
   string            NameObj(void)                       const { return this.GetProperty(CANV_ELEMENT_PROP_NAME_OBJ);               }
   string            NameRes(void)                       const { return this.GetProperty(CANV_ELEMENT_PROP_NAME_RES);               }
   long              ChartID(void)                       const { return this.GetProperty(CANV_ELEMENT_PROP_CHART_ID);               }
   int               WindowNum(void)                     const { return (int)this.GetProperty(CANV_ELEMENT_PROP_WND_NUM);           }
                    
  };
//+------------------------------------------------------------------+

Vamos a analizar con más detalle la implementación de los métodos declarados.

Constructor paramétrico de la clase:

//+------------------------------------------------------------------+
//| Parametric constructor                                           |
//+------------------------------------------------------------------+
CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                           const int      element_id,
                           const int      element_num,
                           const long     chart_id,
                           const int      wnd_num,
                           const string   name,
                           const int      x,
                           const int      y,
                           const int      w,
                           const int      h,
                           const color    colour,
                           const uchar    opacity,
                           const bool     movable=true,
                           const bool     activity=true,
                           const bool     redraw=false)
                                          
  {
   this.m_name=this.m_name_prefix+name;
   this.m_chart_id=chart_id;
   this.m_subwindow=wnd_num;
   if(this.Create(chart_id,wnd_num,this.m_name,x,y,w,h,colour,opacity,redraw))
     {
      this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,this.m_canvas.ResourceName()); // Graphical resource name
      this.SetProperty(CANV_ELEMENT_PROP_CHART_ID,CGBaseObj::ChartID());         // Chart ID
      this.SetProperty(CANV_ELEMENT_PROP_WND_NUM,CGBaseObj::SubWindow());        // Chart subwindow index
      this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,CGBaseObj::Name());            // Element object name
      this.SetProperty(CANV_ELEMENT_PROP_TYPE,element_type);                     // Graphical element type
      this.SetProperty(CANV_ELEMENT_PROP_ID,element_id);                         // Element ID
      this.SetProperty(CANV_ELEMENT_PROP_NUM,element_num);                       // Element index in the list
      this.SetProperty(CANV_ELEMENT_PROP_COORD_X,x);                             // Element's X coordinate on the chart
      this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,y);                             // Element's Y coordinate on the chart
      this.SetProperty(CANV_ELEMENT_PROP_WIDTH,w);                               // Element width
      this.SetProperty(CANV_ELEMENT_PROP_HEIGHT,h);                              // Element height
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,0);                      // Active area offset from the left edge of the element
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,0);                       // Active area offset from the upper edge of the element
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,0);                     // Active area offset from the right edge of the element
      this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,0);                    // Active area offset from the bottom edge of the element
      this.SetProperty(CANV_ELEMENT_PROP_OPACITY,opacity);                       // Element opacity
      this.SetProperty(CANV_ELEMENT_PROP_COLOR_BG,colour);                       // Element color
      this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,movable);                       // Element moveability flag
      this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,activity);                       // Element activity flag
      this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.RightEdge());                // Element right border
      this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.BottomEdge());              // Element bottom border
      this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_X,this.ActiveAreaLeft());     // X coordinate of the element active area
      this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y,this.ActiveAreaTop());      // Y coordinate of the element active area
      this.SetProperty(CANV_ELEMENT_PROP_ACT_RIGHT,this.ActiveAreaRight());      // Right border of the element active area
      this.SetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM,this.ActiveAreaBottom());    // Bottom border of the element active area
     }
   else
     {
      ::Print(CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.m_name);
     }
  }
//+------------------------------------------------------------------+

Aquí, primero creamos el nombre del objeto, que consta del prefijo del nombre del objeto creado en la clase principal y el nombre transmitido en los parámetros del constructor. De esta forma, el nombre único del objeto tendrá el aspecto siguiente: "Prefijo_Nombre_del_Objeto".
A continuación, escribimos en las variables de la clase padre el identificador del gráfico y el número de subventana transmitidos ​​en los parámetros.
A continuación, llamamos al método para crear un objeto gráfico en el lienzo. Tras crear el objeto con éxito, escribimos todos los datos en las propiedades del objeto de elemento. Si no hemos podido crear el objeto gráfico de la clase CCanvas, informamos sobre ello en el diario. En este caso, se creará ya el nombre con el prefijo y se establecerá el identificador del gráfico y su subventana. Por consiguiente, podemos intentar de nuevo crear un objeto de la clase CCanvas llamando nuevamente al método Create(). Por defecto, al crear un objeto, el desplazamiento del área activa se establece como cero en cada lado, es decir, el área activa del objeto coincidirá con el tamaño del elemento gráfico creado. Después de crearlo, el tamaño y la posición del área activa siempre se podrán cambiar usando los métodos adecuados que analizaremos a continuación.

En el destructor de la clase, eliminamos el objeto creado de la clase CCanvas:

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CGCnvElement::~CGCnvElement()
  {
   this.m_canvas.Destroy();
  }
//+------------------------------------------------------------------+

Método que compara objetos de elemento gráfico entre sí según la propiedad indicada:

//+----------------------------------------------------------------------+
//|Compare CGCnvElement objects with each other by the specified property|
//+----------------------------------------------------------------------+
int CGCnvElement::Compare(const CObject *node,const int mode=0) const
  {
   const CGCnvElement *obj_compared=node;
//--- compare integer properties of two objects
   if(mode<CANV_ELEMENT_PROP_INTEGER_TOTAL)
     {
      long value_compared=obj_compared.GetProperty((ENUM_CANV_ELEMENT_PROP_INTEGER)mode);
      long value_current=this.GetProperty((ENUM_CANV_ELEMENT_PROP_INTEGER)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- compare real properties of two objects
   else if(mode<CANV_ELEMENT_PROP_DOUBLE_TOTAL+CANV_ELEMENT_PROP_INTEGER_TOTAL)
     {
      double value_compared=obj_compared.GetProperty((ENUM_CANV_ELEMENT_PROP_DOUBLE)mode);
      double value_current=this.GetProperty((ENUM_CANV_ELEMENT_PROP_DOUBLE)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- compare string properties of two objects
   else if(mode<ORDER_PROP_DOUBLE_TOTAL+ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_STRING_TOTAL)
     {
      string value_compared=obj_compared.GetProperty((ENUM_CANV_ELEMENT_PROP_STRING)mode);
      string value_current=this.GetProperty((ENUM_CANV_ELEMENT_PROP_STRING)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
   return 0;
  }
//+------------------------------------------------------------------+

El método es estándar para todos los objetos de la biblioteca, y ya lo hemos analizado anteriormente: Resumiendo: transmitimos al método el objeto cuyo parámetro indicado debemos comparar con el parámetro correspondiente del objeto actual. Dependiendo del parámetro transmitido, obtenemos del actual el parámetro análogo y retornamos el resultado de la comparación de los parámetros de los dos objetos (1, -1 y 0, para mayor, menor e igual, respectivamente).

Método que compara objetos de elemento gráfico entre sí según todas las propiedades:

//+------------------------------------------------------------------+
//| Compare CGCnvElement objects with each other by all properties   |
//+------------------------------------------------------------------+
bool CGCnvElement::IsEqual(CGCnvElement *compared_obj) const
  {
   int beg=0, end=CANV_ELEMENT_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_CANV_ELEMENT_PROP_INTEGER prop=(ENUM_CANV_ELEMENT_PROP_INTEGER)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   beg=end; end+=CANV_ELEMENT_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_CANV_ELEMENT_PROP_DOUBLE prop=(ENUM_CANV_ELEMENT_PROP_DOUBLE)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   beg=end; end+=CANV_ELEMENT_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_CANV_ELEMENT_PROP_STRING prop=(ENUM_CANV_ELEMENT_PROP_STRING)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   return true;
  }
//+------------------------------------------------------------------+

Este método también es estándar para todos los objetos de la biblioteca. Resumiendo: transmitimos al método el objeto cuyos parámetros al completo debemos comparar con los parámetros correspondientes del objeto actual. En tres ciclos por todas las propiedades del objeto, comparamos cada propiedad sucesiva de los dos objetos, y si se encuentran propiedades desiguales, el método retornará false: los objetos comparados no son iguales. Tras completar los tres ciclos, se retornará true: todas las propiedades son iguales para los dos objetos comparados.

Método que crea un objeto de elemento gráfico:

//+------------------------------------------------------------------+
//| Create the graphical element object                              |
//+------------------------------------------------------------------+
bool CGCnvElement::Create(const long chart_id,     // Chart ID
                          const int wnd_num,       // Chart subwindow
                          const string name,       // Element name
                          const int x,             // X coordinate
                          const int y,             // Y coordinate
                          const int w,             // Width
                          const int h,             // Height
                          const color colour,      // Background color
                          const uchar opacity,     // Opacity
                          const bool redraw=false) // Flag indicating the need to redraw
                         
  {
   if(this.m_canvas.CreateBitmapLabel(chart_id,wnd_num,name,x,y,w,h,COLOR_FORMAT_ARGB_NORMALIZE))
     {
      this.m_canvas.Erase(::ColorToARGB(colour,opacity));
      this.m_canvas.Update(redraw);
      this.m_shift_y=(int)::ChartGetInteger(chart_id,CHART_WINDOW_YDISTANCE,wnd_num);
      return true;
     }
   return false;
  }
//+------------------------------------------------------------------+

Transmitimos al método todos los parámetros necesarios para la construcción y llamamos al segundo formulario del método CreateBitmapLabel() de la clase CCanvas. Si el recurso gráfico vinculado al objeto gráfico se ha creado correctamente, el elemento gráfico se rellenará con color y se llamará al método Update() para mostrar en la pantalla los cambios realizados. En este caso, transmitimos al método la bandera que indica la necesidad de redibujar la pantalla: si actualizamos un objeto compuesto que consta de varios elementos gráficos, el gráfico deberá redibujarse después de realizar los cambios en todos los elementos del objeto compuesto, de modo que no provoque múltiples actualizaciones del gráfico después de cambiar cada elemento. A continuación, escribimos en la variable de la clase padre m_shift el valor del cambio de coordenada Y para la subventana y retornamos true. Si no se crea el objeto CCanvas, retornaremos false.

Método que retorna la posición del cursor respecto al elemento:

//+------------------------------------------------------------------+
//| Return the cursor position relative to the element               |
//+------------------------------------------------------------------+
bool CGCnvElement::CursorInsideElement(const int x,const int y)
  {
   return(x>=this.CoordX() && x<=this.RightEdge() && y>=this.CoordY() && y<=this.BottomEdge());
  }
//+------------------------------------------------------------------+

Transmitimos al método las coordenadas enteras del cursor X e Y, y luego retornamos la posición de las coordenadas transmitidas en relación con las dimensiones del elemento; el valor true se retornará solo si el cursor está dentro del elemento.

Método que retorna la posición del cursor respecto al área activa del elemento:

//+------------------------------------------------------------------+
//| Return the cursor position relative to the element active area   |
//+------------------------------------------------------------------+
bool CGCnvElement::CursorInsideActiveArea(const int x,const int y)
  {
   return(x>=this.ActiveAreaLeft() && x<=this.ActiveAreaRight() && y>=this.ActiveAreaTop() && y<=this.ActiveAreaBottom());
  }
//+------------------------------------------------------------------+

La lógica del método es similar a la lógica del método anterior, pero se retorna la posición de las coordenadas del cursor en relación con los límites del área activa del elemento; el valor true se retornará solo si el cursor se encuentra dentro del área activa.

Método que actualiza las coordenadas del elemento:

//+------------------------------------------------------------------+
//| Update the coordinate elements                                   |
//+------------------------------------------------------------------+
bool CGCnvElement::Move(const int x,const int y,const bool redraw=false)
  {
//--- Leave if the element is not movable or inactive
   if(!this.Movable())
      return false;
//--- If failed to set new values into graphical object properties, return 'false'
   if(!this.SetCoordX(x) || !this.SetCoordY(y))
      return false;
   //--- If the update flag is activated, redraw the chart.
   if(redraw)
      ::ChartRedraw(this.ChartID());
   //--- Return 'true'
   return true;
  }
//+------------------------------------------------------------------+

Transmitimos al método las nuevas coordenadas de la esquina superior izquierda del elemento gráfico (en las que deberemos colocarlo), y la bandera para redibujar el gráfico. Luego, verificamos la bandera de movilidad del objeto y salimos si el objeto no se puede desplazar. Si podemos establecer nuevas coordenadas para el objeto usando los métodos descritos a continuación, retornaremos false. A continuación, si la bandera de redibujo del gráfico está activada, actualizaremos el gráfico. Como resultado, retornaremos true.

Método para establecer una nueva coordenada X:

//+------------------------------------------------------------------+
//| Set the new  X coordinate                                        |
//+------------------------------------------------------------------+
bool CGCnvElement::SetCoordX(const int coord_x)
  {
   int x=(int)::ObjectGetInteger(this.ChartID(),this.NameObj(),OBJPROP_XDISTANCE);
   if(coord_x==x)
     {
      if(coord_x==GetProperty(CANV_ELEMENT_PROP_COORD_X))
         return true;
      this.SetProperty(CANV_ELEMENT_PROP_COORD_X,coord_x);
      return true;
     }
   if(::ObjectSetInteger(this.ChartID(),this.NameObj(),OBJPROP_XDISTANCE,coord_x))
     {
      this.SetProperty(CANV_ELEMENT_PROP_COORD_X,coord_x);
      return true;
     }
   return false;
  }
//+------------------------------------------------------------------+

Transmitimos al método el valor necesario de la coordenada X. Luego, obtenemos esta coordenada del objeto. Si la coordenada transmitida y la coordenada del objeto son iguales, el objeto no necesitará ser desplazado, pero deberemos verificar si el mismo valor está establecido en las propiedades del objeto. Si los valores coinciden, retornaremos de inmediato true ; de lo contrario, primero estableceremos en la propiedad del objeto el nuevo valor de las coordenadas transmitido y luego retornaremos true.
Si la coordenada transmitida al método y la coordenada del objeto no son iguales, estableceremos una nueva coordenada para el objeto y , si la configuración tiene éxito, escribiremos este valor en la propiedad del objeto y retornaremos true En cualquier otro caso, retornaremos false.

Método para establecer la nueva coordenada Y:

//+------------------------------------------------------------------+
//| Set the new Y coordinate                                         |
//+------------------------------------------------------------------+
bool CGCnvElement::SetCoordY(const int coord_y)
  {
   int y=(int)::ObjectGetInteger(this.ChartID(),this.NameObj(),OBJPROP_YDISTANCE);
   if(coord_y==y)
     {
      if(coord_y==GetProperty(CANV_ELEMENT_PROP_COORD_Y))
         return true;
      this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,coord_y);
      return true;
     }
   if(::ObjectSetInteger(this.ChartID(),this.NameObj(),OBJPROP_YDISTANCE,coord_y))
     {
      this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,coord_y);
      return true;
     }
   return false;
  }
//+------------------------------------------------------------------+

La lógica del método es similar al método de establecimiento de la coordenada Y analizado anteriormente.

Método para establecer la nueva anchura del objeto:

//+------------------------------------------------------------------+
//| Set the new width                                                |
//+------------------------------------------------------------------+
bool CGCnvElement::SetWidth(const int width)
  {
   return this.m_canvas.Resize(width,this.m_canvas.Height());
  }
//+------------------------------------------------------------------+

Transmitimos al método la nueva anchura del objeto y retornamos el resultado de la llamada del método Resize(), que cambia el tamaño del recurso gráfico.
Transmitimos al método Resize() la nueva anchura y la altura actual del objeto.

Método para establecer la nueva altura del objeto:

//+------------------------------------------------------------------+
//| Set the new height                                               |
//+------------------------------------------------------------------+
bool CGCnvElement::SetHeight(const int height)
  {
   return this.m_canvas.Resize(this.m_canvas.Width(),height);
  }
//+------------------------------------------------------------------+

Transmitimos al método la nueva altura del objeto y retornamos el resultado de la llamada del método Resize(), que cambia el tamaño del recurso gráfico.
Transmitimos al método Resize() la nueva anchura y la altura actual del objeto.

Nota para los dos métodos analizados: cuando cambiamos el tamaño del recurso, se sobrescribe la imagen anterior dibujada en el lienzo.
Por eso, perfeccionaremos estos métodos aún más; por ahora, lo dejaremos simplemente para el futuro.

Método para establecer todos los desplazamientos del área activa respecto al elemento.

//+------------------------------------------------------------------+
//| Set all shifts of the active area relative to the element        |
//+------------------------------------------------------------------+
void CGCnvElement::SetActiveAreaShift(const int left_shift,const int bottom_shift,const int right_shift,const int top_shift)
  {
   this.SetActiveAreaLeftShift(left_shift);
   this.SetActiveAreaBottomShift(bottom_shift);
   this.SetActiveAreaRightShift(right_shift);
   this.SetActiveAreaTopShift(top_shift);
  }
//+------------------------------------------------------------------+

Transmitimos al método todos los valores de las magnitudes requeridas desde los bordes del objeto "elemento gráfico" hacia adentro; a continuación, establecemos las cuatro magnitudes una a una llamando a los métodos correspondientes.

Método que establece la opacidad de un elemento:

//+------------------------------------------------------------------+
//| Set the element opacity                                          |
//+------------------------------------------------------------------+
void CGCnvElement::SetOpacity(const uchar value,const bool redraw=false)
  {
   this.m_canvas.TransparentLevelSet(value);
   this.SetProperty(CANV_ELEMENT_PROP_OPACITY,value);
   this.m_canvas.Update(redraw);
  }
//+------------------------------------------------------------------+

Transmitimos al método el valor de opacidad requerido del objeto (0 - completamente transparente, 255 - completamente opaco) y la bandera que redibuja el gráfico.
A continuación, llamamos al método TransparentLevelSet() de la clase CCanvas, escribimos el nuevo valor de la propiedad en las propiedades del objeto y actualizamos el objeto con la bandera transmitida de su redibujado.

El objeto "elemento gráfico" está listo. Ahora, necesitaremos posibilitar la clasificación de estos objetos en las listas donde se almacenarán. Para conseguirlo, ya disponemos de la clase CSelect, en la que registramos los métodos para clasificar y buscar todos los objetos de la biblioteca.

Abramos el archivo \MQL5\Include\DoEasy\Services\Select.mqh y añadimos en él la adición del archivo de la clase de objeto "elemento gráfico". Al final del cuerpo de la clase, añadimos la declaración de los métodos que clasifican y buscan los objetos de "elemento gráfico" según sus propiedades :

//+------------------------------------------------------------------+
//|                                                       Select.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
#include "..\Objects\Orders\Order.mqh"
#include "..\Objects\Events\Event.mqh"
#include "..\Objects\Accounts\Account.mqh"
#include "..\Objects\Symbols\Symbol.mqh"
#include "..\Objects\PendRequest\PendRequest.mqh"
#include "..\Objects\Series\SeriesDE.mqh"
#include "..\Objects\Indicators\Buffer.mqh"
#include "..\Objects\Indicators\IndicatorDE.mqh"
#include "..\Objects\Indicators\DataInd.mqh"
#include "..\Objects\Ticks\DataTick.mqh"
#include "..\Objects\Book\MarketBookOrd.mqh"
#include "..\Objects\MQLSignalBase\MQLSignal.mqh"
#include "..\Objects\Chart\ChartObj.mqh"
#include "..\Objects\Graph\GCnvElement.mqh"
//+------------------------------------------------------------------+

...

//+----------------------------------------------------------------------------------+
//| The methods of working with data of the graphical elements on the canvas         |
//+----------------------------------------------------------------------------------+
   //--- Return the list of objects with one of (1) integer, (2) real and (3) string properties meeting a specified criterion
   static CArrayObj *ByGraphCanvElementProperty(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByGraphCanvElementProperty(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByGraphCanvElementProperty(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Return the chart index with the maximum value of the (1) integer, (2) real and (3) string properties
   static int        FindGraphCanvElementMax(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_INTEGER property);
   static int        FindGraphCanvElementMax(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_DOUBLE property);
   static int        FindGraphCanvElementMax(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_STRING property);
   //--- Return the chart index with the minimum value of the (1) integer, (2) real and (3) string properties
   static int        FindGraphCanvElementMin(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_INTEGER property);
   static int        FindGraphCanvElementMin(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_DOUBLE property);
   static int        FindGraphCanvElementMin(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_STRING property);
//---
  };
//+------------------------------------------------------------------+

Al final del listado del archivo, implementamos los nuevos métodos declarados:

//+------------------------------------------------------------------+
//+---------------------------------------------------------------------------+
//| The methods of working with data of the graphical elements on the canvas  |
//+---------------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Return the list of objects with one integer                      |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByGraphCanvElementProperty(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   int total=list_source.Total();
   for(int i=0; i<total; i++)
     {
      CGCnvElement *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      long obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the list of objects with one real                         |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByGraphCanvElementProperty(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   for(int i=0; i<list_source.Total(); i++)
     {
      CGCnvElement *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      double obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the list of objects with one string                       |
//| property meeting the specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByGraphCanvElementProperty(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   for(int i=0; i<list_source.Total(); i++)
     {
      CGCnvElement *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      string obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the object index in the list                              |
//| with the maximum integer property value                          |
//+------------------------------------------------------------------+
int CSelect::FindGraphCanvElementMax(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_INTEGER property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CGCnvElement *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CGCnvElement *obj=list_source.At(i);
      long obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      long obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the object index in the list                              |
//| with the maximum real property value                             |
//+------------------------------------------------------------------+
int CSelect::FindGraphCanvElementMax(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_DOUBLE property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CGCnvElement *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CGCnvElement *obj=list_source.At(i);
      double obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      double obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the object index in the list                              |
//| with the maximum string property value                           |
//+------------------------------------------------------------------+
int CSelect::FindGraphCanvElementMax(CArrayObj *list_source,ENUM_CANV_ELEMENT_PROP_STRING property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CGCnvElement *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CGCnvElement *obj=list_source.At(i);
      string obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      string obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the object index in the list                              |
//| with the minimum integer property value                          |
//+------------------------------------------------------------------+
int CSelect::FindGraphCanvElementMin(CArrayObj* list_source,ENUM_CANV_ELEMENT_PROP_INTEGER property)
  {
   int index=0;
   CGCnvElement *min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CGCnvElement *obj=list_source.At(i);
      long obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      long obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the object index in the list                              |
//| with the minimum real property value                             |
//+------------------------------------------------------------------+
int CSelect::FindGraphCanvElementMin(CArrayObj* list_source,ENUM_CANV_ELEMENT_PROP_DOUBLE property)
  {
   int index=0;
   CGCnvElement *min_obj=NULL;
   int total=list_source.Total();
   if(total== 0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CGCnvElement *obj=list_source.At(i);
      double obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      double obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the object index in the list                              |
//| with the minimum string property value                           |
//+------------------------------------------------------------------+
int CSelect::FindGraphCanvElementMin(CArrayObj* list_source,ENUM_CANV_ELEMENT_PROP_STRING property)
  {
   int index=0;
   CGCnvElement *min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CGCnvElement *obj=list_source.At(i);
      string obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      string obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+

Podrá leer más información sobre el funcionamiento de los métodos en el tercer artículo, donde discutimos la creación de la clase CSelect.

Con esto, podemos dar por finalizado el material de hoy. Vamos a poner a prueba lo que hemos obtenido.


Simulación

Para la simulación, vamos a tomar el asesor del artículo anterior y a guardarlo en la nueva carpeta \MQL5\Experts\TestDoEasy\Part74\ con el nuevo nombre TestDoEasyPart74.mq5.

Añadimos al asesor el archivo de la clase de matriz dinámica de los punteros a las instancias de la clase CObject y sus herederos, la biblioteca estándar y
los archivos de las clases CSelect y CGCnvElement de la biblioteca.
A continuación, indicamos el número de objetos de "elemento gráfico" a crear y declaramos la lista en la que colocaremos los elementos gráficos generados:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart74.mq5 |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//--- includes
#include <Arrays\ArrayObj.mqh>
#include <DoEasy\Services\Select.mqh>
#include <DoEasy\Objects\Graph\GCnvElement.mqh>
//--- defines
#define        FORMS_TOTAL (2)
//--- input parameters
sinput   bool  InpMovable  = true;  // Movable flag
//--- global variables
CArrayObj      list_elements;
//+------------------------------------------------------------------+

En el manejador OnInit() del asesor, creamos los nuevos objetos de elemento gráfico, transmitiendo al constructor de la clase todos los parámetros necesarios:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Set the permissions to send cursor movement and mouse scroll events
   ChartSetInteger(ChartID(),CHART_EVENT_MOUSE_MOVE,true);
   ChartSetInteger(ChartID(),CHART_EVENT_MOUSE_WHEEL,true);
//--- Set EA global variables

//--- Create the specified number of graphical elements on the canvas
   int total=FORMS_TOTAL;
   for(int i=0;i<total;i++)
     {
      //--- When creating an object, pass all the required parameters to it
      CGCnvElement *element=new CGCnvElement(GRAPH_ELEMENT_TYPE_ELEMENT,i,0,ChartID(),0,"Element_0"+(string)(i+1),300,40+(i*80),100,70,clrSilver,200,InpMovable,true,true);
      if(element==NULL)
         continue;
      //--- Add objects to the list
      if(!list_elements.Add(element))
        {
         delete element;
         continue;
        }
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

En el manejador OnDeinit(), eliminamos todos los comentarios del gráfico:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   Comment("");
  }
//+------------------------------------------------------------------+

En el manejador OnChartEvent(), capturamos el clic en el objeto, obtenemos el objeto de elemento de la lista de objetos con el nombre correspondiente al nombre del objeto en el que hemos clicado, escrito en el manejador sparam parámetro, e incrementamos su nivel de opacidad en 5 unidades. En el comentario del gráfico, mostramos un mensaje con el nombre del objeto procesado y el valor de su nivel de opacidad:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- If clicking on an object
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- In the new list, get the element object with the name corresponding to the sparam string parameter value of the OnChartEvent() handler
      CArrayObj *obj_list=CSelect::ByGraphCanvElementProperty(GetPointer(list_elements),CANV_ELEMENT_PROP_NAME_OBJ,sparam,EQUAL);
      if(obj_list!=NULL && obj_list.Total()>0)
        {
         //--- Get the pointer to the object in the list
         CGCnvElement *obj=obj_list.At(0);
         //--- and set the new opacity level for it
         uchar opasity=obj.Opacity();
         if((opasity+5)>255)
            opasity=0;
         else 
            opasity+=5;
         //--- Set the new opacity to the object and display the object name and opacity level in the journal
         obj.SetOpacity(opasity);
         Comment(DFUN,"Object name: ",obj.NameObj(),", opasity=",opasity);
        }
     }
  }
//+------------------------------------------------------------------+

Compilamos el asesor y lo iniciamos en el gráfico del símbolo. Al clicar en cualquiera de los objetos "elemento gráfico", su opacidad aumentará hasta 255; después de alcanzar el valor máximo (255), se incrementará desde 0 hasta 255, y en los comentarios al gráfico se mostrará el nombre del objeto sobre el que hemos clicado, así como su nivel de opacidad:



¿Qué es lo próximo?

En el próximo artículo, continuaremos desarrollando el objeto "elemento gráfico", y comenzaremos a añadir los métodos para mostrar en él las primitivas gráficas y el texto.

Más abajo se adjuntan todos los archivos de la versión actual de la biblioteca y el archivo del asesor de prueba para MQL5. Puede descargarlo todo y ponerlo a prueba por sí mismo.
Si tiene preguntas, observaciones o sugerencias, podrá concretarlas en los comentarios al artículo.

Volver al contenido

*Artículos de esta serie:

Gráficos en la biblioteca DoEasy (Parte 73): Objeto de formulario del elemento gráfico

Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/9493

Archivos adjuntos |
MQL5.zip (3957.08 KB)
Gráficos en la biblioteca DoEasy (Parte 75): Métodos de trabajo con primitivas y texto en el elemento gráfico básico Gráficos en la biblioteca DoEasy (Parte 75): Métodos de trabajo con primitivas y texto en el elemento gráfico básico
En el presente artículo, continuaremos el desarrollo de la clase de elemento gráfico de todos los elementos gráficos de la biblioteca creados sobre la base de la Biblioteca Estándar CCanvas. En concreto, crearemos los métodos para dibujar las primitivas gráficas y los métodos para mostrar el texto en un objeto de elemento gráfico.
Combinatoria y teoría de la probabilidad en el trading (Parte I): Fundamentos Combinatoria y teoría de la probabilidad en el trading (Parte I): Fundamentos
En esta serie de artículos, buscaremos una aplicación práctica de la teoría de probabilidad para describir el proceso del trading y la fijación de los precios. En el primer artículo, nos familiarizaremos con los conceptos básicos de la combinatoria y la teoría de probabilidad, y analizaremos el primer ejemplo de la aplicación de fractales dentro de la teoría de probabilidad.
Análisis de clústeres (Parte I): Usando la inclinación de las líneas de indicador Análisis de clústeres (Parte I): Usando la inclinación de las líneas de indicador
El análisis de clústeres es uno de los elementos más importantes de la inteligencia artificial. En este artículo, trataremos de aplicar el análisis de inclinación del clúster del indicador para obtener valores de umbral que nos ayuden a determinar la naturaleza plana o de tendencia del mercado.
Gráficos en la biblioteca DoEasy (Parte 73): Objeto de formulario del elemento gráfico Gráficos en la biblioteca DoEasy (Parte 73): Objeto de formulario del elemento gráfico
En el presente artículo, iniciaremos un nuevo apartado del trabajo con gráficos. En esta ocasión, vamos a crear el objeto de estado del ratón, el objeto básico de todos los elementos gráficos y la clase de objeto de formulario de los elementos gráficos de la biblioteca.