Interfaces gráficas X: Campo de edición del texto, slider de imágenes y controles simples (build 5)
Índice
- Introducción
- Control «Campo de edición del texto»
- Clase para crear el control «Campo de edición del texto»
- Control «Slider de imágenes»
- Clase para crear el control «Slider de imágenes»
- Controles «Etiqueta de texto» e «Imagen»
- Clase CFonts para trabajar con las fuentes
- Lista de actualizaciones adicionales de la librería
- 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.
En este artículo vamos a analizar los controles nuevos, tales como: «Campo de edición del texto», «Slider de imágenes», así como veremos los controles simples adicionales, «Etiqueta de texto» e «Imagen» que también pueden ser útiles en diferentes situaciones. La librería sigue desarrollándose, y además de la aparición de controles nuevos, se van mejorando los que ya han sido creados anteriormente. Puesto que muchos usuarios ya utilizan esta librería en su trabajo, ellos envían diferentes observaciones y proposiciones. Muchas de sus observaciones han sido implementadas en la versión nueva de la librería. Además, algunos de los algoritmos han sido optimizados. Eso ha reducido aún más el consumo de los recursos de la CPU. Para más información sobre todo eso, lea a continuación.
Control «Campo de edición del texto»
Hasta ahora ya teníamos el control «Campo de edición» (clase CSpinEdit) en nuestra librería, pero servía sólo para la introducción de valores numéricos. Completaremos la librería con un control más, que nos permitirá introducir cualquier texto en el campo. El control «Campo de edición del texto» puede ser necesario en diferentes situaciones. Por ejemplo, se puede crear la búsqueda de una línea en los archivos del «entorno protegido» (file sandbox) del terminal. Otra opción de su uso es proporcionar al usuario final de la aplicación MQL la posibilidad de introducir por sí mismo el array de símbolos para el trading. En total, puede ser cualquier tipo de datos necesarios para el trabajo.
Vamos a nombrar todos los componentes que van a formar parte del control «Campo de edición del texto»:
- Fondo
- Icono
- Descripción
- Campo de edición
Fig. 1. Partes integrantes del control «Campo de edición del texto».
Vamos a ver con más detalles cómo está organizada la clase de este control.
Clase para crear el control «Campo de edición del texto»
Creamos el archivo TextEdit.mqh con la clase CTextEdit con los métodos estándar para todos los controles, y lo incluimos en el motor de la librería (archivo WndContainer.mqh). Abajo se listan las propiedades que estarán disponibles para la configuración personalizada del control.
- Color del fondo del control
- Iconos del control para el estado activo y bloqueado
- Márgenes del icono por dos ejes (x, y)
- Texto de la descripción del control
- Márgenes de la etiqueta de texto por dos ejes (x, y)
- Color del texto en diferentes estados del control
- Tamaños del campo de edición
- Márgenes del campo de edición por dos ejes (x, y)
- Color del campo de edición y del texto en diferentes estados
- Alineación del texto dentro del campo de edición (a la izquierda/a la derecha/centrado);
- Modo de visualización del puntero para seleccionar el texto
- Modo para resetear el valor en el campo de edición
//| Clase para crear el control Campo de edición del texto |
//+------------------------------------------------------------------+
class CTextEdit : public CElement
{
private:
//--- Color del fondo del control
color m_area_color;
//--- Iconos del control en el estado activo y bloqueado
string m_icon_file_on;
string m_icon_file_off;
//--- Márgenes del icono
int m_icon_x_gap;
int m_icon_y_gap;
//--- Texto de descripción del campo de edición
string m_label_text;
//--- Márgenes de la etiqueta de texto
int m_label_x_gap;
int m_label_y_gap;
//--- Colores del texto en diferentes estados
color m_label_color;
color m_label_color_hover;
color m_label_color_locked;
color m_label_color_array[];
//--- Valor actual en el campo de edición
string m_edit_value;
//--- Tamaños del campo de edición
int m_edit_x_size;
int m_edit_y_size;
//--- Márgenes del campo de edición
int m_edit_x_gap;
int m_edit_y_gap;
//--- Colores del campo de edición y del texto dentro en diferentes estados
color m_edit_color;
color m_edit_color_locked;
color m_edit_text_color;
color m_edit_text_color_locked;
color m_edit_text_color_highlight;
//--- Colores del borde del campo de edición en diferentes estados
color m_edit_border_color;
color m_edit_border_color_hover;
color m_edit_border_color_locked;
color m_edit_border_color_array[];
//--- Modo para resetear el valor (línea vacía)
bool m_reset_mode;
//--- Modo de visualización del puntero para seleccionar el texto
bool m_show_text_pointer_mode;
//--- Modo de alineación del texto
ENUM_ALIGN_MODE m_align_mode;
//---
public:
//--- Márgenes del icono
void IconXGap(const int x_gap) { m_icon_x_gap=x_gap; }
void IconYGap(const int y_gap) { m_icon_y_gap=y_gap; }
//--- (1) Color del fondo, (2) texto de la descripción del campo de edición, (3) márgenes de la etiqueta de texto
void AreaColor(const color clr) { m_area_color=clr; }
string LabelText(void) const { return(m_label.Description()); }
void LabelText(const string text) { m_label.Description(text); }
void LabelXGap(const int x_gap) { m_label_x_gap=x_gap; }
void LabelYGap(const int y_gap) { m_label_y_gap=y_gap; }
//--- Colores de la etiqueta de texto en diferentes estados
void LabelColor(const color clr) { m_label_color=clr; }
void LabelColorHover(const color clr) { m_label_color_hover=clr; }
void LabelColorLocked(const color clr) { m_label_color_locked=clr; }
//--- (1) Tamaños del campo de edición, (2) margen del campo de edición desde el borde derecho
void EditXSize(const int x_size) { m_edit_x_size=x_size; }
void EditYSize(const int y_size) { m_edit_y_size=y_size; }
//--- Márgenes del campo de edición
void EditXGap(const int x_gap) { m_edit_x_gap=x_gap; }
void EditYGap(const int y_gap) { m_edit_y_gap=y_gap; }
//--- Colores del campo de edición en diferentes estados
void EditColor(const color clr) { m_edit_color=clr; }
void EditColorLocked(const color clr) { m_edit_color_locked=clr; }
//--- Colores del texto del campo de edición en diferentes estados
void EditTextColor(const color clr) { m_edit_text_color=clr; }
void EditTextColorLocked(const color clr) { m_edit_text_color_locked=clr; }
void EditTextColorHighlight(const color clr) { m_edit_text_color_highlight=clr; }
//--- Colores del borde del campo de edición en diferentes estados
void EditBorderColor(const color clr) { m_edit_border_color=clr; }
void EditBorderColorHover(const color clr) { m_edit_border_color_hover=clr; }
void EditBorderColorLocked(const color clr) { m_edit_border_color_locked=clr; }
//--- (1) Modo del reseteo al pulsar en la etiqueta de texto, (2) modo de visualización del puntero de selección del texto
bool ResetMode(void) { return(m_reset_mode); }
void ResetMode(const bool mode) { m_reset_mode=mode; }
void ShowTextPointerMode(const bool mode) { m_show_text_pointer_mode=mode; }
//--- Modo de alineación del texto
void AlignMode(ENUM_ALIGN_MODE mode) { m_align_mode=mode; }
//--- Establecer los iconos para el control en el estado activo y bloqueado
void IconFileOn(const string file_path);
void IconFileOff(const string file_path);
};
El modo del visualización del puntero para seleccionar el texto significa que cuando situamos el cursor sobre el campo de edición éste va a completarse con un icono que indica que aquí podemos introducir el texto. Para que eso funcione, ha sido añadido un identificador más (MP_TEXT_SELECT) a la enumeración ENUM_MOUSE_POINTER.
//| Enumeración de los tipos de punteros |
//+------------------------------------------------------------------+
enum ENUM_MOUSE_POINTER
{
MP_CUSTOM =0,
MP_X_RESIZE =1,
MP_Y_RESIZE =2,
MP_XY1_RESIZE =3,
MP_XY2_RESIZE =4,
MP_X_SCROLL =5,
MP_Y_SCROLL =6,
MP_TEXT_SELECT =7
};
La adición correspondiente ha sido introducida en la clase CPointer (véase el código de abajo). La imagen para el icono del puntero de selección del texto va adjunta al final del artículo .
//| Pointer.mqh |
//| Copyright 2015, MetaQuotes Software Corp. |
//| http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
//--- Recursos
...
#resource "\\Images\\EasyAndFastGUI\\Controls\\pointer_text_select.bmp"
//+------------------------------------------------------------------+
//| Establecimiento de imágenes para el puntero según el tipo del puntero |
//+------------------------------------------------------------------+
void CPointer::SetPointerBmp(void)
{
switch(m_type)
{
case MP_X_RESIZE :
m_file_on ="Images\\EasyAndFastGUI\\Controls\\pointer_x_rs_blue.bmp";
m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_x_rs.bmp";
break;
case MP_Y_RESIZE :
m_file_on ="Images\\EasyAndFastGUI\\Controls\\pointer_y_rs_blue.bmp";
m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_y_rs.bmp";
break;
case MP_XY1_RESIZE :
m_file_on ="Images\\EasyAndFastGUI\\Controls\\pointer_xy1_rs_blue.bmp";
m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_xy1_rs.bmp";
break;
case MP_XY2_RESIZE :
m_file_on ="Images\\EasyAndFastGUI\\Controls\\pointer_xy2_rs_blue.bmp";
m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_xy2_rs.bmp";
break;
case MP_X_SCROLL :
m_file_on ="Images\\EasyAndFastGUI\\Controls\\pointer_x_scroll_blue.bmp";
m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_x_scroll.bmp";
break;
case MP_Y_SCROLL :
m_file_on ="Images\\EasyAndFastGUI\\Controls\\pointer_y_scroll_blue.bmp";
m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_y_scroll.bmp";
break;
case MP_TEXT_SELECT :
m_file_on ="Images\\EasyAndFastGUI\\Controls\\pointer_text_select.bmp";
m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_text_select.bmp";
break;
}
//--- Si se indica el tipo personalizado (MP_CUSTOM)
if(m_file_on=="" || m_file_off=="")
::Print(__FUNCTION__," > Para el puntero del cursor hay que establecer ambas imágenes!");
}
Para crear el elemento «Campo de edición del texto», vamos a necesitar cinco métodos privados (private) y un método público (public):
{
private:
//--- Objetos para crear el campo de edición
CRectLabel m_area;
CBmpLabel m_icon;
CLabel m_label;
CEdit m_edit;
CPointer m_text_select;
//---
public:
//--- Métodos para crear el campo de edición
bool CreateTextEdit(const long chart_id,const int subwin,const string label_text,const int x,const int y);
//---
private:
bool CreateArea(void);
bool CreateIcon(void);
bool CreateLabel(void);
bool CreateEdit(void);
bool CreateTextSelectPointer(void);
};
Por lo demás, la clase CTextEdit no tiene nada más de lo que no hemos hablado en los artículos anteriores de esta serie. Por eso, Usted puede conocer sus posibilidades por sí mismo. La versión actual del control «Campo de edición del texto» tiene limitación de 63 caracteres.
Control «Slider de imágenes»
El «Slider de imágenes» pertenece a los elementos informativos de la interfaz gráfica. Puede ser usado para crear la guía de usuario breve en la que de forma ilustrativa se muestran diversas situaciones en los gráficos de precios o descripciones breves sobre el uso de uno u otro control de la la interfaz gráfica en una aplicación MQL.
Vamos a nombrar todos los componentes que van a formar parte del control «Slider de imágenes»:
- Fondo
- Botones del cambio consecutivo de imágenes
- Grupo de botones de opción (radio buttons)
- Grupo de imágenes relacionado con el grupo de botones de opción
Fig. 2. Partes integrantes del control “Slider de imágenes”.
Clase para crear el control «Slider de imágenes»
En el directorio que contiene todos los demás archivos de los controles de la librería se crea el archivo PicturesSlider.mqh con los métodos estándar que están incluidos en los demás controles, e incluimos este archivo en el archivo WndContainer.mqh. Abajo se listan las propiedades del control que estarán disponibles para la configuración personalizada del control.
- Color del fondo del control
- Color del borde del fondo del control
- Margen para las imágenes por el eje Y
- Márgenes para los botones del cambio consecutivo de imágenes por dos ejes (x, y)
- Márgenes para los botones de opción por dos ejes (x, y)
- Margen entre los botones de opción
//| Clase para la creación del slider de imágenes |
//+------------------------------------------------------------------+
class CPicturesSlider : public CElement
{
private:
//--- Color del fondo y del borde del control
color m_area_color;
color m_area_border_color;
//--- Margen para las imágenes por el eje Y
int m_pictures_y_gap;
//--- Márgenes para los botones
int m_arrows_x_gap;
int m_arrows_y_gap;
//--- Márgenes para los botones de opción
int m_radio_buttons_x_gap;
int m_radio_buttons_y_gap;
int m_radio_buttons_x_offset;
//---
public:
//--- (1) Color del fondo y (2) borde del fondo
void AreaColor(const color clr) { m_area_color=clr; }
void AreaBorderColor(const color clr) { m_area_border_color=clr; }
//--- Márgenes para los botones de flechas
void ArrowsXGap(const int x_gap) { m_arrows_x_gap=x_gap; }
void ArrowsYGap(const int y_gap) { m_arrows_y_gap=y_gap; }
//--- Margen para las imágenes por el eje Y
void PictureYGap(const int y_gap) { m_pictures_y_gap=y_gap; }
//--- (1) Márgenes de los botones de opción, (2) distancia entre los botones de opción
void RadioButtonsXGap(const int x_gap) { m_radio_buttons_x_gap=x_gap; }
void RadioButtonsYGap(const int y_gap) { m_radio_buttons_y_gap=y_gap; }
void RadioButtonsXOffset(const int x_offset) { m_radio_buttons_x_offset=x_offset; }
};
Para crear el elemento «Slider de imágenes», vamos a necesitar cinco métodos privados (private) y un método público (public):
{
private:
//--- Objetos para crear el control
CRectLabel m_area;
CBmpLabel m_pictures[];
CRadioButtons m_radio_buttons;
CIconButton m_left_arrow;
CIconButton m_right_arrow;
//---
public:
//--- Métodos para crear el slider de imágenes
bool CreatePicturesSlider(const long chart_id,const int subwin,const int x,const int y);
//---
private:
bool CreateArea(void);
bool CreatePictures(void);
bool CreateRadioButtons(void);
bool CreateLeftArrow(void);
bool CreateRightArrow(void);
};
El ancho del control va a calcularse automáticamente a base de los parámetros establecidos por el usuario. A estos parámetros les pertenece el margen del grupo de los botones de opción desde el borde izquierdo del control, respecto al cual también se calcula la coordenada para el botón conmutador derecho del slider de imágenes. El alto del control también depende del tamaño de las imágenes. Se supone que el tamaño de las imágenes va a ser el mismo, por eso en los cálculos se utilizan las dimensiones de la primera imagen del grupo.
Antes de llamar al método principal de la creación del control, hay que añadir las imágenes al array usando el método CPicturesSlider::AddPicture(). Si no enviamos la ruta hacia la imagen como el único argumento de este método, va a aplicarse la ruta por defecto.
//| PicturesSlider.mqh |
//| Copyright 2016, MetaQuotes Software Corp. |
//| http://www.mql5.com |
//+------------------------------------------------------------------+
...
//--- Imagen predefinida
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp64\\no_image.bmp"
//+------------------------------------------------------------------+
//| Clase para crear el slider de imágenes |
//+------------------------------------------------------------------+
class CPicturesSlider : public CElement
{
private:
//--- Array de imágenes (ruta hacia las imágenes)
string m_file_path[];
//--- Ruta hacia la imagen por defecto
string m_default_path;
//---
public:
//--- Añade la imagen
void AddPicture(const string file_path="");
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CPicturesSlider::CPicturesSlider(void) : m_default_path("Images\\EasyAndFastGUI\\Icons\\bmp64\\no_image.bmp"),
m_area_color(clrNONE),
m_area_border_color(clrNONE),
m_arrows_x_gap(2),
m_arrows_y_gap(2),
m_radio_button_width(12),
m_radio_buttons_x_gap(25),
m_radio_buttons_y_gap(1),
m_radio_buttons_x_offset(20),
m_pictures_y_gap(25)
{
//--- Guardamos el nombre de la clase del control en la clase base
CElement::ClassName(CLASS_NAME);
//--- Establecemos las prioridades para el clic izquierdo del ratón
m_zorder=0;
}
//+------------------------------------------------------------------+
//| Añade la imagen |
//+------------------------------------------------------------------+
void CPicturesSlider::AddPicture(const string file_path="")
{
//--- Aumentamos el tamaño del array a un elemento
int array_size=::ArraySize(m_pictures);
int new_size=array_size+1;
::ArrayResize(m_pictures,new_size);
::ArrayResize(m_file_path,new_size);
//--- Guardamos los valores de los parámetros enviados
m_file_path[array_size]=(file_path=="")? m_default_path : file_path;
}
Para mostrar la imagen del grupo, hay que usar el método CPicturesSlider::SelectPicture(). Este método va a invocarse cuando se pulsan los botones de conmutación y los botones de opción, en el manejador de la clase CPicturesSlider.
{
public:
//--- Cambia la imagen según el índice especificado
void SelectPicture(const uint index);
};
//+------------------------------------------------------------------+
//| Indica qué imagen debe visualizarse |
//+------------------------------------------------------------------+
void CPicturesSlider::SelectPicture(const uint index)
{
//--- Obtenemos el número de imágenes
uint pictures_total=PicturesTotal();
//--- Si en el grupo no hay imágenes, avisar sobre ello
if(pictures_total<1)
{
::Print(__FUNCTION__," > La llamada a este método debe realizarse, "
"cuando en el grupo hay por lo menos una imagen! Utilice el método CPicturesSlider::AddPicture()");
return;
}
//--- Corregir el valor del índice si supera el rango
uint correct_index=(index>=pictures_total)? pictures_total-1 : index;
//--- Seleccionar el botón de opción según este índice
m_radio_buttons.SelectRadioButton(correct_index);
//--- Cambiar de la imagen
for(uint i=0; i<pictures_total; i++)
{
if(i==correct_index)
m_pictures[i].Timeframes(OBJ_ALL_PERIODS);
else
m_pictures[i].Timeframes(OBJ_NO_PERIODS);
}
}
Cuando se pulsan los botones para la conmutación consecutiva de las imágenes, en el manejador del control se llaman a los métodos CPicturesSlider::OnClickLeftArrow() y CPicturesSlider::OnClickRightArrow(). Abajo se muestra el código del método para el botón izquierdo. En caso de necesidad, los eventos del clic en los botones del slider de imágenes se puede seguir en la clase personalizada de la aplicación MQL.
{
public:
private:
//--- Procesamiento del clic en el botón izquierdo
bool OnClickLeftArrow(const string clicked_object);
//--- Procesamiento del clic en el botón derecho
bool OnClickRightArrow(const string clicked_object);
};
//+------------------------------------------------------------------+
//| Clic en el botón izquierdo |
//+------------------------------------------------------------------+
bool CPicturesSlider::OnClickLeftArrow(const string clicked_object)
{
//--- Salimos si el clic no ha sido hecho en el botón
if(::StringFind(clicked_object,CElement::ProgramName()+"_icon_button_",0)<0)
return(false);
//--- Obtenemos el identificador del control desde el nombre del objeto
int id=CElement::IdFromObjectName(clicked_object);
//--- Obtenemos el índice del control desde el nombre del objeto
int index=CElement::IndexFromObjectName(clicked_object);
//--- Salir si los identificadores de controles no coinciden
if(id!=CElement::Id())
return(false);
//--- Salir si los índices de controles no coinciden
if(index!=0)
return(false);
//--- Obtenemos el índice actual del botón de opción seleccionado
int selected_radio_button=m_radio_buttons.SelectedButtonIndex();
//--- Cambio de la imagen
SelectPicture(--selected_radio_button);
//--- Enviamos el mensaje sobre ello
::EventChartCustom(m_chart_id,ON_CLICK_BUTTON,CElement::Id(),CElement::Index(),"");
return(true);
}
Abajo se muestra el código reducido del manejador de eventos del slider de imágenes. Se ve que aquí mismo se controla el clic en los botones de opción del slider. Se puede comprender que el clic ha sido hecho en el botón de opción en el grupo local gracias al identificador del control que coincide con el identificador del slider de imágenes.
//| Manejador de eventos |
//+------------------------------------------------------------------+
void CPicturesSlider::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
{
//--- Procesamiento del evento del desplazamiento del cursor
//--- Procesamiento del evento del clic en el botón de opción
if(id==CHARTEVENT_CUSTOM+ON_CLICK_LABEL)
{
//--- Si es el botón de opción del slider, cambiar de la imagen
if(lparam==CElement::Id())
SelectPicture(m_radio_buttons.SelectedButtonIndex());
//---
return;
}
//--- Procesamiento del evento del clic izquierdo en el objeto
if(id==CHARTEVENT_OBJECT_CLICK)
{
//--- Si se pulsa el botón con flecha del slider, cambiar de la imagen
if(OnClickLeftArrow(sparam))
return;
if(OnClickRightArrow(sparam))
return;
//---
return;
}
}
Controles «Etiqueta de texto» e «Imagen»
Como adición, en la librería han sido incluidas dos clases nuevas, CTextLabel y CPicture, para la creación de los controles simples de la interfaz, «Etiqueta de texto» e «Imagen». Se puede utilizarlos como objetos separados, es decir sin vinculación estricta a algún otro control. Su contenido es muy simple. En la clase CPicture, el usuario puede modificar sólo una propiedad, es la ruta hacia la imagen. Para eso se utiliza el método CPicture::Path(). Si la ruta no está especificada, se usará la imagen predefinida. La imagen se puede cambar en cualquier momento con medios del programa después de la creación de la la interfaz gráfica de la aplicación MQL.
//| Picture.mqh |
//| Copyright 2016, MetaQuotes Software Corp. |
//| http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
#include "Window.mqh"
//--- Recursos
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp64\\no_image.bmp"
//+------------------------------------------------------------------+
//| Clase para crear la imagen |
//+------------------------------------------------------------------+
class CPicture : public CElement
{
private:
//--- Ruta hacia la imagen
string m_path;
//---
public:
//--- Devuelve/establece la ruta hacia la imagen
string Path(void) const { return(m_path); }
void Path(const string path);
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CPicture::CPicture(void) : m_path("Images\\EasyAndFastGUI\\Icons\\bmp64\\no_image.bmp")
{
//--- Guardamos el nombre de la clase del control en la clase base
CElement::ClassName(CLASS_NAME);
//--- Establecemos las prioridades para el clic izquierdo del ratón
m_zorder=0;
}
//+------------------------------------------------------------------+
//| Establecimiento de la imagen |
//+------------------------------------------------------------------+
void CPicture::Path(const string path)
{
m_path=path;
m_picture.BmpFileOn("::"+path);
m_picture.BmpFileOff("::"+path);
}
Lo que se refiere al control «Etiqueta de texto», aquí también todo es muy sencillo y el usuario puede establecer solamente cuatro propiedades:
- Texto de la etiqueta
- Color del texto
- Fuente
- Tamaño de la fuente
//| Clase para crear el la etiqueta de texto |
//+------------------------------------------------------------------+
class CTextLabel : public CElement
{
public:
//--- Devuelve/establece el texto de la etiqueta
string LabelText(void) const { return(m_label.Description()); }
void LabelText(const string text) { m_label.Description(text); }
//--- Establecimiento del (1) color, (2) fuente y (3) tamaño de la fuente de la etiqueta de texto
void LabelColor(const color clr) { m_label.Color(clr); }
void LabelFont(const string font) { m_label.Font(font); }
void LabelFontSize(const int size) { m_label.FontSize(size); }
};
Clase CFonts para trabajar con las fuentes
Para que la selección de las fuentes sea más fácil, ha sido escrita la clase adicional CFonts. En esta clase hay 187 fuentes disponibles. Son las fuentes de sistema del terminal cuya lista seguramente ya haya visto en los ajustes de algunos objetos gráficos.
Fig. 3. Fuentes de sistema del terminal.
El archivo con las fuentes (Fonts.mqh) se ubica en el directorio "MetaTrader 5\MQL5\Include\EasyAndFastGUI\Fonts.mqh" y para el acceso completo en el esquema de la librería está incluido en el archivo Objects.mqh:
//| Objects.mqh |
//| Copyright 2015, MetaQuotes Software Corp. |
//| http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Enums.mqh"
#include "Defines.mqh"
#include "..\Fonts.mqh"
#include "..\Canvas\Charts\LineChart.mqh"
#include <ChartObjects\ChartObjectSubChart.mqh>
#include <ChartObjects\ChartObjectsBmpControls.mqh>
#include <ChartObjects\ChartObjectsTxtControls.mqh>
En la clase CFonts hay solamente dos métodos públicos para obtener el tamaño del array de las fuentes y para obtener el nombre de la fuente por el índice. El array de las fuentes se inicializa en el constructor de la clase.
//| Fonts.mqh |
//| Copyright 2016, MetaQuotes Software Corp. |
//| http://www.mql5.com |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Clase para trabajar con la fuente |
//+------------------------------------------------------------------+
class CFonts
{
private:
//--- Array de las fuentes
string m_fonts[];
//---
public:
CFonts(void);
~CFonts(void);
//--- Devuelve el número de las fuentes
int FontsTotal(void) const { return(::ArraySize(m_fonts)); }
//--- Devuelve la fuente por el índice
string FontsByIndex(const uint index);
//---
private:
//--- Inicialización del array de las fuentes
void InitializeFontsArray(void);
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CFonts::CFonts(void)
{
//--- Inicialización del array de las fuentes
InitializeFontsArray();
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CFonts::~CFonts(void)
{
::ArrayFree(m_fonts);
}
Cuando se llama al método CFonts::FontsByIndex() se realiza la corrección que impide la salida fuera de los límites del array:
//| Devuelve la fuente por el índice |
//+------------------------------------------------------------------+
string CFonts::FontsByIndex(const uint index)
{
//--- Tamaño del array
uint array_size=FontsTotal();
//--- Corrección en caso de salir fuera del rango
uint i=(index>=array_size)? array_size-1 : index;
//--- Devolver la fuente
return(m_fonts[i]);
}
Lista de actualizaciones adicionales de la librería
1. Ha sido corregida la visualización incorrecta de las descripciones emergentes (tooltip) en las ventanas de diálogo. Ahora el estatus del botón de las tooltips en la ventana principal es válido para todas las demás ventanas de la interfaz gráfica cuando este botón se pulsa. Al hacer clic en el botón, se genera el mensaje con el identificador nuevo del evento ON_WINDOW_TOOLTIPS (ver archivo Defines.mqh).
//| Defines.mqh |
//| Copyright 2015, MetaQuotes Software Corp. |
//| http://www.mql5.com |
//+------------------------------------------------------------------+
...
#define ON_WINDOW_TOOLTIPS (29) // Clic en el botón «Descripción emergente»
Por lo tanto, ha sido añadido el método OnClickTooltipsButton() a la clase CWindow para el procesamiento del clic en el botón de tooltips:
//| Clase de creación del formulario para los controles |
//+------------------------------------------------------------------+
class CWindow : public CElement
{
private:
//--- Procesamiento del clic en el botón «Descripciones emergentes»
bool OnClickTooltipsButton(const string clicked_object);
};
//+------------------------------------------------------------------+
//| Manejador de eventos del gráfico |
//+------------------------------------------------------------------+
void CWindow::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
{
//--- Procesamiento del evento del clic en el objeto
if(id==CHARTEVENT_OBJECT_CLICK)
{
//--- Si el clic en el botón «Descripciones emergentes»
if(OnClickTooltipsButton(sparam))
return;
}
}
//+------------------------------------------------------------------+
//| Procesamiento del clic en el botón «Descripciones emergentes» |
//+------------------------------------------------------------------+
bool CWindow::OnClickTooltipsButton(const string clicked_object)
{
//--- Este botón no es necesario si la ventana es de diálogo
if(m_window_type==W_DIALOG)
return(false);
//--- Salimos si el clic no ha sido hecho en el botón de opción
if(::StringFind(clicked_object,CElement::ProgramName()+"_window_tooltip_",0)<0)
return(false);
//--- Obtenemos el identificador del control desde el nombre del objeto
int id=CElement::IdFromObjectName(clicked_object);
//--- Salir si los identificadores de controles no coinciden
if(id!=CElement::Id())
return(false);
//--- Recordamos el estado en el campo de la clase
m_tooltips_button_state=m_button_tooltip.State();
//--- Enviamos el mensaje sobre ello
::EventChartCustom(m_chart_id,ON_WINDOW_TOOLTIPS,CElement::Id(),CElement::Index(),"");
return(true);
}
Para que todo eso funcione en el motor de la librería (clase CWndEvents), ha sido añadido el método OnWindowTooltips() para el procesamiento de eventos con el identificador ON_WINDOW_TOOLTIPS:
{
private:
//--- Activar/desactivar descripciones emergentes
bool OnWindowTooltips(void);
};
//+------------------------------------------------------------------+
//| Evento CHARTEVENT_CUSTOM |
//+------------------------------------------------------------------+
void CWndEvents::ChartEventCustom(void)
{
//--- Si hay la señal para minimizar el formulario
//--- Si hay la señal para maximizar el formulario
//--- Si hay señal para cambiar el tamaño de los controles por el eje X
//--- Si hay señal para cambiar el tamaño de los controles por el eje Y
//--- Si hay la señal para activar/desactivar descripciones emergentes
if(OnWindowTooltips())
return;
//--- Si hay señal para ocultar los menús contextuales desde el elemento iniciador
//--- Si hay señal para cerrar todos los menús contextuales
//--- Si hay la señal para abrir la ventana de diálogo
//--- Si hay la señal para cerrar la ventana de diálogo
//--- Si hay señal para resetear el color de los controles en el formulario especificado
//--- Si hay señal para resetear las prioridades para el clic izquierdo del ratón
//--- Si hay señal para recuperar las prioridades para el clic izquierdo del ratón
}
//+------------------------------------------------------------------+
//| Evento ON_WINDOW_TOOLTIPS |
//+------------------------------------------------------------------+
bool CWndEvents::OnWindowTooltips(void)
{
//--- Si hay la señal para «Activar/desactivar descripciones emergentes»
if(m_id!=CHARTEVENT_CUSTOM+ON_WINDOW_TOOLTIPS)
return(false);
//--- Si los identificadores de la ventana coinciden
if(m_lparam!=m_windows[0].Id())
return(true);
//--- Sincronizar el modo de las descripciones emergentes entre todas las ventanas
int windows_total=WindowsTotal();
for(int w=0; w<windows_total; w++)
{
if(w>0)
m_windows[w].TooltipButtonState(m_windows[0].TooltipButtonState());
}
//---
return(true);
}
2. Ha sido añadida la posibilidad de editar el texto en la descripción de los siguientes controles tras su creación:
Fig. 4. Lista de controles con la posibilidad de editar el texto después de la creación.
3. En todos los controles donde eso podía ser necesario ahora se puede establecer la imagen (ver la tabla de abajo). Aparte de eso, ha sido añadida la posibilidad de modificar las imágenes de los controles ya después de su creación:
Fig. 5. Lista de controles con la posibilidad de cambiar la imagen después de la creación.
Para cambiar la imagen en todos los controles listados más arriba, existen los métodos IconFileOn() y IconFileOff().
4. Ha sido añadida la posibilidad de manejar mediante pprogramación todos los tipos de botones y pestañas (pulsada/suelta) después de su creación. A continuación, se muestran los controles a los que se refiere esta adición:
Fig. 6. Lista de controles con la posibilidad de cambiar el estado (pulsado/suelto) después de la creación.
5. Ha sido optimizado el algoritmo para resaltar los elementos al situar el cursor en los siguientes controles:
Fig. 7. Controles con la optimización del algoritmo del resalto de los elementos del control.
Antes, en las listas de los elementos de los controles de la tabla de arriba, el programa repasaba todos los elementos comprobando la posición del cursor sobre ellos. De esta manera, el elemento debajo del cursor se resaltaba con otro color, y para los demás se establecía el color predefinido. Pero este método requiere muchos recursos, por eso aquí era necesario realizar la optimización. Ahora en vez del ciclo por el array de los elementos en el cambio del color participan sólo dos elementos. La búsqueda en el ciclo se realiza solamente cuando ha sido notado el cambio al otro elemento, es decir cuando se cambia el foco.
A continuación veremos cómo eso ha sido implementado en la clase CListView. Para implementar todo eso, fue necesario añadir (1) el campo de la clase m_prev_item_index_focus para guardar el índice del último elemento en el foco, (2) el método CListView::CheckItemFocus() para comprobar el foco sobre el elemento y (3) cambiar el algoritmo en el método CListView::ChangeItemsColor().
//| Clase para crear la lista |
//+------------------------------------------------------------------+
class CListView : public CElement
{
private:
//--- Para determinar el momento del cambio del cursor desde un elemento al otro
int m_prev_item_index_focus;
//---
private:
//--- Cambio del color de los elementos de la lista al situar el cursor encima
void ChangeItemsColor(void);
//--- Comprobación del foco de la línea de la lista al situar el cursor encima
void CheckItemFocus(void);
};
El método CListView::CheckItemFocus() es llamado sólo si el cursor ha entrado en la zona del control (en este caso se trata de la lista – CListView), así como cuando se realiza el paso del cursor desde un elemento al otro (véase el código de abajo). En cuanto el elemento encima del cual se sitúa el cursor se encuentre, su índice se guarda.
//| Comprobación del foco de la línea de la lista al situar el cursor encima |
//+------------------------------------------------------------------+
void CListView::CheckItemFocus(void)
{
//--- Obtenemos la posición actual del deslizador de la barra de desplazamiento
int v=m_scrollv.CurrentPos();
//--- Buscamos el elemento sobre el que se sitúa el cursor y lo resaltamos
for(int i=0; i<m_visible_items_total; i++)
{
//--- Aumentamos el contador si estamos dentro del diapasón de la lista
if(v>=0 && v<m_items_total)
v++;
//--- Omitimos el elemento seleccionado
if(m_selected_item_index==v-1)
{
m_items[i].BackColor(m_item_color_selected);
m_items[i].Color(m_item_text_color_selected);
continue;
}
//--- Si el cursor se encuentra sobre este elemento, lo resaltamos
if(m_mouse.X()>m_items[i].X() && m_mouse.X()<m_items[i].X2() &&
m_mouse.Y()>m_items[i].Y() && m_mouse.Y()<m_items[i].Y2())
{
m_items[i].BackColor(m_item_color_hover);
m_items[i].Color(m_item_text_color_hover);
//--- Recordar el elemento
m_prev_item_index_focus=i;
break;
}
}
}
La llamada al método CListView::CheckItemFocus() se realiza dentro del método CListView::ChangeItemsColor() en los casos como ha sido descrito en el párrafo anterior (véase el código de abajo):
//| Cambio del color de la línea de la lista al situar el cursor encima |
//+------------------------------------------------------------------+
void CListView::ChangeItemsColor(void)
{
//--- Salimos si el resalto está desactivado al apuntar con el cursor o el srolling está activo
if(!m_lights_hover || m_scrollv.ScrollState())
return;
//--- Salimos si el control no es desplegable y el formulario está bloqueado
if(!CElement::IsDropdown() && m_wnd.IsLocked())
return;
//--- Si hemos entrado en la lista de nuevo
if(m_prev_item_index_focus==WRONG_VALUE)
{
//--- Comprobación del foco en el elemento actual
CheckItemFocus();
}
else
{
//--- Comprobamos el foco en la fila actual
int i=m_prev_item_index_focus;
bool condition=m_mouse.X()>m_items[i].X() && m_mouse.X()<m_items[i].X2() &&
m_mouse.Y()>m_items[i].Y() && m_mouse.Y()<m_items[i].Y2();
//--- Si es el paso al siguiente elemento
if(!condition)
{
//--- Resetear el color del elemento anterior
m_items[i].BackColor(m_item_color);
m_items[i].Color(m_item_text_color);
m_prev_item_index_focus=WRONG_VALUE;
//--- Comprobación del foco en el elemento actual
CheckItemFocus();
}
}
}
En el manejador de eventos CListView::OnEvent() lla llamada al método CListView::ChangeItemsColor() se realiza sólo si el cursor se encuentra dentro del área del control. En cuanto el cursor salga fuera del área del control, se establecen los colores por defecto y se resetea el valor del índice del control. Abajo se muestra la versión reducida del manejador de eventos.
//| Manejador de eventos |
//+------------------------------------------------------------------+
void CListView::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
{
//--- Procesamiento del evento del desplazamiento del cursor
if(id==CHARTEVENT_MOUSE_MOVE)
{
//--- Salir si el control está ocultado
//--- Salir si los números de las subventanas no coinciden
//--- Comprobación del foco sobre los controles
//--- Si es la lista desplegable y el botón del ratón está pulsado
//--- Desplazamos la lista si el manejo del deslizador está activado
//--- Resetear los colores del control si no se encuentra en el foco
if(!CElement::MouseFocus())
{
//--- Si ya hay un elemento en el foco
if(m_prev_item_index_focus!=WRONG_VALUE)
{
//--- Resetear los colores de la lista
ResetColors();
m_prev_item_index_focus=WRONG_VALUE;
}
return;
}
//--- Cambia el color de las líneas de la lista al situar el cursor encima
ChangeItemsColor();
return;
}
}
El mismo principio ha sido implementado en las clases CTable, CCalendar y CTreeView, pero con algunas diferencias debidas a las particularidades de cada control.
6. Al pulsar el botón tipo CIconButton en modo doble (cuando el botón no se suelta tras la pulsación), aparece otra imagen, si ha sido establecida Para establecer la imagen para el botón pulsado, se puede usar los métodos CIconButton::IconFilePressedOn() y CIconButton::IconFilePressedOff().
//| Clase para crear un botón con imagen |
//+------------------------------------------------------------------+
class CIconButton : public CElement
{
private:
//--- Iconos del botón en el estado activo, bloqueado y pulsado
string m_icon_file_on;
string m_icon_file_off;
string m_icon_file_pressed_on;
string m_icon_file_pressed_off;
//---
public:
//--- Establecer los iconos para el botón en el estado pulsado, activo y bloqueado
void IconFileOn(const string file_path);
void IconFileOff(const string file_path);
void IconFilePressedOn(const string file_path);
void IconFilePressedOff(const string file_path);
};
//+------------------------------------------------------------------+
//| Establecer la imagen para el estado pulsado «Activado» |
//+------------------------------------------------------------------+
void CIconButton::IconFilePressedOn(const string file_path)
{
//--- Salir si el modo «Dos estados» está desactivado
if(!m_two_state)
return;
//--- Guardar la ruta hacia la imagen
m_icon_file_pressed_on=file_path;
//--- Establecer si el botón está pulsado
if(m_button.State())
m_icon.BmpFileOn("::"+file_path);
}
//+------------------------------------------------------------------+
//| Establecer la imagen para el estado pulsado «Desactivado» |
//+------------------------------------------------------------------+
void CIconButton::IconFilePressedOff(const string file_path)
{
//--- Salir si el modo «Dos estados» está desactivado
if(!m_two_state)
return;
//--- Guardar la ruta hacia la imagen
m_icon_file_pressed_off=file_path;
//--- Establecer si el botón está pulsado
if(m_button.State())
m_icon.BmpFileOff("::"+file_path);
}
7. Ha sido añadida la posibilidad de seleccionar una fila en la tabla (CTable) de forma programada. Para eso se utiliza el método CTable::SelectRow(). La indicación del índice de una fila seleccionada quita la selección.
//| Clase par crear la tabla de los campos de edición |
//+------------------------------------------------------------------+
class CTable : public CElement
{
public:
//--- Selección de la fila especificada
void SelectRow(const uint row_index);
};
//+------------------------------------------------------------------+
//| Selección de la fila especificada |
//+------------------------------------------------------------------+
void CTable::SelectRow(const uint row_index)
{
//--- Corrección en caso de salir fuera del diapasón
uint index=(row_index>=(uint)m_rows_total)? m_rows_total-1 : row_index;
//--- Si está fila ya está seleccionada, quitamos la selección
bool is_selected=(index==m_selected_item);
//--- Guardamos el índice de la fila
m_selected_item=(is_selected)? WRONG_VALUE : (int)index;
//--- Guardamos la línea de la celda
m_selected_item_text=(is_selected)? "" : m_vcolumns[0].m_vrows[index];
//--- Formar la línea con los parámetros de la celda
string cell_params=string(0)+"_"+string(index)+"_"+m_vcolumns[0].m_vrows[index];
//--- Resetear el foco
m_prev_item_index_focus=WRONG_VALUE;
//--- Actualiza la tabla
UpdateTable();
//--- Resalto de la fila seleccionada
HighlightSelectedItem();
}
8. Ha sido solucionado el problema de la visualización de los controles de la pestaña seleccionada del control tipo CIconTabs. Este problema surgía al abrir y maximizar el formulario con este tipo de pestañas.
Aplicación para la prueba del control
Vamos a escribir la aplicación de prueba en el que Ud. podrá probar todos los controles nuevos y practicar con ellos activando sus diferentes modos. Vamos a crear el control «Pestañas» (clase CTabs) que va a contener cuatro pestañas con el siguiente contenido:
1. La primera pestaña:
- La barra de progreso (CProgressBar).
- Campo de edición del texto (CTextEdit).
- Combobox con lista desplegable (CCombobox).
- Campo de edición para valores numéricos (CSpinEdit).
- Botón para abrir la paleta de colores (CColorButton).
- Etiqueta de texto (CTextLabel).
Establecemos un icono para todos los controles, salvo la etiqueta de texto. Hagamos que los controles «Barra de progreso» y «Campo de edición del texto» cambien la descripción del control dentro de unos determinados intervalos de tiempo para demostrar que esta posibilidad existe. Colocaremos los nombres de todas las fuentes de la clase CFonts en la lista del combobox. Configuraremos el modelo de eventos de la aplicación MQL de prueba de tal manera que la selección de la fuente desde la lista del combobox se visualice en la etiqueta de texto. De la misma manera, el campo de edición numérico para el cambio del tamaño de la fuente y la selección del color en la paleta serán vinculados con la etiqueta de texto.
Entonces, el manejador de eventos para la gestión de los parámetros del control «Etiqueta de texto» tendrá el siguiente aspecto:
//| Manejador de eventos del gráfico |
//+------------------------------------------------------------------+
void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
{
//--- Evento de la selección en la lista
if(id==CHARTEVENT_CUSTOM+ON_CLICK_COMBOBOX_ITEM)
{
//--- Si los identificadores de los controles coinciden
if(lparam==m_combobox1.Id())
{
//--- Cambiar la fuente
m_text_label1.LabelFont(m_combobox1.ButtonText());
}
//---
return;
}
//--- Evento del clic en los botones del campo de edición
if(id==CHARTEVENT_CUSTOM+ON_CLICK_INC ||
id==CHARTEVENT_CUSTOM+ON_CLICK_DEC)
{
//--- Si los identificadores de los controles coinciden
if(lparam==m_spin_edit1.Id())
{
//--- Cambiar el tamaño de la fuente
m_text_label1.LabelFontSize(int(m_spin_edit1.GetValue()));
}
//---
return;
}
//--- Evento del cambio del color a través de la paleta de colores
if(id==CHARTEVENT_CUSTOM+ON_END_EDIT)
{
//--- Si los identificadores de los controles coinciden
if(lparam==m_spin_edit1.Id())
{
//--- Cambiar el tamaño de la fuente
m_text_label1.LabelFontSize(int(m_spin_edit1.GetValue()));
}
//---
return;
}
//--- Evento del cambio del color a través de la paleta de colores
if(id==CHARTEVENT_CUSTOM+ON_CHANGE_COLOR)
{
//--- Si los identificadores de los controles coinciden
if(lparam==m_color_picker.Id())
{
//--- Si es la respuesta del primer botón
if(sparam==m_color_button1.LabelText())
{
//--- Cambiar el color del objeto
m_text_label1.LabelColor(m_color_button1.CurrentColor());
return;
}
}
return;
}
//--- Evento del clic en el botón
if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON)
{
//--- Si ha sido pulsado el primer botón para abrir la paleta de colores
if(sparam==m_color_button1.LabelText())
{
//--- Pasar el puntero del botón, lo que abrirá automáticamente la ventana con la paleta de colores
m_color_picker.ColorButtonPointer(m_color_button1);
return;
}
//---
return;
}
}
En la captura de abajo se muestra cómo al final se puede configurar la visualización del texto mediante la interfaz gráfica desarrollada con esta librería:
Fig. 8. Grupo de controles en la primera pestaña.
2. En la segunda pestaña colocaremos el único control, «Slider de imágenes» (clase CPicturesSlider). Vamos a añadir sólo tres imágenes por defecto para que Usted pueda probar rápidamente este control personalmente. Para el trabajo correcto del este control, por favor, prepare las imágenes del mismo tamaño.
Fig. 9. «Slider de imágenes» en la segunda pestaña.
Para el cambio automático de imágenes, utilice el método CPicturesSlider::SelectPicture().
3. La tercera pestaña tendrá la tabla tipo CTable. Para la selección automática de una fila utilice el método CTable::SelectRow().
Fig. 10 Control «Tabla» en la tercera pestaña.
4. Colocaremos tres controles en la cuarta pestaña: (1) calendario, (2) lista desplegable y (3) el botón con dos imágenes diferentes para el estado pulsado/suelto.
Fig. 11. Grupo de controles de la cuarta pestaña.
Puede descargar esta aplicación de prueba al final del artículo para estudiarla más detalladamente.
Conclusión
En esta fase del desarrollo de la librería para la creación de las interfaces gráficas, su esquema general tiene el siguiente aspecto.
Fig. 12. Estructura de la librería en la fase actual del desarrollo.
La siguiente versión de la librería será ampliada con los controles adicionales. Además, vamos a mejorar y completar los controles ya existentes con nuevas posibilidades.
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/2829
- 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