English Русский 中文 Deutsch 日本語 Português
Interfaces gráficas X: Actualizaciones para la librería Easy And Fast (build 3)

Interfaces gráficas X: Actualizaciones para la librería Easy And Fast (build 3)

MetaTrader 5Ejemplos | 17 octubre 2016, 10:41
1 563 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 los artículos de cada parte se puede encontrar la lista de los capítulos con los enlaces, así como 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 este artículo se muestra la siguiente versión de la librería Easy And Fast (versión 3). Hemos corregido algunos fallos, así como hemos añadido nuevas posibilidades. Para más información, lea a continuación.

A partir de esta versión, la librería va a desarrollarse exclusivamente para la plataforma MetaTrader 5. Eso está relacionado con algunas diferencias principales de arquitectura y limitaciones en MetaTrader 4. No obstante, en caso de estricta necesidad de que el autor de la librería soporte su versión para la versión anterior de la plataforma, puede colocar el encargo personal en el servicio Freelance al nombre del autor, o bien encargarlo a cualquier otro desarrolladar que querrá y será capaz de ejecutar esta tarea. 

 

Lista de actualizaciones

1. Hasta este momento en las aplicaciones MQL para los artículos anteriores se demostraba cómo se podía implementar la interfaz gráfica en la subventana del indicador. Desde luego, este enfoque sería suficiente para algunas aplicaciones simples. Pero a algunos les gustaría tener la posibilidad de crear la interfaz gráfica en la subventana para el programa tipo «Asesor Experto». De esta manera, podríamos diseñar los paneles comerciales completamente funcionales y que estarían totalmente separados de la ventana principal del gráfico. El trabajo en este modo va a ser más cómodo: es decir, el gráfico de precios y algunos datos importantes sobre él siempre se quedarían abiertos sin que la interfaz gráfica de la aplicación los estorbe o tape. A continuación describiremos con más detalles cómo ha sido implementado eso en el entorno de la librería Easy And Fast.

La tarea consistía en que la activación del modo «Asesor Experto» se realizara mediante la conexión del recurso con el «indicador vacío» (SubWindow.ex5) al archivo principal de la aplicación MQL. El indicador vacío es simplemente una subventana sin la parte de cálculo de las series. Puesto que por ahora no es posible averiguar con los medios de MQL si el indicador está incluido como recurso, insertaremos temporalmente la directiva con el identificador de la constante EXPERT_IN_SUBWINDOW en el archivo Defines.mqh. Es necesario para que en el registro no aparezca el mensaje sobre el error que surge al intentar obtener el manejador (handle) del indicador que no está disponible según el directorio especificado.

Por defecto, se establece el valor false, o sea el modo está desactivado y la interfaz gráfica va a crearse en la ventana principal del gráfico (consulte el código de abajo).

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//--- Modo «EA en la ventana»
#define EXPERT_IN_SUBWINDOW false
...

Si necesitamos crear la interfaz gráfica en la subventana, hay que poner el valor true e conectar el recurso con el «indicador vacío» con el archivo principal de la aplicación MQL

//+------------------------------------------------------------------+
//|                                                TestLibrary01.mq5 |
//|                        Copyright 2016, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2016, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
//--- Conexión del indicador para el modo «EA en la subventana»
#resource \\Indicators\\SubWindow.ex5
...

Ahora veremos qué es lo que representa el indicador SubWindow.ex5 y de qué manera está organizada la conexión entre él y el EA. Abajo se muestra el código completo del indicador SubWindow.ex5. En las propiedades del programa se indica que este indicador es una subventana del gráfico, el número de sus búfers y series es igual a cero, el máximo y el mínimo de la escala vertical también es igual a cero. 

El indicador y el EA van a intercambiar los mensajes uno con otro. Es que el indicador «no sabe» qué modos están seleccionados en el EA para la construcción de la interfaz gráfica. Hay que enviarle un mensaje si el desarrollador de la aplicación ha decidido hacer la subventana de altura fija, así como el valor de la altura en píxeles. Para la creación del mensaje vamos a necesitar otro identificador de evento, ON_SUBWINDOW_CHANGE_HEIGHT. Este identificador también debe ubicarse en el archivo Defines.mqh para que esté disponible en las aplicaciones donde se utiliza la librería para la creación de las interfaces gráficas. 

Para comprender cuándo el EA tiene que generar el evento para el cambio del alto de la ventana, tras las inicialización exitosa del indicador durante la primera llamada automática a la función ::OnCalculate(), cuando el argumento prev_calculated es igual a cero, vamos a enviar el mensaje para el EA. Para conseguir la identificación unívoca de que el mensaje haya llegado del indicador SubWindow.ex5, aparte del identificador del evento ON_SUBWINDOW_CHANGE_HEIGHT va a enviarse el nombre del programa como parámetro string (sparam). El seguimiento del mensaje va a realizarse en el manejador de la clase CWindow. Si ahí todas las condiciones se cumplen, al indicador se le manda la respuesta con el mismo (1) identificador (ON_SUBWINDOW_CHANGE_HEIGHT), (2) el valor de la altura en el parámetro long del evento (lparam) y (3) el nombre del EA. Cuando este mensaje llegue, será procesado en la función ::OnChartEvent(). Tras la verificación del nombre, el alto de la subventana se establece según el valor transferido

//+------------------------------------------------------------------+
//|                                                    SubWindow.mq5 |
//|                        Copyright 2016, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2016, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property indicator_separate_window
#property indicator_plots   0
#property indicator_buffers 0
#property indicator_minimum 0.0
#property indicator_maximum 0.0
//--- Nombre del programa
#define PROGRAM_NAME ::MQLInfoString(MQL_PROGRAM_NAME)
//--- Identificador del evento para el cambio del alto de la subventana del EA
#define ON_SUBWINDOW_CHANGE_HEIGHT (25)
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit(void)
  {
//--- Nombre breve del indicador
   ::IndicatorSetString(INDICATOR_SHORTNAME,PROGRAM_NAME);
//--- Inicialización con éxito
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Deinicialización                                                  |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int    rates_total,
                const int    prev_calculated,
                const int    begin,
                const double &price[])
  {
//--- Si la inicialización ha pasado con éxito
   if(prev_calculated<1)
      //--- Enviamos el mensaje al EA para recibir de él el tamaño para la subventana
      ::EventChartCustom(0,ON_SUBWINDOW_CHANGE_HEIGHT,0,0.0,PROGRAM_NAME);
//---
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int    id,
                  const long   &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Procesamiento del evento del cambio del alto de la subventana del EA
   if(id==CHARTEVENT_CUSTOM+ON_SUBWINDOW_CHANGE_HEIGHT)
     {
      //--- Recibir los mensajes sólo del nombre del EA
      if(sparam==PROGRAM_NAME)
         return;
      //--- Cambiar el alto de la subventana
      ::IndicatorSetInteger(INDICATOR_HEIGHT,(int)lparam);
     }
  }
//+------------------------------------------------------------------+

Además, al manejador de eventos de la clase CWindow hay que añadirle el bloque del código, tal como se muestra más abajo. Cuando llegue el evento con el identificador ON_SUBWINDOW_CHANGE_HEIGHT, hay que realizar una serie de comprobaciones. El programa saldrá del método si:

  • el mensaje ha sido enviado al indicador por el EA;
  • este programa no es un EA;
  • no ha sido establecido el modo del alto fijo de la subventana del EA 
//+------------------------------------------------------------------+
//| Manejador de eventos del gráfico                                       |
//+------------------------------------------------------------------+
void CWindow::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
...
//--- Procesamiento del evento del cambio del alto de la subventana del EA
   if(id==CHARTEVENT_CUSTOM+ON_SUBWINDOW_CHANGE_HEIGHT)
     {
      //--- Salir si este mensaje ha llegado del EA
      if(sparam==PROGRAM_NAME)
         return;
      //--- Salir si este programa no es un EA
      if(CElement::ProgramType()!=PROGRAM_EXPERT)
         return;
      //--- Salir si no ha sido establecido el modo del alto fijo de la subventana
      if(!m_height_subwindow_mode)
         return;
      //--- Calcular y cambiar el alto de la subventana
      m_subwindow_height=(m_is_minimized)? m_caption_height+3 : m_bg_full_height+3;
      ChangeSubwindowHeight(m_subwindow_height);
      return;
     }
  }

Si todas las comprobaciones han sido superadas, se calcula el alto de la subventana tomando en cuenta el estado actual de la ventana (minimizada/maximizada) y luego se llama el método CWindow::ChangeSubwindowHeight() en el que también ha sido introducida una pequeña adición (ver el siguiente código), cuyo sentido es el siguiente: si el tipo del programa es «Asesor Experto», entonces se genera el mensaje para el indicador SubWindow.ex5

//+------------------------------------------------------------------+
//| Cambia el alto de la subventana del indicador                               |
//+------------------------------------------------------------------+
void CWindow::ChangeSubwindowHeight(const int height)
  {
//--- Si la interfaz gráfica no se encuentra en la subventana o el programa es del tipo «Script»
   if(CElement::m_subwin<=0 || CElement::m_program_type==PROGRAM_SCRIPT)
      return;
//--- Si hay que cambiar el alto de la subventana
0.
     {
      //--- Si es indicador
      if(CElement::m_program_type==PROGRAM_INDICATOR)
        {
         if(!::IndicatorSetInteger(INDICATOR_HEIGHT,height))
            ::Print(__FUNCTION__," > ¡Fallo al cambiar el alto de la subventana del indicador! Número del error: ",::GetLastError());
        }
      //--- Si es Asesor Experto
      else
        {
         //--- Enviar el mensaje al indicador SubWindow.ex5 sobre la necesidad de cambiar los tamaños de la ventana
         ::EventChartCustom(m_chart_id,ON_SUBWINDOW_CHANGE_HEIGHT,(long)height,0,PROGRAM_NAME);
        }
     }
  }

También habrá que introducir algunas adiciones en el motor de la librería (clase CWndEvents) para poder determinar, comprobar y corregir el número de la subventana en la que va a ubicarse la interfaz gráfica de la aplicación MQL tipo «Asesor Experto». Es necesario añadir el bloque del código para el modo «EA en la subventana» en el método CWndEvents::DetermineSubwindow(). Abajo se muestra la versión reducida de este método. La entrada en el bloque se realiza bajo la condición de que el tipo del programa sea «Asesor Experto». Luego se comprueba si el modo «EA en la subventana» está activado. Si esta activado, obtenemos el manejador del indicador SubWindow.ex5 y si no ha surgido ningún problema con eso, primero determinamos el número actual de las subventanas en la ventana del gráfico. Con el valor obtenido se determina el número de la subventana del indicador SubWindow.ex5, cuya colocación se realiza mediante la función ::ChartIndicatorAdd(). Luego, si no han surgido problemas con la colocación de la subventana, se guarda (1) el número de la subventana del EA, (2) el número actual de las subventanas existentes y (3) el nombre breve del indicador SubWindow.ex5.  

//+------------------------------------------------------------------+
//| Clase para procesar los eventos                                      |
//+------------------------------------------------------------------+
class CWndEvents : public CWndContainer
  {
protected:
   //--- Manejador de la subventana del EA
   int               m_subwindow_handle;
   //--- Nombre de la subventana del EA
   string            m_subwindow_shortname;
   //--- Número de subventanas en el gráfico tras la colocación de la subventana del EA
   int               m_subwindows_total;
  };
//+------------------------------------------------------------------+
//| Determinar el número de la subventana                                       |
//+------------------------------------------------------------------+
void CWndEvents::DetermineSubwindow(void)
  {
//--- Salimos si el tipo del programa es «Script»
//--- Resetear el último error

//--- Si el tipo del programa es EA
   if(PROGRAM_TYPE==PROGRAM_EXPERT)
     {
      //--- Salir si necesitamos tener la interfaz gráfica del EA en la ventana principal
      if(!EXPERT_IN_SUBWINDOW)
         return;
      //--- Obtenemos el manejador del «indicador vacío» (ventana vacía)
      m_subwindow_handle=iCustom(Symbol(),Period(),"::Indicators\\SubWindow.ex5");
      //--- Si este indicador no está presente, avisar sobre el error en el registro
      if(m_subwindow_handle==INVALID_HANDLE)
         ::Print(__FUNCTION__," > Error al obtener el manejador del indicador en el directorio ::Indicators\\SubWindow.ex5 !");
      //--- Si el manejador ha sido obtenido, eso significa que el indicador existe, está conectado a la aplicación,
      //    y eso a su vez quiere decir que la interfaz gráfica de la aplicación debe pasar a la subventana.
      else
        {
         //--- Obtenemos el número de subventanas en el gráfico
         int subwindows_total=(int)::ChartGetInteger(m_chart_id,CHART_WINDOWS_TOTAL);
         //--- Colocamos la subventana para la interfaz gráfica del EA
         if(::ChartIndicatorAdd(m_chart_id,subwindows_total,m_subwindow_handle))
           {
            //--- Guardamos el número de subventana y el número total de subventanas en el gráfico
            m_subwin           =subwindows_total;
            m_subwindows_total =subwindows_total+1;
            //--- Obtenemos y guardamos el nombre breve de la subventana del EA
            m_subwindow_shortname=::ChartIndicatorName(m_chart_id,m_subwin,0);
           }
         //--- Si la subventana no ha sido colocada
         else
            ::Print(__FUNCTION__," > ¡Fallo al colocar la subventana del EA! Número del error: ",::GetLastError());
        }
      //---
      return;
     }
//--- Determinar el número de la ventana del indicador
//--- Si no se ha podido determinar el número, salimos
//--- Si no es la ventana principal del gráfico
...
  }

Además, al añadir o eliminar otros indicadores, es necesario cuidar de que el número de la subventana del EA se corrija para el correcto funcionamiento de su interfaz gráfica. Aparte de eso, hagamos que si el usuario elimina la subventana del EA, éste también se elimine, dejando previamente el mensaje sobre la razón de la eliminación desde el gráfico en el registro en la pestaña «Asesores Expertos» de la ventana «Caja de herramientas». Para eso ha sido implementado el método CWndEvents::CheckExpertSubwindowNumber(). La entrada en este método se realiza bajo la condición de que el programa tenga el tipo «Asesor Experto». Si la comprobación ha sido superada, obtenemos el número de subventanas en la ventana del gráfico. Si resulta que el número de subventanas no ha cambiado, el programa sale del método .

Luego en el ciclo se busca la subventana del EA, y si existe, se comprueba si ha cambiado el número de su subventana. La razón que puede provocar el cambio del número de la subventana puede ser la adición o eliminación de algún indicador que también se ubicaba en su subventana. Si el número de la subventana ha cambiado, hay que guardar su número y actualizar el valor en todos los controles de la ventana principal

Si la subventana no ha sido encontrada, la única razón es que ha sido eliminada. En este caso, el EA también se elimina desde el gráfico

class CWndEvents : public CWndContainer
  {
private:
  //--- Comprobación y actualización del número de la ventana del EA
   void              CheckExpertSubwindowNumber(void);
  };
//+------------------------------------------------------------------+
//| Comprobación y actualización del número de la ventana del EA                       |
//+------------------------------------------------------------------+
void CWndEvents::CheckExpertSubwindowNumber(void)
  {
//--- Salir si no es un EA
   if(PROGRAM_TYPE!=PROGRAM_EXPERT)
      return;
//--- Obtenemos el número de subventanas en el gráfico
   int subwindows_total=(int)::ChartGetInteger(m_chart_id,CHART_WINDOWS_TOTAL);
//--- Salir si el número de subventanas y el número de indicadores no ha cambiado
   if(subwindows_total==m_subwindows_total)
      return;
//--- Guardamos el número actual de subventanas
   m_subwindows_total=subwindows_total;
//--- Para comprobar la presencia de la subventana del EA
   bool is_subwindow=false;
//--- Buscamos la subventana del EA
   for(int sw=0; sw<subwindows_total; sw++)
     {
      //--- Detener el ciclo si la subventana del EA existe
      if(is_subwindow)
         break;
      //--- Cuántos indicadores contiene esta ventana/subventana
      int indicators_total=::ChartIndicatorsTotal(m_chart_id,sw);
      //--- Repasamos todos los indicadores en la ventana 
      for(int i=0; i<indicators_total; i++)
        {
         //--- Obtenemos el nombre breve del indicador
         string indicator_name=::ChartIndicatorName(m_chart_id,sw,i);
         //--- Si no es la subventana del EA, ir al siguiente
         if(indicator_name!=m_subwindow_shortname)
            continue;
        //--- Anotamos que la subventana del EA existe
         is_subwindow=true;
         //--- Si el número de la subventana ha cambiado, 
         //    hay que guardar el número nuevo en todos los controles del formulario principal
         if(sw!=m_subwin)
           {
            //--- Guardamos el número de la subventana
            m_subwin=sw;
          //   También lo guardamos en todos los controles del formulario principal de la interfaz
            int elements_total=CWndContainer::ElementsTotal(0);
            for(int e=0; e<elements_total; e++)
               m_wnd[0].m_elements[e].SubwindowNumber(m_subwin);
           }
         //---
         break;
        }
     }
//--- Si la subventana del EA no ha sido encontrada, eliminamos el EA
   if(!is_subwindow)
     {
      ::Print(__FUNCTION__," > ¡La eliminación de la subventana del EA provoca la eliminación del EA!");
      //--- Eliminación del Asesor Experto del gráfico
      ::ExpertRemove();
     }
  }

 

2. En la versión anterior de la librería hemos añadido la posibilidad de activar el modo para el cambio automático del ancho del formulario. Vamos a añadir la misma posibilidad para cambiar su alto. Ahora no nos vale el identificador del evento del cambio de los tamaños del formulario ON_WINDOW_CHANGE_SIZE para solucionar este problema, puesto que los cambios de los tamaños por el alto y por el ancho van a procesarse como eventos separados. Por eso en el archivo Defines.mqh en vez del identificador ON_WINDOW_CHANGE_SIZE ahora van a haber dos identificadores diferentes tal como se muestra en el código de abajo. El reemplazo correspondiente del identificador ha sido realizado en otros archivos de la librería.  

#define ON_WINDOW_CHANGE_XSIZE     (3)  // Cambio del tamaño de la ventana por el eje X
#define ON_WINDOW_CHANGE_YSIZE     (4)  // Cambio del tamaño de la ventana por el eje Y

Para cambiar el alto del formulario, añadimos el método CWindow::ChangeWindowHeight(). Cuando se llama a este método, después del cambio de los tamaños del formulario se genera el mensaje sobre la acción realizada

//+------------------------------------------------------------------+
//| Clase de creación del formulario para los controles                    |
//+------------------------------------------------------------------+
class CWindow : public CElement
  {
public:
   //--- Gestión de tamaños
   void              ChangeWindowHeight(const int height);
  };
//+------------------------------------------------------------------+
//| Cambia el alto de la ventana                                             |
//+------------------------------------------------------------------+
void CWindow::ChangeWindowHeight(const int height)
  {
//--- Si el alto no ha cambiado, salimos
   if(height==m_bg.YSize())
      return;
//--- Salir si la ventana está minimizada
   if(m_is_minimized)
      return;
//--- Actualizamos el alto para el fondo
   CElement::YSize(height);
   m_bg.YSize(height);
   m_bg.Y_Size(height);
   m_bg_full_height=height;
//--- Mensaje sobre el cambio de los tamaños de la ventana
   ::EventChartCustom(m_chart_id,ON_WINDOW_CHANGE_YSIZE,(long)CElement::Id(),0,"");
  }

Para que la altura del formulario cambie automáticamente, el usuario-desarrollador de la aplicación MQL debe establecer el modo correspondiente usando el método CElement::AutoYResizeMode(): 

//+------------------------------------------------------------------+
//| Clase base del control                                |
//+------------------------------------------------------------------+
class CElement
  {
protected:
   //--- Modo del cambio automático de los tamaños del control
   bool              m_auto_yresize_mode;
   //---
public:
   //--- (1) Modo del cambio automático del alto del control
   bool              AutoYResizeMode(void)                     const { return(m_auto_yresize_mode);          }
   void              AutoYResizeMode(const bool flag)                { m_auto_yresize_mode=flag;             }
  };

Ahora si los modos del cambio automático de los tamaños del formulario están activados, entonces cuando se cambian los tamaños de la ventana del gráfico, en el manejador de eventos del formulario se corrigen los tamaños del formulario según el evento CHARTEVENT_CHART_CHANGE. Eso va a funcionar tanto en la subventana del indicador, como en la ventana principal del gráfico. Usted puede activar uno de los modos, o los dos a la vez. 

//--- Evento del cambio de propiedades del gráfico
   if(id==CHARTEVENT_CHART_CHANGE)
     {
      //--- Si el botón está suelto
      if(m_clamping_area_mouse==NOT_PRESSED)
        {
         //--- Obtenemos las dimensiones de la ventana del gráfico
         SetWindowProperties();
         //--- Corrección de coordenadas
         UpdateWindowXY(m_x,m_y);
        }
      //--- Si el modo está activado, cambiar el alto
      if(CElement::AutoXResizeMode())
         ChangeWindowWidth(m_chart.WidthInPixels()-2);
      //--- Si el modo está activado, cambiar el alto
      if(CElement::AutoYResizeMode())
         ChangeWindowHeight(m_chart.HeightInPixels(m_subwin)-3);
      //---
      return;
     }

 

3. El modo del cambio automático de la altura también es válido para todos los demás controles de la librería. Sin embargo, en la versión actual este concepto va a referirse sólo a los controles listados a continuación:

  • CTabs – pestañas simples.
  • CIconTabs – pestañas con imágenes.
  • CCanvasTable – tabla dibujada.
  • CLineGraph – gráfico lineal.

Igual que antes, a la clase CElement ha sido añadido el método virtual CElement::ChangeWidthByRightWindowSide() para cambiar el ancho del control, vamos a añadir el método del cambio de la altura. Además de eso, vamos a crear los métodos para establecer el margen desde el borde inferior del formulario igual que lo hemos hecho antes para el margen desde el borde derecho del formulario para el cambio del ancho. 

class CElement
  {
protected:
   //--- Margen desde el borde derecho/inferior en modo del cambio automático del ancho/alto del control
   int               m_auto_xresize_right_offset;
   int               m_auto_yresize_bottom_offset;
   //---
public:
   //--- Obtener/establecer el margen desde el borde inferior del formulario
   int               AutoYResizeBottomOffset(void)             const { return(m_auto_yresize_bottom_offset); }
   void              AutoYResizeBottomOffset(const int offset)       { m_auto_yresize_bottom_offset=offset;  }
   //---
public:
   //--- Cambiar el alto por el borde inferior de la ventana
   virtual void      ChangeHeightByBottomWindowSide(void) {}
  };

Cada control tendrá su propia implementación del método ChangeWidthByRightWindowSide() tomando en cuenta sus modos y particularidades únicas. Como ejemplo abajo mostramos el código de este método en la clase CCanvasTable

//+------------------------------------------------------------------+
//| Cambiar el alto por el borde inferior de la ventana                             |
//+------------------------------------------------------------------+
void CCanvasTable::ChangeHeightByBottomWindowSide(void)
  {
//--- Salir si el modo del anclaje al borde inferior del formulario está activado  
   if(m_anchor_bottom_window_side)
     return;
//--- Coordenadas
   int y=0;
//--- Tamaños
   int x_size=(m_auto_xresize_mode)? m_wnd.X2()-m_area.X()-m_auto_xresize_right_offset : m_x_size;
   int y_size=m_wnd.Y2()-m_area.Y()-m_auto_yresize_bottom_offset;
//--- Salir si el tamaño es menor que el especificado
   if(y_size<60)
     return;
//--- Establecer nuevo tamaño para el fondo de la tabla
   ChangeMainSize(x_size,y_size);
//--- Calcular los tamaños de la tabla
   CalculateTableSize();
//--- Comprobar la presencia de las barras de desplazamiento
   bool is_scrollh=!(m_table_visible_x_size>=m_table_x_size);
   bool is_scrollv=!(m_table_visible_y_size>=m_table_y_size);
//--- Margen respecto a la presencia de las barras de desplazamiento
   int offset=(is_scrollh || (!is_scrollh && !is_scrollv))? 0 : 2;
//--- Calcular y establecer nueva coordenada para la barra horizontal de desplazamiento
   y=m_area.Y2()-m_scrollh.ScrollWidth();
   m_scrollh.YDistance(y);
//--- Inicialización de la barra horizontal para el tamaño nuevo
   m_scrollh.Reinit(m_table_x_size,m_table_visible_x_size);
//--- Calcular y cambiar el alto de la barra vertical de desplazamiento
   m_scrollv.Reinit(m_table_y_size,m_table_visible_y_size-offset);
   m_scrollv.ChangeYSize((is_scrollh)? m_table_visible_y_size+2 : m_table_visible_y_size);
//--- Si la barra de desplazamiento vertical no es necesaria
   if(!is_scrollv)
     {
      //--- Ocultar la barra de desplazamiento vertical
      m_scrollv.Hide();
      //--- Cambiar el ancho de la barra de desplazamiento horizontal
      m_scrollh.ChangeXSize(m_area.XSize());
     }
   else
     {
      //--- Mostrar la barra de desplazamiento vertical
      if(CElement::IsVisible())
         m_scrollv.Show();
      //--- Calcular y modificar el ancho de la barra de desplazamiento horizontal
      m_scrollh.ChangeXSize(m_area.XSize()-m_scrollv.ScrollWidth()+1);
     }
//--- Gestionar la visibilidad de la barra de desplazamiento horizontal
   if(CElement::IsVisible())
     {
      if(!is_scrollh) m_scrollh.Hide();
      else m_scrollh.Show();
     }
//--- Establecer nuevo tamaño de la tabla
   ChangeTableSize(m_table_x_size,m_table_y_size,m_table_visible_x_size,m_table_visible_y_size-offset);
//--- Dibujar la tabla
   DrawTable();
//--- Actualizar la posición de los objetos
   Moving(m_wnd.X(),m_wnd.Y());
  }

Para que todo eso empiece a funcionar, hay que realizar algunos cambios y adiciones en la clase CWndEvents. Puesto que ahora los eventos del cambio de tamaños (ancho y alto) se generan usando los identificadores únicos, hacen falta dos métodos separados para su procesamiento. 

//+------------------------------------------------------------------+
//| Clase para procesar los eventos                                      |
//+------------------------------------------------------------------+
class CWndEvents : public CWndContainer
  {
private:
   //--- Procesamiento del cambio del tamaño de la ventana
   bool              OnWindowChangeXSize(void);
   bool              OnWindowChangeYSize(void);
  };
//+------------------------------------------------------------------+
//| Evento ON_WINDOW_CHANGE_XSIZE                                   |
//+------------------------------------------------------------------+
bool CWndEvents::OnWindowChangeXSize(void)
  {
//--- Hay señal «Cambiar el tamaño de los controles»
   if(m_id!=CHARTEVENT_CUSTOM+ON_WINDOW_CHANGE_XSIZE)
      return(false);
//--- Índice de la ventana activa
   int awi=m_active_window_index;
//--- Si los identificadores de la ventana coinciden
   if(m_lparam!=m_windows[awi].Id())
      return(true);
//--- Cambiar el ancho de todos los controles, excepto el formulario
   int elements_total=CWndContainer::ElementsTotal(awi);
   for(int e=0; e<elements_total; e++)
     {
      //--- Si es la ventana, ir a la siguiente
      if(m_wnd[awi].m_elements[e].ClassName()=="CWindow")
         continue;
      //--- Si el modo está activado, ajustar el ancho
      if(m_wnd[awi].m_elements[e].AutoXResizeMode())
         m_wnd[awi].m_elements[e].ChangeWidthByRightWindowSide();
     }
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Evento ON_WINDOW_CHANGE_YSIZE                                   |
//+------------------------------------------------------------------+
bool CWndEvents::OnWindowChangeYSize(void)
  {
//--- Hay señal «Cambiar el tamaño de los controles»
   if(m_id!=CHARTEVENT_CUSTOM+ON_WINDOW_CHANGE_YSIZE)
      return(false);
//--- Índice de la ventana activa
   int awi=m_active_window_index;
//--- Si los identificadores de la ventana coinciden
   if(m_lparam!=m_windows[awi].Id())
      return(true);
//--- Cambiar el ancho de todos los controles, excepto el formulario
   int elements_total=CWndContainer::ElementsTotal(awi);
   for(int e=0; e<elements_total; e++)
     {
      //--- Si es la ventana, ir a la siguiente
      if(m_wnd[awi].m_elements[e].ClassName()=="CWindow")
         continue;
      //--- Si el modo está activado, ajustar el alto
      if(m_wnd[awi].m_elements[e].AutoYResizeMode())
         m_wnd[awi].m_elements[e].ChangeHeightByBottomWindowSide();
     }
//---
   return(true);
  }

La llamada a los métodos CWndEvents::OnWindowChangeXSize() y CWndEvents::OnWindowChangeYSize() se realiza en el método común CWndEvents::ChartEventCustom() para el procesamiento de los eventos de usuario. Abajo se muestra la versión reducida de este método. 

//+------------------------------------------------------------------+
//| Evento CHARTEVENT_CUSTOM                                        |
//+------------------------------------------------------------------+
void CWndEvents::ChartEventCustom(void)
  {
//--- Si hay la señal para minimizar el formulario
//--- Si hay la señal para maximizar el formulario

//--- Si hay señal para cambiar el tamaño de los controles por el eje X
   if(OnWindowChangeXSize())
      return;
//--- Si hay señal para cambiar el tamaño de los controles por el eje Y
   if(OnWindowChangeYSize())
      return;

//--- Si hay señal para ocultar los menús contextuales desde el elemento iniciador
//--- Si hay señal para cerrar todos los menús contextuales

//--- Si hay la señal para abrir la ventana de diálogo
//--- Si hay la señal para cerrar la ventana de diálogo
//--- Si hay señal para resetear el color de los elementos en el formulario especificado
//--- Si hay señal para resetear las prioridades para el clic izquierdo del ratón
//--- Si hay señal para recuperar las prioridades para el clic izquierdo del ratón
  }

Ahora al cambiar los tamaños de la ventana del gráfico, si los modos del cambio de los tamaños del formulario y de los controles especificados están activados, sus tamaños también van a cambiarse automáticamente. 

En las capturas de pantalla se muestra el ejemplo de la interfaz gráfica de la aplicación MQL que ha sido creado en la ventana principal del gráfico. Para la ventana de la aplicación (CWindow) han sido establecidos los modos del cambio automático del ancho y alto según el tamaño de la ventana del gráfico. Para los controles «Menú principal» (CMenuBar) y «Barra de estado» (CStatusBar) ha sido activado el modo del cambio automático del ancho según el ancho de la ventana de la aplicación MQL. Para los controles «Pestañas» (CTabs) y «Tabla dibujada» (CCanvasTable) han sido establecidos los modos del cambio automático del ancho y del alto según el tamaño del formulario, y han sido especificados los márgenes desde el borde inferior de la aplicación MQL.

Fig. 1. Tamaños mínimos de la ventana del terminal. La interfaz gráfica de la aplicación MQL con los modos del cambio automático de los tamaños en estado activado. 

Fig. 1. Tamaños mínimos de la ventana del terminal. La interfaz gráfica de la aplicación MQL con los modos del cambio automático de los tamaños en estado activado.


Si cambiamos el tamaño de la ventana del terminal teniendo activados los modos arriba mencionados, los tamaños de la interfaz gráfica de la aplicación MQL también se cambiarán de manera correspondiente.

Fig. 2. Al cambiar los tamaños de la ventana del terminal, los tamaños de la interfaz gráfica de la aplicación MQL también van a cambiar. 

Fig. 2. Al cambiar los tamaños de la ventana del terminal, los tamaños de la interfaz gráfica de la aplicación MQL también van a cambiar.


4. Durante el diseño de la la interfaz gráfica en la que los tamaños del formulario pueden cambiar, muy a menudo podemos necesitar que los controles se posiciones en la parte derecha o inferior de la ventana de la aplicación. Antes, para eso había la posibilidad de indicar simplemente las coordenadas para el control respecto al punto superior izquierdo del formulario al que éste se adjunta. Ahora se puede seleccionar entre cuatro opciones para posicionar el control respecto al formulario:

  • Arriba y a la izquierda.
  • Arriba y a la derecha.
  • Abajo y a la derecha.
  • Abajo y a la izquierda.

Para realizar lo planteado, vamos a añadir a la clase base de los controles (CElement) dos métodos para establecer/obtener los modos de posicionamiento del control en la parte inferior y la parte derecha del formulario: 

class CElement
  {
protected:
   //--- Puntos de anclaje del control en la parte derecha e inferior de la ventana
   bool              m_anchor_right_window_side;
   bool              m_anchor_bottom_window_side;
   //---
public:
   //--- Modo (obtener/establecer) de anclaje del control al (1) borde derecho e (2) izquierdo de la ventana
   bool              AnchorRightWindowSide(void)               const { return(m_anchor_right_window_side);   }
   void              AnchorRightWindowSide(const bool flag)          { m_anchor_right_window_side=flag;      }
   bool              AnchorBottomWindowSide(void)              const { return(m_anchor_bottom_window_side);  }
   void              AnchorBottomWindowSide(const bool flag)         { m_anchor_bottom_window_side=flag;     }
  };

Para que todo funcione de acuerdo con estos modos, han sido introducidas modificaciones en todas las clases de los controles de la librería. Como ejemplo, mostraremos el código del método de creación de la etiqueta de texto para el control «Casilla de verificación» (CCheckBox). Preste atención en las líneas donde se calculan las coordenadas y los márgenes para el objeto gráfico. 

//+------------------------------------------------------------------+
//| Crea la etiqueta de texto del checkbox                                 |
//+------------------------------------------------------------------+
bool CCheckBox::CreateLabel(void)
  {
//--- Formación del nombre del objeto
   string name=CElement::ProgramName()+"_checkbox_lable_"+(string)CElement::Id();
//--- Coordenadas
   int x =(m_anchor_right_window_side)? m_x-m_label_x_gap : m_x+m_label_x_gap;
   int y =(m_anchor_bottom_window_side)? m_y-m_label_y_gap : m_y+m_label_y_gap;
//--- Color del texto dependiendo del estado
   color label_color=(m_check_button_state) ? m_label_color : m_label_color_off;
//--- Establecemos el objeto
   if(!m_label.Create(m_chart_id,name,m_subwin,x,y))
      return(false);
//--- Establecemos las propiedades
   m_label.Description(m_label_text);
   m_label.Font(FONT);
   m_label.FontSize(FONT_SIZE);
   m_label.Color(label_color);
   m_label.Corner(m_corner);
   m_label.Anchor(m_anchor);
   m_label.Selectable(false);
   m_label.Z_Order(m_zorder);
   m_label.Tooltip("\n");
//--- Márgenes desde el punto extremo
   m_label.XGap((m_anchor_right_window_side)? x : x-m_wnd.X());
   m_label.YGap((m_anchor_bottom_window_side)? y : y-m_wnd.Y());
//--- Inicialización del array del gradiente
   CElement::InitColorArray(label_color,m_label_color_hover,m_label_color_array);
//--- Guardamos el puntero del objeto
   CElement::AddToArray(m_label);
   return(true);
  }

Ahora los modos de posicionamiento de los controles también se toman en cuenta en los métodos Moving() de todos los controles de la librería. Como ejemplo, abajo se muestra el código del método CCheckBox::Moving(): 

//+------------------------------------------------------------------+
//| Desplazamiento de controles                                            |
//+------------------------------------------------------------------+
void CCheckBox::Moving(const int x,const int y)
  {
//--- Salir si el control está ocultado
   if(!CElement::IsVisible())
      return;
//--- Si el anclaje está a la derecha
   if(m_anchor_right_window_side)
     {
      //--- Guardar las coordenadas en los campos del control
      CElement::X(m_wnd.X2()-XGap());
      //--- Guardar las coordenadas en los campos de los objetos
      m_area.X(m_wnd.X2()-m_area.XGap());
      m_check.X(m_wnd.X2()-m_check.XGap());
      m_label.X(m_wnd.X2()-m_label.XGap());
     }
   else
     {
      //--- Guardar las coordenadas en los campos de los objetos
      CElement::X(x+XGap());
      //--- Guardar las coordenadas en los campos de los objetos
      m_area.X(x+m_area.XGap());
      m_check.X(x+m_check.XGap());
      m_label.X(x+m_label.XGap());
     }
//--- Si el anclaje está abajo
   if(m_anchor_bottom_window_side)
     {
      //--- Guardar las coordenadas en los campos del control
      CElement::Y(m_wnd.Y2()-YGap());
      //--- Guardar las coordenadas en los campos de los objetos
      m_area.Y(m_wnd.Y2()-m_area.YGap());
      m_check.Y(m_wnd.Y2()-m_check.YGap());
      m_label.Y(m_wnd.Y2()-m_label.YGap());
     }
   else
     {
      //--- Guardar las coordenadas en los campos de los objetos
      CElement::Y(y+YGap());
      //--- Guardar las coordenadas en los campos de los objetos
      m_area.Y(y+m_area.YGap());
      m_check.Y(y+m_check.YGap());
      m_label.Y(y+m_label.YGap());
     }
//--- Actualizando las coordenadas de objetos gráficos
   m_area.X_Distance(m_area.X());
   m_area.Y_Distance(m_area.Y());
   m_check.X_Distance(m_check.X());
   m_check.Y_Distance(m_check.Y());
   m_label.X_Distance(m_label.X());
   m_label.Y_Distance(m_label.Y());
  }

En la imagen de abajo se muestra de forma esquemática todas las posibles combinaciones de los modos de posicionamiento y cambio automático de los tamaños del control. Es un ejemplo abstracto en el que el formulario (véase la columna «Result») está representado como el rectángulo blanco con el borde negro de tamaño 400 x 400 píxeles, y el control se representa por el rectángulo gris de tamaño 200 x 200 píxeles. Además, para cada opción se muestran las coordenadas relativas y el tamaño del control. Las rayas significan que en estos casos no hace falta indicar el tamaño (si el modo del cambio automático del tamaño está activado). 

Fig. 3. Tabla con diferentes opciones en combinación con el posicionamiento del control y el cambio automático de sus tamaños. 

Fig. 3. Tabla con diferentes opciones en combinación con el posicionamiento del control y el cambio automático de sus tamaños.


5. Ha sido añadido el identificador del evento ON_CLICK_TAB para la generación del evento de conmutación de pestañas en las clases CTabs y CIconTabs

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
...
#define ON_CLICK_TAB               (27) // Conmutación de la pestaña

Ahora se puede monitorear el evento con el identificador ON_CLICK_TAB en el manejador de la clase de usuario, lo que ofrece aún más posibilidades para gestionar la apariencia de la interfaz gráfica. 


6. Ha sido añadida la actualización forzada de las coordenadas en los métodos Show() de todos los controles. Como ejemplo, en el código de abajo se muestra este método de la clase CSimpleButton (línea en color amarillo:

//+------------------------------------------------------------------+
//| Muestra el botón                                                |
//+------------------------------------------------------------------+
void CSimpleButton::Show(void)
  {
//--- Salir si el control ya está visible
   if(CElement::IsVisible())
      return;
//--- Hacer que todos los objetos sean visibles
   m_button.Timeframes(OBJ_ALL_PERIODS);
//--- Estado de visibilidad
   CElement::IsVisible(true);
//--- Actualizar la posición de los objetos
   Moving(m_wnd.X(),m_wnd.Y());
  }

En algunas ocasiones, durante el uso activo de la la interfaz gráfica de la aplicación MQL, surgía la situación cuando algunos de sus controles no estaban en su sitio. Ahora este problema está solucionado.


7. Han sido añadidos los métodos para obtener los punteros a los botones en la clase CWindow

//+------------------------------------------------------------------+
//| Clase de creación del formulario para los controles                    |
//+------------------------------------------------------------------+
class CWindow : public CElement
  {
public:
   //--- Devuelve los punteros a los botones del formulario
   CBmpLabel        *GetCloseButtonPointer(void)                             { return(::GetPointer(m_button_close));   }
   CBmpLabel        *GetRollUpButtonPointer(void)                            { return(::GetPointer(m_button_unroll));  }
   CBmpLabel        *GetUnrollButtonPointer(void)                            { return(::GetPointer(m_button_rollup));  }
   CBmpLabel        *GetTooltipButtonPointer(void)                           { return(::GetPointer(m_button_tooltip)); }
  };

De esta manera, el usuario de la librería puede cambiar las propiedades de estos objetos gráficos en cualquier momento del ciclo vital de la aplicación MQL ya después de la creación de la la interfaz gráfica. Por ejemplo, si antes las descripciones para los botones estaban representadas con valores predefinidos, ahora el usuario puede establecerlas personalmente. Eso puede ser útil durante la creación de las aplicaciones en varios idiomas.


8. Corrección en la clase CTable. Ha sido añadida una comprobación en el método principal de la creación del control (véase el código de abajo). La parte visible no puede ser más grande que la parte general. Ahora, si el usuario se equivoca al establecer las propiedades de la tabla, los valores se corrigen automáticamente. 

//+------------------------------------------------------------------+
//| Crea la tabla a base de los campos de edición                                   |
//+------------------------------------------------------------------+
bool CTable::CreateTable(const long chart_id,const int subwin,const int x,const int y)
  {
//--- Salir si no hay puntero al formulario
   if(!CElement::CheckWindowPointer(::CheckPointer(m_wnd)))
      return(false);
//--- La parte visible no puede ser más grande que la parte general
   m_visible_rows_total    =(m_visible_rows_total>m_rows_total)? m_rows_total : m_visible_rows_total;
   m_visible_columns_total =(m_visible_columns_total>m_columns_total)? m_columns_total : m_visible_columns_total;
//--- Inicialización de variables
   m_id       =m_wnd.LastId()+1;
   m_chart_id =chart_id;
   m_subwin   =subwin;
   m_x        =x;
   m_y        =y;
   m_x_size   =(m_x_size<1 || m_auto_xresize_mode)? (m_anchor_right_window_side)? m_wnd.X2()-m_x+m_x_size-(m_wnd.X2()-m_x)+1-m_auto_xresize_right_offset : m_wnd.X2()-m_x-m_auto_xresize_right_offset : m_x_size;
   m_y_size   =m_row_y_size*m_visible_rows_total-(m_visible_rows_total-1)+2;
//--- Márgenes desde el punto extremo
   CElement::XGap((m_anchor_right_window_side)? m_x : m_x-m_wnd.X());
   CElement::YGap((m_anchor_bottom_window_side)? m_y : m_y-m_wnd.Y());
//--- Creación de la tabla
  if(mi.TypeMenuItem()!=MI_HAS_CONTEXT_MENU)
      return(false);
   if(!CreateCells())
      return(false);
   if(!CreateScrollV())
      return(false);
   if(!CreateScrollH())
      return(false);
//--- Ocultar el elemento si es la ventana de diálogo o está minimizada
   if(m_wnd.WindowType()==W_DIALOG || m_wnd.IsMinimized())
      Hide();
//---
   return(true);
  }


 

Aplicación para la prueba de actualizaciones

Para esa prueba vamos a modificar un poco la aplicación MQL del artículo anterior para que haya posibilidad de mostrar todo de lo que hemos hablado en este artículo. Creamos el EA en la subventana del indicador «SubWindow». Los tamaños de la ventana principal de la interfaz gráfica van a ajustarse automáticamente a los tamaños de la subventana. El alto de la subventana puede cambiarse manualmente. Para eso al llamar al método CWindow::RollUpSubwindowMode(), hay que pasar el valor false (en el código está marcado en verde). 

Además, en el código de abajo se muestra cómo se puede obtener el acceso a la gestión de las propiedades de los botones en la ventana principal de la la interfaz gráfica. En este caso, el ejemplo está relacionado con las descripciones emergentes.

//+------------------------------------------------------------------+
//| Crea el formulario para los controles                           |
//+------------------------------------------------------------------+
bool CProgram::CreateWindow(const string caption_text)
  {
//--- Añadimos el puntero de la ventana en el array de ventanas
   CWndContainer::AddWindow(m_window);
//--- Coordenadas
   int x=1;
   int y=1;
//--- Propiedades
   m_window.Movable(false);
   m_window.UseRollButton();
   m_window.AutoXResizeMode(true);
   m_window.AutoYResizeMode(true);
   m_window.RollUpSubwindowMode(false,false);
//--- Creación del formulario
   if(!m_window.CreateWindow(m_chart_id,m_subwin,caption_text,x,y))
      return(false);
//--- Establecemos las descripciones emergentes
   m_window.GetCloseButtonPointer().Tooltip("Close program");
   m_window.GetUnrollButtonPointer().Tooltip("Unroll");
   m_window.GetRollUpButtonPointer().Tooltip("Roll up");
   return(true);
  }

Todos los controles de la primera pestaña estarán vinculados a la parte derecha del formulario (véase la captura de pantalla a continuación). Al cambiar el ancho del formulario, van a quedarse a la misma distancia desde su borde derecho. 

 Fig. 4. Los controles de la primera pestaña están vinculados a la parte derecha del formulario.

Fig. 4. Los controles de la primera pestaña están vinculados a la parte derecha del formulario.


Abajo se muestra el ejemplo del código del método de la creación para el control «Botón simple» (CSimpleButton). Para vincular este control al borde derecho será suficiente llamar al método AnchorRightWindowSide(), pasándole el valor true

//+------------------------------------------------------------------+
//| Crea el botón simple 1                                         |
//+------------------------------------------------------------------+
bool CProgram::CreateSimpleButton1(const int x_gap,const int y_gap,string button_text)
  {
//--- Guardamos el puntero a la ventana
   m_simple_button1.WindowPointer(m_window);
//--- Adjuntar a la primera pestaña
   m_tabs.AddToElementsArray(0,m_simple_button1);
//--- Coordenadas
   int x=m_window.X()+x_gap;
   int y=m_window.Y()+y_gap;
//--- Establecemos las propiedades antes de la creación
   m_simple_button1.ButtonXSize(140);
   m_simple_button1.BackColor(C'255,140,140');
   m_simple_button1.BackColorHover(C'255,180,180');
   m_simple_button1.BackColorPressed(C'255,120,120');
   m_simple_button1.AnchorRightWindowSide(true);
//--- Crear el botón
   if(!m_simple_button1.CreateSimpleButton(m_chart_id,m_subwin,button_text,x,y))
      return(false);
//--- Añadimos el puntero al control a la base
   CWndContainer::AddToElementsArray(0,m_simple_button1);
   return(true);
  }

A la segunda pestaña le adjuntamos sólo la tabla dibujada (CCanvasTable) que va a ajustarse a los tamaños del formulario al cambiar el ancho y el alto de la subventana.

 Fig. 5. Tabla dibujada que se ajusta a los tamaños del formulario.

Fig. 5. Tabla dibujada que se ajusta a los tamaños del formulario.


Para que todo eso funcione de esta manera, en el método de la creación del control en la clase de usuario usamos los métodos AutoXResizeMode() y AutoYResizeMode() para activar los modos para el cambio automático de los tamaños por la horizontal y vertical. A través de los métodos AutoXResizeRightOffset() y AutoYResizeBottomOffset() se puede ajustar los márgenes del borde derecho e inferior del control desde el borde derecho e inferior del formulario. En este caso, desde el borde derecho tenemos el margen en 1 píxel, y desde el borde inferior tenemos 25 píxeles (véase el código de abajo). 

//+------------------------------------------------------------------+
//| Crea la tabla dibujada                                     |
//+------------------------------------------------------------------+
bool CProgram::CreateCanvasTable(const int x_gap,const int y_gap)
  {
#define COLUMNS3_TOTAL 15
#define ROWS3_TOTAL    30
//--- Guardamos el puntero al formulario
   m_canvas_table.WindowPointer(m_window);
//--- Adjuntar a la segunda pestaña
   m_tabs.AddToElementsArray(1,m_canvas_table);
//--- Coordenadas
   int x=m_window.X()+x_gap;
   int y=m_window.Y()+y_gap;
//--- Array del ancho de las columnas
   int width[COLUMNS3_TOTAL];
   ::ArrayInitialize(width,70);
   width[0]=100;
   width[1]=90;
//--- Array de la alineación del texto en las columnas
   ENUM_ALIGN_MODE align[COLUMNS3_TOTAL];
   ::ArrayInitialize(align,ALIGN_CENTER);
   align[0]=ALIGN_RIGHT;
   align[1]=ALIGN_RIGHT;
   align[2]=ALIGN_LEFT;
//--- Establecemos las propiedades antes de la creación
   m_canvas_table.XSize(400);
   m_canvas_table.YSize(200);
   m_canvas_table.TableSize(COLUMNS3_TOTAL,ROWS3_TOTAL);
   m_canvas_table.TextAlign(align);
   m_canvas_table.ColumnsWidth(width);
   m_canvas_table.GridColor(clrLightGray);
   m_canvas_table.AutoXResizeMode(true);
   m_canvas_table.AutoYResizeMode(true);
   m_canvas_table.AutoXResizeRightOffset(1);
   m_canvas_table.AutoYResizeBottomOffset(25);
//--- Llenamos la tabla con datos
   for(int c=0; c<COLUMNS3_TOTAL; c++)
      for(int r=0; r<ROWS3_TOTAL; r++)
         m_canvas_table.SetValue(c,r,string(c)+":"+string(r));
//--- Creamos el control
   if(!m_canvas_table.CreateTable(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_canvas_table);
   return(true);
  }

Vamos a colocar el gráfico lineal (CLineGraph) a la tercera pestaña. Sus tamaños también van a ajustarse a los tamaños del formulario:

Fig. 6. Control «Gráfico lineal» que se ajusta a los tamaños del formulario. 

Fig. 6. Control «Gráfico lineal» que se ajusta a los tamaños del formulario.


En la cuarta y la quinta pestaña se muestra el enlace de otros controles de la librería al borde derecho:

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

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


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

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


Puede descargar esta aplicación de prueba al final del artículo para estudiarla más detalladamente. 

 


Conclusión

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

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

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


La siguiente versión de la librería será ampliada con los controles adicionales que pueden ser necesarios en las aplicaciones MQL desarrolladas. Los controles existentes van a desarrollarse y completarse con nuevas posibilidades.

Puede descargar la tercera versión de la librería Easy And Fast al final del artículo. Si le surgen algunas preguntas sobre el uso del material de estos archivos, puede dirigirse a la descripción detallada de esta serie de artículos, o bien hacer su pregunta en los comentarios para el artículo. 


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

Archivos adjuntos |
Interfaces gráficas X: Control "Gráfico estándar" (build 4) Interfaces gráficas X: Control "Gráfico estándar" (build 4)
En este artículo vamos a analizar el control de la interfaz gráfica como «Gráfico estándar». Nos permitirá crear los arrays de objetos-gráficos con posibilidad del desplazamiento horizontal sincronizado del gráfico. Aparte de eso, continuaremos optimizando el código de la librería para reducir el consumo de los recursos de CPU.
Distribuciones Estadísticas en MQL5: tomamos lo mejor de R y lo hacemos más rápido Distribuciones Estadísticas en MQL5: tomamos lo mejor de R y lo hacemos más rápido
Vamos a analizar las funciones para trabajar con las principales distribuciones estadísticas implementadas en el lenguaje R. Se trata de las distribuciones de Cauchy, Weibull, normal, log-normal, logística, exponencial, uniforme, la distribución gamma, la distribución beta central y no central, la distribución chi-cuadrado, la distribución F de Fisher, la distribución t de Student, así como las distribuciones binomial discreta y binomial negativa, la geométrica, la hipergeométrica y la distribución de Poisson. Además, también existen funciones de cálculo de los momentos teóricos de las distribuciones, que permiten valorar el grado de correspondencia entre la distribución real y la modelada.
LifeHack para tráders: Optimización "silenciosa" o Trazando la distribución de trades LifeHack para tráders: Optimización "silenciosa" o Trazando la distribución de trades
Análisis de la historia comercial y la construcción de los gráficos HTML de distribuciónde de los resultados comerciales dependiendo de la hora de entrada en la posición. Los gráficos se representan en tres segmentos, por horas, días y meses.
Comparando MQL5 y QLUA - ¿Por qué las operaciones comerciales en MQL5 son hasta 28 veces más rápidas? Comparando MQL5 y QLUA - ¿Por qué las operaciones comerciales en MQL5 son hasta 28 veces más rápidas?
Muchos tráders a menudo no reflexionan sobre la velocidad a la que su solicitud llega hasta la bolsa, cuánto tiempo tarda en ejecutarse una vez está allí, y en qué momento el terminal del tráder conoce finalmente el resultado de la operación comercial. Habíamos prometido comparar la velocidad de las operaciones comerciales, porque nadie había hecho antes que nosotros semejantes mediciones con la ayuda de los programas en MQL5 y QLUA.