English Русский 中文 Español Deutsch 日本語
preview
DoEasy. Controles (Parte 27): Continuamos a trabalhar no objeto WinForms "ProgressBar"

DoEasy. Controles (Parte 27): Continuamos a trabalhar no objeto WinForms "ProgressBar"

MetaTrader 5Exemplos | 1 março 2023, 09:00
449 0
Artyom Trishkin
Artyom Trishkin

Conteúdo


Ideia

No artigo anterior, demos início ao desenvolvimento do controle ProgressBar. Esse elemento é composto por dois componentes principais: o fundo e a barra de progresso. No futuro, será possível adicionar outros elementos de design no fundo, como textos e imagens. Atualmente, o controle é um objeto estático WinForms. No entanto, hoje vamos desenvolver uma nova funcionalidade que nos permitirá controlar programaticamente a barra de progresso. Com essa nova funcionalidade, poderemos utilizá-la em nossos programas para indicar o progresso de quaisquer operações que exijam um número significativo de ações idênticas.

Ao observar barras de progresso no ambiente Windows, é possível notar que durante o processo de preenchimento, alguns efeitos visuais como luzes em movimento são exibidos. Além de tornar a interface do programa mais atraente, os efeitos visuais na barra de progresso também servem como uma indicação adicional do progresso da conclusão do processo. Se o número de operações exibido na barra de progresso for grande, a barra não será preenchida a cada operação concluída, mas sim após um determinado número delas. Dessa forma, mesmo que a barra de progresso esteja cheia, as operações ainda podem continuar. Nessa situação, a continuação dos efeitos visuais na barra de progresso indica que o processo ainda não foi concluído. Quando o processo terminar, a barra de progresso não exibirá mais os efeitos visuais.

Normalmente, quando o processo é concluído, o controle ProgressBar é ocultado e, portanto, a indicação descrita acima não fica mais visível. No entanto, se a barra de progresso for criada em uma janela separada, que deve ser fechada manualmente após a conclusão de todas as operações, é possível observar a indicação de conclusão/não conclusão do processo por meio de efeitos visuais na barra de progresso. Outra situação em que isso é possível é quando várias barras de progresso exibem um processo dividido em subprocessos. Nesse caso, as barras de progresso dos subprocessos já concluídos são exibidas até que o processo geral seja concluído. Além disso, em controles ProgressBar que exibem subprocessos, é possível observar essa indicação dupla - se a barra de progresso estiver completamente preenchida, mas os efeitos visuais ainda estiverem sendo exibidos, isso indica que o subprocesso ainda não foi totalmente concluído.

Ao implementarmos a funcionalidade de efeitos visuais para o controle ProgressBar, poderemos utilizar esse conceito em outros controles da biblioteca. Para cada objeto que precisar ter efeitos visuais, o mesmo será adicionado à lista de elementos ativos. A biblioteca irá verificar essa lista e chamar os manipuladores de eventos do temporizador de cada um dos objetos adicionados à lista. A implementação da funcionalidade necessária para os efeitos visuais será realizada nos próprios objetos, por meio de seus manipuladores de temporizador.

Além disso, nesta etapa vamos aprimorar o design dos ícones predefinidos. No artigo anterior, foi mencionado um comportamento estranho das linhas de suavização ao serem desenhadas com transparência. De fato, todas as primitivas gráficas desenhadas com os métodos de suavização da Biblioteca Padrão apresentam uma aparência anormal quando definidas como transparentes. Isso significa que não é possível obter um efeito suave de aparecimento/desaparecimento de tais linhas. No entanto, podemos recorrer a um truque: se definirmos a opacidade da cor para um valor específico, por exemplo, de 128 a 255, podemos desenhar a linha usando o método de suavização. Já se definirmos a opacidade de 127 a 0, a linha será desenhada utilizando o método convencional, sem suavização. Essa abordagem proporciona um efeito visual suave de aparecimento/desaparecimento das primitivas gráficas desenhadas, enquanto a transparência esconde as "rugosidades" das linhas desenhadas sem suavização de serrilhado.


Modificando as classes da biblioteca

No último artigo, criamos um objeto brilho a partir de um objeto sombra, pressupondo que ele seria derivado para aproveitar os métodos de desfoque disponíveis nesta classe. No entanto, na prática, essa abordagem tem se mostrado desnecessária e limitante, além de complicar a adição de efeitos visuais em outros objetos. Ao utilizar um objeto de brilho como sucessor de um objeto de sombra, sua utilização fica restrita ao âmbito de um conceito já criado, o que limita a sua flexibilidade.

Sendo assim, a partir de agora, o objeto brilho será herdado da classe de objeto base de todos os objetos WinForms da biblioteca CWinFormBase, e estará incluído na lista de objetos auxiliares. Com isso, poderemos adicioná-lo facilmente com apenas uma linha de código na classe em que ele deve ser utilizado. Já foram escritos manipuladores para objetos ativos, o que nos salvará de ter que adicionar rotineiramente funcionalidades idênticas de processamento de objetos sombra em diferentes classes da biblioteca. Dessa forma, utilizaremos os métodos prontos para adicionar objetos vinculados e processá-los, tornando a adição de efeitos visuais em nossos objetos mais fácil e eficiente.

No arquivo \MQL5\Include\DoEasy\Defines.mqh, vamos mover a linha do identificador do objeto brilho da seção de objetos gráficos para a seção de controles auxiliares da biblioteca:

//+------------------------------------------------------------------+
//| The list of graphical element types                              |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_ELEMENT_TYPE
  {
   GRAPH_ELEMENT_TYPE_STANDARD,                       // Standard graphical object
   GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED,              // Extended standard graphical object
   GRAPH_ELEMENT_TYPE_SHADOW_OBJ,                     // Shadow object
                                                                    
   GRAPH_ELEMENT_TYPE_ELEMENT,                        // Element
   GRAPH_ELEMENT_TYPE_FORM,                           // Form
   GRAPH_ELEMENT_TYPE_WINDOW,                         // Window
   //--- WinForms
   GRAPH_ELEMENT_TYPE_WF_UNDERLAY,                    // Panel object underlay
   GRAPH_ELEMENT_TYPE_WF_BASE,                        // Windows Forms Base
   //--- 'Container' object types are to be set below
   GRAPH_ELEMENT_TYPE_WF_CONTAINER,                   // Windows Forms container base object
   GRAPH_ELEMENT_TYPE_WF_PANEL,                       // Windows Forms Panel
   GRAPH_ELEMENT_TYPE_WF_GROUPBOX,                    // Windows Forms GroupBox
   GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,                 // Windows Forms TabControl
   GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER,             // Windows Forms SplitContainer
   //--- 'Standard control' object types are to be set below
   GRAPH_ELEMENT_TYPE_WF_COMMON_BASE,                 // Windows Forms base standard control
   GRAPH_ELEMENT_TYPE_WF_LABEL,                       // Windows Forms Label
   GRAPH_ELEMENT_TYPE_WF_BUTTON,                      // Windows Forms Button
   GRAPH_ELEMENT_TYPE_WF_CHECKBOX,                    // Windows Forms CheckBox
   GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON,                 // Windows Forms RadioButton
   GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX,           // Base list object of Windows Forms elements
   GRAPH_ELEMENT_TYPE_WF_LIST_BOX,                    // Windows Forms ListBox
   GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX,            // Windows Forms CheckedListBox
   GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX,             // Windows Forms ButtonListBox
   GRAPH_ELEMENT_TYPE_WF_TOOLTIP,                     // Windows Forms ToolTip
   GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,                // Windows Forms ProgressBar
   //--- Auxiliary elements of WinForms objects
   GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM,               // Windows Forms ListBoxItem
   GRAPH_ELEMENT_TYPE_WF_TAB_HEADER,                  // Windows Forms TabHeader
   GRAPH_ELEMENT_TYPE_WF_TAB_FIELD,                   // Windows Forms TabField
   GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL,       // Windows Forms SplitContainerPanel
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON,                // Windows Forms ArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP,             // Windows Forms UpArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN,           // Windows Forms DownArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT,           // Windows Forms LeftArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT,          // Windows Forms RightArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX,        // Windows Forms UpDownArrowButtonsBox
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX,        // Windows Forms LeftRightArrowButtonsBox
   GRAPH_ELEMENT_TYPE_WF_SPLITTER,                    // Windows Forms Splitter
   GRAPH_ELEMENT_TYPE_WF_HINT_BASE,                   // Windows Forms HintBase
   GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT,              // Windows Forms HintMoveLeft
   GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT,             // Windows Forms HintMoveRight
   GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP,                // Windows Forms HintMoveUp
   GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN,              // Windows Forms HintMoveDown
   GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR,            // Windows Forms BarProgressBar
   GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ,                   // Glare object
  };
//+------------------------------------------------------------------+


Para desenhar efeitos visuais (neste caso, destaques), precisamos definir uma lista de estilos:

//+------------------------------------------------------------------+
//| List of visual effect styles                                     |
//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_VISUAL_EFF_STYLE
  {
   CANV_ELEMENT_VISUAL_EFF_STYLE_RECTANGLE,           // Rectangle
   CANV_ELEMENT_VISUAL_EFF_STYLE_PARALLELOGRAMM,      // Parallelogram
  };
//+------------------------------------------------------------------+

Inicialmente, serão criados apenas dois tipos de realces - um retângulo regular e um paralelogramo. No entanto, será possível desenhar qualquer número de formas de realce predefinidas e utilizá-las para criar diversos efeitos visuais. Além disso, essas formas não precisam ser primitivas gráficas, podendo ser qualquer imagem sobreposta a objetos como efeitos, o que oferece ainda mais possibilidades para a criação de efeitos visuais. Vale ressaltar que essas imagens podem ser animadas, o que será explorado posteriormente como exemplo.

No arquivo \MQL5\Include\DoEasy\Data.mqh, adicionamos os índices das novas mensagens da biblioteca:

//--- CForm
   MSG_FORM_OBJECT_TEXT_NO_SHADOW_OBJ_FIRST_CREATE_IT,// No shadow object. Create it using the CreateShadowObj() method
   MSG_FORM_OBJECT_TEXT_NO_GLARE_OBJ_FIRST_CREATE_IT, // No glare object. We must first create it using the CreateGlareObj() method
   MSG_FORM_OBJECT_ERR_FAILED_CREATE_SHADOW_OBJ,      // Failed to create new shadow object
   MSG_FORM_OBJECT_ERR_FAILED_CREATE_GLARE_OBJ,       // Failed to create new glare object
   MSG_FORM_OBJECT_ERR_FAILED_CREATE_PC_OBJ,          // Failed to create new pixel copier object
   MSG_FORM_OBJECT_PC_OBJ_ALREADY_IN_LIST,            // Pixel copier object with ID already present in the list 

e mensagens de texto correspondentes aos índices recém-adicionados:

//--- CForm
   {"Отсутствует объект тени. Необходимо сначала его создать при помощи метода CreateShadowObj()","There is no shadow object. You must first create it using the CreateShadowObj() method"},
   {"Отсутствует объект блика. Необходимо сначала его создать при помощи метода CreateGlareObj()","There is no glare object. You must first create it using the CreateGlareObj() method"},
   {"Не удалось создать новый объект для тени","Failed to create new object for shadow"},
   {"Не удалось создать новый объект для блика","Failed to create new object for glare"},
   {"Не удалось создать новый объект-копировщик пикселей","Failed to create new pixel copier object"},
   {"В списке уже есть объект-копировщик пикселей с идентификатором ","There is already a pixel copier object in the list with ID "},

Vamos mover o índice de mensagem "objeto brilho" para o grupo de objetos WinForms padrão:

   MSG_GRAPH_ELEMENT_TYPE_ELEMENT,                    // Element
   MSG_GRAPH_ELEMENT_TYPE_SHADOW_OBJ,                 // Shadow object
                                                                    
   MSG_GRAPH_ELEMENT_TYPE_FORM,                       // Form
   MSG_GRAPH_ELEMENT_TYPE_WINDOW,                     // Window

   MSG_GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR,        // BarProgressBar control
   MSG_GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,            // ProgressBar control
   MSG_GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ,               // Glare object
//---
   MSG_GRAPH_ELEMENT_TYPE_WF_WRONG_TYPE_PASSED,       // Incorrect control type

E da mesma forma vamos transferir o texto desta mensagem:

   {"Элемент","Element"},
   {"Объект тени","Shadow object"},
                     
   {"Форма","Form"},
   {"Окно","Window"},

   {"Элемент управления BarProgressBar","Control element \"BarProgressBar\""},
   {"Элемент управления ProgressBar","Control element \"ProgressBar\""},
   {"Объект блика","Glare object"},
   
   {"Передан не правильный тип элемента управления","Wrong control type passed"},


No arquivo \MQL5\Include\DoEasy\GraphINI.mqh, onde os parâmetros de vários esquemas de cores são especificados, adicionaremos uma propriedade para especificar o valor de opacidade para o realce. Vamos aumentar o número total de propriedades para 12:

//+------------------------------------------------------------------+
//| List of form style parameter indices                             |
//+------------------------------------------------------------------+
enum ENUM_FORM_STYLE_PARAMS
  {
   //--- CForm
   FORM_STYLE_FRAME_GLARE_OPACITY,              // Glare opacity
   FORM_STYLE_FRAME_SHADOW_OPACITY,             // Shadow opacity
   FORM_STYLE_FRAME_SHADOW_BLUR,                // Shadow blur
   FORM_STYLE_DARKENING_COLOR_FOR_SHADOW,       // Form shadow color darkening
   FORM_STYLE_FRAME_SHADOW_X_SHIFT,             // Shadow X axis shift
   FORM_STYLE_FRAME_SHADOW_Y_SHIFT,             // Shadow Y axis shift
   FORM_STYLE_GRADIENT_V,                       // Vertical gradient filling flag
   FORM_STYLE_GRADIENT_C,                       // Cyclic gradient filling flag
   //--- CPanel
   FORM_STYLE_FRAME_WIDTH_LEFT,                 // Panel frame width to the left
   FORM_STYLE_FRAME_WIDTH_RIGHT,                // Panel frame width to the right
   FORM_STYLE_FRAME_WIDTH_TOP,                  // Panel frame width on top
   FORM_STYLE_FRAME_WIDTH_BOTTOM,               // Panel frame width below
  };
#define TOTAL_FORM_STYLE_PARAMS        (12)     // Number of form style parameters
//+------------------------------------------------------------------+


Vamos escrever os valores padrão para a opacidade dos realces no array de propriedades para os dois esquemas de cores atualmente disponíveis na biblioteca:

//+------------------------------------------------------------------+
//| Array containing form style parameters                           |
//+------------------------------------------------------------------+
int array_form_style[TOTAL_FORM_STYLES][TOTAL_FORM_STYLE_PARAMS]=
  {
//--- "Flat form" style parameters
   {
      //--- CForm
      40,                                       // Glare opacity
      80,                                       // Shadow opacity
      4,                                        // Shadow blur
      80,                                       // Form shadow color darkening
      2,                                        // Shadow X axis shift
      2,                                        // Shadow Y axis shift
      false,                                    // Vertical gradient filling flag
      false,                                    // Cyclic gradient filling flag
      //--- CPanel
      3,                                        // Panel frame width to the left
      3,                                        // Panel frame width to the right
      3,                                        // Panel frame width on top
      3,                                        // Panel frame width below
   },
//--- "Embossed form" style parameters
   {
      //--- CForm
      40,                                       // Glare opacity
      80,                                       // Shadow opacity
      4,                                        // Shadow blur
      80,                                       // Form shadow color darkening
      2,                                        // Shadow X axis shift
      2,                                        // Shadow Y axis shift
      true,                                     // Vertical gradient filling flag
      false,                                    // Cyclic gradient filling flag
      //--- CPanel
      3,                                        // Panel frame width to the left
      3,                                        // Panel frame width to the right
      3,                                        // Panel frame width on top
      3,                                        // Panel frame width below
   },
  };
//+------------------------------------------------------------------+

No futuro, mudaremos radicalmente e complementaremos a composição desses valores de propriedade para diferentes esquemas de cores quando começarmos a implementá-los.


Já que mudamos a categoria do elemento gráfico brilho hoje, no arquivo \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh da classe do objeto base de todos os objetos gráficos da biblioteca, em seu método que retorna uma descrição do tipo do elemento gráfico, passaremos o processamento do tipo "objeto brlilho" passado para o método de sua localização anterior para o local correspondente ao tipo:

//+------------------------------------------------------------------+
//| Return the description of the graphical element type             |
//+------------------------------------------------------------------+
string CGBaseObj::TypeElementDescription(const ENUM_GRAPH_ELEMENT_TYPE type)
  {
   return
     (
      type==GRAPH_ELEMENT_TYPE_STANDARD                  ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD)                 :
      type==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED)        :
      type==GRAPH_ELEMENT_TYPE_ELEMENT                   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_ELEMENT)                  :
      type==GRAPH_ELEMENT_TYPE_SHADOW_OBJ                ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_SHADOW_OBJ)               :
                                                                                                                            
      type==GRAPH_ELEMENT_TYPE_FORM                      ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_FORM)                     :
      type==GRAPH_ELEMENT_TYPE_WINDOW                    ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WINDOW)                   :
      //--- WinForms
      type==GRAPH_ELEMENT_TYPE_WF_UNDERLAY               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_UNDERLAY)              :
      type==GRAPH_ELEMENT_TYPE_WF_BASE                   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BASE)                  :
      //--- Containers
      type==GRAPH_ELEMENT_TYPE_WF_CONTAINER              ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CONTAINER)             :
      type==GRAPH_ELEMENT_TYPE_WF_GROUPBOX               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_GROUPBOX)              :
      type==GRAPH_ELEMENT_TYPE_WF_PANEL                  ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_PANEL)                 :
      type==GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL)           :
      type==GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER        ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER)       :
      //--- Standard controls
      type==GRAPH_ELEMENT_TYPE_WF_COMMON_BASE            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_COMMON_BASE)           :
      type==GRAPH_ELEMENT_TYPE_WF_LABEL                  ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LABEL)                 :
      type==GRAPH_ELEMENT_TYPE_WF_CHECKBOX               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKBOX)              :
      type==GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON)           :
      type==GRAPH_ELEMENT_TYPE_WF_BUTTON                 ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON)                :
      type==GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX      ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX)     :
      type==GRAPH_ELEMENT_TYPE_WF_LIST_BOX               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX)              :
      type==GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM          ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM)         :
      type==GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX       ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX)      :
      type==GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX        ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX)       :
      type==GRAPH_ELEMENT_TYPE_WF_TOOLTIP                ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TOOLTIP)               :
      type==GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR           ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR)          :
      //--- Auxiliary control objects
      type==GRAPH_ELEMENT_TYPE_WF_TAB_HEADER             ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_HEADER)            :
      type==GRAPH_ELEMENT_TYPE_WF_TAB_FIELD              ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_FIELD)             :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON           ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON)          :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP        ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP)       :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN      ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN)     :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT      ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT)     :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT     ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT)    :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX)  :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX)  :
      type==GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL  ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL) :
      type==GRAPH_ELEMENT_TYPE_WF_SPLITTER               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLITTER)              :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_BASE              ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_BASE)             :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT)        :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT        ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT)       :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP           ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP)          :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN)        :
      type==GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR       ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR)      :
      type==GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ              ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ)             :
      "Unknown"
     );
  }  
//+------------------------------------------------------------------+

Tecnicamente, não há diferença na ordem de verificação dos elementos, mas por uma questão de organização, vamos movê-los para a posição "correta". É importante destacar que, tecnicamente, há uma diferença na velocidade de acesso, uma vez que a verificação if-else é realizada aqui e, se algum tipo for verificado com mais frequência do que os outros, mas estiver localizado mais próximo do final da lista de verificações, o tempo de acesso pode aumentar. No entanto, isso é apenas teoricamente dentro da estrutura dos ciclos do processador. Para garantir que o acesso a qualquer elemento seja o mesmo, todas essas verificações devem ser feitas nos métodos da biblioteca usando a instrução switch, o que será feito posteriormente, no final da criação da biblioteca.

Inicialmente, planejamos (e implementamos no último artigo) que o objeto brilho seria herdado do objeto sombra, devido à presença dos métodos de desfoque gaussiano na classe do objeto sombra. No entanto, agora abandonamos essa abordagem porque não precisamos necessariamente utilizar o desfoque para implementar realces, e, além disso, utilizar um objeto sombra como pai do objeto brilho tornaria muito mais complicado adicionar o objeto brilho a outros elementos gráficos, como parte dos objetos vinculados a eles. Sendo assim, o objeto brilho será um objeto WinForms auxiliar independente. No entanto, para que possamos utilizar o desfoque nos objetos em que for necessário (e não apenas no objeto sombra), transferiremos os métodos de desfoque da classe do objeto sombra para a classe do elemento gráfico - a partir de lá esses métodos estarão disponíveis para todos os elementos gráficos da biblioteca.

No arquivo \MQL5\Include\DoEasy\Objects\Graph\ShadowObj.mqh, removemos a inclusão da biblioteca AlgLib, removemos a declaração de métodos para desfoque e removemos os códigos de implementação para estes dois métodos:

//+------------------------------------------------------------------+
//|                                                    ShadowObj.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 "GCnvElement.mqh"
#include <Math\Alglib\alglib.mqh>
//+------------------------------------------------------------------+
//| Shadows object class                                             |
//+------------------------------------------------------------------+
class CShadowObj : public CGCnvElement
  {
private:
   color             m_color;                         // Shadow color
   uchar             m_opacity;                       // Shadow opacity
   uchar             m_blur;                          // Blur
//--- Draw the object shadow form
   void              DrawShadowFigureRect(const int w,const int h);
protected:
//--- Gaussian blur
   bool              GaussianBlur(const uint radius);
//--- Return the array of weight ratios
   bool              GetQuadratureWeights(const double mu0,const int n,double &weights[]);
//--- Protected constructor with object type, chart ID and subwindow
                     CShadowObj(const ENUM_GRAPH_ELEMENT_TYPE type,
                                CGCnvElement *main_obj,CGCnvElement *base_obj,
                                const long chart_id,
                                const int subwindow,
                                const string descript,
                                const int x,
                                const int y,
                                const int w,
                                const int h);
public:


No arquivo \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh , escreveremos a inclusão da biblioteca AlgLib e declararemos os métodos para implementar o desfoque na seção protegida:

//+------------------------------------------------------------------+
//|                                                  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"
#include <Math\Alglib\alglib.mqh>
//+------------------------------------------------------------------+
//| Class of the graphical element object                            |
//+------------------------------------------------------------------+
class CGCnvElement : public CGBaseObj
  {
protected:
   CGCnvElement     *m_element_main;                           // Pointer to the initial parent element within all the groups of bound objects
   CGCnvElement     *m_element_base;                           // Pointer to the parent element within related objects of the current group
   CCanvas           m_canvas;                                 // CCanvas class object
   CPause            m_pause;                                  // Pause class object
   bool              m_shadow;                                 // Shadow presence
   color             m_chart_color_bg;                         // Chart background color
   uint              m_duplicate_res[];                        // Array for storing resource data copy
   color             m_array_colors_bg[];                      // Array of element background colors
   color             m_array_colors_bg_dwn[];                  // Array of control background colors when clicking on the control
   color             m_array_colors_bg_ovr[];                  // Array of control background colors when hovering the mouse over the control
   bool              m_gradient_v;                             // Vertical gradient filling flag
   bool              m_gradient_c;                             // Cyclic gradient filling flag
   int               m_init_relative_x;                        // Initial relative X coordinate
   int               m_init_relative_y;                        // Initial relative Y coordinate
   color             m_array_colors_bg_init[];                 // Array of element background colors (initial color)

//--- Create (1) the object structure and (2) the object from the structure
   virtual bool      ObjectToStruct(void);
   virtual void      StructToObject(void);
//--- Copy the color array to the specified background color array
   void              CopyArraysColors(color &array_dst[],const color &array_src[],const string source);
   
//--- Return the number of graphical elements (1) by type, (2) by name and type
   int               GetNumGraphElements(const ENUM_GRAPH_ELEMENT_TYPE type) const;
   int               GetNumGraphElements(const string name,const ENUM_GRAPH_ELEMENT_TYPE type) const;
//--- Create and return the graphical element name by its type
   string            CreateNameGraphElement(const ENUM_GRAPH_ELEMENT_TYPE type);
   
//--- Gaussian blur
   bool              GaussianBlur(const uint radius);
//--- Return the array of weight ratios
   bool              GetQuadratureWeights(const double mu0,const int n,double &weights[]);

private:


Fora do corpo da classe, adicionamos a implementação dos métodos de desenfoque removidos da classe CShadowObj:

//+------------------------------------------------------------------+
//| Gaussian blur                                                    |
//| https://www.mql5.com/en/articles/1612#chapter4                   |
//+------------------------------------------------------------------+
bool CGCnvElement::GaussianBlur(const uint radius)
  {
//---
   int n_nodes=(int)radius*2+1;
//--- Read graphical resource data. If failed, return false
   if(!CGCnvElement::ResourceStamp(DFUN))
      return false;
//--- Check the blur amount. If the blur radius exceeds half of the width or height, return 'false'
   if((int)radius>=this.Width()/2 || (int)radius>=this.Height()/2)
     {
      ::Print(DFUN,CMessage::Text(MSG_SHADOW_OBJ_IMG_SMALL_BLUR_LARGE));
      return false;
     }
     
//--- Decompose image data from the resource into a, r, g, b color components
   int  size=::ArraySize(this.m_duplicate_res);
//--- arrays for storing A, R, G and B color components
//--- for horizontal and vertical blur
   uchar a_h_data[],r_h_data[],g_h_data[],b_h_data[];
   uchar a_v_data[],r_v_data[],g_v_data[],b_v_data[];
   
//--- Change the size of component arrays according to the array size of the graphical resource data
   if(::ArrayResize(a_h_data,size)==-1)
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
      ::Print(DFUN_ERR_LINE,": \"a_h_data\"");
      return false;
     }
   if(::ArrayResize(r_h_data,size)==-1)
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
      ::Print(DFUN_ERR_LINE,": \"r_h_data\"");
      return false;
     }
   if(::ArrayResize(g_h_data,size)==-1)
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
      ::Print(DFUN_ERR_LINE,": \"g_h_data\"");
      return false;
     }
   if(ArrayResize(b_h_data,size)==-1)
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
      ::Print(DFUN_ERR_LINE,": \"b_h_data\"");
      return false;
     }
   if(::ArrayResize(a_v_data,size)==-1)
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
      ::Print(DFUN_ERR_LINE,": \"a_v_data\"");
      return false;
     }
   if(::ArrayResize(r_v_data,size)==-1)
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
      ::Print(DFUN_ERR_LINE,": \"r_v_data\"");
      return false;
     }
   if(::ArrayResize(g_v_data,size)==-1)
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
      ::Print(DFUN_ERR_LINE,": \"g_v_data\"");
      return false;
     }
   if(::ArrayResize(b_v_data,size)==-1)
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
      ::Print(DFUN_ERR_LINE,": \"b_v_data\"");
      return false;
     }
//--- Declare the array for storing blur weight ratios and,
//--- if failed to get the array of weight ratios, return 'false'
   double weights[];
   if(!this.GetQuadratureWeights(1,n_nodes,weights))
      return false;
      
//--- Set components of each image pixel to the color component arrays
   for(int i=0;i<size;i++)
     {
      a_h_data[i]=GETRGBA(this.m_duplicate_res[i]);
      r_h_data[i]=GETRGBR(this.m_duplicate_res[i]);
      g_h_data[i]=GETRGBG(this.m_duplicate_res[i]);
      b_h_data[i]=GETRGBB(this.m_duplicate_res[i]);
     }

//--- Blur the image horizontally (along the X axis)
   uint XY; // Pixel coordinate in the array
   double a_temp=0.0,r_temp=0.0,g_temp=0.0,b_temp=0.0;
   int coef=0;
   int j=(int)radius;
   //--- Loop by the image width
   for(int Y=0;Y<this.Height();Y++)
     {
      //--- Loop by the image height
      for(uint X=radius;X<this.Width()-radius;X++)
        {
         XY=Y*this.Width()+X;
         a_temp=0.0; r_temp=0.0; g_temp=0.0; b_temp=0.0;
         coef=0;
         //--- Multiply each color component by the weight ratio corresponding to the current image pixel
         for(int i=-1*j;i<j+1;i=i+1)
           {
            a_temp+=a_h_data[XY+i]*weights[coef];
            r_temp+=r_h_data[XY+i]*weights[coef];
            g_temp+=g_h_data[XY+i]*weights[coef];
            b_temp+=b_h_data[XY+i]*weights[coef];
            coef++;
           }
         //--- Save each rounded color component calculated according to the ratios to the component arrays
         a_h_data[XY]=(uchar)::round(a_temp);
         r_h_data[XY]=(uchar)::round(r_temp);
         g_h_data[XY]=(uchar)::round(g_temp);
         b_h_data[XY]=(uchar)::round(b_temp);
        }
      //--- Remove blur artifacts to the left by copying adjacent pixels
      for(uint x=0;x<radius;x++)
        {
         XY=Y*this.Width()+x;
         a_h_data[XY]=a_h_data[Y*this.Width()+radius];
         r_h_data[XY]=r_h_data[Y*this.Width()+radius];
         g_h_data[XY]=g_h_data[Y*this.Width()+radius];
         b_h_data[XY]=b_h_data[Y*this.Width()+radius];
        }
      //--- Remove blur artifacts to the right by copying adjacent pixels
      for(int x=int(this.Width()-radius);x<this.Width();x++)
        {
         XY=Y*this.Width()+x;
         a_h_data[XY]=a_h_data[(Y+1)*this.Width()-radius-1];
         r_h_data[XY]=r_h_data[(Y+1)*this.Width()-radius-1];
         g_h_data[XY]=g_h_data[(Y+1)*this.Width()-radius-1];
         b_h_data[XY]=b_h_data[(Y+1)*this.Width()-radius-1];
        }
     }

//--- Blur vertically (along the Y axis) the image already blurred horizontally
   int dxdy=0;
   //--- Loop by the image height
   for(int X=0;X<this.Width();X++)
     {
      //--- Loop by the image width
      for(uint Y=radius;Y<this.Height()-radius;Y++)
        {
         XY=Y*this.Width()+X;
         a_temp=0.0; r_temp=0.0; g_temp=0.0; b_temp=0.0;
         coef=0;
         //--- Multiply each color component by the weight ratio corresponding to the current image pixel
         for(int i=-1*j;i<j+1;i=i+1)
           {
            dxdy=i*(int)this.Width();
            a_temp+=a_h_data[XY+dxdy]*weights[coef];
            r_temp+=r_h_data[XY+dxdy]*weights[coef];
            g_temp+=g_h_data[XY+dxdy]*weights[coef];
            b_temp+=b_h_data[XY+dxdy]*weights[coef];
            coef++;
           }
         //--- Save each rounded color component calculated according to the ratios to the component arrays
         a_v_data[XY]=(uchar)::round(a_temp);
         r_v_data[XY]=(uchar)::round(r_temp);
         g_v_data[XY]=(uchar)::round(g_temp);
         b_v_data[XY]=(uchar)::round(b_temp);
        }
      //--- Remove blur artifacts at the top by copying adjacent pixels
      for(uint y=0;y<radius;y++)
        {
         XY=y*this.Width()+X;
         a_v_data[XY]=a_v_data[X+radius*this.Width()];
         r_v_data[XY]=r_v_data[X+radius*this.Width()];
         g_v_data[XY]=g_v_data[X+radius*this.Width()];
         b_v_data[XY]=b_v_data[X+radius*this.Width()];
        }
      //--- Remove blur artifacts at the bottom by copying adjacent pixels
      for(int y=int(this.Height()-radius);y<this.Height();y++)
        {
         XY=y*this.Width()+X;
         a_v_data[XY]=a_v_data[X+(this.Height()-1-radius)*this.Width()];
         r_v_data[XY]=r_v_data[X+(this.Height()-1-radius)*this.Width()];
         g_v_data[XY]=g_v_data[X+(this.Height()-1-radius)*this.Width()];
         b_v_data[XY]=b_v_data[X+(this.Height()-1-radius)*this.Width()];
        }
     }
     
//--- Set the twice blurred (horizontally and vertically) image pixels to the graphical resource data array
   for(int i=0;i<size;i++)
      this.m_duplicate_res[i]=ARGB(a_v_data[i],r_v_data[i],g_v_data[i],b_v_data[i]);
//--- Display the image pixels on the canvas in a loop by the image height and width from the graphical resource data array
   for(int X=0;X<this.Width();X++)
     {
      for(uint Y=radius;Y<this.Height()-radius;Y++)
        {
         XY=Y*this.Width()+X;
         this.m_canvas.PixelSet(X,Y,this.m_duplicate_res[XY]);
        }
     }
//--- Done
   return true;
  }
//+------------------------------------------------------------------+
//| Return the array of weight ratios                                |
//| https://www.mql5.com/en/articles/1612#chapter3_2                 |
//+------------------------------------------------------------------+
bool CGCnvElement::GetQuadratureWeights(const double mu0,const int n,double &weights[])
  {
   CAlglib alglib;
   double  alp[];
   double  bet[];
   ::ArrayResize(alp,n);
   ::ArrayResize(bet,n);
   ::ArrayInitialize(alp,1.0);
   ::ArrayInitialize(bet,1.0);
//---
   double out_x[];
   int    info=0;
   alglib.GQGenerateRec(alp,bet,mu0,n,info,out_x,weights);
   if(info!=1)
     {
      string txt=(info==-3 ? "internal eigenproblem solver hasn't converged" : info==-2 ? "Beta[i]<=0" : "incorrect N was passed");
      ::Print("Call error in CGaussQ::GQGenerateRec: ",txt);
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+

Os métodos foram escritos por nós há muito tempo, já foram discutidos em artigos anteriores e são simplesmente transferidos de uma classe para outra.


Foi mencionado anteriormente que seria necessário aprimorar os métodos de desenho de primitivas gráficas e imagens pré-definidas. De fato, os métodos de desenho de primitivas com suavização não são muito eficazes ao desenhar linhas translúcidas, sendo necessário substituir o desenho de primitivas suavizadas por outras não suavizadas quando a transparência da linha ultrapassar um limite definido, que corresponde a metade do valor total da opacidade.

Vamos dar uma olhada nos métodos atualizados. Se o valor de opacidade da primitiva gráfica desenhada exceder o valor 127, então a primitiva é desenhada pelo método de suavização Caso contrário, desenhamos usando o método sem suavização

//+------------------------------------------------------------------+
//| Draw the Info icon                                               |
//+------------------------------------------------------------------+
void CGCnvElement::DrawIconInfo(const int coord_x,const int coord_y,const uchar opacity)
  {
   int x=coord_x+8;
   int y=coord_y+8;
   this.DrawCircleFill(x,y,7,C'0x00,0x77,0xD7',opacity);
   if(opacity>127)
      this.DrawCircleWu(x,y,7.5,C'0x00,0x3D,0x8C',opacity);
   else
      this.DrawCircle(x,y,8,C'0x00,0x3D,0x8C',opacity);
   this.DrawRectangle(x,y-5,x+1,y-4, C'0xFF,0xFF,0xFF',opacity);
   this.DrawRectangle(x,y-2,x+1,y+4,C'0xFF,0xFF,0xFF',opacity);
  }
//+------------------------------------------------------------------+
//| Draw the Warning icon                                            |
//+------------------------------------------------------------------+
void CGCnvElement::DrawIconWarning(const int coord_x,const int coord_y,const uchar opacity)
  {
   int x=coord_x+8;
   int y=coord_y+1;
   this.DrawTriangleFill(x,y,x+8,y+14,x-8,y+14,C'0xFC,0xE1,0x00',opacity);
   if(opacity>127)
      this.DrawTriangleWu(x,y,x+8,y+14,x-7,y+14,C'0xFF,0xB9,0x00',opacity);
   else
      this.DrawTriangle(x,y,x+8,y+14,x-7,y+14,C'0xFF,0xB9,0x00',opacity);
   this.DrawRectangle(x,y+5,x+1,y+9,  C'0x00,0x00,0x00',opacity);
   this.DrawRectangle(x,y+11,x+1,y+12,C'0x00,0x00,0x00',opacity);
  }
//+------------------------------------------------------------------+
//| Draw the Error icon                                              |
//+------------------------------------------------------------------+
void CGCnvElement::DrawIconError(const int coord_x,const int coord_y,const uchar opacity)
  {
   int x=coord_x+8;
   int y=coord_y+8;
   this.DrawCircleFill(x,y,7,C'0xF0,0x39,0x16',opacity);
   if(opacity>127)
     {
      this.DrawCircleWu(x,y,7.5,C'0xA5,0x25,0x12',opacity);
      this.DrawLineWu(x-3,y-3,x+3,y+3,C'0xFF,0xFF,0xFF',opacity);
      this.DrawLineWu(x+3,y-3,x-3,y+3,C'0xFF,0xFF,0xFF',opacity);
     }
   else
     {
      this.DrawCircle(x,y,8,C'0xA5,0x25,0x12',opacity);
      this.DrawLine(x-3,y-3,x+3,y+3,C'0xFF,0xFF,0xFF',opacity);
      this.DrawLine(x+3,y-3,x-3,y+3,C'0xFF,0xFF,0xFF',opacity);
     }
  }
//+------------------------------------------------------------------+

É importante ressaltar que essa abordagem apenas mascara o problema. No entanto, há uma justificativa para essa solução: ao diminuir a opacidade da linha de 255 para 128, a linha suavizada parece relativamente normal, mas começa a quebrar e se desintegrar em pixels em vez de se tornar mais transparente. Nesse contexto, substituir o desenho de uma linha suavizada por um desenho sem serrilhado é completamente imperceptível - uma vez que a linha já é semitransparente, sua graduação não é perceptível, o que não seria o caso se estivéssemos desenhando uma linha completamente opaca e não suavizada.

É importante ressaltar que o restante dos métodos de desenho de formas predefinidos nesta classe também utilizam métodos de desenho com suavização. No entanto, ainda não testamos como esses métodos se comportarão ao alterarmos a transparência. Caso também ocorram artefatos visuais nesses métodos, os refinaremos da mesma forma que os outros.


Devido ao fato de que o objeto brilho não é mais um tipo separado e isolado (como um objeto de sombra), mas agora faz parte da lista de objetos WinForms auxiliares, a inclusão do arquivo de classe deste objeto agora precisa ser feita no arquivo de classe do objeto painel. Esse objeto painel é um objeto contêiner, ao qual estão anexados os arquivos de todos os objetos WinForms da biblioteca.
No arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqh, excluímos a string de inclusão da classe do objeto brilho:

//+------------------------------------------------------------------+
//|                                                  WinFormBase.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "GlareObj.mqh"
#include "..\Form.mqh"
#include "..\..\..\Services\Select.mqh"
//+------------------------------------------------------------------+

e escrevemos a inclusão do arquivo da classe do objeto brilho no arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqh da classe do objeto painel:

//+------------------------------------------------------------------+
//|                                                        Panel.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Container.mqh"
#include "..\Helpers\TabField.mqh"
#include "..\Helpers\ArrowUpButton.mqh"
#include "..\Helpers\ArrowDownButton.mqh"
#include "..\Helpers\ArrowLeftButton.mqh"
#include "..\Helpers\ArrowRightButton.mqh"
#include "..\Helpers\ArrowUpDownBox.mqh"
#include "..\Helpers\ArrowLeftRightBox.mqh"
#include "..\Helpers\HintMoveLeft.mqh"
#include "..\Helpers\HintMoveRight.mqh"
#include "..\Helpers\HintMoveUp.mqh"
#include "..\Helpers\HintMoveDown.mqh"
#include "GroupBox.mqh"
#include "TabControl.mqh"
#include "SplitContainer.mqh"
#include "..\..\WForms\Common Controls\ListBox.mqh"
#include "..\..\WForms\Common Controls\CheckedListBox.mqh"
#include "..\..\WForms\Common Controls\ButtonListBox.mqh"
#include "..\..\WForms\Common Controls\ToolTip.mqh"
#include "..\..\WForms\Common Controls\ProgressBar.mqh"
#include "..\..\WForms\GlareObj.mqh"
//+------------------------------------------------------------------+
//| Panel object class of WForms controls                            |
//+------------------------------------------------------------------+
class CPanel : public CContainer

A partir de agora, o objeto brilho estará disponível em todos os objetos contêiner, permitindo que ele seja criado e anexado às listas de objetos anexados em qualquer outro elemento gráfico da biblioteca. Além disso, como qualquer janela GUI independente é considerada um contêiner, podemos adicionar um objeto brilho (e subsequentemente um objeto de efeito visual) a qualquer controle vinculado a essa janela.


No arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqh da classe do objeto contêiner base, em seu método que define os parâmetros do objeto anexado, adicionamos um bloco de código ao final para definir os parâmetros do objeto de brilho recém-criado:

//+------------------------------------------------------------------+
//| Set parameters for the attached object                           |
//+------------------------------------------------------------------+
void CContainer::SetObjParams(CWinFormBase *obj,const color colour)
  {
//--- Set the text color of the object to be the same as that of the base container
   obj.SetForeColor(this.ForeColor(),true);
//--- If the created object is not a container, set the same group for it as the one for its base object
   if(obj.TypeGraphElement()<GRAPH_ELEMENT_TYPE_WF_CONTAINER || obj.TypeGraphElement()>GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER)
      obj.SetGroup(this.Group());
//--- Depending on the object type
   switch(obj.TypeGraphElement())
     {
      //--- For the Container, Panel and GroupBox WinForms objects
      case GRAPH_ELEMENT_TYPE_WF_CONTAINER            :
      case GRAPH_ELEMENT_TYPE_WF_PANEL                :
      case GRAPH_ELEMENT_TYPE_WF_GROUPBOX             :
        obj.SetBorderColor(obj.BackgroundColor(),true);
        break;
      //--- For "Label", "CheckBox" and "RadioButton" WinForms objects

      //---...
      //---...

      //--- For ProgressBar WinForms object
      case GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR         :
        obj.SetBackgroundColor(CLR_DEF_CONTROL_PROGRESS_BAR_BACK_COLOR,true);
        obj.SetBorderColor(CLR_DEF_CONTROL_PROGRESS_BAR_BORDER_COLOR,true);
        obj.SetForeColor(CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR,true);
        obj.SetBorderStyle(FRAME_STYLE_SIMPLE);
        break;
      //--- For GlareObj WinForms object
      case GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ            :
        obj.SetBackgroundColor(CLR_CANV_NULL,true);
        obj.SetBorderColor(CLR_CANV_NULL,true);
        obj.SetForeColor(CLR_CANV_NULL,true);
        obj.SetBorderStyle(FRAME_STYLE_NONE);
        break;
      default:
        break;
     }
   obj.Crop();
  }
//+------------------------------------------------------------------+

Após a criação de qualquer um dos controles gráficos anexados ao contêiner, é chamado este método, que define as propriedades mínimas do objeto para que ele funcione corretamente.


Vamos refinar a classe de objeto brilho no arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\GlareObj.mqh.

A classe agora herdará da classe do objeto WinForms base. Incluímos seu arquivo ao arquivo da classe do objeto brilho e alteramos a classe pai:

//+------------------------------------------------------------------+
//|                                                     GlareObj.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "WinFormBase.mqh"
//+------------------------------------------------------------------+
//| Glare object class                                               |
//+------------------------------------------------------------------+
class CGlareObj : public CWinFormBase
  {
   //---...


Na seção privada, declararemos uma variável para armazenar o estilo de brilho e alteraremos os métodos de desenho de brilho declarando outra:

class CGlareObj : public CWinFormBase
  {
private:
   ENUM_CANV_ELEMENT_VISUAL_EFF_STYLE m_style;        // Glare style
   color             m_color;                         // Glare color
   uchar             m_opacity;                       // Glare opacity
   uchar             m_blur;                          // Blur
//--- Draw the object glare form
   void              DrawGlareFigure(void);
   void              DrawFigureRectangle(void);
   void              DrawFigureParallelogram(void);
protected:

O estilo de brilho determinará qual método será chamado para gerá-lo. Nesse caso, o método DrawGlareFigure() será responsável por chamar os métodos de desenho correspondentes ao estilo definido. Não haverá parâmetros formais nos métodos de desenho de brilho, já que ainda não há nada a ser especificado para garantir a renderização correta de uma imagem em um elemento gráfico com tamanho predefinido. A cor e a opacidade do destaque que está sendo desenhado serão definidas previamente pelos métodos apropriados, e o desfoque ainda não será utilizado.

Na seção pública da classe, serão declarados os métodos para desenhar um destaque, redesenhá-lo, definir a opacidade, inicializar propriedades, e escrever métodos para definir e obter algumas propriedades do objeto.

public:
//--- Constructor indicating the main and base objects, chart ID and subwindow
                     CGlareObj(CGCnvElement *main_obj,CGCnvElement *base_obj,
                               const long chart_id,
                               const int subwindow,
                               const string descript,
                               const int x,
                               const int y,
                               const int w,
                               const int h);

//--- Supported object properties (1) integer, (2) real and (3) string ones
   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; }
   
//--- Draw the object glare
   void              Draw(void);
//--- Redraw the object
   virtual void      Redraw(bool redraw);
//--- Set the element opacity
   virtual void      SetOpacity(const uchar value,const bool redraw=false);
//--- Initialize the properties
   void              Initialize(void);

//+------------------------------------------------------------------+
//| Methods of simplified access to object properties                |
//+------------------------------------------------------------------+
//--- (1) Set and (2) return the glare color
   void              SetColor(const color colour)                             { this.m_color=colour;     }
   color             Color(void)                                        const { return this.m_color;     }
//--- (1) Set and (2) return the opacity of the drawn glare
   void              SetOpacityDraw(const uchar opacity)                      { this.m_opacity=opacity;  }
   uchar             OpacityDraw(void)                                  const { return this.m_opacity;   }
//--- (1) Set and (2) return the glare blur
   void              SetBlur(const uchar blur)                                { this.m_blur=blur;        }
   uchar             Blur(void)                                         const { return this.m_blur;      }
//--- (1) Set and (2) return the glare visual effect
   void              SetVisualEffectStyle(const ENUM_CANV_ELEMENT_VISUAL_EFF_STYLE style)
                       { this.m_style=style;                                                             }
   ENUM_CANV_ELEMENT_VISUAL_EFF_STYLE VisualEffectStyle(void)           const { return this.m_style;     }
//--- Set the glare visual effect style as (1) rectangle and (2) parallelogram
   void              SetStyleRectangle(void)
                       { this.SetVisualEffectStyle(CANV_ELEMENT_VISUAL_EFF_STYLE_RECTANGLE);             }
   void              SetStyleParallelogramm(void)
                       { this.SetVisualEffectStyle(CANV_ELEMENT_VISUAL_EFF_STYLE_PARALLELOGRAMM);        }
  };
//+------------------------------------------------------------------+

Na seção de métodos para acesso simplificado às propriedades do objeto, são escritos métodos nos quais os valores passados ​​são simplesmente definidos para variáveis privadas e os valores registrados são retornados a partir dessas variáveis. Esses métodos ajudam a manter a encapsulação da classe, permitindo que o acesso aos valores das propriedades seja controlado de forma mais efetiva e garantindo que as mudanças em uma propriedade não afetem outras partes do código.

Nos construtores de classe, substituiremos a inicialização da classe pai CShadowObj por CWinFormBase e adicionaremos uma chamada ao método de inicialização da variável:

//+------------------------------------------------------------------+
//| Protected constructor with an object type,                       |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CGlareObj::CGlareObj(const ENUM_GRAPH_ELEMENT_TYPE type,
                     CGCnvElement *main_obj,CGCnvElement *base_obj,
                     const long chart_id,
                     const int subwindow,
                     const string descript,
                     const int x,
                     const int y,
                     const int w,
                     const int h) : CWinFormBase(type,main_obj,base_obj,chart_id,subwindow,descript,x-OUTER_AREA_SIZE,y-OUTER_AREA_SIZE,w+OUTER_AREA_SIZE*2,h+OUTER_AREA_SIZE*2)
  {
//--- Set the specified graphical element type for the object and assign the library object type to the current object
   this.SetTypeElement(type);
   this.m_type=OBJECT_DE_TYPE_GGLARE;
   this.Initialize();
  }
//+------------------------------------------------------------------+
//| Constructor indicating the main and base objects,                |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CGlareObj::CGlareObj(CGCnvElement *main_obj,CGCnvElement *base_obj,
                     const long chart_id,
                     const int subwindow,
                     const string descript,
                     const int x,
                     const int y,
                     const int w,
                     const int h) : CWinFormBase(GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
   this.m_type=OBJECT_DE_TYPE_GGLARE; 
   this.Initialize();
  }
//+------------------------------------------------------------------+

Agora, em vez de escrever em cada construtor as strings de inicialização para cada variável (o mesmo para ambos os construtores), simplesmente chamamos o método de inicialização da propriedade do objeto:

//+------------------------------------------------------------------+
//| Initialize the properties                                        |
//+------------------------------------------------------------------+
void CGlareObj::Initialize(void)
  {
   CGCnvElement::SetBackgroundColor(CLR_CANV_NULL,true);
   CGCnvElement::SetActive(false);
   this.SetBlur(4);
   this.SetColor(clrWhite);
   this.SetOpacity(127);
   this.m_shadow=false;
   this.SetVisibleFlag(false,false);
   this.SetVisualEffectStyle(CANV_ELEMENT_VISUAL_EFF_STYLE_RECTANGLE);
   this.SetDisplayState(CANV_ELEMENT_DISPLAY_STATE_NORMAL);
   this.SetDisplayed(false);
  }
//+------------------------------------------------------------------+

Este método define os valores padrão para as propriedades do objeto. O desfoque é definido como 4 e a cor do brilho é branco. A opacidade é definida como 127, o que significa que o objeto é translúcido e não possui sombra. O tipo de efeito visual é um brilho retangular e o estado de exibição é normal (ou seja, o objeto está oculto). O sinalizador de invisibilidade é definido e o sinalizador de exibição é limpo, o que significa que o objeto não deve ser mostrado no objeto pai exibido. Essas configurações podem ser modificadas pelos métodos públicos fornecidos, permitindo que o usuário personalize o objeto de destaque de acordo com suas necessidades específicas.


Método que define a opacidade do elemento:

//+------------------------------------------------------------------+
//| Set the element opacity                                          |
//+------------------------------------------------------------------+
void CGlareObj::SetOpacity(const uchar value,const bool redraw=false)
  {
   CGCnvElement::SetOpacity(0,false);
   this.SetOpacityDraw(value>(uchar)CLR_DEF_SHADOW_OPACITY ? (uchar)CLR_DEF_SHADOW_OPACITY : value);
   this.m_canvas.Update(redraw);
  }
//+------------------------------------------------------------------+

O método utilizado é exatamente igual ao método do objeto sombra com o mesmo nome. O plano de fundo no qual o objeto brilho é colocado é configurado com transparência total e a cor utilizada para desenhar a imagem destacada é definida com base no valor passado para o método. No entanto, se o valor de opacidade passado para o método for maior que o valor de opacidade padrão para sombra, o valor padrão será usado para evitar que o objeto fique completamente opaco. A escolha do valor padrão para a sombra se dá pela sua adequação para esse propósito específico.

Método que desenha o brilho:

//+------------------------------------------------------------------+
//| Draw the object glare                                            |
//+------------------------------------------------------------------+
void CGlareObj::Draw(void)
  {
   if(!this.IsVisible())
      return;
//--- Draw the glare
   this.DrawGlareFigure();
  }
//+------------------------------------------------------------------+

Se o objeto estiver oculto, não é necessário desenhar nada, e saímos do método.
Chamamos o método de seleção de tipo de desenho:

//+------------------------------------------------------------------+
//| Draw the object glare form                                       |
//+------------------------------------------------------------------+
void CGlareObj::DrawGlareFigure()
  {
   switch(this.VisualEffectStyle())
     {
      case CANV_ELEMENT_VISUAL_EFF_STYLE_RECTANGLE       : this.DrawFigureRectangle();       break;
      case CANV_ELEMENT_VISUAL_EFF_STYLE_PARALLELOGRAMM  : this.DrawFigureParallelogram();   break;
      default:
        break;
     }
  }
//+------------------------------------------------------------------+

Dependendo do tipo de brilho que está sendo colocado, chamamos o método apropriado, que realmente desenha a forma do brilho.


Método que desenha uma forma retangular para o destaque do objeto:

//+------------------------------------------------------------------+
//| Draw the rectangle object glare form                             |
//+------------------------------------------------------------------+
void CGlareObj::DrawFigureRectangle(void)
  {
   CGCnvElement::DrawRectangleFill(0,0,this.Width()-1,this.Height()-1,this.m_color,this.OpacityDraw());
   CGCnvElement::Update();
  }
//+------------------------------------------------------------------+

O comportamento do código em questão é o de desenhar um retângulo preenchido que cobre completamente o objeto em questão, utilizando a cor do objeto e a opacidade previamente definidas para o desenho.


Método que desenha a forma do brilho do objeto como se fosse um paralelogramo:

//+------------------------------------------------------------------+
//| Draw the shape of the object glare as a parallelogram            |
//+------------------------------------------------------------------+
void CGlareObj::DrawFigureParallelogram(void)
  {
   int array_x[]={6,this.Width()-1,this.Width()-1-6,0};
   int array_y[]={0,0,this.Height()-1,this.Height()-1};
   CGCnvElement::DrawPolygonFill(array_x,array_y,this.m_color,this.OpacityDraw());
   CGCnvElement::Update();
  }
//+------------------------------------------------------------------+

Neste código, é desenhado um retângulo com bordas chanfradas. Para fazê-lo, inicialmente declaramos matrizes de coordenadas X e Y dos vértices do polígono. Em seguida, usando esses vértices, desenhamos um polígono preenchido na forma de um retângulo com bordas chanfradas, ou seja, um paralelogramo. A cor e a opacidade do desenho são definidas para corresponderem ao objeto em questão.


Se você desenhar um objeto e movê-lo, pode notar que o efeito de brilho permanecerá visível quando o objeto ultrapassar os limites da barra de progresso (isto é, quando ele se mover ao longo da barra). Isso pode dar a impressão visual de que o brilho "salta" para fora da barra de progresso e é desenhado no painel ao qual o controle ProgressBar está anexado. Para evitar esse efeito, precisamos redesenhar o objeto, recortando as partes que ultrapassam o seu contêiner. Para isso, é necessário um método de redesenho do objeto.

Método que redesenha o objeto:

//+------------------------------------------------------------------+
//| Redraw the object                                                |
//+------------------------------------------------------------------+
void CGlareObj::Redraw(bool redraw)
  {
   CGCnvElement::Erase(false);
   this.Draw();
   this.Crop();
   this.Update(redraw);
  }
//+------------------------------------------------------------------+

Aqui: primeiro limpamos completamente o objeto com o preenchimento de cor e opacidade respectivo. Como definimos a cor de fundo do objeto como transparente e o valor de opacidade é zero, o objeto se torna completamente transparente. Em seguida, chamamos o método previamente discutido para desenhar a forma do efeito de brilho, aparamos as áreas salientes do objeto que estão fora do contêiner, e atualizamos a tela.

Para evitar que o efeito de brilho ultrapasse o contêiner, é necessário chamar esse método durante o movimento do objeto pela barra de progresso.


Vamos dar ao objeto ProgressBar a funcionalidade para controlar sua barra de progresso.

Para exibir o progresso de um processo, é necessário aumentar a largura do objeto BarProgressBar consoante a etapa do processo. Em geral, todos os valores da largura da barra de progresso serão relativos, ou seja, uma porcentagem da largura do objeto ProgressBar e uma porcentagem do valor máximo do parâmetro Value da barra de progresso. A barra de progresso pode ter seus valores alterados dentro dos limites definidos pelos parâmetros Minimum e Maximum, porém, esses valores não devem ser definidos em pixels, mas sim como uma porcentagem da largura do objeto. Por exemplo, um valor de Value de 100 teria 100 pixels de largura se a ProgressBar tivesse 100 pixels de largura, mas seria 200 pixels se a ProgressBar tivesse 200 pixels de largura. Nesse caso, se o valor de Value for 50, isso corresponderá a 50% da largura do objeto, ou seja, 50 pixels para uma largura de objeto de 100 pixels e 100 pixels para uma largura de objeto de 200 pixels.

Para controlar os valores da barra de progresso, é necessário definir os valores mínimo e máximo dos limites, bem como o passo com que o valor da largura da barra de progresso deve ser aumentado antes de usar o objeto, isto é, antes do início do ciclo cujo processo deve ser exibido. Após a conclusão da próxima ação no laço rastreado, basta chamar o método PerformStep() do objeto ProgressBar, que aumentará a largura da barra de progresso de acordo com o incremento especificado. É importante agir sempre dessa forma: sabendo o número de iterações do loop rastreado, definimos os valores necessários para o objeto ProgressBar. Dentro do loop, ao concluir cada iteração, chamamos o método PerformStep(), que fará a barra de progresso avançar um passo.

No arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\ProgressBar.mqh, em sua seção privada, declaramos variáveis para armazenar valores da barra de progresso e um método para calcular a largura da mesma:

//+------------------------------------------------------------------+
//| ArrowLeftRightBox object class of WForms controls                |
//+------------------------------------------------------------------+
class CProgressBar : public CContainer
  {
private:
   int               m_progress_bar_max;  // Maximum progress bar width
   int               m_value_by_max;      // Value relative to Maximum
   int               m_steps_skipped;     // Number of skipped steps of increasing the width of the progress bar
//--- Create a new graphical object
   virtual CGCnvElement *CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                          const int element_num,
                                          const string descript,
                                          const int x,
                                          const int y,
                                          const int w,
                                          const int h,
                                          const color colour,
                                          const uchar opacity,
                                          const bool movable,
                                          const bool activity);
//--- Create the progress bar object
   void              CreateProgressBar(void);
//--- Calculate the progress bar width
   int               CalculateProgressBarWidth(void);
//--- Initialize the element properties
   void              Initialize(void);

protected:


Retiramos a implementação do método público SetValue() para fora do corpo da classe, deixando apenas sua declaração no corpo:

//--- (1) Set and (2) return the progress bar increment to redraw it
   void              SetStep(const int value)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STEP,value);
                        CBarProgressBar *bar=this.GetProgressBar();
                        if(bar!=NULL)
                           bar.SetStep(value);
                       }
   int               Step(void)                          const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STEP);    }
   
//--- (1) Set and (2) return the current value of the progress bar in the range from Min to Max
   void              SetValue(const int value);
   int               Value(void)                         const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE);   }
   
//--- (1) Set and (2) return the upper bound of the ProgressBar operating range
   void              SetMaximum(const int value)               { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MAXIMUM,value);       }
   int               Maximum(void)                       const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MAXIMUM); }


E declaramos mais alguns métodos:

//--- Return the pointer to the progress bar object
   CBarProgressBar  *GetProgressBar(void)                { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR,0);     }

//--- Resets the progress bar values to the set minimum
   void              ResetProgressBar(void)              { this.SetValue(this.Minimum());    }
//--- Set the element width
   virtual bool      SetWidth(const int width);
//--- Initialize values for handling in PerformStep
   void              SetValuesForProcessing(const int minimum,const int maximum,const int step,const int steps_skipped);
//--- Increase the current position of the progress bar by the step value
   void              PerformStep();

//--- Supported object properties (1) integer, (2) real and (3) string ones
   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; }

Implementamos o método ResetProgressBar() diretamente no corpo da classe para definir o Value da barra de progresso dentro da propriedade Minimum, retornando a barra de progresso ao seu estado original.

No método de inicialização de valor padrão, definimos o valor da barra de progresso para 50% da largura do objeto, calculamos a largura máxima em pixels para o objeto BarProgressBar, que é a barra de progresso, e calculamos Value como uma porcentagem da largura do objeto:

//+------------------------------------------------------------------+
//| Initialize the element properties                                |
//+------------------------------------------------------------------+
void CProgressBar::Initialize(void)
  {
   this.SetBorderSizeAll(1);
   this.SetBorderStyle(FRAME_STYLE_SIMPLE);
   this.SetBackgroundColor(CLR_DEF_CONTROL_PROGRESS_BAR_BACK_COLOR,true);
   this.SetBorderColor(CLR_DEF_CONTROL_PROGRESS_BAR_BORDER_COLOR,true);
   this.SetForeColor(CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR,true);
   this.SetMarqueeAnimationSpeed(10);
   this.SetMaximum(100);
   this.SetMinimum(0);
   this.SetValue(50);
   this.SetStep(10);
   this.SetStyle(CANV_ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS);
   this.m_progress_bar_max=this.Width()-this.BorderSizeLeft()-this.BorderSizeRight();
   this.m_value_by_max=this.Value()*100/this.Maximum();
   this.m_steps_skipped=0;
  }
//+------------------------------------------------------------------+

Para garantir que a barra de progresso caiba dentro da moldura desenhada no suporte e não ultrapasse seus limites, é importante que sua largura máxima seja igual à largura do suporte, descontando o tamanho da moldura à esquerda e à direita.


No método que cria o objeto barra de progresso, definimos a largura do objeto criado a partir do valor calculado pelo método CalculateProgressBarWidth() e adicionamos a criação de um objeto brilho:

//+------------------------------------------------------------------+
//| Create the progress bar object                                   |
//+------------------------------------------------------------------+
void CProgressBar::CreateProgressBar(void)
  {
//--- Set the length of the progress bar equal to the object Value()
//--- The height of the progress bar is equal to the height of the object minus the top and bottom frame sizes
   int w=this.CalculateProgressBarWidth();
   int h=this.Height()-this.BorderSizeTop()-this.BorderSizeBottom();
//--- Create the progress bar object
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR,0,0,w,h,clrNONE,255,false,false);
//--- Create the glare object
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ,0,0,w,h,CLR_CANV_NULL,0,true,false);
//--- Add the current CProgressBar object to the list of active elements of the collection
   this.AddObjToListActiveElements();
  }
//+------------------------------------------------------------------+

A lógica do método é descrita nos comentários do código. Tudo é simples aqui: calculamos a largura da barra de progresso e criamos um objeto com a largura calculada. O padrão é 50% da largura do objeto ProgressBar. Em seguida, criamos um objeto brilho e adicionamos o objeto atual à lista de objetos ativos. Todos os objetos que estão na lista de elementos ativos são processados no temporizador da biblioteca e podem executar algumas ações independentes que são implementadas no manipulador do temporizador desses objetos. Para este controle, o temporizador controlará a aparência do efeito de brilho na barra de progresso.


Método que inicializa valores para processamento no PerformStep:

//+------------------------------------------------------------------+
//| Initialize values for handling in PerformStep                    |
//+------------------------------------------------------------------+
void CProgressBar::SetValuesForProcessing(const int minimum,const int maximum,const int step,const int steps_skipped)
  {
   this.SetMinimum(minimum<0 ? 0 : minimum);
   this.SetMaximum(maximum<this.Minimum() ? this.Minimum() : maximum);
   this.SetStep(step<0 ? 0 : step);
   this.m_steps_skipped=steps_skipped;
  }
//+------------------------------------------------------------------+

Nesta seção, o método recebe como parâmetros os valores mínimo e máximo, dos quais depende o valor da barra de progresso. Dentro do método, a cada mudança de passo, o valor atual é atualizado e o número de passos ignorados é armazenado em uma variável. É importante ressaltar que, embora o valor das etapas ignoradas seja registrado, ele não é utilizado em nenhuma parte do código, uma vez que o valor da barra de progresso é calculado como uma porcentagem da largura do objeto ProgressBar. Serão realizados testes para verificar se o valor dos passos ignorados é relevante em algum cenário específico.

Método que define o valor atual da barra de progresso:

//+------------------------------------------------------------------+
//| Set the current value of the progress bar                        |
//+------------------------------------------------------------------+
void CProgressBar::SetValue(const int value)
  {
//--- Correct the value passed to the method and set it to the object property
   int v=(value<this.Minimum() ? this.Minimum() : value>this.Maximum() ? this.Maximum() : value);
   this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE,v);
//--- Get the progress bar object
   CBarProgressBar *bar=this.GetProgressBar();
   if(bar!=NULL)
     {
      //--- Set 'value' for the progress bar
      bar.SetValue(v);
      //--- Calculate the width of the progress bar object
      int w=this.CalculateProgressBarWidth();
      //--- If the calculated width is greater than the maximum possible value, set the maximum width
      if(w>this.m_progress_bar_max)
         w=this.m_progress_bar_max;
      //--- If the width is less than 1, then
      if(w<1)
        {
         //--- hide the progress bar and redraw the chart to display changes immediately
         bar.Hide();
         ::ChartRedraw(bar.ChartID());
        }
      //--- If the width value is not less than 1
      else
        {
         //--- If the progress bar is hidden, display it and
         if(!bar.IsVisible())
            bar.Show();
         //--- change its size according to the received width
         bar.Resize(w,bar.Height(),true);
        }
     }
  }
//+------------------------------------------------------------------+

A lógica do método é descrita nos comentários ao código. Caso a largura do objeto seja menor que um pixel, ele não poderá ser definido, portanto é atribuída uma largura de um pixel e o objeto é ocultado, simulando assim uma barra de progresso de largura zero. Essa solução é importante para garantir a usabilidade do componente, uma vez que uma barra de progresso que não seja visível pode confundir o usuário.


Método que aumenta a posição atual da barra de progresso com base no valor do passo:

//+----------------------------------------------------------------------+
//| Increase the current position of the progress bar by the step value  |
//+----------------------------------------------------------------------+
void CProgressBar::PerformStep(void)
  {
   this.SetValue(this.Value()+this.Step());
  }
//+------------------------------------------------------------------+

Aqui nós simplesmente definimos a propriedade de Valor do objeto com seu valor anterior mais o valor incremental previamente definido desse valor. Assim, cada chamada posterior a este método aumentará o valor do Valor pelo passo incremental escrito em Step.


Método que calcula a largura da barra de progresso:

//+------------------------------------------------------------------+
//| Calculate the width of the progress bar                          |
//+------------------------------------------------------------------+
int CProgressBar::CalculateProgressBarWidth(void)   

  {
   this.m_value_by_max=this.Value()*100/this.Maximum();
   return this.m_progress_bar_max*this.m_value_by_max/100;
  }
//+------------------------------------------------------------------+

Aqui: primeiro calculamos Value como uma porcentagem do máximo possível e, em seguida, retornamos a largura relativa em relação à largura máxima da barra de progresso como uma porcentagem do valor calculado acima.


Método que define a nova largura do objeto:

//+------------------------------------------------------------------+
//| Set a new width                                                  |
//+------------------------------------------------------------------+
bool CProgressBar::SetWidth(const int width)
  {
   if(!CGCnvElement::SetWidth(width))
      return false;
   this.m_progress_bar_max=this.Width()-this.BorderSizeLeft()-this.BorderSizeRight();
   CBarProgressBar *bar=this.GetProgressBar();
   if(bar==NULL)
      return false;
   int w=this.CalculateProgressBarWidth();
   bar.SetWidth(w);
   return true;
  }
//+------------------------------------------------------------------+

Aqui: primeiro definimos a nova largura de preenchimento e calculamos a largura máxima da barra de progresso em pixels.
Em seguida, obtemos a largura relativa em porcentagem da barra de progresso e a definimos no objeto CBarProgressBar, que é a barra de progresso.


Para que os efeitos visuais da barra de progresso sejam apresentados de forma adequada, é necessário processá-los no temporizador do objeto BarProgressBar. O efeito de brilho deve ser exibido em toda a extensão da barra de progresso, com uma pausa entre cada ciclo de exibição. Dessa forma, é possível criar uma sequência de pausa-brilho-pausa-brilho, e assim por diante. Para implementar esse comportamento, é preciso definir algumas propriedades que os objetos já possuem. Essas propriedades foram definidas anteriormente para criar o efeito de aparecimento/desaparecimento suave das dicas de ferramentas. No caso do objeto de brilho, as mesmas propriedades podem ser utilizadas com sucesso.

Incluímos o arquivo do objeto brilho no arquivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\BarProgressBar.mqh da classe de objetos da barra de progresso, e em sua seção privada escrevemos métodos para definir/retornar o atraso e declaramos um método para inicializar as propriedades do objeto:

//+------------------------------------------------------------------+
//|                                               BarProgressBar.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\WinFormBase.mqh"
#include "..\GlareObj.mqh"
//+------------------------------------------------------------------+
//| BarProgressBar object class of the ProgressBar control           |
//+------------------------------------------------------------------+
class CBarProgressBar : public CWinFormBase
  {
private:
//--- (1) Set and (2) return a pause before displaying the effect
   void              SetShowDelay(const long delay)               { this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY,delay);             }
   ulong             ShowDelay(void)                              { return this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY);            }
//--- Initialize the properties
   void              Initialize(void);
protected:


Em ambos os construtores da classe, em vez das mesmas linhas para definir as propriedades, escrevemos uma chamada para o método para defini-las:

//+------------------------------------------------------------------+
//| Protected constructor with an object type,                       |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CBarProgressBar::CBarProgressBar(const ENUM_GRAPH_ELEMENT_TYPE type,
                                 CGCnvElement *main_obj,CGCnvElement *base_obj,
                                 const long chart_id,
                                 const int subwindow,
                                 const string descript,
                                 const int x,
                                 const int y,
                                 const int w,
                                 const int h) : CWinFormBase(type,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
//--- Set the specified graphical element type for the object and assign the library object type to the current object
   this.SetTypeElement(type);
   this.m_type=OBJECT_DE_TYPE_GWF_HELPER;
   this.Initialize();
  }
//+------------------------------------------------------------------+
//| Constructor indicating the main and base objects,                |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CBarProgressBar::CBarProgressBar(CGCnvElement *main_obj,CGCnvElement *base_obj,
                                 const long chart_id,
                                 const int subwindow,
                                 const string descript,
                                 const int x,
                                 const int y,
                                 const int w,
                                 const int h) : CWinFormBase(GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
   this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR);
   this.m_type=OBJECT_DE_TYPE_GWF_HELPER;
   this.Initialize();
  }
//+------------------------------------------------------------------+


Movemos as strings de inicialização de propriedades, removidas dos construtores, para o método de inicialização:

//+------------------------------------------------------------------+
//| Initialize the properties                                        |
//+------------------------------------------------------------------+
void CBarProgressBar::Initialize(void)
  {
   this.SetPaddingAll(0);
   this.SetMarginAll(0);
   this.SetBorderSizeAll(0);
   this.SetBackgroundColor(CLR_DEF_CONTROL_PROGRESS_BAR_BAR_COLOR,true);
   this.SetBorderColor(CLR_DEF_CONTROL_PROGRESS_BAR_BAR_COLOR,true);
   this.SetForeColor(CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR,true);
   this.SetShowDelay(2000);
  }
//+------------------------------------------------------------------+

Além das linhas movidas, adicionamos aqui uma configuração de atraso de dois segundos entre o aparecimento de efeitos de brilho na barra de progresso.

Antes usávamos um comentário para enviar o valor ao gráfico desde a função GetTickCount(), tudo isso no temporizador do objeto.
Agora vamos escrever um manipulador completo.

Manipulador de eventos do temporizador:

//+------------------------------------------------------------------+
//| Timer                                                            |
//+------------------------------------------------------------------+
void CBarProgressBar::OnTimer(void)
  {
   CWinFormBase *base=this.GetBase();
   if(base==NULL)
      return;
   CWinFormBase *glare=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ,0);
   if(glare==NULL)
      return;
      
//--- If the object is in the normal state (hidden)
   if(glare.DisplayState()==CANV_ELEMENT_DISPLAY_STATE_NORMAL)
     {
      //--- set the state of waiting for fading in to the object (in our case, waiting for a shift along the progress bar),
      //--- set the waiting duration and set the countdown time
      glare.SetDisplayState(CANV_ELEMENT_DISPLAY_STATE_WAITING_FADE_IN);
      this.m_pause.SetWaitingMSC(this.ShowDelay());
      this.m_pause.SetTimeBegin();
      //--- If the right edge of the glare object is to the right of the left edge of the progress bar object
      if(glare.RightEdge()>=this.CoordX())
        {
         //--- Hide the glare object and move it beyond the right edge of the progress bar
         glare.Hide();
         if(glare.Move(this.CoordX()-glare.Width(),this.CoordY()))
           {
            //--- Set the relative coordinates of the glare object
            glare.SetCoordXRelative(glare.CoordX()-this.CoordX());
            glare.SetCoordYRelative(glare.CoordY()-this.CoordY());
            //--- and its visibility scope equal to the entire object
            glare.SetVisibleArea(0,0,glare.Width(),glare.Height());
           }
        }
      return;
     }
//--- If the object is in the state of waiting for fading in (in our case, waiting for a shift along the progress bar)
   if(glare.DisplayState()==CANV_ELEMENT_DISPLAY_STATE_WAITING_FADE_IN)
     {
      //--- If the waiting time has not yet passed, leave
      if(this.m_pause.Passed()<this.ShowDelay())
         return;
      //--- Set the state of the object being in the process of shifting along the progress bar and
      //---  the process start countdown time
      glare.SetDisplayState(CANV_ELEMENT_DISPLAY_STATE_PROCESS_FADE_IN);
      this.m_pause.SetTimeBegin();
      return;
     }
//--- If the object is in the state of a shift along the progress bar
   if(glare.DisplayState()==CANV_ELEMENT_DISPLAY_STATE_PROCESS_FADE_IN)
     {
      //--- If the glare object X coordinate still has not gone beyond the right edge of the progress bar
      if(glare.CoordX()<=this.RightEdge())
        {
         //--- set the display flag and show the object
         if(!glare.Displayed())
            glare.SetDisplayed(true);
         if(!glare.IsVisible())
            glare.Show();
         //--- bring the highlight object to the foreground
         glare.BringToTop();
         //--- Shift the highlight by 16 pixels to the right
         if(glare.Move(glare.CoordX()+16,this.CoordY()))
           {
            //--- Set the relative coordinates of the highlight object and redraw it
            glare.SetCoordXRelative(glare.CoordX()-this.CoordX());
            glare.SetCoordYRelative(glare.CoordY()-this.CoordY());
            glare.Redraw(true);
           }
         return;
        }
      //--- Set the completion state of the shift along the progress bar
      glare.SetDisplayState(CANV_ELEMENT_DISPLAY_STATE_COMPLETED_FADE_IN);
     }
//--- If the object is in the state of completion of shifting along the progress bar
   if(glare.DisplayState()==CANV_ELEMENT_DISPLAY_STATE_COMPLETED_FADE_IN)
     {
      //--- set the object to its normal state (invisible),
      //--- hide the object and set its invisibility flag
      glare.SetDisplayState(CANV_ELEMENT_DISPLAY_STATE_NORMAL);
      glare.Hide();
      glare.SetDisplayed(false);
      return;
     }
  }
//+------------------------------------------------------------------+

A lógica do método, descrita nos comentários do código, é idêntica à do manipulador de eventos do temporizador que escrevemos para objetos de dica de ferramenta no artigo anterior, sé que esta é apenas uma versão mais simplificada, já que, para objetos dica de ferramenta, esperamos que o objeto comece a aparecer gradualmente (neste caso, ele corre ao longo da barra de progresso) e espera que ele desapareça. Essa é toda a diferença. Mas para o efeito de bilho aparecer e correr ao longo da barra de progresso, usamos os mesmos valores ​da enumeração ENUM_CANV_ELEMENT_DISPLAY_STATE como nos objetos dica de ferramenta:

//+------------------------------------------------------------------+
//| List of control display states                                   |
//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_DISPLAY_STATE
  {
   CANV_ELEMENT_DISPLAY_STATE_NORMAL,                 // Normal
   CANV_ELEMENT_DISPLAY_STATE_WAITING_FADE_IN,        // Wait for fading in
   CANV_ELEMENT_DISPLAY_STATE_PROCESS_FADE_IN,        // Fading in
   CANV_ELEMENT_DISPLAY_STATE_COMPLETED_FADE_IN,      // Fading in end
   CANV_ELEMENT_DISPLAY_STATE_WAITING_FADE_OUT,       // Wait for fading out
   CANV_ELEMENT_DISPLAY_STATE_PROCESS_FADE_OUT,       // Fading out
   CANV_ELEMENT_DISPLAY_STATE_COMPLETED_FADE_OUT,     // Fading out end
   CANV_ELEMENT_DISPLAY_STATE_COMPLETED,              // Complete processing
  };
//+------------------------------------------------------------------+

Em outras palavras, as constantes dessa enumeração são universais o suficiente para lidaremos com diferentes eventos em diferentes objetos. Se necessário, a esta lista será possível adicionar constantes que descrevam alguns outros eventos que podem ser mais avançados.

Agora estamos prontos para o teste.


Teste

Para testar, vamos pegar o Expert Advisor do artigo anterior e salvá-lo na nova pasta \MQL5\Experts\TestDoEasy\Part127\ com o novo nome TestDoEasy127.mq5.

Anteriormente já tínhamos criado um objeto estático ProgressBar no segundo painel do controle SplitContainer, localizado na primeira aba do controle TabControl. Agora, precisamos obter um ponteiro para o objeto ProgressBar e primeiro aumentar seu tamanho em um laço, para ver como isto muda o tamanho relativo da barra de progresso, definido para 50% da largura da barra de progresso. Depois disso, em outro laço, aumentaremos o valor da barra de progresso usando o método PerformStep. Para que o método funcione, vamos definir os parâmetros necessários com antecedência: mínimo = 0, máximo = 350, passo = 1. Após a conclusão dos dois laços, obteremos um ponteiro para o objeto brilho e definiremos os parâmetros de exibição para ele.

Para implementar tudo isso, no final do manipulador OnInit() escrevemos o seguinte bloco de código:

//--- Display and redraw all created panels
   for(int i=0;i<FORMS_TOTAL;i++)
     {
      //--- Get the panel object
      pnl=engine.GetWFPanel("WinForms Panel"+(string)i);
      if(pnl!=NULL)
        {
         //--- display and redraw the panel
         pnl.Show();
         pnl.Redraw(true);
         //--- Get the TabControl object from the panel
         CTabControl *tc=pnl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,0);
         //--- Get the SplitContainer object from the first tab of the TabControl object
         CSplitContainer *sc=tc.GetTabElementByType(0,GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER,0);
         //--- Get the second panel from the SplitContainer object
         CSplitContainerPanel *scp=sc.GetPanel(1);
         //--- Get the ProgressBar object from the received panel
         CProgressBar *pb=scp.GetElementByType(GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,0);
         //--- Wait for 1/10 of a second
         Sleep(100);
         //--- Get the width of the ProgressBar object
         int w=pb.Width();
         //--- In the loop, increase the width of the ProgressBar by 180 pixels with a delay of 1/50
         for(int n=0;n<180;n++)
           {
            Sleep(20);
            pb.Resize(w+n,pb.Height(),true);
           }
         //--- Set the values for PerformStep of the ProgressBar object
         pb.SetValuesForProcessing(0,350,1,0);
         //--- Reset ProgressBar to minimum
         pb.ResetProgressBar();
         //--- Wait for 1/5 second
         Sleep(200);
         //--- In the loop from the minimum to the maximum value of ProgressBar
         for(int n=0;n<=pb.Maximum();n++)
           {
            //--- call the method for increasing the progress bar by a given step with a wait of 1/5 second
            pb.PerformStep();
            Sleep(20);
           }
         //--- Get the glare object
         CGlareObj *gl=pb.GetElementByType(GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ,0);
         if(gl!=NULL)
           {
            //--- Set the glare type - rectangle, opacity 40, color - white
            gl.SetStyleRectangle();
            gl.SetOpacity(40);
            gl.SetColor(clrWhite);
           }
        }
     }
        
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+


Compilamos e executamos o Expert Advisor no gráfico:


No primeiro loop, podemos observar o aumento da largura do objeto ProgressBar, que é acompanhado pelo aumento proporcional da largura da barra de progresso. Ao mesmo tempo, Value é definido como 50 inicialmente e permanece constante, já que a largura da barra de progresso é definida em relação a esse valor.

No segundo loop, o método PerformStep é chamado a cada iteração, aumentando o valor relativo em cada passo de incremento. Embora haja 350 incrementos, é perceptível que a barra de progresso está crescendo em mais de um pixel por vez. Isso ocorre porque todos os valores são calculados como uma porcentagem da largura da barra de progresso e são relativos entre si. É importante lembrar que não é possível aumentar exatamente um pixel por vez, pois se a largura da barra de progresso for de 100 pixels e o número de etapas for 1000, então serão necessárias 10 etapas de incremento para cada pixel. Esses valores relativos são corretos e esperados, e não afetam o cálculo do progresso geral.

Ao finalizar o laço de incremento da barra de progresso, é possível notar um reflexo passando por ela a cada dois segundos. Embora isso seja suficiente para demostrar a funcionalidade, a aparência da barra ainda pode ser melhorada. Em primeiro lugar, o brilho deveria percorrer a barra em movimento e não a já concluída. Em segundo lugar, o efeito atual é bastante simples e pode ser aprimorado. Felizmente, todas essas questões serão gradualmente refinadas, corrigidas e aprimoradas.

Agora as dicas de ferramenta desaparecem suavemente ao longo do processo de esmaecimento, em vez de permanecerem visíveis até o final. Eles desaparecem da vista juntamente com o objeto no último passo de opacidade 0.


O que virá a seguir?

No próximo artigo, continuaremos trabalhando nos objetos da biblioteca WinForms e no objeto ProgressBar.

Voltar ao conteúdo

*Artigos desta série:

 
DoEasy. Controles (Parte 20): Objeto WinForms SplitContainer
DoEasy. Controles (Parte 21): Controle SplitContainer. Separador de painéis
DoEasy. Controles (Parte 22): SplitContainer. Alterando as propriedades do objeto criado
DoEasy. Controles (Parte 23): Apurando os objetos WinForms TabControl e SplitContainer
DoEasy. Controles (Parte 24): Objeto WinForms dica
DoEasy. Controles (Parte 25): Objeto WinForms Tooltip
DoEasy. Controles (Parte 26): Finalizamos o objeto WinForms "ToolTip" e começamos a desenvolver a barra de progresso "ProgressBar"

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/11764

Arquivos anexados |
MQL5.zip (4524.46 KB)
Desenvolvendo um sistema de Replay — Simulação de mercado (Parte 01): Primeiros experimentos (I) Desenvolvendo um sistema de Replay — Simulação de mercado (Parte 01): Primeiros experimentos (I)
Que tal criar um sistema para estudar o mercado quando ele está fechado, ou mesmo simular situações de mercado. Aqui vamos iniciar uma nova sequencia de artigos, a fim de tratar deste tema.
Guia Prático MQL5 — Serviços Guia Prático MQL5 — Serviços
O artigo descreve os recursos versáteis dos serviços — programas em MQL5 que não necessitam de gráficos para serem anexados. Eu ambém destacarei as diferenças dos serviços de outros programas em MQL5 e enfatizarei as nuances do trabalho do desenvolvedor com os serviços. Como exemplos, são oferecidas ao leitor várias tarefas que abrangem uma ampla gama de funcionalidades que podem ser implementadas como um serviço.
Teoria das Categorias em MQL5 (Parte 1) Teoria das Categorias em MQL5 (Parte 1)
A Teoria das Categorias é um ramo diverso da Matemática e em expansão, sendo uma área relativamente recente na comunidade MQL. Esta série de artigos visa introduzir e examinar alguns de seus conceitos com o objetivo geral de estabelecer uma biblioteca aberta que atraia comentários e discussões enquanto esperamos promover o uso deste campo notável no desenvolvimento da estratégia dos traders.
Redes neurais de maneira fácil (Parte 33): regressão quantílica em aprendizado Q distribuído, Redes neurais de maneira fácil (Parte 33): regressão quantílica em aprendizado Q distribuído,
Continuamos a estudar o aprendizado Q distribuído e hoje veremos essa abordagem de outro ponto de vista. Falaremos sobre a possibilidade de usar regressão quantílica para resolver o problema de previsão de movimentos de preços.