English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
Manuale MQL5: Controlli della finestra secondaria dell'indicatore - Pulsanti

Manuale MQL5: Controlli della finestra secondaria dell'indicatore - Pulsanti

MetaTrader 5Esempi | 11 gennaio 2022, 17:12
79 0
Anatoli Kazharski
Anatoli Kazharski

Introduzione

In questo articolo considereremo un esempio di sviluppo di un'interfaccia utente con controlli a pulsante. Per trasmettere l'idea di interattività all'utente, i pulsanti cambiano colore quando il cursore passa sopra di essi. Con il cursore posizionato su un pulsante, il colore del pulsante sarà leggermente più scuro, diventando notevolmente più scuro quando si fa clic su di esso. Inoltre, aggiungeremo suggerimenti a ciascun pulsante, creando così un'interfaccia intuitiva.

L'articolo tratterà anche alcuni eventi: l'evento di spostamento del mouse, lo stato del pulsante sinistro del mouse, il clic sinistro su un oggetto e l'evento di modifica delle proprietà del grafico. Creeremo un pannello dei pulsanti che occuperà l'intero spazio della sottofinestra dell'indicatore. A scopo illustrativo, i pulsanti saranno disposti su tre file, con quattro pulsanti in ciascuna riga.

 

Sviluppo

In MQL5, i pulsanti possono essere creati utilizzando vari oggetti grafici, come OBJ_BUTTON (Button), OBJ_BITMAP (Bitmap), OBJ_BITMAP_LABEL (Bitmap Label) o OBJ_EDIT (Edit).

In questo articolo, creeremo pulsanti utilizzando OBJ_EDIT. Gli oggetti di questo tipo possono essere resi di sola lettura. Sono anche utili in quanto possono visualizzare il testo specificato. Inoltre, puoi rendere nitidi gli angoli dell'oggetto, mantenendone il bordo.

Quindi, creiamo un indicatore utilizzando la procedura guidata MQL5. Leggermente rielaborato, il codice sorgente dell'indicatore sarà il seguente:

//+------------------------------------------------------------------+
//|                                                  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)
  {
//---

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

Quello che abbiamo adesso è una finestra vuota con zero serie di grafici. La necessità di un timer sarà discussa un po' più avanti.

Aggiungiamo ora costanti, variabili e array che verranno utilizzati nella creazione di funzioni. Tutti gli array sono bidimensionali. La prima dimensione indica il numero di pulsanti lungo l'altezza della finestra e la seconda dimensione indica il numero di pulsanti lungo la larghezza della finestra:

//---
#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 il caricamento dell'indicatore nel grafico, gli array devono essere inizializzati alle proprietà dell'oggetto nelle OnInit(), dopo aver calcolato le coordinate e le dimensioni. Dovremmo anche abilitare il tracciamento del cursore. E infine, dobbiamo aggiungere pulsanti alla sottofinestra dell'indicatore. Per comodità, queste azioni verranno eseguite in funzioni separate che esamineremo una per una più avanti. Di conseguenza, le OnInit() avrà il seguente aspetto:

//+------------------------------------------------------------------+
//| 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);
  }

Nella funzione AddPrefix(), al nome di ogni oggetto grafico viene aggiunto il prefisso, ovvero il nome breve dell'indicatore. Ciò è necessario per escludere la sostituzione/cancellazione/spostamento di oggetti in caso di nomi di oggetti corrispondenti in cui è in esecuzione più di un programma sul grafico.

//+------------------------------------------------------------------+
//| 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];
  }

Le proprietà del grafico necessarie per i calcoli verranno inizializzate nella funzione 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);
  }

Dopo aver ottenuto le proprietà del grafico, possiamo eseguire calcoli per determinare i colori dei pulsanti, i valori delle coordinate e le dimensioni. Tutte queste azioni vengono eseguite in tre funzioni separate fornite di seguito:

//+------------------------------------------------------------------+
//| 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 infine, la funzione AddButtonsPanel() aggiunge pulsanti alla sottofinestra dell'indicatore:

//+------------------------------------------------------------------+
//| 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]);
        }
     }
  }

Il codice sorgente della funzione ausiliaria CreateButton() è il seguente:

//+------------------------------------------------------------------+
//| 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"
     }
  }

Notare l'ultimo parametro della funzione CreateButton(): è responsabile del suggerimento quando il cursore del mouse passa su un oggetto grafico. Ad esempio, nella funzione AddButtonsPanel() questo parametro è rappresentato dai valori passati dall'array button_texts (testo visualizzato sui pulsanti). Se lo desideri, puoi creare un array separato con descrizioni più dettagliate.

Ora, se colleghi l'indicatore al grafico, il risultato sarà il seguente:

Fig. 1. Pulsanti aggiunti alla sottofinestra dell'indicatore

Fig. 1. Pulsanti aggiunti alla sottofinestra dell'indicatore

Al momento, questi sono semplici oggetti disposti nella sottofinestra dell'indicatore. L'interazione con l'utente non è ancora stata implementata. Ora "respiriamo vita" in questi oggetti.

Innanzitutto implementeremo la possibilità di regolare le dimensioni dei pulsanti in base alle dimensioni della sottofinestra quando quest'ultima viene ridimensionata. A questo scopo, scriveremo altre due funzioni: UpdateButtonCoordinates() e ResizeButtons(). Imposteranno le coordinate e le dimensioni dei pulsanti:

//+------------------------------------------------------------------+
//| 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]);
        }
     }
  }

Per gestire l'evento di modifica delle proprietà del grafico e ridimensionamento del grafico, è necessario utilizzare CHARTEVENT_CHART_CHANGE. Di seguito puoi vedere il codice che devi aggiungere alle 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 aggiungiamo ora l'indicatore al grafico (o ricompiliamo il codice se l'indicatore è già sul grafico), i pulsanti verranno automaticamente ridimensionati e riposizionati non appena verrà ridimensionata la finestra del grafico o la sottofinestra dell'indicatore.

Implementiamo ulteriormente la modifica del colore del pulsante quando il cursore passa sopra un pulsante. Ma prima di scrivere il codice della funzione, esaminiamo prima il processo di consegna dell'evento con i CHARTEVENT_MOUSE_MOVE.

Nelle OnInit(), abbiamo già una stringa che dice al programma di tenere traccia del movimento del cursore del mouse, nonché dello stato del pulsante sinistro del mouse:

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

Senza questa stringa (o se l'ultimo valore del parametro passato è falso), gli eventi con l'identificatore CHARTEVENT_MOUSE_MOVE non verranno tracciati nelle OnChartEvent(). Questo può sembrare molto utile in quanto potrebbe non essere necessario tenere traccia di tali eventi in ogni programma.

Per capire come funziona il tracciamento degli eventi del mouse, possiamo aggiungere temporaneamente al codice della funzione OnChartEvent() la possibilità di visualizzare il commento corrispondente nel grafico:

//--- 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 ora inizi a spostare il cursore del mouse nel grafico, sarai in grado di vedere le coordinate correnti del cursore nell'angolo in alto a sinistra. Quando si fa clic con il tasto sinistro, le modifiche verranno visualizzate nella riga di commento sparam (stato dei pulsanti del mouse), dove uno (1) significa che il pulsante del mouse è stato cliccato e zero (0) significa che è stato rilasciato.

Se è necessario conoscere la sottofinestra in cui si trova attualmente il cursore del mouse, è possibile utilizzare le ChartXYToTimePrice(). Ottiene le coordinate e restituisce il numero della finestra/sottofinestra, l'ora e il prezzo (alle variabili passate per riferimento). Puoi vederlo in azione testando il seguente codice:

//--- 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;
     }

I calcoli nella sottofinestra dell'indicatore saranno più facili se vengono utilizzate le coordinate relative. In questo caso, riguarda la coordinata Y (scala dei prezzi). Per ottenere il valore relativo, devi solo sottrarre la distanza dalla parte superiore del grafico alla sottofinestra dell'indicatore dal valore corrente. Questo può essere fatto come 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)
                 );
        }

Ora, il valore nella variabile y sarà negativo se il cursore del mouse si trova sopra la sottofinestra dell'indicatore e positivo quando il cursore passa sopra l'area della sottofinestra.

Per impostazione predefinita, è possibile scorrere il grafico lungo la scala temporale, indipendentemente dalla posizione del cursore sul grafico. Lo scorrimento del grafico può comunque essere disabilitato, se e quando necessario. Sarà principalmente necessario quando il cursore si trova sopra il pannello o i controlli personalizzati. Il codice per disabilitare lo scorrimento del grafico quando il cursore si trova nella sottofinestra dell'indicatore e abilitarlo quando il cursore si sposta fuori dalla sottofinestra può essere, ad esempio, il seguente:

         //--- 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);

Inoltre, scriviamo una funzione che cambierà il colore del pulsante quando il cursore passa sopra il pulsante corrispondente - 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);
        }
     }
  }

Di conseguenza, abbiamo il seguente codice sorgente nei 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;
     }

Ora, se sposti il cursore sui pulsanti, sarai in grado di vedere il colore del pulsante cambiare / tornare alla normalità.

Attualmente, solo il pulsante 01 ha il colore del pulsante cliccato. Se provi a fare clic su altri pulsanti, non ci sarà risposta e quindi nessun cambiamento di colore. Per implementare il cambio di colore in questo caso, è necessario utilizzare un evento con l'identificatore CHARTEVENT_OBJECT_CLICK.

Scriviamo due funzioni: InitializeButtonStates() e ChangeButtonColorOnClick(). La funzione InitializeButtonStates() verificherà se un dato pulsante è stato cliccato, prendendo in considerazione il prefisso nel suo nome. Se viene identificato l'evento click, l'array di stati del pulsante (button_states) viene quindi inizializzato in un ciclo e la funzione restituisce 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);
  }

Successivamente, la funzione ChangeButtonColorOnClick() imposta i colori dei pulsanti in base ai valori dell'array 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);
        }
     }
  }

Per far funzionare tutto, assicurati di aggiungere la gestione dei clic sui pulsanti alla funzione di tracciamento degli 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;
     }

Ora, una volta cliccato, il pulsante cambierà colore.

Abbiamo ancora alcuni punti di cui dobbiamo occuparci. Nelle OnDeinit(), dovremmo abilitare lo scorrimento del grafico nell'area della sottofinestra e disabilitare il tracciamento degli eventi del mouse, quando si elimina l'indicatore dal grafico. Questo può essere importante se nel grafico sono in esecuzione contemporaneamente più programmi che utilizzano il rilevamento degli eventi.

//+------------------------------------------------------------------+
//| 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();
     }
  }

Funzioni per la cancellazione degli oggetti grafici del programma:

//+------------------------------------------------------------------+
//| 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 infine, ecco il motivo per cui abbiamo bisogno di un timer in questo programma. Ad esempio, se nel grafico è in esecuzione più di un programma e ciascuno dei programmi deve tenere traccia degli eventi del mouse, quando uno di essi viene eliminato dal grafico, il monitoraggio verrà disabilitato nelle OnDeinit() funzione per tutti i programmi. Pertanto, in alternativa, puoi eseguire un controllo ogni secondo per vedere se il monitoraggio degli eventi del mouse è abilitato:

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

  }

Il codice della funzione CheckChartEventMouseMove() è fornito di seguito:

A volte, può essere sufficiente eseguire questo controllo per un evento con i CHARTEVENT_CHART_CHANGE.

Di seguito puoi vedere il video che mostra ciò che abbiamo ottenuto come risultato:

 

Conclusione

Bene, siamo arrivati alla fine. L'indicatore TestButtons.mq5 è allegato all'articolo ed è disponibile per il download. Con un ulteriore sviluppo, questo esempio potrebbe diventare un menu principale interessante. Ad esempio, l'utente potrebbe passare all'informazione pertinente facendo clic su un determinato pulsante. Il numero di pulsanti potrebbe essere aumentato, se necessario.

Tradotto dal russo da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/ru/articles/750

File allegati |
testbuttons.mq5 (20.47 KB)
Manuale MQL5: Controlli della finestra secondaria degli indicatori - Barra di scorrimento Manuale MQL5: Controlli della finestra secondaria degli indicatori - Barra di scorrimento
Continuiamo ad esplorare i vari controlli e questa volta rivolgiamo la nostra attenzione alla barra di scorrimento. Proprio come nel precedente articolo intitolato "Manuale MQL5: Controlli della finestra secondaria dell'indicatore - Pulsanti", tutte le operazioni verranno eseguite nella finestra secondaria dell'indicatore. Prenditi un momento per leggere l'articolo sopra menzionato in quanto fornisce una descrizione dettagliata dell'utilizzo degli eventi nella funzione OnChartEvent(). Questo argomento verrà solo menzionato in questo articolo. A scopo illustrativo, questa volta creeremo una barra di scorrimento verticale per un ampio elenco di tutte le proprietà degli strumenti finanziari che possono essere ottenute utilizzando le risorse MQL5.
Manuale MQL5: Monitoraggio di più intervalli di tempo in un'unica finestra Manuale MQL5: Monitoraggio di più intervalli di tempo in un'unica finestra
Ci sono 21 intervalli di tempo disponibili in MetaTrader 5 per l'analisi. È possibile sfruttare gli oggetti del grafico speciali che è possibile posizionare sul grafico esistente e impostare il simbolo, l'intervallo di tempo e alcune altre proprietà. Questo articolo fornirà informazioni dettagliate su tali oggetti grafici del grafico: creeremo un indicatore con controlli (pulsanti) che ci permetterà di impostare più oggetti grafici in una sottofinestra contemporaneamente. Inoltre, gli oggetti del grafico si adatteranno con precisione alla sottofinestra e verranno regolati automaticamente quando il grafico principale o la finestra del terminale viene ridimensionato.
MQL5 Cookbook: Sviluppo di un indicatore di volatilità multi-simbolo in MQL5 MQL5 Cookbook: Sviluppo di un indicatore di volatilità multi-simbolo in MQL5
In questo articolo, considereremo lo sviluppo di un indicatore di volatilità multi-simbolo. Lo sviluppo di indicatori multi-simbolo può presentare alcune difficoltà per gli sviluppatori MQL5 alle prime armi che questo articolo aiuta a chiarire. Le principali questioni che sorgono nel corso dello sviluppo di un indicatore multi-simbolo hanno a che fare con la sincronizzazione dei dati di altri simboli rispetto al simbolo corrente, con la mancanza di alcuni dati indicatori e con l'identificazione dell'inizio di barre "vere" di un determinato intervallo di tempo. Tutti questi problemi saranno attentamente considerati nell'articolo.
Manuale MQL5: Notifiche sonore per gli eventi di trading metaTrader 5 Manuale MQL5: Notifiche sonore per gli eventi di trading metaTrader 5
In questo articolo, considereremo problemi come l'inclusione di file audio nel file di Expert Advisor e quindi l'aggiunta di notifiche sonore agli eventi di trading. Il fatto che i file saranno inclusi significa che i file audio si troveranno all'interno dell'Expert Advisor. Quindi, quando si fornisce la versione compilata di Expert Advisor (*.ex5) a un altro utente, non sarà necessario fornire anche i file audio e spiegare dove devono essere salvati.