![Gráficos en la biblioteca DoEasy (Parte 74): Elemento gráfico básico sobre la clase CCanvas](https://c.mql5.com/i/articles/overlay.png)
Gráficos en la biblioteca DoEasy (Parte 74): Elemento gráfico básico sobre la clase CCanvas
Contenido
- Concepto
- Mejorando las clases de la biblioteca
- Objeto básico de todos los objetos gráficos de la biblioteca basados en el lienzo.
- Simulación
- ¿Qué es lo próximo?
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.
*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
![Gráficos en la biblioteca DoEasy (Parte 75): Métodos de trabajo con primitivas y texto en el elemento gráfico básico](https://c.mql5.com/2/42/MQL5-avatar-doeasy-library3-2__2.png)
![Combinatoria y teoría de la probabilidad en el trading (Parte I): Fundamentos](https://c.mql5.com/2/42/dj1f.png)
![Análisis de clústeres (Parte I): Usando la inclinación de las líneas de indicador](https://c.mql5.com/2/42/mql5-avatar-cluster_analysis.png)
![Gráficos en la biblioteca DoEasy (Parte 73): Objeto de formulario del elemento gráfico](https://c.mql5.com/2/42/MQL5-avatar-doeasy-library3-2.png)
![MQL5 - Lenguaje de estrategias comerciales para el terminal de cliente MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso