
Guia prático do MQL5: Controles de sub-janela indicadora - botões
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
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





- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso