English Русский 中文 Deutsch 日本語 Português
Interfaces gráficas X: Gestión ampliada de las listas y tablas. Optimización de código (build 7)

Interfaces gráficas X: Gestión ampliada de las listas y tablas. Optimización de código (build 7)

MetaTrader 5Ejemplos | 13 febrero 2017, 09:53
917 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.

Al código de nuestra librería le iría bastante bien una optimización que lo haría más ordenado y, por consecuencia, más legible y más comprensible para el análisis. Aparte de eso, continuaremos el desarrollo de los controles creados anteriormente en los artículos anteriores: listas, barras de desplazamiento y las tablas. Añadiremos los métodos que permitirán gestionar las propiedades de estos controles directamente en el proceso de la ejecución de la aplicación MQL usando la programación. 


Cambios en el esquema de la librería y optimización del código

El código en todos los archivos de la librería pertenecientes a los controles ha sido optimizado parcialmente. Los casos con las repeticiones frecuentes del código han sido colocados en los métodos separados, que han sido trasladados, en su lugar, en una clase separada.

Explicaremos cómo ha sido hecho eso. La clase CElement ha sido renombrada en CElementBase. Es la clase base para todos los controles de la librería. Ahora, la siguiente clase derivada después de ésta es la clase nueva CElement que contiene los métodos repetidos frecuentemente en todos los controles. Son los siguientes:

  • Método para almacenar el puntero del formulario al que se adjunta el control;
  • Comprobación de la presencia del puntero al formulario;
  • Comprobación del identificador del control activado;
  • Cálculo de coordenadas absolutas;
  • Cálculo de las coordenadas desde el punto extremo del formulario.

Las clases CElementBase y CElement se ubican en los archivos diferentes, en ElementBase.mqh y Element.mqh, respectivamente. Por eso, el archivo ElementBase.mqh con la clase base lo incluimos en el archivo Element.mqh. Puesto que aquí es necesario definir el tipo CWindows, incluimos también el archivoWindow.mqh. En el código de abajo se muestra su implementación:

//+------------------------------------------------------------------+
//|                                                      Element.mqh |
//|                        Copyright 2016, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "ElementBase.mqh"
#include "Controls\Window.mqh"
//+------------------------------------------------------------------+
//| Clase para obtener los parámetros del ratón                              |
//+------------------------------------------------------------------+
class CElement : public CElementBase
  {
protected:
   //--- Puntero al formulario al que está adjuntado el control
   CWindow          *m_wnd;
   //---
public:
                     CElement(void);
                    ~CElement(void);
   //--- Guarda el puntero del formulario
   void              WindowPointer(CWindow &object) { m_wnd=::GetPointer(object); }
   //---
protected:
   //--- Comprobación de la presencia del puntero al formulario
   bool              CheckWindowPointer(void);
   //--- Comprobación del identificador del control activado
   bool              CheckIdActivatedElement(void);
  
   //--- Cálculo de coordenadas absolutas
   int               CalculateX(const int x_gap);
   int               CalculateY(const int y_gap);
   //--- Cálculo de las coordenadas desde el punto extremo del formulario
   int               CalculateXGap(const int x);
   int               CalculateYGap(const int y);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CElement::CElement(void)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CElement::~CElement(void)
  {
  }

Todos estos métodos y su código se repetían antes con frecuencia en todas las clases de los controles. Reunirlos en una clase separada ha hecho que el código de las clases de los controles sea más comprensible y legible. El código de todos estos métodos es muy simple y cabe prácticamente en una sola línea (véase el código de abajo). Además, durante el cálculo de las coordenadas, se toma en cuenta el posicionamiento del control respecto a uno de los lados del formulario.

//+------------------------------------------------------------------+
//| Comprobación del identificador del control activado                 |
//| Cálculo de la coordenada absoluta X                                   |
//| Cálculo de la coordenada absoluta Y                                   |
//| Cálculo de la coordenada absoluta X desde el punto extremo del formulario         |
//| Cálculo de la coordenada absoluta Y desde el punto extremo del formulario         |
//+------------------------------------------------------------------+
int CElement::CalculateYGap(const int y)
  {
   return((CElementBase::AnchorBottomWindowSide())? m_wnd.Y2()-y : y-m_wnd.Y());
  }

Algunos pueden preguntar, ¿por qué estos métodos no han sido colocados en la versión antigua de la clase CElement?”. Era imposible hacerlo, porque durante la inclusión del archivo Window.mqh y la compilación ocurría el error de la falta del tipo, y como consecuencia, muchos otros errores relaccionados.

 Fig. 1. Mensaje de ausencia del tipo CElement durante la compilación.

Fig. 1. Mensaje de ausencia del tipo CElement durante la compilación


Si intentamos saltarse esta dificultad e incluir el archivo Window.mqh después del cuerpo de la clase CElement, cuando el objeto tipo CWindow ya ha sido declarado en el cuerpo de esta clase, durante la compilación veremos el error conocido sobre la falta del tipo especificado:

 Fig. 2. Mensaje de ausencia del tipo CWindow durante la compilación.

Fig. 2. Mensaje de ausencia del tipo CWindow durante la compilación


Por eso, se ha decidido crear una clase intermedia adicional heredada, en la que se puede colocar el código repetido con frecuencia y los métodos para el trabajo con el puntero al formulario al que se adjuntan los controles. Es una parte del esquema de la librería en cuanto a las interacciones entre el formulario y los controles:

 Fig. 3. Parte del esquema de la librería en cuanto a las interacciones entre el formulario y los controles.

Fig. 3. Parte del esquema de la librería en cuanto a las interacciones entre el formulario y los controles


Como se puede ver en el esquema de arriba, la clase CWindow se deriva directamente de la clase CElementBase, porque la clase intermedia CElement ya es redundante e inapropiada para el formulario. Las demás clases de los controles se derivan de la clase intermediaCElement

 

Gestión de la barra de desplazamiento usando programación

Durante el uso de la librería, se ha madurado la necesidad de gestionar las barras de desplazamiento usando programación. Para eso, en las clases CScrollV y CScrollH ha sido implementado el método MovingThumb(), que permite desplazar el deslizador de la barra de desplazamiento a una posición especificada. 

Abajo se muestra el código sólo para la barra vertical porque es prácticamente idéntica a la barra horizontal. El método tiene un argumento cuyo valor predefinido es WRONG_VALUE. Si llamamos al método sin especificar la posición (con el valor por defecto), el deslizador será desplazado a la última posición de la lista. Eso puede ser conveniente cuando en la lista se añaden los elementos en el momento de la ejecución del programa, y permite implementar el desplazamiento (scrolling) automático de la lista.

//+------------------------------------------------------------------+
//| Clase para manejar la barra de desplazamiento vertical              |
//+------------------------------------------------------------------+
class CScrollV : public CScroll
  {
public:
   //--- Mueve el deslizador a una posición especificada
//| Mueve el deslizador a una posición especificada                         |
//--- Salir si la barra de desplazamiento no es necesaria
//--- Para comprobar la posición del deslizador
   uint check_pos=0;
//--- Corregimos la posición en caso de salir fuera del diapasón
//--- Guardamos la posición del deslizador
   CScroll::CurrentPos(check_pos);
//| Cálculo y establecimiento de la coordenada Y del deslizador de la barra de desplazamiento
   CalculateThumbY();
  }

 

Gestión de las listas usando programación

Para la gestión de las listas han sido implementados los métodos públicos responsables de la ejecución de las siguientes acciones:

  • Reconstrucción de la lista
  • Adición del elemento al final de la lista
  • Limpieza de la lista (eliminación de todos los elementos)
  • Desplazamiento (scrolling) de la lista

Además de eso, como parte de la optimización del código de la librería, a las clases de las listas han sido añadidos los métodos privados para el código repetido.

  • Cálculo de coordenada Yen puntos
  • Cálculo del ancho de los elementos
  • Cálculo del tamaño de la lista por el eje Y

Vamos a analizar la estructura de estos métodos en la clase CListView. Los métodos privados son unos métodos auxiliares del código que se repite más de una vez en diferentes lugares de la clase. Ocupan sólo una línea en cada método:

//+------------------------------------------------------------------+
//| Clase para crear la lista                                        |
   //--- Cálculo de la coordenada Y del elemento
   //--- Cálculo del ancho de los elementos
   int               CalculationItemsWidth(void);
   //--- Cálculo del tamaño de la lista por el eje Y
//| Cálculo de la coordenada Y del elemento                                       |
//| Cálculo del ancho de los elementos                                            |
//| Cálculo del tamaño de la lista por el eje Y                                   |
//+------------------------------------------------------------------+
int CListView::CalculationYSize(void)
  {
   return(m_item_y_size*m_visible_items_total-(m_visible_items_total-1)+2);
  }

La limpieza de la lista dice por sí misma: todos los elementos de la lista se eliminan. para eso se utiliza el método CListView::Clear(). Aquí, en primer lugar se eliminan los primitivos gráficos, se libera el array de punteros a estos objetos y se establecen los valores predefinidos para algunos campos de la clase. Después de eso, el tamaño de la lista se establece a cero y se resetean los parámetros de la barra de desplazamiento. Al final del método, hay que añadir de nuevo el puntero al fondo de la lista en el array de los punteros del control, porque antes ha sido eliminado por el método CElementBase::FreeObjectsArray().

//+------------------------------------------------------------------+
//| Clase para crear la lista                                        |
//+------------------------------------------------------------------+
class CListView : public CElement
  {
public:
   //--- Limpia la lista (eliminación de todos los elementos)
//| Limpia la lista (eliminación de todos los elementos)                           |
//+------------------------------------------------------------------+
void CListView::Clear(void)
  {
//--- Eliminar los objetos-elementos
//--- Limpiar el array de punteros a objetos
   CElementBase::FreeObjectsArray();
//--- Establecer los valores por defecto
//--- Establecer el tamaño cero de la lista
   ListSize(0);
//--- Resetear los valores de la barra de desplazamiento
   m_scrollv.Hide();
   m_scrollv.MovingThumb(0);
   m_scrollv.ChangeThumbSize(m_items_total,m_visible_items_total);
//--- Añadir el fondo de la lista al array de los punteros a los objetos del control
   CElementBase::AddToArray(m_area);
  }

Para la reconstrucción de la lista, hay que usar el método CListView::Rebuilding(). La reconstrucción es una situación cuando es necesario volver a formar por completo la lista. Este método se utiliza para cambiar el número total de los elementos y el número de los elementos visibles. Es decir, el tamaño de la lista también se cambiará si indicamos el número de los elementos visibles distinto del valor inicial.

Al principio del método CListView::Rebuilding(), la lista se limpia. Luego, basándose en los valores de los argumentos transferidos, se establecen nuevos tamaños y se corrige el alto de la lista si el número de los elementos visibles ha cambiado. A continuación, se corrigen los tamaños de los objetos de la barra de desplazamiento. Después de eso, se crea la lista, y si el número total de los elementos supera su número visible establecido, se muestra la barra de desplazamiento. 

//+------------------------------------------------------------------+
//| Clase para crear la lista                                        |
//+------------------------------------------------------------------+
class CListView : public CElement
  {
public:
//| Reconstrucción de la lista                                             |
//+------------------------------------------------------------------+
void CListView::Rebuilding(const int items_total,const int visible_items_total)
  {
//--- Limpiar la lista
   Clear();
//--- Establecemos el tamaño de la lista y de su parte visible
   ListSize(items_total);
   VisibleListSize(visible_items_total);
//--- Corregir el tamaño de la lista
   int y_size=CalculationYSize();
   if(y_size!=CElementBase::YSize())
     {
      m_area.YSize(y_size);
      m_area.Y_Size(y_size);
      CElementBase::YSize(y_size);
     }
//--- Corregir el tamaño de la barra de desplazamiento
   m_scrollv.ChangeThumbSize(m_items_total,m_visible_items_total);
   m_scrollv.ChangeYSize(y_size);
//--- Создать список
   CreateList();
//--- Mostrar la barra de desplazamiento si hace falta
   if(m_items_total>m_visible_items_total)
     {
      if(CElementBase::IsVisible())
         m_scrollv.Show();
     }
  }

Para la creación de un elemento ha sido implementado el método separado CListView::CreateItem(), porque a la hora de añadir el elemento a la lista en el proceso de ejecución del programa, su código va a utilizarse en el método CListView::AddItem(), y no sólo cuando se crea la lista entera en el ciclo dentro del método CListView::CreateList(). 

El método CListView::AddItem() recibe sólo un argumento, texto del elemento a visualizar. Por defecto, es una línea vacía. Se puede añadir el texto después de la creación a través del método CListView::SetItemValue(). Al principio del método CListView::AddItem(), el array de los elementos se aumenta a un elemento. Luego, si en este momento el número total de los elementos no supera el número de los elementos visibles, eso significa que es necesario crear un objeto gráfico. Si hemos superado el límite del número visible, hay que mostrar la barra de desplazamiento y corregir el tamaño de su deslizador, así como corregir el ancho de los elementos. 

//+------------------------------------------------------------------+
//| Clase para crear la lista                                        |
//+------------------------------------------------------------------+
class CListView : public CElement
  {
public:
   //--- Añade el elemento a la lista
//| Añade el elemento a la lista                                         |
//+------------------------------------------------------------------+
void CListView::AddItem(const string value="")
  {
//--- Aumentamos el tamaño del array a un elemento
   int array_size=ItemsTotal();
   m_items_total=array_size+1;
   ::ArrayResize(m_item_value,m_items_total);
   m_item_value[array_size]=value;
//--- Número total de los elementos supera el número visible
   if(m_items_total>m_visible_items_total)
     {
      //--- Corregir el tamaño del deslizador y mostrar la barra de desplazamiento
      m_scrollv.ChangeThumbSize(m_items_total,m_visible_items_total);
      if(CElementBase::IsVisible())
         m_scrollv.Show();
      //--- Salir si el array tiene menos de un elemento
      //--- Cálculo del ancho de los elementos de la lista
      int width=CElementBase::XSize()-m_scrollv.ScrollWidth()-1;
      if(width==m_items[0].XSize())
         return;
      //--- Establecer nuevo tamaño para los elementos de la lista
      for(int i=0; i<m_items_total && i<m_visible_items_total; i++)
        {
         m_items[i].XSize(width);
         m_items[i].X_Size(width);
        }
      //---
      return;
     }
//--- Cálculo de coordenadas
   int x=CElementBase::X()+1;
   int y=CalculationItemY(array_size);
//--- Cálculo del ancho de los elementos de la lista
   int width=CalculationItemsWidth();
//--- Creación del objeto
   CreateItem(array_size,x,y,width);
//--- Resaltar el elemento seleccionado
   HighlightSelectedItem();
//--- Guardamos el texto del elemento seleccionado
   if(array_size==1)
      m_selected_item_text=m_item_value[0];
  }

El método CListView::Scrolling() está destinado para el desplazamiento de la lista usando la programación. Como el único argumento se recibe el número de la posición en la lista. Por defecto, se establece el valor WRONG_VALUE, lo que significa el desplazamiento de la lista a la última posición.

//+------------------------------------------------------------------+
//| Clase para crear la lista                                        |
//+------------------------------------------------------------------+
class CListView : public CElement
  {
public:
   //--- Desplazamiento (scrolling) de la lista
   void              Scrolling(const int pos=WRONG_VALUE);
  };
//+------------------------------------------------------------------+
//| Desplazamiento (scrolling) de la lista                                                 |
//--- Salir si la barra de desplazamiento no es necesaria
//--- Para determinar la posición del deslizador
   int index=0;
//--- Indice de la última posición
   int last_pos_index=m_items_total-m_visible_items_total;
//--- Corrección en caso de salir fuera del diapasón
   if(pos<0)
      index=last_pos_index;
   else
      index=(pos>last_pos_index)? last_pos_index : pos;
//--- Desplazamos el deslizador de la barra de desplazamiento
   m_scrollv.MovingThumb(index);
//--- Сдвигаем список
   UpdateList(index);
  }

Los mismos métodos están implementados también para la lista tipo CCheckBoxList

 

Optimización del código de la tabla tipo CTable

El código de la clase CTable también ha sido optimizado. Ahora es más compacto y se lee mejor gracias a la adición de una serie de métodos privados que incluyen el código repetido con frecuencia. Son los siguientes métodos:

  • Cambio del tamaño de los array de la fila
  • Inicialización de las celdas con valores por defecto
  • Cálculo del tamaño de la tabla por el eje X
  • Cálculo del tamaño de la tabla por el eje Y
  • Cálculo de la coordenada X de la celda
  • Cálculo de la coordenada Y de la celda
  • Cálculo del ancho de la columna
  • Cambio del ancho de las columnas
  • Cambio del tamaño de la tabla por el eje Y
//+------------------------------------------------------------------+
//| Clase par crear la tabla de los campos de edición                        |
//+------------------------------------------------------------------+
class CTable : public CElement
  {
private:
   //--- Cambio del tamaño de los array de la fila
   void              RowResize(const uint column_index,const uint new_size);
   //--- Inicialización de las celdas con valores por defecto
   void              CellInitialize(const uint column_index,const int row_index=WRONG_VALUE);
   //--- Cálculo del tamaño de la tabla por el eje X
   int               CalculationXSize(void);
   //--- Cálculo del tamaño de la tabla por el eje Y
   int               CalculationYSize(void);
   //--- Cálculo de la coordenada X de la celda
   int               CalculationCellX(const int column_index=0);
   //--- Cálculo de la coordenada Y de la celda
   int               CalculationCellY(const int row_index=0);
   //--- Cálculo del ancho de la columna
   int               CalculationColumnWidth(const bool is_last_column=false);
   //--- Cambio del ancho de las columnas
   void              ColumnsXResize(void);
   //--- Cambio del tamaño de la tabla por el eje Y
   void              YResize(void);
  };

El método CTable::CalculationColumnWidth() sirve para calcular el ancho de las columnas de la tabla y recibe sólo un argumento con el valor false. El valor predefinido se usa para calcular el ancho total para todas las columnas. Si se pasa el valor true, se calcula el ancho para la última columna. En este caso se usa la llamada recursiva del método. La división entre el cálculo del ancho total y ancho de la última columna es necesaria, dado que durante el cálculo general, el borde derecho de la última columna puede no coincidir con el borde derecho de la tabla.

//+------------------------------------------------------------------+
//| Cálculo del ancho de la columna                                            |
//+------------------------------------------------------------------+
int CTable::CalculationColumnWidth(const bool is_last_column=false)
  {
   int width=0;
//--- Comprobación de la presencia de la barra de desplazamiento vertical
   bool is_scrollv=m_rows_total>m_visible_rows_total;
//---
   if(!is_last_column)
     {
      if(m_visible_columns_total==1)
         width=(is_scrollv)? m_x_size-m_scrollv.ScrollWidth() : width=m_x_size-2;
      else
        {
         if(is_scrollv)
            width=(m_x_size-m_scrollv.ScrollWidth())/int(m_visible_columns_total);
         else
            width=m_x_size/(int)m_visible_columns_total+1;
        }
     }
   else
     {
      width=CalculationColumnWidth();
      int last_column=(int)m_visible_columns_total-1;
      int w=m_x_size-(width*last_column-last_column);
      width=(is_scrollv) ? w-m_scrollv.ScrollWidth()-1 : w-2;
     }
//---
   return(width);
  }

Cuando la tabla se crea o cuando se cambia el ancho de la tabla, se realiza la llamada al método CTable::ColumnsXResize(). Aquí, para el cálculo del ancho de las columnas se llama el método CTable::CalculationColumnWidth() considerado anteriormente. Si la tabla está ordenada, al final del método hay que corregir la posición de la flecha de indicio de la tabla ordenada.

//+------------------------------------------------------------------+
//| Cambio del ancho de las columnas                                        |
//+------------------------------------------------------------------+
void CTable::ColumnsXResize(void)
  {
//--- Cálculo del ancho de las columnas
   int width=CalculationColumnWidth();
//--- Columnas
   for(uint c=0; c<m_columns_total && c<m_visible_columns_total; c++)
     {
      //--- Cálculo de la coordenada X
      int x=CalculationCellX(c);
      //--- Corrección del ancho de la última columna
      if(c+1>=m_visible_columns_total)
         width=CalculationColumnWidth(true);

      //--- Filas
      for(uint r=0; r<m_rows_total && r<m_visible_rows_total; r++)
        {
        //--- Coordenadas
         m_columns[c].m_rows[r].X(x);
         m_columns[c].m_rows[r].X_Distance(x);
        //--- Ancho
         m_columns[c].m_rows[r].XSize(width);
         m_columns[c].m_rows[r].X_Size(width);
        //--- Márgenes desde el punto extremo del panel
         m_columns[c].m_rows[r].XGap(CalculateXGap(x));
        }
     }
//--- Salir si la tabla no está ordenada
   if(m_is_sorted_column_index==WRONG_VALUE)
      return;
//--- Desplazamiento a un índice si el modo de encabezados fijos está activado
   int l=(m_fix_first_column) ? 1 : 0;
//--- Obtenemos las posiciones actuales de los deslizadores de la barra de desplazamiento vertical y horizontal
   int h=m_scrollh.CurrentPos()+l;
//--- Si no salimos fuera del rango
   if(m_is_sorted_column_index>=h && m_is_sorted_column_index<(int)m_visible_columns_total)
     {
      //--- Desplazamiento de la flecha a una columna ordenada de la tabla
      ShiftSortArrow(m_is_sorted_column_index);
     }
  }

Usted puede estudiar personalmente el código de los demás métodos privados presentados en la lista al principio de esta sección ya que no contienen nada complicado que podría suponer problemas.

Además de los métodos descritos anteriormente, como parte de la optimización ha sido implementado el método privado CTable::CreateCell() para la creación de la celda de la tabla. Otra adición útil para la tabla tipo CTable en esta actualización es el formateo automático en el estilo «Cebra». Antes, si el usuario de la librería necesitaba una tabla rayada para la mejor percepción del array de datos, tenía que usar el método CTable::CellColor(). O sea, tenía que asignar un color determinado para todas las celdas de la tabla. No es muy cómodo y requiere tiempo. Ahora, para hacer una tabla rayada, sólo hay que llamar al método CTable::IsZebraFormatRows(), antes de crear el control y pasar el segundo color como el único argumento. Para el primer color se usa el valor que se establece con el método CTable::CellColor() para todas las celdas de la tabla (por defecto, es blanco). 

//+------------------------------------------------------------------+
//| Clase par crear la tabla de los campos de edición                        |
//+------------------------------------------------------------------+
class CTable : public CElement
  {
private:
   //--- Modo del coloreo rayado de la tabla tipo «Cebra»
   color             m_is_zebra_format_rows;
   //---
public:
   //--- Modo del formato de las filas en el estilo «Cebra»
   void              IsZebraFormatRows(const color clr)                         { m_is_zebra_format_rows=clr;      }
  };

Si el segundo color para el formateo en el estilo «Cebra» está establecido, en todos los sitios necesarios se llama al método CTable::ZebraFormatRows(). 

//+------------------------------------------------------------------+
//| Clase par crear la tabla de los campos de edición                        |
//+------------------------------------------------------------------+
class CTable : public CElement
  {
private:
   //--- Formatea la tabla en el estilo «Cebra»
//| Formatea la tabla en el estilo «Cebra»                              |
//+------------------------------------------------------------------+
void CTable::ZebraFormatRows(void)
  {
//--- Salir si el modo está desactivado
   if(m_is_zebra_format_rows==clrNONE)
      return;
//--- Color por defecto
   color clr=m_cell_color;
//---
   for(uint c=0; c<m_columns_total; c++)
     {
      for(uint r=0; r<m_rows_total; r++)
        {
         if(m_fix_first_row)
           {
            if(r==0)
               continue;
            //---
            clr=(r%2==0)? m_is_zebra_format_rows : m_cell_color;
           }
         else
            clr=(r%2==0)? m_cell_color : m_is_zebra_format_rows;
         //--- Guardar el color del fondo de la celda en el array común
         m_vcolumns[c].m_cell_color[r]=clr;
        }
     }
  }

 

Gestión de la tabla tipo CTable usando programación

En esta actualización de la librería, sólo la tabla tipo CTable adquiere la gestión programada. Han sido implementados unos métodos públicos para las siguientes acciones:

  • Reconstrucción de la tabla
  • Inserción de la columna
  • Inserción de la fila
  • Limpieza de la tabla (eliminación de todas las columnas y filas)
  • Desplazamiento horizontal y vertical de la tabla
//+------------------------------------------------------------------+
//| Clase par crear la tabla de los campos de edición                        |
//+------------------------------------------------------------------+
class CTable : public CElement
  {
public:
   //--- Reconstrucción de la tabla
   void              Rebuilding(const int columns_total,const int visible_columns_total,const int rows_total,const int visible_rows_total);
   //--- Inserta una columna en la tabla
   void              AddColumn(void);
   //--- Inserta una fila en la tabla
   void              AddRow(void);
   //--- Limpia la tabla (eliminación de todas las columnas y filas)
   void              Clear(void);
   //--- Desplazamiento de la tabla: (1) horizontal y (2) vertical
   void              VerticalScrolling(const int pos=WRONG_VALUE);
   void              HorizontalScrolling(const int pos=WRONG_VALUE);
  };

No vamos a considerar el método CTable::Clear() para la limpieza de la tabla, ya que es prácticamente semejante al de las listas, que hemos considerado en los apartados anteriores del artículo.

Para la reconstrucción de la tabla hay que llamar al método CTable::Rebuilding(), en el que se pasa el número total de las columnas y filas como argumentos, así como su número visible. Aquí, al principio del método, la tabla se limpia, o sea, se eliminan todas las columnas y filas. Luego, se establecen nuevos tamaños para los arras según los valores de argumentos pasados. Dependiendo de la relación entre el número total de las filas y columnas y su número visible, se establecen los tamaños para las barras de desplazamiento. Después de terminar todos los cálculos, se crean las celdas de la tabla, y luego si es necesario, las barras de desplazamiento se hacen visibles. 

//+------------------------------------------------------------------+
//| Reconstrucción de la tabla                                            |
//+------------------------------------------------------------------+
void CTable::Rebuilding(const int columns_total,const int visible_columns_total,const int rows_total,const int visible_rows_total)
  {
//--- Limpiar la tabla
   Clear();
//--- Establecemos el tamaño de la tabla y de su parte visible
   TableSize(columns_total,rows_total);
   VisibleTableSize(visible_columns_total,visible_rows_total);
//--- Corregir el tamaño de las barras de desplazamiento
//--- Comprobación de la presencia de la barra de desplazamiento vertical
   bool is_scrollv=m_rows_total>m_visible_rows_total;
//--- Comprobar la presencia de la bbarra de desplazamiento horizontal
   bool is_scrollh=m_columns_total>m_visible_columns_total;
//--- Calculamos el tamaño de la tabla por el eje Y
   int y_size=CalculationYSize();
//--- Establecemos tamaño nuevo para la barra vertical
   m_scrollv.ChangeYSize(y_size);
//--- Establecemos nuevo tamaño de la tabla
   m_y_size=(is_scrollh)? y_size+m_scrollh.ScrollWidth()-1 : y_size;
   m_area.YSize(m_y_size);
   m_area.Y_Size(m_y_size);
//--- Corregimos la posición de la barra horizontal por el eje Y
   m_scrollh.YDistance(CElementBase::Y2()-m_scrollh.ScrollWidth());
//--- Si hace falta la barra de desplazamiento horizontal
   if(is_scrollh)
     {
      //--- Establecemos el tamaño respecto la presencia de la barra vertical
      if(!is_scrollv)
         m_scrollh.ChangeXSize(m_x_size);
      else
        {
        //--- Calcular y modificar el ancho de la barra de desplazamiento horizontal
         int x_size=m_area.XSize()-m_scrollh.ScrollWidth()+1;
         m_scrollh.ChangeXSize(x_size);
        }
     }
//--- Crear las celdas de la tabla
//--- Mostrar la barra de desplazamiento si hace falta
   if(rows_total>visible_rows_total)
     {
      if(CElementBase::IsVisible())
         m_scrollv.Show();
     }
   if(columns_total>visible_columns_total)
     {
      if(CElementBase::IsVisible())
         m_scrollh.Show();
     }
  }

Los algoritmos de los métodos para insertar la columna CTable::AddColumn() y la fila CTable::AddRow() son muy parecidos, por eso vamos a considerar sólo uno de ellos. 

Al principio del método CTable::AddColumn(), se establece el tamaño del array de las columnas y filas en esta columna. Luego, se utiliza el método CTable::CellInitialize() para inicializar las celdas de la columna insertada con valores predefinidos. Después de eso, si el número total de las columnas no supera el número visible establecido: 

  1. Se calcula el ancho de las columnas
  2. Se crea un determinado número de objetos gráficos (celdas de la tabla) para la columna insertada
  3. Si hace falta, la tabla se formatea en el estilo «Cebra»
  4. Al final del método, la tabla se actualiza

Si después del incremento de los arrays de las columnas y filas, resulta que el número total de las columnas es mayor que el número visible establecido, eso significa que es necesario mostrar la barra de desplazamiento horizontal, y por tanto, corregir el alto de la tabla. Después de eso, la tabla se formatea en el estilo «Cebra», se actualiza y el programa sale del método. 

//+------------------------------------------------------------------+
//| Inserta una columna en la tabla                                      |
//--- Aumentamos el tamaño del array a un elemento
//--- Establecer el tamaño para el array de filas
//--- Inicialización de los arrays con valores predefinidos
//--- Si el número total de las columnas supera el número visible
   if(m_columns_total>m_visible_columns_total)
     {
      //--- Corregir el tamaño de la tabla por el eje Y
      YResize();
      //--- Si no hay barra vertical, hacer que la barra horizontal ocupe todo el ancho de la tabla
      //--- Corregir el tamaño del deslizador y mostrar la barra de desplazamiento
      //--- Mostrar la barra de desplazamiento
      if(CElementBase::IsVisible())
         m_scrollh.Show();
       //--- Formateo de las filas en el estilo «Cebra»
      ZebraFormatRows();
      //--- Actualizar la tabla
      UpdateTable();
      return;
     }
//--- Cálculo del ancho de las columnas
   int width=CalculationColumnWidth();
//--- Corrección del ancho de la última columna
   if(m_columns_total>=m_visible_columns_total)
      width=CalculationColumnWidth(true);
//--- Cálculo de la coordenada X
   int x=CalculationCellX(array_size);
//---
   for(uint r=0; r<m_rows_total && r<m_visible_rows_total; r++)
     {
      //--- Cálculo de la coordenada Y
      int y=CalculationCellY(r);
      //--- Creación del objeto
      CreateCell(array_size,r,x,y,width);
      //--- Establecer el color correspondiente del encabezado
      if(m_fix_first_row && r==0)
         m_columns[array_size].m_rows[r].BackColor(m_headers_color);
     }
//--- Formateo de las filas en el estilo «Cebra»
   ZebraFormatRows();
//--- Actualizar la tabla
   UpdateTable();
  }

Los métodos para el scrolling de la tabla CTable::VerticalScrolling() y CTable::HorizontalScrolling() son prácticamente idénticos a los métodos del apartado de las listas, por eso no vamos a mostrar aquí su código. Puede estudiarlos peronalmente en los archivos adjuntos al artículo.

A continuación, vamos a crear la aplicación MQL que nos permitirá demostrar nuevas posibilidades de las listas y de la tabla tipo CTable

 

Aplicación para la prueba del control

Para la prueba vamos a escribir una aplicación MQL que no permita ver en el acto cómo trabajan los métodos que hemos incluido en la clase de las listas y la tabla tipo CTable. Creamos dos pestañas en la interfaz gráfica de esta aplicación. La tabla tipo CTable se colocará en la primera pestaña, y encima de la tabla crearemos los controles para manejar las propiedades de esta tabla. Serán dos botones y cuatro campos de edición numéricos:

  • Botón «CLEAR TABLE» para limpiar la tabla (eliminar todas las columnas y filas)
  • Botón «REBUILD TABLE» para reconstruir la tabla de acuerdo con los parámetros establecidos en los campos de edición numéricos
  • Campo de edición «Rows total» para indicar el número total de las filas de la tabla
  • Campo de edición «Columns total» para indicar el número total de las columnas de la tabla
  • Campo de edición «Visible rows total» para indicar el número visible de las filas de la tabla
  • Campo de edición «Visible columns total» para indicar el número visible de las columnas de la tabla

En la captura de pantalla de abajo se muestra el resultado:

 Fig. 4. Grupo de controles en la primera pestaña.

Fig. 4. Grupo de controles en la primera pestaña


En la segunda pestaña se ubicarán dos listas (lista simple y lista con checkbox). Aquí habrá los siguientes controles para demostrar la gestión de las listas mediante la programación:

  • Botón «CLEAR LISTS» para limpiar las listas (eliminar todos los elementos)
  • Botón «REBUILD LISTS» para reconstruir las listas de acuerdo con los parámetros establecidos en los campos de edición numéricos
  • Campo de edición «Items total» para indicar el número total de los elementos de las listas
  • Campo de edición «Visible items total» para indicar el número visible de los elementos de las listas

 En la captura de pantalla de abajo se muestran los controles de la segunda pestaña. En adición, han sido creados dos controles más: calendario desplegable y el control «Hora». 

 Fig. 5. Grupo de controles en la segunda pestaña.

Fig. 5. Grupo de controles en la segunda pestaña


Antes de seguir contando sobre la demostración de las capacidades funcionales de las listas y la tabla implementadas en esta actualización, vamos a fijarnos en otra adición que facilitará el trabajo del desarrollador MQL en el temporizador de su aplicación MQL. SEE trata de la clase CTimeCounter. Ella permitirá controlar la frecuencia de la actualización (redibujo) para los grupos separados de los controles de la interfaz gráfica dentro de los intervalos temporales especificados. La clase CTimeCounter contiene sólo tres campos y dos métodos (véase el código de abajo).

//+------------------------------------------------------------------+
//|                                                  TimeCounter.mqh |
//|                        Copyright 2016, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Contador de tiempo                                                  |
   //--- Paso del contador
   //--- Peíodo de tiempo
   //--- Contador de tiempo
   uint              m_time_counter;
   //---
public:
                        //--- Establecimiento del paso y del intervalo de tiempo
   void              SetParameters(const uint step,const uint pause);
   //--- Comprueba si el intervalo especificado ha transcurrido
   bool              CheckTimeCounter(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CTimeCounter::CTimeCounter(void) : m_step(16),
                                   m_pause(1000),
                                   m_time_counter(0)
                                  
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CTimeCounter::~CTimeCounter(void)
  {
  }

A través del método CTimeCounter::SetParameters() se puede establecer el paso del incremento del contador y el intervalo de tiempo para la pausa:

//+------------------------------------------------------------------+
//| Establecimiento del paso y del intervalo de tiempo                            |
//+------------------------------------------------------------------+
void CTimeCounter::SetParameters(const uint step,const uint pause)
  {
   m_step  =step;
   m_pause =pause;
  }

El método CTimeCounter::CheckTimeCounter() sirve para comprobar si el intervalo temporal especificado en los parámetros de la clase ha transcurrido. Si el intervalo temporal ha transcurrido, el m´´etodo devuelve true.

//+------------------------------------------------------------------+
//| Comprueba si el intervalo especificado ha transcurrido            |
//--- Aumentamos el contador si no hemos pasado el intervalo especificado
   if(m_time_counter<m_pause)
     {
      m_time_counter+=m_step;
      return(false);
     }
//--- Poner el contador a cero
   m_time_counter=0;
   return(true);
  }

Antes de seguir, cabe mencionar que la ubicación de los archivos en los directorios de nuestra librería ha cambiado. Ahora, en el directorio «MetaTrader 5\MQL5\Include\EasyAndFastGUI\Controls» se ubican sólo los archivos que contienen las clases de los controles. Los demás archivos han sido trasladados en el directorio raíz de la librería: «MetaTrader 5\MQL5\Include\EasyAndFastGUI». Por eso, para incluir la librería en el archivo de su clase personalizada, ha que escribir la ruta tal como se muestra a continuación. Además, aquí se muestra cómo se incluye el archivo con la clase CTimeCounter (va a usarse en los ejemplos de prueba). 

//+------------------------------------------------------------------+
//|                                                      Program.mqh |
//|                        Copyright 2016, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include <EasyAndFastGUI\WndEvents.mqh>
#include <EasyAndFastGUI\TimeCounter.mqh>

Vamos a colocar el establecimiento de los parámetros de los contadores de tiempo en el constructor de la clase personalizada:

//+------------------------------------------------------------------+
//| Clase para crear la aplicación                                     |
   //--- Contadores de tiempo
   CTimeCounter      m_counter1; // para actualizar la barra de estado
   CTimeCounter      m_counter2; // para actualizar las listas y la tabla
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CProgram::CProgram(void)
  {
//--- Establecimiento de los parámetros para los contadores de tiempo
   m_counter1.SetParameters(16,500);
   m_counter2.SetParameters(16,150);
  }

La demostración de la inserción de los elementos en las listas, así como las columnas y filas en la tabla, después de la limpieza completa de estos controles, se implementará en el temporizador. Dentro del intervalo de tiempo especificado, si el número de los elementos/columnas/filas es menor que el número establecido en los campos de edición correspondientes, ellos van a añadirse en este bloque (véase el código de abajo). Para demostrar la gestión programada de la barra de desplazamiento, con cada adición del elemento en las listas los deslizadores de las barras de desplazamiento van a moverse al final de las listas

//+------------------------------------------------------------------+
//| Temporizador                                                           |
//+------------------------------------------------------------------+
void CProgram::OnTimerEvent(void)
  {
   CWndEvents::OnTimerEvent();
...
//--- Pausa entre la actualización de los controles
   if(m_counter2.CheckTimeCounter())
     {
      //--- Insertamos una fila en la tabla si el número total es menos que el establecido
      if(m_table.RowsTotal()<m_spin_edit1.GetValue())
         m_table.AddRow();
      //--- Insertamos una columna en la tabla si el número total es menos que el establecido
      if(m_table.ColumnsTotal()<m_spin_edit2.GetValue())
         m_table.AddColumn();
      //--- Insertamos el elemento en la lista si el número total es menos que el establecido
      if(m_listview.ItemsTotal()<m_spin_edit5.GetValue())
        {
         m_listview.AddItem("SYMBOL "+string(m_listview.ItemsTotal()));
        //--- Desplazar el deslizador de la barra de desplazamiento al final de la lista
         m_listview.Scrolling();
        }
      //--- Insertamos el elemento en la lista de checkbox si el número total es menos que el establecido
      if(m_checkbox_list.ItemsTotal()<m_spin_edit5.GetValue())
        {
         m_checkbox_list.AddItem("Checkbox "+string(m_checkbox_list.ItemsTotal()));
        //--- Desplazar el deslizador de la barra de desplazamiento al final de la lista
         m_checkbox_list.Scrolling();
        }
      //--- Redibujar el gráfico
      m_chart.Redraw();
     }
  }

Procesamiento del clic en el botón para la limpieza y reconstrucción de las listas y la tabla: 

//+------------------------------------------------------------------+
//| Manejador de eventos del gráfico                                       |
//+------------------------------------------------------------------+
void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Evento del clic en el botón
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON)
     {
      Print(__FUNCTION__," > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam);
       //--- Evento del primer botón
      if(lparam==m_simple_button1.Id())
        {
         //--- Vaciar la tabla
         m_table.Clear();
         return;
        }
      //--- Evento del segundo botón
      if(lparam==m_simple_button2.Id())
        {
         //--- Reconstruir la tabla
         m_table.Rebuilding((int)m_spin_edit3.GetValue(),(int)m_spin_edit4.GetValue(),
                            (int)m_spin_edit1.GetValue(),(int)m_spin_edit2.GetValue());
         //--- Inicialización de la tabla
         InitializeTable();
         //--- Actualizamos la tabla para visualizar los cambios
         m_table.UpdateTable();
         return;
        }
      //--- Evento del tercer botón
      if(lparam==m_simple_button3.Id())
        {
         //--- Vaciar las listas
         m_listview.Clear();
         m_checkbox_list.Clear();
         return;
        }
      //--- Evento del cuarto botón
      if(lparam==m_simple_button4.Id())
        {
         //--- Reconstruir las listas
         m_checkbox_list.Rebuilding((int)m_spin_edit5.GetValue(),(int)m_spin_edit6.GetValue());
         m_listview.Rebuilding((int)m_spin_edit5.GetValue(),(int)m_spin_edit6.GetValue());
         //--- Seleccionar el octavo elemento en la lista simple
         m_listview.SelectItem(7);
        //--- Llenar las listas con los datos
         int items_total=m_listview.ItemsTotal();
         for(int i=0; i<items_total; i++)
            m_listview.SetItemValue(i,"SYMBOL "+string(i));
         //--- Llenado de las lista de checkbox con datos, marcar cada segundo checkbox
         items_total=m_checkbox_list.ItemsTotal();
         for(int i=0; i<items_total; i++)
           {
            m_checkbox_list.SetItemValue(i,"Checkbox "+string(i));
            m_checkbox_list.SetItemState(i,(i%2!=0)? true : false);
           }
         //---
         return;
        }
      //---
      return;
     }
  }

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 tiene el siguiente aspecto:

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

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


En la siguiente versión de la librería, vamos a desarrollar los controles ya existentes y completarlos con nuevas funcionalidades. Abajo puede descargar la última versión de la librería y los archivos para las pruebas.

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 de esta serie, 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/2943

Archivos adjuntos |
Incorpore el terminal web MetaTrader 4/5 en sus páginas web, es gratuito, y además podrá ganar dinero con ello Incorpore el terminal web MetaTrader 4/5 en sus páginas web, es gratuito, y además podrá ganar dinero con ello
Los tráders ya conocen bien el terminal web, que permite comerciar en los mercados financieros directamente desde el navegador. Le proponemos que lo incorpore en su página web, es algo totalmente gratuito. Usted tiene visitas a su página, los brókeres se interesan por clientes potenciales, y nosotros proporcionamos una solución web ya lista. Para que todo ello funcione, solo es necesario que incluya un iframe en su página web.
Cómo desarrollar y poner a prueba una estrategia de opciones binarias en el Simulador de Estrategias de MetaTrader 4 Cómo desarrollar y poner a prueba una estrategia de opciones binarias en el Simulador de Estrategias de MetaTrader 4
Guía de desarrollo de estrategias para opciones binarias y su correspondiente simulación en el Simulador de Estrategias de MetaTrader 4, usando la utilidad Binary-Options-Strategy-Tester del Mercado en MQL5.com.
Interfaces gráficas X: Control "Campo de edición del texto multilínea" (build 8) Interfaces gráficas X: Control "Campo de edición del texto multilínea" (build 8)
Se considera el control «Campo de edición multilínea». A diferencia del objeto gráfico OBJ_EDIT, en esta versión no habrá limitación alguna para el número de los caracteres a introducir. Aparte de eso, se hace disponible el modo cuando el campo de edición se convierte en un sencillo editor de texto donde se puede mover el cursor usando el ratón o el teclado.
Localización automática de extremos basada en un salto de precio establecido Localización automática de extremos basada en un salto de precio establecido
Al automatizar estrategias comerciales que usen modelos gráficos, es necesario encontrar los extremos en los gráficos para su posterior procesamiento e interpretación. Los instrumentos existentes no siempre dan la posibilidad de hacer esto. Los algoritmos presentados en el artículo permiten encontrar todos los extremos en los gráficos. Los instrumentos desarrollados son igualmente efectivos tanto para trabajar en el mercado de tendencia, como para el movimiento lateral. Los datos obtenidos dependen en poca medida del marco temporal elegido, y se definen solo por la escala establecida.