Gráficos na biblioteca DoEasy (Parte 74): elemento gráfico básico baseado na classe CCanvas
Sumário
- Ideia
- Aprimorando as classes da biblioteca
- Objeto base que compreende todos os objetos gráficos da biblioteca baseados em canvas
- Teste
- O que vem agora?
Ideia
No último artigo abrimos uma seção grande sobre a biblioteca para trabalhar com gráficos e começamos a criar um objeto-forma, como o objeto base que compreendia todos os objetos gráficos presentes na biblioteca criada com base na classe de biblioteca padrão CCanvas. Testamos alguns mecanismos e tivemos que desenvolver ainda mais o objeto criado. Só que uma análise cuidadosa mostrou que o conceito escolhido, em primeiro lugar, difere do de construção de objetos de biblioteca e, em segundo lugar, o objeto-forma já é algo mais complexo do que o objeto base.
Para o objeto gráfico base na tela, introduziremos o conceito de "elemento", para, assim, construirmos os objetos gráficos restantes. Por exemplo, basicamente, um objeto-forma também é um objeto com as condições mínimas para plotar qualquer construção gráfica no programa, e pode ser um objeto independente para desenhar. Ele terá a capacidade de desenhar a borda do objeto, diversas formas e textos. Já o objeto-elemento servirá como base para a criação de todos os objetos subsequentes na hierarquia "gráfica" da biblioteca, por exemplo:
- O objeto gráfico básico é herdeiro de CObject e contém propriedades inerentes aos objetos gráficos que podem ser construídos no terminal;
- O objeto-elemento na tela possui as propriedades de um objeto construído com base em um objeto-tela;
- O objeto-forma possui propriedades adicionais e funcionalidade para projetar a aparência do objeto-elemento;
- O objeto-janela é um objeto composto baseado em objetos-elementos e objetos-formas;
- etc.
Com base neste novo conceito, hoje iremos retrabalhar a classe base de objetos gráficos da biblioteca CGBaseObj e criar um novo objeto "elemento gráfico", que irá repetir completamente todo o conceito de construção dos principais objetos da biblioteca. Essa abordagem nos permitirá, no futuro, pesquisar rapidamente os objetos gráficos de que precisamos, classificá-los e controlar seu comportamento e renderização.
Aprimorando as classes da biblioteca
No arquivo MQL5\Include\DoEasy\Data.mqh inserimos o índice da nova mensagem:
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
e o texto correspondente ao índice recém-adicionado:
{"Не удалось создать папку хранения файлов. Ошибка: ","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 o novo elemento "elemento gráfico", no arquivo \MQL5\Include\DoEasy\Defines.mqh adicionamos seu tipo à lista-enumeração correspondente, bem como suas propriedades inteiras e de 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 //+------------------------------------------------------------------+
Uma vez que os objetos baseados em canvas ainda não têm propriedades reais, mas o conceito de construção de objetos de biblioteca requer sua presença, então nós, como a única propriedade real, simplesmente adicionamos uma propriedade stub real.
Para podermos classificar objetos-elementos gráficos por propriedades, adicionamos uma enumeração com possíveis critérios de classificação:
//+------------------------------------------------------------------+ //| 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 }; //+------------------------------------------------------------------+
Todos essas enumerações foram descritas no primeiro artigo, portanto não falaremos sobre elas aqui.
Antes de começarmos a criar o objeto "elemento gráfico", iremos retrabalhar a classe do objeto base de todos os objetos gráficos da biblioteca no arquivo MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh.
Neste objeto iremos armazenar todas as propriedades gerais dos objetos gráficos, como o tipo que está sendo criado, o identificador do gráfico e o número da subjanela onde o objeto gráfico é construído, seu nome e prefixo. Todos os objetos gráficos na biblioteca serão herdados desta classe.
Será mais conveniente para nós recriar completamente esta classe do que consertar a existente. Para isso, vamos simplesmente excluir tudo neste arquivo e inserir um novo:
//+------------------------------------------------------------------+ //| 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() { } //+------------------------------------------------------------------+
Ao arquivo são imediatamente anexados o arquivo de funções de serviço da biblioteca e o arquivo da classe CGraphic da Biblioteca Padrão, ao qual já foi anexado o arquivo da classe CCanvas. Além disso, a classe CGraphic possui um amplo conjunto de métodos para desenhar gráficos, dos quais também precisaremos no futuro.
A classe é herdada da classe base da Biblioteca Padrão, o que nos permitirá criar elementos gráficos como objetos da classe CObject e armazenar as listas de objetos gráficos da biblioteca da mesma forma que já armazenamos todos os nossos objetos em suas respectivas coleções.
Na variável privada m_type vamos armazenar o tipo de objeto a partir da enumeração ENUM_GRAPH_ELEMENT_TYPE discutida acima.
Por padrão, o tipo de objeto é zero e é retornado pelo método virtual Type() da classe base da biblioteca padrão:
//--- method of identifying the object virtual int Type(void) const { return(0); }
Aqui substituímos este método. Ele retornará o valor da variável m_type, que dependerá do tipo de objeto gráfico que está sendo criado.
Variáveis protegidas da classe:
- m_name_prefix - aqui vamos armazenar o prefixo dos nomes dos objetos para identificar objetos gráficos pelo fato de pertencerem ao programa. Assim, aqui iremos escrever o nome do programa criado com base nesta biblioteca.
- m_name - armazena o nome do objeto gráfico. O nome completo do objeto será criado com ajudo do prefixo e do nome. Assim, ao criar objetos, precisaremos apenas especificar um nome único para o objeto recém-criado, e a própria classe de objeto "elemento gráfico" adicionará um prefixo ao nome, pelo qual será possível identificar o objeto com o programa que o criou.
- m_chart_id - aqui vamos escrever o identificador do gráfico no qual o objeto gráfico será criado.
- m_subwindow - a subjanela do gráfico, na qual o objeto gráfico será construído.
- m_shift_y - valor do deslocamento da coordenada Y do objeto criado na subjanela do gráfico.
Os métodos públicos apenas retornam os valores das variáveis de classe correspondentes:
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; }
Na lista de inicialização do construtor da classe definimos o valor do deslocamento da coordenada Y, o tipo de objeto (0 por padrão) e o prefixo do nome consistindo no nome do programa e um sublinhado:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CGBaseObj::CGBaseObj() : m_shift_y(0), m_type(0), m_name_prefix(::MQLInfoString(MQL_PROGRAM_NAME)+"_") { } //+------------------------------------------------------------------+
Objeto base que compreende todos os objetos gráficos da biblioteca baseados em canvas
Vamos começar a criar a classe de objeto "elemento gráfico" baseado na classe CCanvas.
Na pasta da biblioteca \MQL5\Include\DoEasy\Objects\Graph\ criaremos o arquivo novo GCnvElement.mqh da classe CGCnvElement.
Ao arquivo da classe anexamos o arquivo principal do objeto principal da biblioteca, objeto esse do qual a classe deve ser herdada:
//+------------------------------------------------------------------+ //| 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 { }
Na seção protegida da classe, declaramos objetos das classes CCanvas e CPause e dois métodos que retornam a posição das coordenadas em relação ao elemento e sua área ativa:
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:
Na seção privada da classe, declararemos matrizes para armazenar propriedades do objeto e escreveremos dois métodos que retornam os índices reais das propriedades especificadas nas matrizes correspondentes:
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:
Na seção pública da classe, colocaremos os métodos padrão dos objetos das classes da biblioteca para escrever propriedades nas matrizes e para retornar propriedades destes últimos, métodos que retornam sinalizadores para indicar que o objeto suporta a propriedade especificada e métodos para comparar dois 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 esses métodos são padrão para os objetos da biblioteca que estudamos no primeiro artigo - você sempre pode rever o material já abordado à vontade.
Além disso, na seção pública da classe, há um método para criar um objeto "elemento gráfico" na tela, um método que retorna um ponteiro para o objeto-tela criado, um método para definir a frequência de atualização da tela, um método para deslocar a tela no gráfico e métodos para acesso simplificado às propriedades do 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 dar uma olhada mais de perto na implementação dos métodos declarados.
Construtor paramétrico da classe:
//+------------------------------------------------------------------+ //| 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); } } //+------------------------------------------------------------------+
Aqui primeiro criamos o nome do objeto, consistindo no prefixo dos nomes dos objetos criados na classe pai e no nome passado nos parâmetros do construtor. Assim, o nome exclusivo do objeto será semelhante a "Prefixo_Object_Name".
Em seguida, nas variáveis da classe pai escrevemos o identificador do gráfico e o número da subjanela transferidos aos parâmetros.
Em seguida, chamamos o método para criar um objeto gráfico na tela. Após a criação bem-sucedida do objeto, registramos todos os dados nas propriedades do objeto-elemento. Se o objeto gráfico da classe CCanvas não puder ser criado - imprimimos isso no log. Neste caso, um nome com um prefixo já será criado e o identificador do gráfico e sua subjanela serão configurados. Assim, você pode tentar criar novamente um objeto da classe CCanvas chamando o método Create() novamente. Por padrão, ao criar um objeto, o deslocamento do núcleo é definido como zero em cada lado, ou seja, a área ativa do objeto corresponderá ao tamanho do elemento gráfico criado. Após sua criação, o tamanho e a posição do núcleo sempre podem ser alterados usando os devidos métodos, que serão discutidos a seguir.
No destruidor da classe destruímos o objeto criado da classe CCanvas:
//+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CGCnvElement::~CGCnvElement() { this.m_canvas.Destroy(); } //+------------------------------------------------------------------+
Método que compara a propriedade especificada de dois objetos-elementos gráficos:
//+----------------------------------------------------------------------+ //|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; } //+------------------------------------------------------------------+
O método é padrão para todos os objetos da biblioteca e foi considerado por nós anteriormente. Resumindo: ao método são transferidos o objeto, o parâmetro especificado do qual deve ser comparado com o parâmetro correspondente do objeto atual. Dependendo do parâmetro passado, obtemos outro e retornamos o resultado da comparação dos parâmetros de dois objetos (1, -1 e 0 para mais, menos e igual, respectivamente).
Método que compara as todas as propriedades dos objetos-elementos gráficos:
//+------------------------------------------------------------------+ //| 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 também é padrão para todos os objetos da biblioteca. Resumindo: ao método é transferido o objeto, todos os parâmetros do que deve ser comparado com os do objeto atual. Em três loops através de todas as propriedades, comparamos dois objetos e, se houver propriedades que diferem, o método retornará false, indicando que os objetos comparados não são iguais. Ao final dos três ciclos, será retornado true - todas as propriedades dos dois objetos comparados são iguais.
Método que cria um objeto-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; } //+------------------------------------------------------------------+
Todos os parâmetros necessários para a construção são passados para o método, e é chamada a segunda forma do método CreateBitmapLabel() da classe CCanvas. Se o recurso gráfico associado ao objeto gráfico for criado com sucesso, o elemento gráfico será preenchido com cor e chamado o membro Update() para exibir as alterações feitas. Além disso, ao método é transferido o sinalizador de necessidade de redesenhar a tela - se atualizarmos um objeto composto que consiste em vários elementos gráficos, teremos de redesenhar o gráfico após fazer alterações em todos os elementos do objeto composto, para não causar várias atualizações do gráfico após alterar cada elemento. Em seguida, na variável da classe pai m_shift escrevemos o valor de deslocamento da coordenada Y para a subjanela e retorno true. Se o objeto da classe CCanvas não for criado, retornamos false.
Método que retorna a posição do cursor em relação ao 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()); } //+------------------------------------------------------------------+
Ao método são transferidas as coordenadas inteiras do cursor X e Y, e é retornada a posição das coordenadas passadas em relação às dimensões do elemento - o valor true só será retornado se o cursor estiver dentro do elemento.
Método que retorna a posição do cursor em relação à área ativa do 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()); } //+------------------------------------------------------------------+
A lógica do método é semelhante à lógica do anterior, mas é retornada a posição das coordenadas do cursor em relação às bordas da zona ativa do elemento - o valor true será retornado apenas se o cursor estiver dentro da zona ativa.
Método que atualiza as coordenadas do 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; } //+------------------------------------------------------------------+
Ao método são passadas novas coordenadas do canto superior esquerdo do elemento gráfico, necessárias para colocá-lo, e o sinalizador de redesenho do gráfico. Em seguida, verificamos o sinalizador de deslocamento do objeto e saímos se o objeto não for móvel. Se não for possível definir novas coordenadas para o objeto, por meio dos métodos que consideraremos a seguir, retornamos false. Depois, se o sinalizador de redesenho do gráfico estiver definido, atualizamos o gráfico. Como resultado, retornamos true.
Método de definição da nova 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; } //+------------------------------------------------------------------+
Passamos o valor da coordenada X necessária para o método. Em seguida, pegamos esta coordenada a partir do objeto. Se a coordenada passada e a do objeto forem iguais, o objeto não precisará ser movido, mas será necessário verificar se o mesmo valor está definido nas propriedades do objeto. Se os valores corresponderem, retornamos true, de outra forma, primeiro definimos o novo valor de coordenada passado para a propriedade do objeto e, em seguida, retornamos true.
Se a coordenada passada para o método e a do objeto não forem iguais,definimos uma nova coordenada para o objeto e após a definição bem-sucedida, escrevemos este valor na propriedade do objeto e retornamos true. Em qualquer outro caso, retornamos false.
Método de definição da nova 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; } //+------------------------------------------------------------------+
A lógica do método é semelhante à do método acima para definir a coordenada X.
Método de definição da nova largura do objeto:
//+------------------------------------------------------------------+ //| Set the new width | //+------------------------------------------------------------------+ bool CGCnvElement::SetWidth(const int width) { return this.m_canvas.Resize(width,this.m_canvas.Height()); } //+------------------------------------------------------------------+
Ao método é passada a nova largura do objeto e é retornado o resultado da chamada do método Resize() que redimensiona o recurso gráfico.
Ao método Resize() são transferidas a nova largura e a largura atual do objeto.
Método para definir a nova altura do objeto:
//+------------------------------------------------------------------+ //| Set the new height | //+------------------------------------------------------------------+ bool CGCnvElement::SetHeight(const int height) { return this.m_canvas.Resize(this.m_canvas.Width(),height); } //+------------------------------------------------------------------+
Ao método é passado a nova altura do objeto e é retornado o resultado da chamada do método Resize() que redimensiona o recurso gráfico.
Ao método Resize() são transferidos a largura atual e a nova altura do objeto.
Observação a propósito dos dois métodos discutidos: quando o recurso é redimensionado, a imagem anterior desenhada na tela é sobrescrita.
Por isso, modificaremos esses métodos no futuro.
O método que define todos os deslocamentos da área ativa em relação ao 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); } //+------------------------------------------------------------------+
Ao método são passados todos os valores dos deslocamentos (desde as bordas do objeto "elemento gráfico" para dentro) e todos os quatro deslocamentos são definidos um por um chamando os métodos apropriados.
Método para definir a opacidade de um 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); } //+------------------------------------------------------------------+
Ao método são transferidos o valor de opacidade de objeto (0 - completamente transparente, 255 - completamente opaco) e sinalizador de redesenho do gráfico.
Em seguida, chamamos o método TransparentLevelSet() da classe CCanvas, escrevemos o novo valor da propriedade nas propriedades do objeto e atualizamos o objeto com o sinalizador de redesenho passado.
O objeto "elemento gráfico" está pronto. Agora precisamos gerar a capacidade de classificar esses objetos nas listas onde eles serão armazenados. Para fazer isso, já temos uma classe CSelect em que registramos métodos para classificar e pesquisar todos os objetos da biblioteca.
Abrimos o arquivo \MQL5\Include\DoEasy\Services\Select.mqh e escrevemos a integração do arquivo da classe do objeto "elemento gráfico" e, no final do corpo da classe, a declaração dos métodos de classe de classificação e busca de objetos "elemento gráfico" de acordo com suas propriedades:
//+------------------------------------------------------------------+ //| 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); //--- }; //+------------------------------------------------------------------+
No final da listagem do arquivo, escreveremos a implementação dos novos 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; } //+------------------------------------------------------------------+
No terceiro artigo você pode ler sobre como usar os métodos.
Teste
Para o teste, vamos pegar o Expert Advisor do artigo anterior e o salvamos na nova pasta \MQL5\Experts\TestDoEasy\Part71\ com o novo nome TestDoEasyPart71.mq5.
Ao Expert Advisor anexamos o arquivo da classe de matriz dinâmica de ponteiros para instâncias da classe CObject e seus herdeiros, bem como o arquivo da biblioteca padrão e
os arquivos das classes CSelect e CGCnvElement da biblioteca, especificamos o número de objetos "elemento gráfico" criados e declaramos a lista na qual colocaremos os objetos gráficos criados:
//+------------------------------------------------------------------+ //| 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; //+------------------------------------------------------------------+
No manipuladorOnInit() do Expert Advisor, criaremos novos objetos-elementos gráficos, passando todos os parâmetros necessários para o construtor da classe:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
No manipulador OnDeinit() removemos todos os comentários do gráfico:
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- destroy timer EventKillTimer(); Comment(""); } //+------------------------------------------------------------------+
No manipulador OnChartEvent() pegamos o clique no objeto, obtemos da lista de objetos um elemento-objeto com o nome correspondente ao nome do objeto clicado, registrado no parâmetro sparam do manipulador, e aumentamos seu nível de opacidade em 5 unidades. No comentário do gráfico, exibimos uma mensagem com o nome do objeto processado e o seu valor de opacidade:
//+------------------------------------------------------------------+ //| 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); } } } //+------------------------------------------------------------------+
Vamos compilar o Expert Advisor e executar no gráfico do símbolo. Ao clicar em qualquer um dos objetos "elemento gráfico", sua opacidade aumentará para 255, enquanto o nome do objeto clicado e seu nível de opacidade serão exibidos no comentário do gráfico:
O que vem agora?
No próximo artigo, continuaremos a desenvolver o objeto "elemento gráfico" e começaremos a adicionar métodos para exibir primitivos gráficos e texto nele.
Todos os arquivos da versão atual da biblioteca e o arquivo do EA de teste para MQL5 estão anexados abaixo. Você pode baixá-los e testar tudo sozinho.
Se você tiver perguntas, comentários e sugestões, poderá expressá-los nos comentários do artigo.
*Artigos desta série:
Gráficos na biblioteca DoEasy (Parte 73): objeto-forma de um elemento gráfico
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/9493
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso