Interfaces gráficas X: Gestión ampliada de las listas y tablas. Optimización de código (build 7)
Índice
- Introducción
- Cambios en el esquema de la librería y optimización del código
- Gestión de la barra de desplazamiento usando programación
- Gestión de las listas usando programación
- Optimización del código de la tabla tipo CTable
- Gestión de la tabla tipo CTable usando programación
- Aplicación para la prueba del control
- Conclusión
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
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
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
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:
- Se calcula el ancho de las columnas
- Se crea un determinado número de objetos gráficos (celdas de la tabla) para la columna insertada
- Si hace falta, la tabla se formatea en el estilo «Cebra»
- 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
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
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.
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
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso