English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
Guia prático do MQL5: Controles de sub-janela indicadora - botões

Guia prático do MQL5: Controles de sub-janela indicadora - botões

MetaTrader 5Exemplos | 27 março 2014, 10:34
3 431 0
Anatoli Kazharski
Anatoli Kazharski

Introdução

Neste artigo, vamos considerar um exemplo de desenvolvimento de uma interface de usuário com controles de botão. Para transmitir a ideia de interatividade ao usuário, os botões mudarão suas cores quando o cursor passar sobre eles. Com o cursor sobre um botão, a cor do botão será ligeiramente escurecida, ficando significativamente mais escura quando o botão for clicado. Além disso, adicionaremos janelas pop-up em cada botão, criando assim, uma interface intuitiva.

O artigo também cobrirá alguns eventos: movimento do mouse, estado do botão esquerdo do mouse, clique do botão esquerdo do mouse em um objeto e evento de modificação de propriedades de gráfico. Nós vamos criar um painel de botões que vai ocupar todo o espaço da sub-janela indicadora. Para fins ilustrativos, os botões serão dispostos em três fileiras, com quatro botões em cada linha.

 

Desenvolvimento

No MQL5, os botões podem ser criados usando vários objetos gráficos, como OBJ_BUTTON (botão), OBJ_BITMAP (Bitmap), OBJ_BITMAP_LABEL (etiqueta Bitmap ) ou OBJ_EDIT (de edição).

Neste artigo, criaremos botões usando OBJ_EDIT. Objetos desse tipo podem ser feitos somente leitura. Eles também são úteis na medida em que podem exibir o texto que você especificar. Além disso, você pode fazer os cantos do objeto pontudos, mantendo o seu limite.

Então, vamos criar um indicador usando o Assistente MQL5. Ligeiramente reformulado, o código-fonte do indicador será como a seguir:

//+------------------------------------------------------------------+
//|                                                  TestButtons.mq5 |
//|                        Copyright 2013, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2013, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
//---
#property indicator_separate_window // Indicator is in the subwindow
#property indicator_plots 0         // No plotting series
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
//---

//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
//---

  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//---

  }
//+------------------------------------------------------------------+

O que temos agora é uma janela vazia com a série de plotagem zero. A necessidade de um temporizador será discutida um pouco mais tarde.

Vamos agora adicionar constantes, variáveis e matrizes que serão utilizadas na criação de funções. Todas as matrizes são bidimensionais. A primeira dimensão indica o número de botões em toda a altura da janela e a segunda dimensão indica o número de botões em toda a largura da janela:

//---
#define BUTTON_COLUMNS  4           // Number of buttons across the width
#define BUTTON_ROWS 3               // Number of buttons across the height
//+------------------------------------------------------------------+
//| Global parameters                                                |
//+------------------------------------------------------------------+
//--- Font
string            font_name="Calibri";
//--- Indicator subwindow properties
int               subwindow_number           =WRONG_VALUE;             // Subwindow number
int               subwindow_height           =0;                       // Subwindow height
string            subwindow_shortname        ="TestButtons";           // Short name of the indicator
string            prefix                     =subwindow_shortname+"_"; // Prefix for object names
int               chart_width                =0;                       // Chart width
int               chart_height               =0;                       // Chart height
int               chart_y_offset             =0;                       // Distance from the chart top to the subwindow
//--- Colors of button elements
color             background_color           =clrSteelBlue;            // Button color
color             font_color                 =clrWhite;                // Font color
color             hover_background_color     =C'38,118,166';           // Button color when the cursor goes over
color             clicked_background_color   =C'2,72,136';             // Clicked button color
//--- Text displayed on buttons
string button_texts[BUTTON_ROWS][BUTTON_COLUMNS]=
  {
     {"Button 01","Button 02","Button 03","Button 04"},
     {"Button 05","Button 06","Button 07","Button 08"},
     {"Button 09","Button 10","Button 11","Button 12"}
  };
//--- Object names
string button_object_names[BUTTON_ROWS][BUTTON_COLUMNS]=
  {
     {"button_01","button_02","button_03","button_04"},
     {"button_05","button_06","button_07","button_08"},
     {"button_09","button_10","button_11","button_12"}
  };
//--- Button widths
int button_widths[BUTTON_ROWS][BUTTON_COLUMNS];
//--- Button heights
int button_heights[BUTTON_ROWS][BUTTON_COLUMNS];
//--- X-coordinates
int button_x_distances[BUTTON_ROWS][BUTTON_COLUMNS];
//--- Y-coordinates
int button_y_distances[BUTTON_ROWS][BUTTON_COLUMNS];
//--- Button states
bool button_states[BUTTON_ROWS][BUTTON_COLUMNS]=
  {
     {true,false,false,false},
     {false,false,false,false},
     {false,false,false,false}
  };
//--- Button colors
color button_colors[BUTTON_ROWS][BUTTON_COLUMNS];

Ao carregar o indicador para o gráfico, as matrizes devem ser inicializadas com as propriedades do objeto na função OnInit() depois de calcular as coordenadas e tamanhos. Devemos também ativar o controle de cursor. E, finalmente, precisamos adicionar botões à sub-janela indicadora. Para maior comodidade, essas ações serão realizadas em funções separadas que vamos olhar para um mais abaixo. Como resultado, o código da função OnInit() ficará da seguinte forma:

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Set the timer at 1-second intervals
   EventSetTimer(1);
//--- Add prefix to object names
   AddPrefix();
//--- Enable tracking of mouse events
   ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,true);
//--- Set the short name
   IndicatorSetString(INDICATOR_SHORTNAME,subwindow_shortname);
//--- Set subwindow properties
   SetSubwindowProperties();
//--- Set button properties
   SetButtonColors();      // Colors
   SetButtonCoordinates(); // Coordinates
   SetButtonSizes();       // Sizes
//--- Add the button panel
   AddButtonsPanel();
//--- Refresh the chart
   ChartRedraw();
//--- Everything completed successfully
   return(INIT_SUCCEEDED);
  }

Na função AddPrefix(), o prefixo, por exemplo, o nome curto do indicador, é adicionado ao nome de cada objeto gráfico. Isso é necessário para excluir a substituição/exclusão/mudança de objetos, no caso de combinar nomes de objetos onde mais de um programa estiver em execução no gráfico.

//+------------------------------------------------------------------+
//| Adding prefix to all object names                                |
//+------------------------------------------------------------------+
void AddPrefix()
  {
//--- Add prefix to object names
   for(int i=0; i<BUTTON_COLUMNS; i++)
      for(int j=0; j<BUTTON_ROWS; j++)
         button_object_names[j][i]=prefix+button_object_names[j][i];
  }

Propriedades de gráfico necessárias para cálculos serão inicializadas na função SetSubwindowProperties():

//+------------------------------------------------------------------+
//| Setting subwindow properties                                     |
//+------------------------------------------------------------------+
void SetSubwindowProperties()
  {
//--- Indicator subwindow number
   subwindow_number=ChartWindowFind(0,subwindow_shortname);
//--- Subwindow width and height
   chart_width=(int)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS);
   subwindow_height=(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS,subwindow_number);
  }

Depois de obter as propriedades do gráfico, podemos fazer cálculos para determinar as cores do botão, valores de coordenadas e tamanhos. Todas essas ações são realizadas em três funções distintas fornecidas abaixo:

//+------------------------------------------------------------------+
//| Setting button color                                             |
//+------------------------------------------------------------------+
void SetButtonColors()
  {
   for(int i=0; i<BUTTON_COLUMNS; i++)
     {
      for(int j=0; j<BUTTON_ROWS; j++)
        {
         //--- If the button is clicked
         if(button_states[j][i])
            button_colors[j][i]=clicked_background_color;
         //--- If the button is unclicked
         else
            button_colors[j][i]=background_color;
        }
     }
  }
//+------------------------------------------------------------------+
//| Setting X and Y coordinates for buttons                          |
//+------------------------------------------------------------------+
void SetButtonCoordinates()
  {
   int button_width=chart_width/BUTTON_COLUMNS;
   int button_height=subwindow_height/BUTTON_ROWS;
//---
   for(int i=0; i<BUTTON_COLUMNS; i++)
     {
      for(int j=0; j<BUTTON_ROWS; j++)
        {
         if(i==0)
            button_x_distances[j][i]=0;
         else
            button_x_distances[j][i]=(button_width*i)-i;
         //---
         if(j==0)
            button_y_distances[j][i]=0;
         else
            button_y_distances[j][i]=(button_height*j)-j;
        }
     }
  }
//+------------------------------------------------------------------+
//| Setting button width and height                                  |
//+------------------------------------------------------------------+
void SetButtonSizes()
  {
   int button_width=chart_width/BUTTON_COLUMNS;
   int button_height=subwindow_height/BUTTON_ROWS;
//---
   for(int i=0; i<BUTTON_COLUMNS; i++)
     {
      for(int j=0; j<BUTTON_ROWS; j++)
        {
         if(i==BUTTON_COLUMNS-1)
            button_widths[j][i]=chart_width-(button_width*(BUTTON_COLUMNS-1)-i);
         else
            button_widths[j][i]=button_width;
         //---
         if(j==BUTTON_ROWS-1)
            button_heights[j][i]=subwindow_height-(button_height*(BUTTON_ROWS-1)-j)-1;
         else
            button_heights[j][i]=button_height;
        }
     }
  }

E, finalmente, a função AddButtonsPanel() que adiciona botões à sub-janela indicadora:

//+------------------------------------------------------------------+
//| Adding buttons to the indicator subwindow                        |
//+------------------------------------------------------------------+
void AddButtonsPanel()
  {
//--- Create buttons
   for(int i=0; i<BUTTON_COLUMNS; i++)
     {
      for(int j=0; j<BUTTON_ROWS; j++)
        {
         CreateButton(0,subwindow_number,button_object_names[j][i],button_texts[j][i],
                      CORNER_LEFT_UPPER,font_name,8,font_color,button_colors[j][i],clrNONE,
                      button_widths[j][i],button_heights[j][i],
                      button_x_distances[j][i],button_y_distances[j][i],2,true,button_texts[j][i]);
        }
     }
  }

O código-fonte da função auxiliar CreateButton() é como se segue:

//+------------------------------------------------------------------+
//| Creating a button (graphical object of the Edit type)            |
//+------------------------------------------------------------------+
void CreateButton(long   chart_id,     // chart id
                  int    sub_window,   // (sub)window number
                  string object_name,  // object name
                  string text,         // displayed text
                  long   corner,       // chart corner
                  string font,         // font
                  int    font_size,    // font size
                  color  c_font,       // font color
                  color  c_background, // background color
                  color  c_border,     // border color
                  int    x_size,       // width
                  int    y_size,       // height
                  int    x_dist,       // X-coordinate
                  int    y_dist,       // Y-coordinate
                  long   zorder,       // Z-order
                  bool   read_only,    // Read Only flag
                  string tooltip)      // tooltip
  {
//--- If the object has been created successfully, set the remaining properties
   if(ObjectCreate(chart_id,object_name,OBJ_EDIT,subwindow_number,0,0))
     {
      ObjectSetString(chart_id,object_name,OBJPROP_TEXT,text);              // name
      ObjectSetInteger(chart_id,object_name,OBJPROP_CORNER,corner);         // chart corner
      ObjectSetString(chart_id,object_name,OBJPROP_FONT,font);              // font
      ObjectSetInteger(chart_id,object_name,OBJPROP_FONTSIZE,font_size);    // font size
      ObjectSetInteger(chart_id,object_name,OBJPROP_COLOR,c_font);          // font color
      ObjectSetInteger(chart_id,object_name,OBJPROP_BGCOLOR,c_background);  // background color
      ObjectSetInteger(chart_id,object_name,OBJPROP_BORDER_COLOR,c_border); // border color
      ObjectSetInteger(chart_id,object_name,OBJPROP_XSIZE,x_size);          // width
      ObjectSetInteger(chart_id,object_name,OBJPROP_YSIZE,y_size);          // height
      ObjectSetInteger(chart_id,object_name,OBJPROP_XDISTANCE,x_dist);      // X-coordinate
      ObjectSetInteger(chart_id,object_name,OBJPROP_YDISTANCE,y_dist);      // Y-coordinate
      ObjectSetInteger(chart_id,object_name,OBJPROP_SELECTABLE,false);      // object is not available for selection
      ObjectSetInteger(chart_id,object_name,OBJPROP_ZORDER,zorder);         // Z-order
      ObjectSetInteger(chart_id,object_name,OBJPROP_READONLY,read_only);    // Read Only text
      ObjectSetInteger(chart_id,object_name,OBJPROP_ALIGN,ALIGN_CENTER);    // align center
      ObjectSetString(chart_id,object_name,OBJPROP_TOOLTIP,tooltip);        // no tooltip if "\n"
     }
  }

Por favor, observe o último parâmetro da funçãoCreateButton(): é responsável pela janela pop-up de ferramenta quando o cursor do mouse passar sobre um objeto gráfico. Por exemplo, na função AddButtonsPanel() este parâmetro é representado pelos valores passados a partir da matriz button_texts (texto exibido nos botões). Você pode criar uma matriz separada, com descrições mais detalhadas, se desejar.

Agora, se você anexar o indicador para o gráfico, o resultado será o seguinte:

Fig. 1. Botões adicionados à sub-janela indicadora

Fig. 1. Botões adicionados à sub-janela indicadora

Neste momento, estes são meros objetos dispostos na sub-janela indicadora. A interação com o usuário ainda não está implementada. Vamos agora "dar vida" a esses objetos.

Em primeiro lugar, vamos implementar a possibilidade de ajustar os tamanhos do botão de acordo com o tamanho da sub-janela, quando esta última for redimensionada. Por essa razão, escreveremos mais duas funções -UpdateButtonCoordinates() e ResizeButtons(). Elas definirão as coordenadas e tamanhos dos botões:

//+------------------------------------------------------------------+
//| Updating button coordinates                                      |
//+------------------------------------------------------------------+
void UpdateButtonCoordinates()
  {
//--- Set coordinates
   for(int i=0; i<BUTTON_COLUMNS; i++)
     {
      for(int j=0; j<BUTTON_ROWS; j++)
        {
         ObjectSetInteger(0,button_object_names[j][i],OBJPROP_XDISTANCE,button_x_distances[j][i]);
         ObjectSetInteger(0,button_object_names[j][i],OBJPROP_YDISTANCE,button_y_distances[j][i]);
        }
     }
  }
//+------------------------------------------------------------------+
//| Updating button sizes                                            |
//+------------------------------------------------------------------+
void ResizeButtons()
  {
//--- Set sizes
   for(int i=0; i<BUTTON_COLUMNS; i++)
     {
      for(int j=0; j<BUTTON_ROWS; j++)
        {
         ObjectSetInteger(0,button_object_names[j][i],OBJPROP_XSIZE,button_widths[j][i]);
         ObjectSetInteger(0,button_object_names[j][i],OBJPROP_YSIZE,button_heights[j][i]);
        }
     }
  }

Para gerenciar o evento de modificar as propriedades do gráfico e redimensioná-lo, precisamos usar o identificador CHARTEVENT_CHART_CHANGE. Abaixo você pode ver o código que precisa adicionar ao corpo da função OnChartEvent():

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,           // event identifier
                  const long &lparam,     // parameter of the event of type long
                  const double &dparam,   // parameter of the event of type double
                  const string &sparam)   // parameter of the event of type string
  {
//--- Tracking the event of modifying the chart properties and resizing the chart
   if(id==CHARTEVENT_CHART_CHANGE)
     {
      //--- Set subwindow properties
      SetSubwindowProperties();
      //--- Set button coordinates
      SetButtonCoordinates();
      //--- Set button sizes
      SetButtonSizes();
      //--- Set new button coordinates
      UpdateButtonCoordinates();
      //--- Set new button sizes
      ResizeButtons();
      //--- Refresh the chart
      ChartRedraw(); return;
     }

  }

Se somarmos o indicador ao gráfico agora (ou recompilarmos o código, se o indicador já estiver no gráfico), os botões serão automaticamente redimensionados e reposicionados assim que a janela do gráfico da sub-janela indicadora for redimensionada.

Implementamos ainda mais a mudança da cor do botão quando o cursor passar sobre um botão. Mas antes de escrever o código de função, vamos primeiro verificar o processo de gerenciamento do evento com o indicador CHARTEVENT_MOUSE_MOVE.

Na função OnInit() já temos uma sequência que diz ao programa para controlar o movimento do cursor do mouse, assim como o estado do botão esquerdo do mouse:

//--- Enable tracking of mouse events
   ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,true);

Sem esta série (ou se o último valor de parâmetro passado for falso), eventos com o identificador CHARTEVENT_MOUSE_MOVE não serão controlados na função OnChartEvent(). Isto pode parecer bastante útil, pois pode não haver a necessidade de controlar tais eventos em cada programa.

Para entender como o controle de eventos do mouse funciona, podemos acrescentar temporariamente ao código de função OnChartEvent() a possibilidade de exibir o comentário correspondente no gráfico:

//--- Mouse movement and left-click tracking
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      Comment("id: ",CHARTEVENT_MOUSE_MOVE,"\n",
              "lparam (x): ",lparam,"\n",
              "dparam (y): ",dparam,"\n",
              "sparam (state of the mouse buttons): ",sparam
              );

Se agora você começar a mover o cursor do mouse no gráfico, você será capaz de ver as coordenadas atuais do cursor no canto superior esquerdo. Ao clicar com o botão esquerdo, as alterações serão exibidas na linha de comentário sparam (estado dos botões do mouse), onde um (1) significa que o botão é clicado, e zero (0) significa que ele é liberado.

Se você precisar saber onde o cursor do mouse está localizado atualmente na sub-janela, você pode usar a função ChartXYToTimePrice(). Ela recebe as coordenadas e retorna o número de janela/sub-janela, tempo e preço (para as variáveis passadas a ela por referência). Você pode ver isso em ação testando o seguinte código:

//--- Mouse movement and left-click tracking
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      int      x      =(int)lparam; // X-coordinate
      int      y      =(int)dparam; // Y-coordinate
      int      window =WRONG_VALUE; // Number of the window where the cursor is located
      datetime time   =NULL;        // Time corresponding to the X-coordinate
      double   price  =0.0;         // Price corresponding to the Y-coordinate
      //--- Get the position of the cursor
      if(ChartXYToTimePrice(0,x,y,window,time,price))
        {
         Comment("id: ",CHARTEVENT_MOUSE_MOVE,"\n",
                 "x: ",x,"\n",
                 "y: ",y,"\n",
                 "sparam (state of the mouse buttons): ",sparam,"\n",
                 "window: ",window,"\n",
                 "time: ",time,"\n",
                 "price: ",DoubleToString(price,_Digits)
                 );
        }
      //---
      return;
     }

Os cálculos na sub-janela indicadora serão mais fáceis, se as coordenadas relativas forem usadas. Neste caso, trata-se da coordenada Y (escala de preço). Para obter o valor relativo, você só precisa subtrair a distância da parte superior do gráfico à sub-janela indicadora do valor atual. Isto pode ser feito como se segue:

      //--- Get the position of the cursor
      if(ChartXYToTimePrice(0,x,y,window,time,price))
        {
         //--- Get the distance from the chart top to the indicator subwindow
         chart_y_offset=(int)ChartGetInteger(0,CHART_WINDOW_YDISTANCE,subwindow_number);
         //--- Convert the Y-coordinate to the relative value
         y-=chart_y_offset;
         Comment("id: ",CHARTEVENT_MOUSE_MOVE,"\n",
                 "x: ",x,"\n",
                 "y: ",y,"\n",
                 "sparam (state of the mouse buttons): ",sparam,"\n",
                 "window: ",window,"\n",
                 "time: ",time,"\n",
                 "price: ",DoubleToString(price,_Digits)
                 );
        }

Agora, o valor da variável y será negativo se o cursor do mouse estiver acima da sub-janela indicadora e positivo quando o cursor passar sobre a área da sub-janela.

Por padrão, existe a possibilidade de rolamento de imagens no gráfico pela escala de tempo, independentemente da posição do cursor no gráfico. O rolamento de imagens do gráfico, entretanto, pode ser desativado, quando necessário. Será necessário principalmente quando o cursor estiver localizado acima do painel ou de controles personalizados. O código para desativar o rolamento de imagens de gráfico quando o cursor estiver na sub-janela indicadora e ativá-lo quando o cursor mover-se para fora da sub-janela pode ser, por exemplo, da seguinte forma:

         //--- If the cursor is in the subwindow area, disable chart scrolling
         if(window==subwindow_number)
            ChartSetInteger(0,CHART_MOUSE_SCROLL,false);
         //--- Enable chart scrolling if the cursor moves out of the indicator subwindow area
         else
            ChartSetInteger(0,CHART_MOUSE_SCROLL,true);

Além disso, vamos escrever uma função que mudará a cor do botão quando o cursor passar sobre o botão correspondente - ChangeButtonColorOnHover():

//+------------------------------------------------------------------+
//| Changing the button color when the cursor hovers over the button |
//+------------------------------------------------------------------+
void ChangeButtonColorOnHover(int x,int y)
  {
   int x1,y1,x2,y2;
//--- Initialize the array of XY coordinates for buttons
   SetButtonCoordinates();
//--- Determine if the cursor is over any of the buttons
   for(int i=0; i<BUTTON_COLUMNS; i++)
     {
      for(int j=0; j<BUTTON_ROWS; j++)
        {
         //--- If this button is clicked, go to the next one
         if(button_states[j][i])
            continue;
         //--- Get the button boundaries
         x1=button_x_distances[j][i];
         y1=button_y_distances[j][i];
         x2=button_x_distances[j][i]+button_widths[j][i];
         y2=button_y_distances[j][i]+button_heights[j][i];
         //--- If the cursor is within the button area, set the new button color
         if(x>x1 && x<x2 && y>y1 && y<y2)
            ObjectSetInteger(0,button_object_names[j][i],OBJPROP_BGCOLOR,hover_background_color);
         //--- Otherwise set the standard color
         else
            ObjectSetInteger(0,button_object_names[j][i],OBJPROP_BGCOLOR,background_color);
        }
     }
  }

Como resultado, temos o seguinte código-fonte na rede identificadora CHARTEVENT_MOUSE_MOVE:

//--- Mouse movement and left-click tracking
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      int      x      =(int)lparam; // X-coordinate
      int      y      =(int)dparam; // Y-coordinate
      int      window =WRONG_VALUE; // Number of the window where the cursor is located
      datetime time   =NULL;        // Time corresponding to the X-coordinate
      double   price  =0.0;         // Price corresponding to the Y-coordinate
      //--- Get the position of the cursor
      if(ChartXYToTimePrice(0,x,y,window,time,price))
        {
         //--- Get the distance from the chart top to the indicator subwindow
         chart_y_offset=(int)ChartGetInteger(0,CHART_WINDOW_YDISTANCE,subwindow_number);
         //--- Convert the Y-coordinate to the relative value
         y-=chart_y_offset;
         //--- If the cursor is in the subwindow area, disable chart scrolling
         if(window==subwindow_number)
            ChartSetInteger(0,CHART_MOUSE_SCROLL,false);
         //--- Enable chart scrolling if the cursor moves out of the indicator subwindow area
         else
            ChartSetInteger(0,CHART_MOUSE_SCROLL,true);
         //--- Change the button color when the cursor is hovered over
         ChangeButtonColorOnHover(x,y);
        }
      //--- Refresh the chart
      ChartRedraw(); 
      return;
     }

Agora, se você mover o cursor sobre os botões, você será capaz de ver a mudança/voltar ao normal da cor do botão.

Atualmente, somente o Botão 01 tem a cor do botão clicado. Se você tentar clicar em outros botões, não haverá nenhuma resposta e, portanto, nenhuma alteração de cor. Para implementar a mudança de cor, neste caso, precisamos usar evento com o identificador CHARTEVENT_OBJECT_CLICK.

Vamos escrever duas funções: InitializeButtonStates() e ChangeButtonColorOnClick(). A função InitializeButtonStates() verificará se um determinado botão foi clicado, levando em consideração o prefixo em seu nome. Se o evento de clique é identificado, a matriz de estados do botão (button_states) é então inicializada em um ciclo e a função retorna verdadeira.

//+------------------------------------------------------------------+
//| Initializing button states in case of click                      |
//+------------------------------------------------------------------+
bool InitializeButtonStates(string clicked_object)
  {
//--- Get the indicator subwindow number
   subwindow_number=ChartWindowFind(0,subwindow_shortname);
//--- If a button in the indicator subwindow has been clicked
   if(ObjectFind(0,clicked_object)==subwindow_number && StringFind(clicked_object,prefix+"button_",0)>=0)
     {
      //--- Determine the clicked button
      for(int i=0; i<BUTTON_COLUMNS; i++)
        {
         for(int j=0; j<BUTTON_ROWS; j++)
           {
            //--- Determine the state of all buttons
            if(clicked_object==button_object_names[j][i])
               button_states[j][i]=true;
            else
               button_states[j][i]=false;
           }
        }
      //---
      return(true);
     }
//---
   return(false);
  }

Seguindo isso, a função ChangeButtonColorOnClick() define as cores do botão de acordo com os valores da matriz button_states.

//+------------------------------------------------------------------+
//| Changing the button color in case of click                       |
//+------------------------------------------------------------------+
void ChangeButtonColorOnClick()
  {
   for(int i=0; i<BUTTON_COLUMNS; i++)
     {
      for(int j=0; j<BUTTON_ROWS; j++)
        {
         //--- If the button has been clicked, it is set a distinctive color
         if(button_states[j][i])
            ObjectSetInteger(0,button_object_names[j][i],OBJPROP_BGCOLOR,clicked_background_color);
         //--- Set the standard color to the unclicked button
         else
            ObjectSetInteger(0,button_object_names[j][i],OBJPROP_BGCOLOR,background_color);
        }
     }
  }

Para fazer tudo funcionar, certifique-se de adicionar o gerenciador de cliques do botão de controle para a função OnChartEvent():

//--- Tracking left mouse button clicks on a graphical object
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- If the button has been clicked
      if(InitializeButtonStates(sparam))
        {
         //--- Set button colors
         ChangeButtonColorOnClick();
        }
      //--- Refresh the chart
      ChartRedraw();
      return;
     }

Agora, quando clicado, o botão vai mudar a sua cor.

Ainda temos alguns pontos que precisam de cuidados. Na função OnDeinit() devemos permitir o rolamento de imagens de gráficos na área da sub-janela e desativar o controle de eventos do mouse, ao excluir o indicador do gráfico. Isso pode ser importante se vários programas que usam o controle de eventos estiverem sendo executados no gráfico ao mesmo tempo.

//+------------------------------------------------------------------+
//| Deinitialization                                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(reason==REASON_REMOVE ||  // If the indicator has been deleted from the chart or
      reason==REASON_RECOMPILE) // the program has been recompiled
     {
      //--- Deactivate the timer
      EventKillTimer();
      //--- Delete the objects
      DeleteButtons();
      //--- Enable chart scrolling
      ChartSetInteger(0,CHART_MOUSE_SCROLL,true);
      //--- Disable tracking of mouse events
      ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,false);
      //--- Refresh the chart
      ChartRedraw();
     }
  }

Funções para excluir os objetos gráficos do programa:

//+------------------------------------------------------------------+
//| Deleting all buttons                                             |
//+------------------------------------------------------------------+
void DeleteButtons()
  {
   for(int i=0; i<BUTTON_COLUMNS; i++)
      for(int j=0; j<BUTTON_ROWS; j++)
         DeleteObjectByName(button_object_names[j][i]);
  }
//+------------------------------------------------------------------+
//| Deleting the object by name                                      |
//+------------------------------------------------------------------+
void DeleteObjectByName(string object_name)
  {
//--- If such object exists
   if(ObjectFind(0,object_name)>=0)
     {
      //--- If an error occurred when deleting, print the relevant message
      if(!ObjectDelete(0,object_name))
         Print("Error ("+IntegerToString(GetLastError())+") when deleting the object!");
     }
  }

E, finalmente, aqui está a razão pela qual precisamos de um temporizador neste programa. Por exemplo, se mais de um programa estiver sendo executado no gráfico e para cada um dos programas for necessário o controle dos eventos do mouse, então, quando um deles for excluído do gráfico, o controle será desativado na função OnDeinit() para todos os programas. Portanto, você pode, como alternativa, executar uma verificação a cada segundo para ver se o controle de eventos do mouse está ativado:

//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- Check whether tracking of mouse events is enabled
   CheckChartEventMouseMove();

  }

O código de função CheckChartEventMouseMove() é fornecido abaixo:

Às vezes, pode ser mais do que suficiente fazer esta verificação para um evento com o identificador CHARTEVENT_CHART_CHANGE.

Abaixo, você pode ver o vídeo que demonstra o que temos como resultado:

 

Conclusão

Bem, isso basicamente se resume. O indicador TestButtons.mq5 está anexo ao artigo e está disponível para download. Com mais desenvolvimento, este exemplo poderia converter-se um menu principal interessante. Por exemplo, o usuário seria capaz de saltar para a informação relevante ao clicar em um determinado botão. O número de botões pode ser aumentado, se necessário.

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

Arquivos anexados |
testbuttons.mq5 (20.47 KB)
Guia prático do MQL5: Controles da sub-janela indicadora - barra de rolagem Guia prático do MQL5: Controles da sub-janela indicadora - barra de rolagem
Vamos continuar a explorar vários controles e desta vez a nossa atenção é para a barra de rolagem. Assim como no artigo anterior intitulado"Guia prático do MQL5: Os controles da sub-janela indicadora - botões", todas as operações serão realizadas na sub-janela indicadora. Tome um tempo para ler o artigo acima mencionado, uma vez que ele fornece uma descrição detalhada do trabalho com eventos na função OnChartEvent(), enquanto este ponto somente será casualmente abordado neste artigo. Para fins ilustrativos, desta vez vamos criar uma barra de rolagem vertical para uma grande lista de todas as propriedades de instrumentos financeiros que possam ser obtidas usando recursos do MQL5.
Guia prático do MQL5: Monitoramento de múltiplos períodos de tempo em uma única janela Guia prático do MQL5: Monitoramento de múltiplos períodos de tempo em uma única janela
Há 21 períodos de tempo disponíveis no MetaTrader 5 para análise. Você pode tirar proveito de objetos gráficos especiais, os quais você pode colocar no gráfico existente e definir o símbolo, período de tempo e algumas outras propriedades ali mesmo. Este artigo fornecerá informações detalhadas sobre tais objetos gráficos do gráfico: vamos criar um indicador com os controles (botões), que nos permitirão definir vários objetos gráficos em uma sub-janela ao mesmo tempo. Além disso, os objetos gráficos se encaixarão com precisão na sub-janela e serão ajustados automaticamente quando o gráfico principal ou a janela do terminal forem redimensionados.
Guia prático do MQL5: Desenvolvimento de um indicador de símbolos múltiplos no MQL5 Guia prático do MQL5: Desenvolvimento de um indicador de símbolos múltiplos no MQL5
Neste artigo, vamos considerar o desenvolvimento de um indicador de volatilidade de símbolos múltiplos. O desenvolvimento de indicadores de símbolos múltiplos pode apresentar algumas dificuldades para os desenvolvedores novatos do MQL5, as quais este artigo ajuda a esclarecer. As principais questões que surgem no curso do desenvolvimento de um indicador de símbolos múltiplos têm a ver com sincronização de dados de outros símbolos em relação ao símbolo atual, a falta de alguns dados de indicadores e a identificação de início de barras 'reais' de um determinado período de tempo. Todas essas questões serão atentamente consideradas no artigo.
Guia prático do MQL5: Notificações sonoras para eventos de negociação do MetaTrader 5 Guia prático do MQL5: Notificações sonoras para eventos de negociação do MetaTrader 5
Neste artigo vamos considerar questões como a inclusão de arquivos sonoros no arquivo do Expert Advisor e, por conseguinte, a adição de notificações sonoras aos eventos de negociação. O fato de que os arquivos serão incluídos significa que os arquivos sonoros estarão localizados dentro do Expert Advisor. Assim, ao dar a versão compilada do Expert Advisor (*.ex5) para outro usuário, você não terá que fornecer também os arquivos sonoros e explicar onde eles precisam ser salvos.