Guía práctica de MQL5: Controles de la subventana del indicador: Botones
Introducción
En este artículo, vamos a ver un ejemplo del desarrollo de una interfaz de usuario con controles mediante botones. Para transmitir la idea de interactividad al usuario, los botones cambiarán de color cuando se pasa el cursor por encima de ellos. Cuando el cursor está encima de un botón, el color de este último será ligeramente oscurecido, haciéndose notablemente más oscuro cuando se pulsa el botón. Además, vamos a añadir tooltips (información sobre herramientas) a cada botón, creando así una interfaz intuitiva.
El artículo abarcará también algunos eventos: evento de movimiento del ratón, estado del botón izquierdo del ratón, clic con el botón izquierdo en un objeto y evento de modificación de las propiedades del gráfico. Vamos a crear un panel de botones que ocupará todo el espacio de la subventana del indicador. Para fines ilustrativos, los botones se dispondrán en tres filas, con cuatro botones en cada fila.
Desarrollo
En MQL5, se pueden crear botones mediante varios objetos gráficos, como OBJ_BUTTON (Botón), OBJ_BITMAP (Bitmap), OBJ_BITMAP_LABEL (Etiqueta Bitmap) o OBJ_EDIT (Editar).
En este artículo, vamos a crear botones mediante OBJ_EDIT. Los objetos de este tipo se pueden hacer de sólo lectura. También son útiles por el hecho de que le permiten mostrar un texto determinado. Además, puede hacer que los objetos tengan esquinas agudas, manteniendo sus bordes.
Así que, vamos a crear un indicador mediante MQL5 Wizard. Ligeramente retocado, el código fuente del indicador será el siguiente:
//+------------------------------------------------------------------+ //| 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) { //--- } //+------------------------------------------------------------------+
Lo que tenemos ahora es una ventana vacía sin representación gráfica. Más adelante, comentaremos la necesidad de un temporizador.
Ahora vamos a añadir las constantes, variables y matrices que se van a utilizar en la creación de las funciones. Todas las matrices son bidimensionales. La primera dimensión indica el número de botones a lo largo de la altura de la ventana y la segunda dimensión indica el número de botones a lo largo de la anchura de la ventana:
//--- #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];
Durante la carga del indicador en el gráfico, hay que inicializar las matrices a las propiedades del objeto en la función OnInit(), después de calcular las coordenadas y los tamaños. Tenemos que activar también el seguimiento del cursor. Y por último, tenemos que añadir botones a la subventana del indicador. Para mayor comodidad, se llevarán a cabo estas acciones en funciones separadas que vamos a estudiar una por una a continuación. Como resultado, la función OnInit() tendrá el siguiente código:
//+------------------------------------------------------------------+ //| 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); }
En la función Addprefix(), se añade el prefijo, es decir, el nombre corto del indicador, al nombre de cada objeto gráfico. Esto es necesario para excluir la sustitución / eliminación / cambio de los objetos en caso de que coincidan los nombres de los objetos, cuando haya más de un programa ejecutándose en un 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]; }
Se inicializarán las propiedades de los gráficos que requieren los cálculos en la función 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); }
Después de obtener las propiedades del gráfico, podemos realizar los cálculos para determinar los colores de los botones, los valores de coordenadas y los tamaños. Todo esto se lleva a cabo en tres funciones separadas que se proporcionan a continuación:
//+------------------------------------------------------------------+ //| 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; } } }
Y por último, la función AddButtonsPanel() añade botones a la subventana del indicador:
//+------------------------------------------------------------------+ //| 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]); } } }
El código fuente de la función adicional CreateButton() es el siguiente:
//+------------------------------------------------------------------+ //| 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" } }
Tenga en cuenta el último parámetro de la función CreateButton(): se encarga de la información sobre herramientas (tooltips) cuando pasa el cursor del ratón por encima de un objeto gráfico. Por ejemplo, en la función AddButtonsPanel() se representa este parámetro por los valores enviados a partir de la matriz button_texts (el texto que se muestra en los botones). Si quiere, puede crear una matriz separada con descripciones más detalladas.
Si carga el indicador en el gráfico ahora, el resultado será el siguiente:
Fig. 1. Botones añadidos a la subventana del indicador
Por el momento, estos son meros objetos dispuestos en la subventana del indicador. La interacción con el usuario aún no está implementada. Ahora vamos a "dar vida" a estos objetos.
En primer lugar, vamos a implementar la posibilidad de ajustar los tamaños de los botones en función del tamaño de la subventana cuando cambia el tamaño de esta última. Para ello, vamos a escribir dos funciones más; UpdateButtonCoordinates() y ResizeButtons(). Establecerán las coordenadas y los tamaños de los botones:
//+------------------------------------------------------------------+ //| 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 controlar el evento de modificación de las propiedades del gráfico y cambiar el tamaño del mismo, tenemos que utilizar el identificador CHARTEVENT_CHART_CHANGE. A continuación, puede ver el código que tiene que añadir al cuerpo de la función 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; } }
Si añadimos el indicador al gráfico ahora (o volvemos a compilar el código si el indicador está ya en el gráfico), nada más cambia el tamaño de la ventana del gráfico o el de la subventana del indicador, cambiará automáticamente el tamaño y posición de los botones.
Implementamos además el cambio de color del botón cuando el cursor pasa por encima de él. Pero antes de escribir el código de la función, examinemos primero el proceso de control del evento con el identificador CHARTEVENT_MOUSE_MOVE.
En la función OnInit(), ya tenemos una cadena que indica al programa que tiene que seguir el movimiento del cursor del ratón, así como el estado del botón izquierdo del ratón:
//--- Enable tracking of mouse events ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,true);
Sin esta cadena (o si el valor del parámetro enviado es false), no se hará el seguimiento de los eventos con el identificador CHARTEVENT_MOUSE_MOVE en la función OnChartEvent(). Esto parece bastante útil, ya que puede existir la necesidad de realizar un seguimiento de este tipo de eventos en cada programa.
Para entender cómo funciona el seguimiento de los eventos del ratón, podemos añadir temporalmente al código de la función OnChartEvent() la posibilidad de mostrar el comentario correspondiente en el 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 );
Si ahora empieza a mover el cursor del ratón en el gráfico, podrá de ver las coordenadas actuales del cursor en la esquina superior izquierda. Al hacer un clic con el botón izquierdo, se mostrarán los cambios en la línea de comentario sparam (estado de los botones del ratón), donde uno (1) significa que el botón del ratón está pulsado y cero (0) significa que está suelto.
Si tiene que saber en qué subventana se encuentra el cursor del ratón en este momento, puede utilizar la función ChartXYToTimePrice(). Obtiene las coordenadas y devuelve la ventana / número de subventana, el tiempo y el precio (para las variables que se le envían por referencia). Puede ver esto probando el siguiente 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; }
Los cálculos en la subventana del indicador serán más fáciles si se usan coordenadas relativas. En este caso, se trata de la coordenada Y (escala del precio). Para obtener el valor relativo, sólo tiene que restar la distancia desde la parte superior del gráfico hasta la subventana del indicador del valor actual. Se puede hacer esto de la siguiente manera:
//--- 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) ); }
Ahora, el valor de la variable y será negativo si el cursor del ratón está por encima de la subventana del indicador y positivo si el cursor está por encima de área de la subventana.
Por defecto, existe una posibilidad para desplazar el gráfico a lo largo de la escala de tiempo, independientemente de la posición del cursor en el gráfico. No obstante, se puede desactivar el desplazamiento del gráfico, si es necesario. Será necesario sobre todo cuando el cursor se encuentra encima del panel o de los controles personalizados. El código para desactivar el desplazamiento del gráfico cuando el cursor se encuentra en la subventana del indicador y desactivarlo cuando el cursor se mueve fuera de la subventana puede ser, por ejemplo, el siguiente:
//--- 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);
Además, vamos a escribir una función que va a cambiar el color del botón cuando el cursor pasa por encima del botón correspondiente; 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, tenemos el siguiente código fuente en la rama del identificador 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; }
Ahora, si mueve el cursor por encima de los botones, podrá observar el cambio de color del botón o su vuelta a la normalidad.
Ahora mismo, solo Button 01 tiene el color de un botón pulsado. Si intenta pulsar otros botones, no habrá respuesta y por lo tanto no hay cambio de color. Para implementar el cambio de color en este caso, tenemos que utilizar un evento con el identificador CHARTEVENT_OBJECT_CLICK.
Vamos a escribir dos funciones: InitializeButtonStates() y ChangeButtonColorOnClick(). La función InitializeButtonStates() comprobará si se ha pulsado un botón, teniendo en cuenta el prefijo en su nombre. Si se identifica el evento clic, la matriz de estados del botón (button_states) se inicializa entonces en un bucle y la función devuelve true.
//+------------------------------------------------------------------+ //| 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); }
A continuación, la función ChangeButtonColorOnClick() establece los colores de los botones en función de los valores de la 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 hacer que todo funcione, asegúrese de añadir el control de los clics de los botones a la función de seguimiento de eventos 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; }
Al hacer clic ahora, el botón cambia de color.
Todavía quedan algunos puntos por resolver. En la función OnDeinit(), al borrar el indicador del gráfico, tenemos que habilitar el desplazamiento del gráfico en el área de la subventana y deshabilitar el seguimiento de eventos del ratón. Esto puede ser importante si se están ejecutando varios programas que utilizan el seguimiento de eventos en el gráfico al mismo tiempo.
//+------------------------------------------------------------------+ //| 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(); } }
Funciones para borrar objetos gráficos del 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!"); } }
Y finalmente, este es el motivo por el cual necesitamos un temporizador en este programa. Por ejemplo, si hay más de un programa ejecutándose en el gráfico y cada uno de los programas es necesaria para el seguimiento de los eventos del ratón, entonces cuando se elimina uno de ellos del gráfico, se deshabilitará el seguimiento en la función OnDeinit() para todos los programas. Por lo tanto, como alternativa, puede ejecutar una comprobación cada segundo para ver si está habilitado el seguimiento de los eventos del ratón:
//+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer() { //--- Check whether tracking of mouse events is enabled CheckChartEventMouseMove(); }
Se proporciona a continuación el código de la función CheckChartEventMouseMove():
A veces, puede ser más que suficiente hacer esta comprobación para un evento con el identificador CHARTEVENT_CHART_CHANGE.
A continuación puede ver el vídeo demostrativo del resultado obtenido:
Conclusión
Bueno, esto es todo. El indicador TestButtons.mq5 está adjunto al artículo para su descarga. Con un mayor desarrollo, este ejemplo podría convertirse en un interesante menú principal. Por ejemplo, el usuario podrá moverse a la información relevante haciendo clic en un determinado botón. Si es necesario, se podría aumentar el número de botones.
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/750
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso