English Русский 中文 Deutsch 日本語 Português
Interfaces gráficas VII: Control "Pestañas" (Capítulo 2)

Interfaces gráficas VII: Control "Pestañas" (Capítulo 2)

MetaTrader 5Ejemplos | 19 julio 2016, 15:07
888 0
Anatoli Kazharski
Anatoli Kazharski

Índice


Introducción

El primer artículo de la serie nos cuenta con más detalles para qué sirve esta librería: Interfaces gráficas I: Preparación de la estructura de la librería (Capítulo 1). Al final de cada artículo de la serie se muestra la lista completa de los capítulos con los enlaces. Además, se puede descargar la versión completa de la librería en la fase actual del desarrollo del proyecto. Es necesario colocar los ficheros en los mismos directorios, tal como están ubicados en el archivo.

En el primer capítulo de la séptima parte han sido presentadas tres clases de los controles para la creación de las tablas: tabla de las etiquetas de texto (CLabelsTable), tabla de los campos de edición (CTable) y la tabla dibujada (CCanvasTable). En este artículo (capítulo 2) hablaremos del control “Pestañas”. Habrá dos clases de este control: una clase simple y otra con posibilidades ampliadas.

 


Control “Pestañas”

Las pestañas sirven para gestionar la visualización de los conjuntos predefinidos de los controles de la interfaz gráfia. En las aplicaciones multifuncionales, a menudo es necesario meter muchos controles en un espacio reducido asignado para la interfaz gráfica. A través de las pestañas, se puede agruparlos por las categorías y activar la visualización del grupo necesario en un momento dado, lo que hace que la interfaz sea mucho más cómoda y más comprensible para el usuario final. Las pestañas externas tienen el aspecto de un grupo de botones con texto (nombre del grupo de controles), pudiendo que sólo una de ellas esté activa.

Vamos a nombrar todas las partes integrantes de este control.

  1. Fondo o área para colocar el grupo de controles
  2. Pestañas

 

Fig. 1. Partes integrantes del control “Pestañas”.

Hagamos que las pestañas se pueda posicionar en cuatro modos (arriba, abajo, a la izquierda y a la derecha) respecto al área donde van a ubicarse los controles. 

 


Desarrollo de la clase para la creación del control “Pestañas”

Creamos el archivo Tabs.mqh y lo incluimos en la librería (archivo WndContainer.mqh):

//+------------------------------------------------------------------+
//|                                                 WndContainer.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Tabs.mqh"

Hay que crear la clase CTabs en el archivo Tabs.mqh. En esta clase, igual que en otras clases de los controles, hay que crear los métodos estándar, así como el método para guardar el puntero al formulario al que será adjuntado este control:

//+------------------------------------------------------------------+
//| Clase para crear las pestañas                                    |
//+------------------------------------------------------------------+
class CTabs : public CElement
  {
private:
   //--- Puntero al formulario al que está adjuntado el control
   CWindow          *m_wnd;
   //---
public:
                     CTabs(void);
                    ~CTabs(void);
    //--- (1) Guarda el puntero del formulario, (2) devuelve los punteros a las barras de desplazamiento
   void              WindowPointer(CWindow &object)               { m_wnd=::GetPointer(object);      }
   //---
public:
   //--- Manejador de eventos del gráfico
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- Temporizador
   virtual void      OnEventTimer(void);
   //--- Desplazamiento del control
   virtual void      Moving(const int x,const int y);
   //--- (1) Mostrar, (2) ocultar, (3) resetear, (4) eliminar
   virtual void      Show(void);
   virtual void      Hide(void);
   virtual void      Reset(void);
   virtual void      Delete(void);
   //--- (1) Establecer, (2) resetear las prioridades para el clic izquierdo del ratón
   virtual void      SetZorders(void);
   virtual void      ResetZorders(void);
   //--- Resetear color
   virtual void      ResetColors(void) {}
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CTabs::CTabs(void)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CTabs::~CTabs(void)
  {
  }

Se puede dividir las propiedades de las pestañas en únicas y generales. Vamos a nombrar ambas listas.

Propiedades únicas

  • Array de punteros a los controles adjuntos a la pestaña
  • Texto
  • Ancho

Vamos a crear la estructura TElements para las propiedades únicas, y declaramos el array dinámico con este tipo: 

class CTabs : public CElement
  {
private:
   //--- Estructura de propiedades y arrays de controles adjuntos a cada pestaña
   struct TElements
     {
      CElement         *elements[];
      string            m_text;
      int               m_width;
     };
   TElements         m_tab[];
  };

Propiedades comunes:

  • Modo de posicionar las pestañas
  • Color del fondo del área
  • Tamaño de las tablas por el eje Y (alto)
  • Colores de las pestañas en diferentes estados
  • Color del texto de las pestañas en diferentes estados
  • Color de los marcos de las pestañas
  • Prioridades para el clic izquierdo del ratón

Hay que añadir la enumeración ENUM_TABS_POSITION al archivo Enums.mqh para establecer el modo de posicionamiento de las pestañas: 

//+------------------------------------------------------------------+
//| Enumeración del posicionamiento de pestañas                      |
//+------------------------------------------------------------------+
enum ENUM_TABS_POSITION
  {
   TABS_TOP    =0,
   TABS_BOTTOM =1,
   TABS_LEFT   =2,
   TABS_RIGHT  =3
  };

El código de abajo contiene la declaración de los campos y los métodos para establecer las propiedades: 

class CTabs : public CElement
  {
private:
   //--- Posicionamiento de las pestañas
   ENUM_TABS_POSITION m_position_mode;
   //--- Color del fondo del área general
   int               m_area_color;
   //--- Tamaño de las tablas por el eje Y
   int               m_tab_y_size;
   //--- Colores de las pestañas en diferentes estados
   color             m_tab_color;
   color             m_tab_color_hover;
   color             m_tab_color_selected;
   color             m_tab_color_array[];
   //--- Color del texto de las pestañas en diferentes estados
   color             m_tab_text_color;
   color             m_tab_text_color_selected;
   //--- Color de los marcos de las pestañas
   color             m_tab_border_color;
   //--- Prioridades para el clic izquierdo del ratón
   int               m_zorder;
   int               m_tab_zorder;
   //---
public:
   //--- (1) Establece/obtiene el posicionamiento de las pestañas (arriba/abajo, a la izquierda/a la derecha), (2) establece el tamaño de las tablas por el eje Y
   void              PositionMode(const ENUM_TABS_POSITION mode)     { m_position_mode=mode;          }
   ENUM_TABS_POSITION PositionMode(void)                       const { return(m_position_mode);       }
   void              TabYSize(const int y_size)                      { m_tab_y_size=y_size;           }
   //--- Color del (1) fondo general, (2) colores de las pestañas en diferentes estados, (3) color de los marcos de las pestañas
   void              AreaColor(const color clr)                      { m_area_color=clr;              }
   void              TabBackColor(const color clr)                   { m_tab_color=clr;               }
   void              TabBackColorHover(const color clr)              { m_tab_color_hover=clr;         }
   void              TabBackColorSelected(const color clr)           { m_tab_color_selected=clr;      }
   void              TabBorderColor(const color clr)                 { m_tab_border_color=clr;        }
   //--- Color del texto de las pestañas en diferentes estados
   void              TabTextColor(const color clr)                   { m_tab_text_color=clr;          }
   void              TabTextColorSelected(const color clr)           { m_tab_text_color_selected=clr; }
  };

Antes de crear el control, primero es necesario añadir la cantidad necesaria de las pestañas indicando el texto a mostrar y el ancho. Para eso escribiremos el método CTabs::AddTab(). Por defecto, los argumentos del método tienen los valores «» (línea vacía) y 50 (ancho)

class CTabs : public CElement
  {
public:
   //--- Añade la pestaña
   void              AddTab(const string tab_text="",const int tab_width=50);
  };
//+------------------------------------------------------------------+
//| Añade la pestaña                                                 |
//+------------------------------------------------------------------+
void CTabs::AddTab(const string tab_text,const int tab_width)
  {
//--- Establecer el tamaño para los arrays de pestañas
   int array_size=::ArraySize(m_tabs);
   ::ArrayResize(m_tabs,array_size+1);
   ::ArrayResize(m_tab,array_size+1);
//--- Guardar las propiedades pasadas
   m_tab[array_size].m_text  =tab_text;
   m_tab[array_size].m_width =tab_width;
//--- Guardar el número de pestañas
   m_tabs_total=array_size+1;
  }

Si hace falta que después de cargar la aplicación MQL quede seleccionada una determinada pestaña, entonces antes de crear el control, hay que especificar su índice usando el método CTabs::SelectedTab(). Además, vamos a necesitar el método privado CTabs::CheckTabIndex() para comprobar y corregir el índice de la pestaña seleccionada en caso de superar el rango. 

class CTabs : public CElement
  {
private:
   //--- Índice de la pestaña seleccionada
   int               m_selected_tab;
   //---
public:
   //--- (1) Guarda y (2) devuelve el índice de la pestaña seleccionada
   void              SelectedTab(const int index)                    { m_selected_tab=index;          }
   int               SelectedTab(void)                         const { return(m_selected_tab);        }
   //---
private:
   //--- Comprobación del índice de la pestaña seleccionada
   void              CheckTabIndex(void);
  };
//+------------------------------------------------------------------+
//| Comprobación del índice de la pestaña seleccionada               |
//+------------------------------------------------------------------+
void CTabs::CheckTabIndex(void)
  {
//--- Comprobar la superación del rango
   int array_size=::ArraySize(m_tab);
   if(m_selected_tab<0)
      m_selected_tab=0;
   if(m_selected_tab>=array_size)
      m_selected_tab=array_size-1;
  }

Para crear el control vamos a necesitar tres métodos privados (private) y uno público (public ):

class CTabs : public CElement
  {
private:
   //--- Objetos para crear el control
   CRectLabel        m_main_area;
   CRectLabel        m_tabs_area;
   CEdit             m_tabs[];
   //---
public:
   //--- Métodos para crear las pestañas
   bool              CreateTabs(const long chart_id,const int subwin,const int x,const int y);
   //---
private:
   bool              CreateMainArea(void);
   bool              CreateTabsArea(void);
   bool              CreateButtons(void);
  };

Si antes de la creación del control no añadimos ninguna pestaña, al llamar al método público CTabs::CreateTabs(), la creación de la interfaz gráfica será interrumpida y en el registro se mostrará el mensaje: 

//--- Si en el grupo no hay pestañas, avisar sobre ello
   if(m_tabs_total<1)
     {
      ::Print(__FUNCTION__," > ¡La llamada a este método debe realizarse "
              "cuando en el grupo hay por lo menos una pestaña! Utilice el método CTabs::AddTab()");
      return(false);
     }

Dependiendo del modo de posicionamiento seleccionado, la determinación y el cálculo de coordenadas de los objetos del control también va a diferenciarse. Para estos cálculos vamos a necesitar el método CTabs::SumWidthTabs() que devuelve el ancho total de todas las pestañas. En el modo de posicionamiento de la pestañas a la izquierda (TABS_LEFT) y a la derecha (TABS_RIGHT), va a devolverse el ancho de la primera pestaña. Para los modos arriba (TABS_TOP) y abajo (TABS_BOTTOM), el ancho de todas las pestañas se suma. 

class CTabs : public CElement
  {
private:
   //--- Posicionamiento de las pestañas
   ENUM_TABS_POSITION m_position_mode;
   //---
private:
   //--- Ancho de todas las pestañas
   int               SumWidthTabs(void);
  };
//+------------------------------------------------------------------+
//| Ancho de todas las pestañas                                      |
//+------------------------------------------------------------------+
int CTabs::SumWidthTabs(void)
  {
   int width=0;
//--- Si las pestañas se ubican a la izquierda y a la derecha, devolver el ancho de la primera pestaña
   if(m_position_mode==TABS_LEFT || m_position_mode==TABS_RIGHT)
      return(m_tab[0].m_width);
//--- Sumamos el ancho de todas las pestañas
   for(int i=0; i<m_tabs_total; i++)
      width=width+m_tab[i].m_width;
//--- Con solapamiento de un píxel
   width=width-(m_tabs_total-1);
   return(width);
  }

El método CTabs::CreateMainArea() sirve para crear el área donde se ubican los grupos de controles. El cálculo de las coordenadas y tamaños del objeto es el siguiente (versión reducida del método): 

//+------------------------------------------------------------------+
//| Crea el fondo general del área                                   |
//+------------------------------------------------------------------+
bool CTabs::CreateMainArea(void)
  {
//--- Formación del nombre del objeto
   string name=CElement::ProgramName()+"_tabs_main_area_"+(string)CElement::Id();
//--- Coordenadas
   int x=0;
   int y=0;
//--- Tamaños
   int x_size=0;
   int y_size=0;
//--- Cálculo de las coordenadas y tamaños respecto al posicionamiento de las pestañas
   switch(m_position_mode)
     {
      case TABS_TOP :
         x      =CElement::X();
         y      =CElement::Y()+m_tab_y_size-1;
         x_size =CElement::XSize();
         y_size =CElement::YSize()-m_tab_y_size;
         break;
      case TABS_BOTTOM :
         x      =CElement::X();
         y      =CElement::Y();
         x_size =CElement::XSize();
         y_size =CElement::YSize()-m_tab_y_size;
         break;
      case TABS_RIGHT :
         x      =CElement::X();
         y      =CElement::Y();
         x_size =CElement::XSize()-SumWidthTabs()+1;
         y_size =CElement::YSize();
         break;
      case TABS_LEFT :
         x      =CElement::X()+SumWidthTabs()-1;
         y      =CElement::Y();
         x_size =CElement::XSize()-SumWidthTabs()+1;
         y_size =CElement::YSize();
         break;
     }
//--- Creación del objeto
   if(!m_main_area.Create(m_chart_id,name,m_subwin,x,y,x_size,y_size))
      return(false);
//--- Establecemos las propiedades
//--- Márgenes desde el punto extremo
//--- Guardamos los tamaños
//--- Guardamos las coordenadas
//--- Guardamos el puntero del objeto
//...
   return(true);
  }

A continuación, se muestra el cálculo de las coordenadas y tamaños del fondo de pestañas dependiendo del modo de posicionamiento de pestañas establecido en el método CTabs::CreateTabsArea(): 

//+------------------------------------------------------------------+
//| Crea el fondo para las pestañas                                  |
//+------------------------------------------------------------------+
bool CTabs::CreateTabsArea(void)
  {
//--- Formación del nombre del objeto
   string name=CElement::ProgramName()+"_tabs_area_"+(string)CElement::Id();
//--- Coordenadas
   int x=CElement::X();
   int y=CElement::Y();
//--- Tamaños
   int x_size=SumWidthTabs();
   int y_size=0;
//--- Cálculo de los tamaños respecto al posicionamiento de las pestañas
   if(m_position_mode==TABS_TOP || m_position_mode==TABS_BOTTOM)
     {
      y_size=m_tab_y_size;
     }
   else
     {
      y_size=m_tab_y_size*m_tabs_total-(m_tabs_total-1);
     }
//--- Corregir las coordenadas para el posicionamiento de las pestañas abajo y arriba
   if(m_position_mode==TABS_BOTTOM)
     {
      y=CElement::Y2()-m_tab_y_size-1;
     }
   else if(m_position_mode==TABS_RIGHT)
     {
      x=CElement::X2()-x_size;
     }
//--- Creación del objeto
   if(!m_tabs_area.Create(m_chart_id,name,m_subwin,x,y,x_size,y_size))
      return(false);
//--- Establecemos las propiedades
//--- Márgenes desde el punto extremo
//--- Guardamos los tamaños
//--- Guardamos las coordenadas
//--- Guardamos el puntero del objeto
//...
   return(true);
  }

En el método CTabs::CreateButtons() para la creación de pestañas vamos a necesitar sólo el cálculo de las coordenadas. El ancho se establece en la clase personalizada de la aplicación para la creación del control. De lo contrario, se utiliza el valor (ancho) predefinido. En el código de abajo se muestra la versión reducida de este método: 

//+------------------------------------------------------------------+
//| Crea las pestañas                                                |
//+------------------------------------------------------------------+
bool CTabs::CreateButtons(void)
  {
//--- Coordenadas
   int x =CElement::X();
   int y =CElement::Y();
//--- Cálculo de las coordenadas respecto al posicionamiento de las pestañas
   if(m_position_mode==TABS_BOTTOM)
      y=CElement::Y2()-m_tab_y_size-1;
   else if(m_position_mode==TABS_RIGHT)
      x=CElement::X2()-SumWidthTabs();
//--- Comprobación del índice de la pestaña seleccionada
   CheckTabIndex();
//--- Creación de pestañas
   for(int i=0; i<m_tabs_total; i++)
     {
      //--- Formación del nombre del objeto
      string name=CElement::ProgramName()+"_tabs_edit_"+(string)i+"__"+(string)CElement::Id();
      //--- Cálculo de las coordenadas respecto al posicionamiento de las pestañas para cada una de las pestañas separadamente
      if(m_position_mode==TABS_TOP || m_position_mode==TABS_BOTTOM)
         x=(i>0) ? x+m_tab[i-1].m_width-1 : CElement::X();
      else
         y=(i>0) ? y+m_tab_y_size-1 : CElement::Y();
      //--- Creación del objeto
      if(!m_tabs[i].Create(m_chart_id,name,m_subwin,x,y,m_tab[i].m_width,m_tab_y_size))
         return(false);
      //--- Establecemos las propiedades
      //--- Márgenes desde el punto extremo del panel
      //--- Coordenadas
      //--- Tamaños
      //--- Inicialización del array del gradiente
      //--- Guardamos el puntero del objeto
     }
//---
   return(true);
  }

Para asignar algún control a una pestañas determinada, escribiremos el método CTabs::AddToElementsArray(). Tiene dos argumentos: (1) el índice de la pestaña a la que se adjunta el control y (2) la referencia al control cuyo puntero hay que guardar en el array de los controles de la pestaña

class CTabs : public CElement
  {
public:
   //--- Añade el control al array
   void              AddToElementsArray(const int tab_index,CElement &object);
  };
//+------------------------------------------------------------------+
//| Añade el control al array de la pestaña especificada             |
//+------------------------------------------------------------------+
void CTabs::AddToElementsArray(const int tab_index,CElement &object)
  {
//--- Comprobar la superación del rango
   int array_size=::ArraySize(m_tab);
   if(array_size<1 || tab_index<0 || tab_index>=array_size)
      return;
//--- Añadimos el puntero del control pasado al array de la pestaña especificada
   int size=::ArraySize(m_tab[tab_index].elements);
   ::ArrayResize(m_tab[tab_index].elements,size+1);
   m_tab[tab_index].elements[size]=::GetPointer(object);
  }

Al cambiar de pestañas, hay que ocultar los controles de la pestaña activa anterior, y mostrar los controles de la pestaña recién activada. Para estos fines crearemos el método CTabs::ShowTabElements(). Al principio de este método se encuentra la comprobación de la visibilidad del control. Si el control está ocultado, entonces el programa sale del método. Luego se comprueba, y en caso de necesidad se corrige, el índice de la pestaña activa. Luego se comprueban todas las pestañas en el ciclo, y se ejecuta la tarea principal del método.  

class CTabs : public CElement
  {
public:
   //--- Mostrar los controles de la pestaña recién seleccionada
   void              ShowTabElements(void);
  };
//+------------------------------------------------------------------+
//| Muestra los controles de la pestaña recién seleccionada          |
//+------------------------------------------------------------------+
void CTabs::ShowTabElements(void)
  {
//--- Salir si las pestañas están ocultadas
   if(!CElement::IsVisible())
      return;
//--- Comprobación del índice de la pestaña seleccionada
   CheckTabIndex();
//---
   for(int i=0; i<m_tabs_total; i++)
     {
      //--- Obtenemos el número de controles adjuntados a la pestaña
      int tab_elements_total=::ArraySize(m_tab[i].elements);
      //--- Si esta pestaña está seleccionada
      if(i==m_selected_tab)
        {
         //--- Mostrar los controles de la pestaña
         for(int j=0; j<tab_elements_total; j++)
            m_tab[i].elements[j].Show();
        }
      //--- Ocultar los controles de las pestañas no activas
      else
        {
         for(int j=0; j<tab_elements_total; j++)
            m_tab[i].elements[j].Hide();
        }
     }
  }

Para el procesamiento del evento del clic en la pestaña vamos a utilizar el método CTabs::OnClickTab(). Primero, el programa tiene que hacer dos comprobaciones: (1) por el nombre del objeto pulsado y (2) por el identificador del control que se extrae desde el nombre del objeto mediante el método CTabs::IdFromObjectName(). Si las comprobaciones han sido superadas con éxito, en el ciclo (1) buscamos la pestaña en la que ha sido hecho el clic, (2) guardamos su índice y (3) establecemos los colores correspondientes. Al final, los controles de la pestaña activa se hacen visibles a través del método CTabs::ShowTabElements().

class CTabs : public CElement
  {
private:
  //--- Procesamiento del clic en la pestaña
   bool              OnClickTab(const string pressed_object);
   //--- Obtención del identificador desde el nombre del objeto
   int               IdFromObjectName(const string object_name);
  };
//+------------------------------------------------------------------+
//| Clic en la pestaña en el grupo                                   |
//+------------------------------------------------------------------+
bool CTabs::OnClickTab(const string clicked_object)
  {
//--- Salimos si el clic no ha sido hecho en la celda de la tabla
   if(::StringFind(clicked_object,CElement::ProgramName()+"_tabs_edit_",0)<0)
      return(false);
//--- Obtenemos el identificador desde el nombre del objeto
   int id=IdFromObjectName(clicked_object);
//--- Salir si el identificador no coincide
   if(id!=CElement::Id())
      return(false);
//---
   for(int i=0; i<m_tabs_total; i++)
     {
      //--- Si esta pestaña está seleccionada
      if(m_tabs[i].Name()==clicked_object)
        {
         //--- Guardar el índice de la pestaña seleccionada
         SelectedTab(i);
         //--- Establecer los colores
         m_tabs[i].Color(m_tab_text_color_selected);
         m_tabs[i].BackColor(m_tab_color_selected);
        }
      else
        {
         //--- Establecer el color para las pestañas no activas
         m_tabs[i].Color(m_tab_text_color);
         m_tabs[i].BackColor(m_tab_color);
        }
     }
//--- Mostrar los controles de la pestaña recién seleccionada
   ShowTabElements();
   return(true);
  }

Entonces, el código del manejador principal de los eventos del control CTabs::OnEvent() va a tener el siguiente aspecto: 

//+------------------------------------------------------------------+
//| Manejador del evento del gráfico                                 |
//+------------------------------------------------------------------+
void CTabs::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Procesamiento del evento del desplazamiento del cursor
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      //--- Salir si el control está ocultado
      if(!CElement::IsVisible())
         return;
      //--- Coordenadas
      int x=(int)lparam;
      int y=(int)dparam;
      for(int i=0; i<m_tabs_total; i++)
         m_tabs[i].MouseFocus(x>m_tabs[i].X() && x<m_tabs[i].X2() && y>m_tabs[i].Y() && y<m_tabs[i].Y2());
      //---
      return;
     }
//--- Procesamiento del evento del clic izquierdo en el objeto
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- Clic en la pestaña
      if(OnClickTab(sparam))
         return;
     }
  }

Hemos analizado todos los métodos de la clase CTabs .Ahora vamos a probar cómo funciona todo eso. 

 


Prueba del control “Pestañas”

Para la prueba vamos a usar el Asesor Experto del artículo anterior dejando en su interfaz gráfica sólo el menú principal y la barra de estado. Hagamos que haya la posibilidad de probar rápidamente todos los modos de posicionamiento de las pestañas (arriba/abajo/a la derecha/a la izquierda), así como ajustar su tamaño (alto). Para eso, en el inicio del archivo con la clase personalizada Program.mqh añadimos los parámetros externos del EA:

//--- Parámetros externos del EA
input ENUM_TABS_POSITION TabsPosition =TABS_TOP; // Tabs Position
input                int TabsHeight   =20;       // Tabs Height

Luego, en la clase personalizada de la aplicación (CProgram), declaramos la instancia de la clase CTabs y el método para la creación del control “Pestañas” con los márgenes desde el punto extremo del formulario: 

class CProgram : public CWndEvents
  {
private:
   //--- Pestañas
   CTabs             m_tabs;
   //---
private:
   //--- Pestañas
#define TABS1_GAP_X           (4)
#define TABS1_GAP_Y           (45)
   bool              CreateTabs(void);
  };

Que haya cuatro pestañas en total. Para ajustar el texto a mostrar y el ancho de las pestañas, se puede usar la inicialización de los arrays. Los valores de sus elementos luego se pasan en el ciclo a través del método CTabs::AddTab(). El alto de las pestañas y su posicionamiento va a establecerse con los parámetros externos. Por defecto, (durante el primer inicio del programa en el gráfico) que la segunda pestaña (índice 1) se quede seleccionada. Abajo se muestra el código completo del método CProgram::CreateTabs(): 

//+------------------------------------------------------------------+
//| Crea el área con las pestañas                                    |
//+------------------------------------------------------------------+
bool CProgram::CreateTabs(void)
  {
#define TABS1_TOTAL 4
//--- Pasar el objeto del panel
   m_tabs.WindowPointer(m_window1);
//--- Coordenadas
   int x=m_window1.X()+TABS1_GAP_X;
   int y=m_window1.Y()+TABS1_GAP_Y;
//--- Arrays con el texto y el ancho para las pestañas
   string tabs_text[]={"Tab 1","Tab 2","Tab 3","Tab 4"};
   int tabs_width[]={90,90,90,90};
//--- Establecemos las propiedades antes de la creación
   m_tabs.XSize(596);
   m_tabs.YSize(243);
   m_tabs.TabYSize(TabsHeight);
   m_tabs.PositionMode(TabsPosition);
   m_tabs.SelectedTab((m_tabs.SelectedTab()==WRONG_VALUE) ? 1 : m_tabs.SelectedTab());
   m_tabs.AreaColor(clrWhite);
   m_tabs.TabBackColor(C'225,225,225');
   m_tabs.TabBackColorHover(C'240,240,240');
   m_tabs.TabBackColorSelected(clrWhite);
   m_tabs.TabBorderColor(clrSilver);
   m_tabs.TabTextColor(clrGray);
   m_tabs.TabTextColorSelected(clrBlack);
//--- Añadimos las pestañas con propiedades especificadas
   for(int i=0; i<TABS1_TOTAL; i++)
      m_tabs.AddTab(tabs_text[i],tabs_width[i]);
//--- Creamos el control
   if(!m_tabs.CreateTabs(m_chart_id,m_subwin,x,y))
      return(false);
//--- Añadimos el objeto al array común de los grupos de objetos
   CWndContainer::AddToElementsArray(0,m_tabs);
   return(true);
  }

La llamada al método debe realizarse en el método principal de la creación de la interfaz gráfica de la aplicación (véase la versión reducida del método más abajo): 

//+------------------------------------------------------------------+
//| Crea el panel del EA                                             |
//+------------------------------------------------------------------+
bool CProgram::CreateExpertPanel(void)
  {
//--- Creación del formulario 1 para los controles
//--- Creación de controles:
//    Menú principal
//--- Menús contextuales
//--- Pestañas
   if(!CreateTabs())
      return(false);
//--- Redibujar el gráfico
   m_chart.Redraw();
   return(true);
  }

Por favor, compile el programa y cárguelo en el gráfico. En los parámetros externos, vamos a cambiar los modos de posicionamiento de las pestañas de manea consecutiva (véase las capturas de pantalla de abajo):

 Fig. 2. Modo de posicionar las pestañas - “Arriba”.

Fig. 2. Modo de posicionar las pestañas - “Arriba”.

 Fig. 3. Modo de posicionar las pestañas - “Abajo”.

Fig. 3. Modo de posicionar las pestañas - “Abajo”.

 Fig. 4. Modo de posicionar las pestañas - “A la izquierda”.

Fig. 4. Modo de posicionar las pestañas - “A la izquierda”.

 Fig. 5. Modo de posicionar las pestañas - “A la derecha”.

Fig. 5. Modo de posicionar las pestañas - “A la derecha”.


Ahora vamos a probar cómo funciona eso con los grupos de controles adjuntos a cada pestaña. Para eso crearemos otra copia del mismo EA y eliminaremos de ahí los parámetros externos. Aquí las pestañas van a ubicarse en la parte superior (TABS_TOP) del área de trabajo. 

  1. A la primera pestaña le adjuntaremos la tabla a base de las etiquetas de texto. 
  2. La segunda pestaña va a incluir la tabla a base de los campos de edición. 
  3. A la tercera pestaña le adjuntaremos la tabla dibujada.
  4. La cuarta pestaña va a incluir el grupo de los siguientes controles:
    • cuatro casillas de verificación (checkbox);
    • cuatro checkbox con campos de edición;
    • cuatro listas combinadas (combobox) con casillas de verificación (checkbox);
    • una línea separadora. 

En la clase personalizada (CProgram) de la aplicación de prueba, declaramos (1) las instancias de las clases de estos controles, (2) métodos para su creación y (3) los márgenes desde el punto extremo del formulario  (véase el código de abajo): 

class CProgram : public CWndEvents
  {
private:
   //--- Tabla de las etiquetas de texto
   CLabelsTable      m_labels_table;
   //--- Tabla a base de los campos de edición
   CTable            m_table;
   //--- Tabla dibujada
   CCanvasTable      m_canvas_table;
   //--- Casillas de verificación (checkbox)
   CCheckBox         m_checkbox1;
   CCheckBox         m_checkbox2;
   CCheckBox         m_checkbox3;
   CCheckBox         m_checkbox4;
   //--- Casillas de verificación con campos de edición
   CCheckBoxEdit     m_checkboxedit1;
   CCheckBoxEdit     m_checkboxedit2;
   CCheckBoxEdit     m_checkboxedit3;
   CCheckBoxEdit     m_checkboxedit4;
   //--- Combobox con checkbox
   CCheckComboBox    m_checkcombobox1;
   CCheckComboBox    m_checkcombobox2;
   CCheckComboBox    m_checkcombobox3;
   CCheckComboBox    m_checkcombobox4;
   //--- Línea separadora
   CSeparateLine     m_sep_line;
   //---
private:
   //--- Tabla de las etiquetas de texto
#define TABLE1_GAP_X          (5)
#define TABLE1_GAP_Y          (65)
   bool              CreateLabelsTable(void);
   //--- Tabla a base de los campos de edición
#define TABLE2_GAP_X          (5)
#define TABLE2_GAP_Y          (65)
   bool              CreateTable(void);
   //--- Tabla dibujada
#define TABLE3_GAP_X          (5)
#define TABLE3_GAP_Y          (65)
   bool              CreateCanvasTable(void);
   //--- Línea separadora
#define SEP_LINE_GAP_X        (300)
#define SEP_LINE_GAP_Y        (70)
   bool              CreateSepLine(void);
   //--- Casillas de verificación (checkbox)
#define CHECKBOX1_GAP_X       (18)
#define CHECKBOX1_GAP_Y       (75)
   bool              CreateCheckBox1(const string text);
#define CHECKBOX2_GAP_X       (18)
#define CHECKBOX2_GAP_Y       (175)
   bool              CreateCheckBox2(const string text);
#define CHECKBOX3_GAP_X       (315)
#define CHECKBOX3_GAP_Y       (75)
   bool              CreateCheckBox3(const string text);
#define CHECKBOX4_GAP_X       (315)
#define CHECKBOX4_GAP_Y       (175)
   bool              CreateCheckBox4(const string text);
   //--- Casillas de verificación con campos de edición
#define CHECKBOXEDIT1_GAP_X   (40)
#define CHECKBOXEDIT1_GAP_Y   (105)
   bool              CreateCheckBoxEdit1(const string text);
#define CHECKBOXEDIT2_GAP_X   (40)
#define CHECKBOXEDIT2_GAP_Y   (135)
   bool              CreateCheckBoxEdit2(const string text);
#define CHECKBOXEDIT3_GAP_X   (337)
#define CHECKBOXEDIT3_GAP_Y   (105)
   bool              CreateCheckBoxEdit3(const string text);
#define CHECKBOXEDIT4_GAP_X   (337)
#define CHECKBOXEDIT4_GAP_Y   (135)
   bool              CreateCheckBoxEdit4(const string text);
   //--- Combobox con checkbox
#define CHECKCOMBOBOX1_GAP_X  (40)
#define CHECKCOMBOBOX1_GAP_Y  (205)
   bool              CreateCheckComboBox1(const string text);
#define CHECKCOMBOBOX2_GAP_X  (40)
#define CHECKCOMBOBOX2_GAP_Y  (235)
   bool              CreateCheckComboBox2(const string text);
#define CHECKCOMBOBOX3_GAP_X  (337)
#define CHECKCOMBOBOX3_GAP_Y  (205)
   bool              CreateCheckComboBox3(const string text);
#define CHECKCOMBOBOX4_GAP_X  (337)
#define CHECKCOMBOBOX4_GAP_Y  (235)
   bool              CreateCheckComboBox4(const string text);
  };

En los artículos anteriores, ya hemos mostrado varias veces cómo se crean los controles y cómo se adjuntan al formulario. Por eso aquí, como ejemplo, vamos a mostrar el código sólo de uno de estos métodos con el fin de demostrar cómo adjuntar el control a la pestaña. De ejemplo, será suficiente mostrar el control más simple de ellos (línea separadora). En el código de abajo, la línea donde se llama al método CTabs::AddToElementsArray() está marcada en amarillo. El índice de la pestaña a la que se adjunta el control se indica como el primer argumento, y aquí es el índice 3, es decir, la cuarta pestaña. El objeto del control que debe adjuntarse a la pestaña especificada se indica como el segundo argumento.

//+------------------------------------------------------------------+
//| Crea la línea separadora                                         |
//+------------------------------------------------------------------+
bool CProgram::CreateSepLine(void)
  {
//--- Guardamos el puntero a la ventana
   m_sep_line.WindowPointer(m_window1);
//--- Adjuntar a la cuarta pestaña del primer grupo de pestañas
   m_tabs.AddToElementsArray(3,m_sep_line);
//--- Coordenadas  
   int x=m_window1.X()+SEP_LINE_GAP_X;
   int y=m_window1.Y()+SEP_LINE_GAP_Y;
//--- Tamaños
   int x_size=2;
   int y_size=210;
//--- Establecemos las propiedades antes de la creación
   m_sep_line.DarkColor(C'213,223,229');
   m_sep_line.LightColor(clrWhite);
   m_sep_line.TypeSepLine(V_SEP_LINE);
//--- Creación del control
   if(!m_sep_line.CreateSeparateLine(m_chart_id,m_subwin,0,x,y,x_size,y_size))
      return(false);
//--- Añadimos el puntero al control a la base
   CWndContainer::AddToElementsArray(0,m_sep_line);
   return(true);
  }

Una vez creada la interfaz gráfica, es obligatorio llamar al método CTabs::ShowTabElements() con el fin de visualizar los controles sólo de la pestaña activa (véase la versión reducida del método más abajo). Si no lo hacemos, se mostrarán los controles de todas las pestañas. 

//+------------------------------------------------------------------+
//| Crea el panel del EA                                             |
//+------------------------------------------------------------------+
bool CProgram::CreateExpertPanel(void)
  {
//--- Creación del formulario 1 para los controles
//--- Creación de controles:
//--- Menú principal
//--- Menús contextuales
//--- Barra de estado
//--- Pestañas
//...
//--- Tabla de las etiquetas de texto
   if(!CreateLabelsTable())
      return(false);
//--- Tabla a base de los campos de edición
   if(!CreateTable())
      return(false);
//--- Creación de la tabla dibujada
   if(!CreateCanvasTable())
      return(false);
//--- Línea separadora
   if(!CreateSepLine())
      return(false);
//--- Casillas de verificación (checkbox)
   if(!CreateCheckBox1("Checkbox 1"))
      return(false);
   if(!CreateCheckBox2("Checkbox 2"))
      return(false);
   if(!CreateCheckBox3("Checkbox 3"))
      return(false);
   if(!CreateCheckBox4("Checkbox 4"))
      return(false);
//--- Casillas de verificación con campos de edición
   if(!CreateCheckBoxEdit1("Checkbox Edit 1:"))
      return(false);
   if(!CreateCheckBoxEdit2("Checkbox Edit 2:"))
      return(false);
   if(!CreateCheckBoxEdit3("Checkbox Edit 3:"))
      return(false);
   if(!CreateCheckBoxEdit4("Checkbox Edit 4:"))
      return(false);
//--- Combobox con checkbox
   if(!CreateCheckComboBox1("CheckCombobox 1:"))
      return(false);
   if(!CreateCheckComboBox2("CheckCombobox 2:"))
      return(false);
   if(!CreateCheckComboBox3("CheckCombobox 3:"))
      return(false);
   if(!CreateCheckComboBox4("CheckCombobox 4:"))
      return(false);
 //--- Mostrar los controles sólo de la pestaña activa
   m_tabs.ShowTabElements();
//--- Redibujar el gráfico
   m_chart.Redraw();
   return(true);
  }

En conclusión, debe obtener el resultado como en las capturas de pantalla de abajo:

 Fig. 6. Controles de la primera pestaña.

Fig. 6. Controles de la primera pestaña.

Fig. 7. Controles de la segunda pestaña. 

Fig. 7. Controles de la segunda pestaña.

 Fig. 8. Controles de la tercera pestaña.

Fig. 8. Controles de la tercera pestaña.

 Fig. 9. Controles de la cuarta pestaña.

Fig. 9. Controles de la cuarta pestaña.


Todo funciona perfectamente, y aquí ya podemos terminar la séptima parte de la serie sobre la librería para la creación de las interfaces gráficas. Como adición, en los archivos adjuntos a este artículo Usted puede descargar la clase del código (CIconTabs) para crear el control “Pestañas” con posibilidades ampliadas. A diferencia de la clase CTabs, en el control tipo CIconTabs se puede insertar los iconos (imágenes) para cada pestaña, lo que permitirá hacer la interfaz gráfica aún más comprensible para el usuario, en caso de necesidad. 

Usted puede colocar con precisión las imágenes y el texto a mostrar respecto al punto extremo de cada pestaña usando los métodos especiales (véase el código de abajo): 

//+------------------------------------------------------------------+
//| Clase para crear las pestañas con imágenes                       |
//+------------------------------------------------------------------+
class CIconTabs : public CElement
  {
private:
   //--- Márgenes del icono
   int               m_icon_x_gap;
   int               m_icon_y_gap;
   //--- Márgenes de la etiqueta de texto
   int               m_label_x_gap;
   int               m_label_y_gap;
   //---
public:
   //--- Márgenes del icono
   void              IconXGap(const int x_gap)                       { m_icon_x_gap=x_gap;            }
   void              IconYGap(const int y_gap)                       { m_icon_y_gap=y_gap;            }
   //--- Márgenes de la etiqueta de texto
   void              LabelXGap(const int x_gap)                      { m_label_x_gap=x_gap;           }
   void              LabelYGap(const int y_gap)                      { m_label_y_gap=y_gap;           }
  };

En la captura de pantalla de abajo se puede ver el ejemplo del control “Pestañas con imágenes”:

 Fig. 10. Control “Pestañas con imágenes”.

Fig. 10 Control “Pestañas con imágenes”.

Se puede descargar el código de este EA desde los archivos adjuntos al artículo.

 

 


Conclusión

En esta fase del desarrollo de la librería para la creación de las interfaces gráficas, su esquema tiene el siguiente aspecto:

 Fig. 11. Estructura de la librería en la fase actual del desarrollo.

Fig. 11. Estructura de la librería en la fase actual del desarrollo.

En la séptima parte de la serie sobre la creación de las interfaces gráficas en los terminales MetaTrader, hemos analizado los controles como las tablas y las pestañas. Para la creación de las tablas, hay tres clases (CLabelsTable, CTable y CCanvasTable), y para la creación de las pestañas, hay dos clases (CTabs y CIconTabs). 

En la siguiente (la octava) parte de la serie hablaremos de los siguientes controles.

  • Calendario estático y desplegable.
  • Lista jerárquica (lista en forma de árbol).
  • Explorador de archivos.

Más abajo puede descargar el material de la séptima parte de la serie para poder probar cómo funciona todo eso. Si le surgen algunas preguntas sobre el uso del material de estos archivos, puede dirigirse a la descripción detallada del proceso de desarrollo de la librería en uno de los artículos listados más abajo, o bien hacer su pregunta en los comentarios para el artículo.

Lista de artículos (capítulos) de la séptima parte:

Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/2503

Archivos adjuntos |
Interfaces gráficas VIII: Control "Calendario" (Capítulo 1) Interfaces gráficas VIII: Control "Calendario" (Capítulo 1)
En la octava parte de la serie sobre la creación de las interfaces gráficas en el entorno de los terminales de trading MetaTrader nosotros vamos a considerar los controles compuestos (complejos): calendarios, lista jerárquica (en forma de árbol), explorador de archivos. A cada uno de estos controles le dedicaremos un artículo personal, puesto que el contenido del material es bastante extenso. Pues bien, en el primer artículo de esta parte se describe el control “Calendario” y su versión ampliada, “Calendario desplegable”.
Interfaces gráficas VII: Control "Tablas" (Capítulo 1) Interfaces gráficas VII: Control "Tablas" (Capítulo 1)
En la séptima parte de la serie de los artículos sobre las interfaces gráficas en los terminales MetaTrader serán presentados tres tipos de tablas: tabla a base de las etiquetas de texto, tabla a base de los campos de edición y tabla dibujada. Otro control importante que se usa con bastante frecuencia son las pestañas a través de los cuales se puede ocultar o mostrar los grupos de otros controles. Eso permite al usuario diseñar las interfaces gráficas muy compactas en sus aplicaciones MQL.
Experto comercial universal: integración con los módulos estándar de señales de MetaTrader (parte 7) Experto comercial universal: integración con los módulos estándar de señales de MetaTrader (parte 7)
Esta parte está dedicada a la integración del motor comercial CStrategy con los módulos de señales incluidos en la biblioteca estándar de MetaTrader. El material describe los métodos de trabajo con las señales y la creación de estrategias de usuario basadas ellas.
Optimización propia de EA: algoritmos genéticos y evolutivos Optimización propia de EA: algoritmos genéticos y evolutivos
Este artículo cubre los principales principios establecidos en los algoritmos evolutivos, su variedad y características. Llevamos a cabo un experimento con un simple Asesor Experto utilizado como ejemplo para mostrar cómo nuestro sistema de trading se beneficia de la optimización. Consideramos los programas de software que implementan genética, evolutivos y de otros tipos de optimización y proporcionar ejemplos de aplicación cuando se optimiza un sistema predictor y los parámetros del sistema de trading.