Interfaces gráficas IX: Elementos "Indicador de progreso" y "Gráfico lineal" (Capítulo 2)
Índice
- Introducción
- Elemento «Indicador de progreso»
- Elemento «Gráfico lineal»
- Mejora de las clases de la biblioteca estándar
- Desarrollo de la clase CLineGraph
- Escribimos la aplicación para la prueba del gráfico lineal
- 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 cada artículo de la serie se muestra la lista completa de los capítulos con los enlaces. Además, se puede descargar la versión completa de la librería en la fase actual del desarrollo del proyecto. Es necesario colocar los ficheros en los mismos directorios, tal como están ubicados en el archivo.
En el artículo anterior hemos analizado dos controles interralacionados: «Paleta para seleccionar el color» y «Botón para abrir la paleta de colores». El segundo capítulo estará dedicado a dos elementos de la interfaz: hablaremos del «Indicador de progreso» y «Gráfico lineal». Como siempre mostraremos los ejemplos detallados de cómo puede usar estos elementos en sus aplicaciones MQL.
Elemento «Indicador de progreso»
Cuando se ejecuta un proceso prolongado en el tiempo, será necesario un elemento de la interfaz que pueda mostrar al usuario en qué fase de ejecución se encuentra, así como ayudar a comprender cuánto tiempo queda aproximadamente hasta su finalización. Normalmente, esta posibilidad se obtiene a través del elemento de la interfaz gráfica «Indicador de progreso». En la implementación más simple, este elemento se compone de dos rectángulos uno de los cuales muestra la duración total del proceso mostrado y el segundo muestra la parte ejecutada de este proceso. En diferentes implementaciones del indicador de progreso, se puede ver con mucha frecuencia el índice porcentual, lo que añade un grado de información aún mayor a este elemento.
Vamos a nombrar todos los componentes que van a formar parte del elemento «Indicador de progreso» en nuestra librería.
- Fondo
- Descripción
- Barra del indicador
- Fondo del indicador
- Índice porcentual
Fig. 1. Partes integrantes del control “Indicador de progreso”.
A continuación, vamos a ver cómo está organizada la clase para la creación de este elemento.
Desarrollo de la clase CProgressBar
Creamos el archivo ProgressBar.mqh con la clase CProgressBar con los métodos estándar para todos los controles, y lo incluimos en el motor de la librería (archivo WndContainer.mqh). Aquí tenemos la lista de las propiedades del elemento que están disponibles para el ajuste por el usuario.
- Color del fondo general del elemento
- Texto de la descripción
- Color del texto de la descripción
- Desplazamiento de la descripción por dos ejes (x, y)
- Color del fondo y del borde del área general del indicador
- Tamaños del fondo del área del indicador
- Grosor del borde del área del indicador
- Color de la barra del indicador
- Desplazamiento de la etiqueta de texto del índice porcentual del proceso en ejecución
- Número de dígitos después de la coma para el índice porcentual
class CProgressBar : public CElement { private: //--- Color del fondo del elemento color m_area_color; //--- Descripción del proceso mostrado string m_label_text; //--- Color del texto color m_label_color; //--- Desplazamiento de la etiqueta de texto por dos ejes int m_label_x_offset; int m_label_y_offset; //--- Color del fondo de la barra del progreso y del borde del fondo color m_bar_area_color; color m_bar_border_color; //--- Tamaños de la barra de progreso int m_bar_x_size; int m_bar_y_size; //--- Desplazamiento de la barra de progreso por dos ejes int m_bar_x_offset; int m_bar_y_offset; //--- Grosor del borde de la barra de progreso int m_bar_border_width; //--- Color del indicador color m_indicator_color; //--- Desplazamiento de la etiqueta del índice de los por cientos int m_percent_x_offset; int m_percent_y_offset; //--- Número de dígitos después de la coma int m_digits; //--- public: //--- Número de dígitos después de la coma void SetDigits(const int digits) { m_digits=::fabs(digits); } //--- (1) Color del fondo, (2) nombre del proceso y (3) color del texto void AreaColor(const color clr) { m_area_color=clr; } void LabelText(const string text) { m_label_text=text; } void LabelColor(const color clr) { m_label_color=clr; } //--- Desplazamiento de la etiqueta de texto (nombre del proceso) void LabelXOffset(const int x_offset) { m_label_x_offset=x_offset; } void LabelYOffset(const int y_offset) { m_label_y_offset=y_offset; } //--- Color del (1) fondo y (2) del borde de proceso, (3) color del indicador void BarAreaColor(const color clr) { m_bar_area_color=clr; } void BarBorderColor(const color clr) { m_bar_border_color=clr; } void IndicatorColor(const color clr) { m_indicator_color=clr; } //--- (1) Grosor del borde, (2) tamaños del área del indicador void BarBorderWidth(const int width) { m_bar_border_width=width; } void BarXSize(const int x_size) { m_bar_x_size=x_size; } void BarYSize(const int y_size) { m_bar_y_size=y_size; } //--- (1) Desplazamiento de la barra de progreso por dos ejes, (2) desplazamiento de la etiqueta del índice de los por cientos void BarXOffset(const int x_offset) { m_bar_x_offset=x_offset; } void BarYOffset(const int y_offset) { m_bar_y_offset=y_offset; } //--- Desplazamiento de la etiqueta de texto (por ciento del proceso) void PercentXOffset(const int x_offset) { m_percent_x_offset=x_offset; } void PercentYOffset(const int y_offset) { m_percent_y_offset=y_offset; } };
Para crear el elemento “Indicador de progreso”, vamos a necesitar cinco métodos privados (private) y un método público (public):
class CProgressBar : public CElement { private: //--- Objetos para crear el elemento CRectLabel m_area; CLabel m_label; CRectLabel m_bar_bg; CRectLabel m_indicator; CLabel m_percent; //--- public: //--- Métodos para crear el elemento bool CreateProgressBar(const long chart_id,const int subwin,const int x,const int y); //--- private: bool CreateArea(void); bool CreateLabel(void); bool CreateBarArea(void); bool CreateIndicator(void); bool CreatePercent(void); };
Para que el indicador de progreso trabaje tal como se espera de él, es necesario indicar el número total de pasos (iteraciones) del proceso al que este indicador «está vinculado» y el índice actual de iteraciones. Para eso vamos a necesitar dos métodos adicionales: CProgressBar::StepsTotal() y CProgressBar::CurrentIndex(). Estos métodos están destinados para el uso interno (private). Ambos métodos reciben sólo un argumento cuyo valor se corrige si es necesario, para prevenir la salida fuera del rango permitido.
class CProgressBar : public CElement { private: //--- Número de pasos del rango double m_steps_total; //--- Posición actual del indicador double m_current_index; //--- private: //--- Establecimiento de nuevos valores para el indicador void CurrentIndex(const int index); void StepsTotal(const int total); }; //+------------------------------------------------------------------+ //| Número de pasos de la barra de progreso | //+------------------------------------------------------------------+ void CProgressBar::StepsTotal(const int total) { //--- Corregir si es menos de 0 m_steps_total=(total<1)? 1 : total; //--- Corregimos el índice si salimos fuera del rango if(m_current_index>m_steps_total) m_current_index=m_steps_total; } //+------------------------------------------------------------------+ //| Posición actual del indicador | //+------------------------------------------------------------------+ void CProgressBar::CurrentIndex(const int index) { //--- Corregir si es menos de 0 if(index<0) m_current_index=1; //--- Corregimos el índice si salimos fuera del rango else m_current_index=(index>m_steps_total)? m_steps_total : index; }
Los métodos CProgressBar::StepsTotal() y CProgressBar::CurrentIndex() se llaman en el método principal para la interracción con el elemento desde fuera: CProgressBar::Update(). Precisamente a este método se le pasan los parámetros para el indicador de progreso respecto a los cuales se realizan los cálculos necesarios y su redibujo. El primer argumento será el índice de iteración del proceso (index), y el segundo será el número total de iteraciones (total). Una vez comprobados estos valores, se calcula nuevo ancho para la barra del indicador. Luego, si hace falta, estos valores se corrigen. A continuación, (1) se establece nuevo ancho para la barra del indicador, (2) se calcula el valor para el índice porcentual y se forma su cadena, y al final del método (3) se establece nuevo valor para la etiqueta de texto del índice porcentual.
class CProgressBar : public CElement { public: //--- Actualización del indicador según los valores especificados void Update(const int index,const int total); }; //+------------------------------------------------------------------+ //| Actualiza la barra de progreso | //+------------------------------------------------------------------+ void CProgressBar::Update(const int index,const int total) { //--- Establecer nuevo índice CurrentIndex(index); //--- Establecer nuevo rango StepsTotal(total); //--- Calculamos el ancho del indicador double new_width=(m_current_index/m_steps_total)*m_bar_bg.XSize(); //--- Corregir si es menos de 1 if((int)new_width<1) new_width=1; else { //--- Corregir tomando en cuenta el ancho del borde int x_size=m_bar_bg.XSize()-(m_bar_border_width*2); //--- Corregir si salimos fuera del límite if((int)new_width>=x_size) new_width=x_size; } //--- Establecemos nuevo ancho para el indicador m_indicator.X_Size((int)new_width); //--- Calculamos el por ciento y formamos la cadena double percent =m_current_index/m_steps_total*100; string desc =::DoubleToString((percent>100)? 100 : percent,m_digits)+"%"; //--- Establecemos valor nuevo m_percent.Description(desc); }
Todos los métodos para la creación y el control del indicador están preparados. Ahora vamos a probarlo y veremos cómo se comporta en la interfaz gráfica de la aplicación MQL.
Prueba del «Indicador de progreso»
Se puede coger el EA del artículo anterior para la prueba y usarlo como plantilla. Eliminamos de ahí todo, salvo el menú principal y la barra de estado. Vamos a añadir a la interfaz gráfica de este EA ocho indicadores de progreso. Para que la tarea sea más interesante, hagamos que el número de iteraciones se controle a través del control «Slider».
Declaramos en el cuerpo de la clase personalizada CProgram las instancias de los tipos necesarios de elementos y los métodos para su creación con los márgenes desde el punto extremo del formulario:
class CProgram : public CWndEvents { private: //--- Sliders CSlider m_slider1; //--- Indicadores de progreso CProgressBar m_progress_bar1; CProgressBar m_progress_bar2; CProgressBar m_progress_bar3; CProgressBar m_progress_bar4; CProgressBar m_progress_bar5; CProgressBar m_progress_bar6; CProgressBar m_progress_bar7; CProgressBar m_progress_bar8; //--- private: //--- Sliders #define SLIDER1_GAP_X (7) #define SLIDER1_GAP_Y (50) bool CreateSlider1(const string text); //--- #define PROGRESSBAR1_GAP_X (7) #define PROGRESSBAR1_GAP_Y (100) bool CreateProgressBar1(void); //--- #define PROGRESSBAR2_GAP_X (7) #define PROGRESSBAR2_GAP_Y (125) bool CreateProgressBar2(void); //--- #define PROGRESSBAR3_GAP_X (7) #define PROGRESSBAR3_GAP_Y (150) bool CreateProgressBar3(void); //--- #define PROGRESSBAR4_GAP_X (7) #define PROGRESSBAR4_GAP_Y (175) bool CreateProgressBar4(void); //--- #define PROGRESSBAR5_GAP_X (7) #define PROGRESSBAR5_GAP_Y (200) bool CreateProgressBar5(void); //--- #define PROGRESSBAR6_GAP_X (7) #define PROGRESSBAR6_GAP_Y (225) bool CreateProgressBar6(void); //--- #define PROGRESSBAR7_GAP_X (7) #define PROGRESSBAR7_GAP_Y (250) bool CreateProgressBar7(void); //--- #define PROGRESSBAR8_GAP_X (7) #define PROGRESSBAR8_GAP_Y (275) bool CreateProgressBar8(void); };
Ya hemos considerado la creación de todos los controles en los artículos anteriores, por eso aquí de ejemplo se puede mostrar el código del método sólo de uno de los indicadores de progreso (véase el código de abajo). Como se puede observar, no tiene nada que pueda suponer dificultades para la comprensión. Todo es bastante transparente y claro.
//+------------------------------------------------------------------+ //| Crea la barra de progreso 1 | //+------------------------------------------------------------------+ bool CProgram::CreateProgressBar1(void) { //--- Guardamos el puntero al formulario m_progress_bar1.WindowPointer(m_window1); //--- Coordenadas int x=m_window1.X()+PROGRESSBAR1_GAP_X; int y=m_window1.Y()+PROGRESSBAR1_GAP_Y; //--- Establecemos las propiedades antes de la creación m_progress_bar1.XSize(220); m_progress_bar1.YSize(15); m_progress_bar1.BarXSize(123); m_progress_bar1.BarYSize(11); m_progress_bar1.BarXOffset(65); m_progress_bar1.BarYOffset(2); m_progress_bar1.LabelText("Progress 01:"); //--- Creación del elemento if(!m_progress_bar1.CreateProgressBar(m_chart_id,m_subwin,x,y)) return(false); //--- Añadimos el puntero al control a la base CWndContainer::AddToElementsArray(0,m_progress_bar1); return(true); }
La llamada a los método debe realizarse en el método principal de la creación de la interfaz gráfica. En el código de abajo se muestra la versión reducida de este método que contiene sólo lo que es necesario añadir ahí:
//+------------------------------------------------------------------+ //| Crea el panel del EA | //+------------------------------------------------------------------+ bool CProgram::CreateExpertPanel(void) { //--- Creación del formulario para los controles //--- Creación de controles: // Menú principal //--- Menús contextuales //--- Sliders if(!CreateSlider1("Iterations total:")) return(false); //--- Indicadores de progreso if(!CreateProgressBar1()) return(false); if(!CreateProgressBar2()) return(false); if(!CreateProgressBar3()) return(false); if(!CreateProgressBar4()) return(false); if(!CreateProgressBar5()) return(false); if(!CreateProgressBar6()) return(false); if(!CreateProgressBar7()) return(false); if(!CreateProgressBar8()) return(false); //--- Redibujar el gráfico m_chart.Redraw(); return(true); }
Ahora hablaremos de cómo va a funcionar eso. Imitamos el proceso en el temporizador de la clase personalizada CProgram::OnTimerEvent() para todos los indicadores de progreso. Se puede controlar el número de iteraciones manualmente a través del slider, y en cada evento del temporizador hay posibilidad de obtener el valor actual en su campo de edición. Cada indicador de progreso tendrá su contador estático cuyo valor vamos a aumentar con diferentes valores de incremento. De esta manera, imitaremos que los indicadores de progreso trabajen en diferentes procesos de forma asincrónica.
//+------------------------------------------------------------------+ //| Temporizador | //+------------------------------------------------------------------+ void CProgram::OnTimerEvent(void) { CWndEvents::OnTimerEvent(); //--- Número de iteraciones int total=(int)m_slider1.GetValue(); //--- Ocho barras de progreso static int count1=0; count1=(count1>=total) ? 0 : count1+=8; m_progress_bar1.Update(count1,total); //--- static int count2=0; count2=(count2>=total) ? 0 : count2+=3; m_progress_bar2.Update(count2,total); //--- static int count3=0; count3=(count3>=total) ? 0 : count3+=12; m_progress_bar3.Update(count3,total); //--- static int count4=0; count4=(count4>=total) ? 0 : count4+=6; m_progress_bar4.Update(count4,total); //--- static int count5=0; count5=(count5>=total) ? 0 : count5+=18; m_progress_bar5.Update(count5,total); //--- static int count6=0; count6=(count6>=total) ? 0 : count6+=10; m_progress_bar6.Update(count6,total); //--- static int count7=0; count7=(count7>=total) ? 0 : count7+=1; m_progress_bar7.Update(count7,total); //--- static int count8=0; count8=(count8>=total) ? 0 : count8+=15; m_progress_bar8.Update(count8,total); //--- Temporizador para la barra de estado static int count9=0; if(count9<TIMER_STEP_MSC*10) { count9+=TIMER_STEP_MSC; return; } count9=0; m_status_bar.ValueToItem(1,::TimeToString(::TimeLocal(),TIME_DATE|TIME_SECONDS)); }
Por favor, compile el programa y cárguelo en el gráfico. El resultado será el siguiente:
Fig. 2. Prueba del control “Indicador de progreso”.
¡No está mal! Más abajo, en la aplicación de prueba para el gráfico lineal, probaremos otra vez el indicador de progreso en un ejemplo más concreto.
Elemento «Gráfico lineal»
El gráfico lineal permite visualizar los arrays de datos en el área rectangular destinada para ello con la escala vertical y horizontal, donde los puntos se unen consecutivamente con una línea. Es difícil menospreciar la utilidad de esta herramienta. Por ejemplo, se puede crear el gráfico donde se puede mostrar el balance y la equidad en la cuenta del trader. Según mi opinión, no es muy conveniente usar para eso el tipo estándar de los programas del terminal MetaTrader, como «Indicador», puesto que no hay posibilidad de demostrar ahí el array de datos completo en la parte visible de la ventana.
Para los gráficos del terminal se puede establecer la escala sólo de 0 (el mayor grado de compresión) hasta 5. En el mayor grado de compresión, se supone que a un elemento del array se le asigna un píxel, y si el array completo de datos no cabe en el área asignada, hay posibilidad de usar el desplazamiento del gráfico. La segunda opción es meter más datos: pasar al período de tiempo mayor, y si tenemos puesto el tipo «Velas» o «Barras», por lo menos se puede ver el diapasón entero de valores para el período seleccionado.
Nos gustaría obtener la posibilidad cuando la escalabilidad del gráfico no se limitaría en nada, y se podría meter el array con datos de cualquier tamaño en el área con el ancho empezando incluso de un píxel. Además, es necesario que los datos siempre se ubiquen justamente desde el principio del área hasta su final. Es decir, el primer punto de datos (elemento del array) debe estar magnetizado hacia el lado izquierdo del área, y el último punto, hacia el lado derecho. Por ejemplo, precisamente con esta calidad se hacen los gráficos en Excel. Ahí se puede meter en el gráfico el array de cualquier tamaño sin perder los mínimos y los máximos.
Como ejemplo, en la captura de pantalla de abajo se muestra el gráfico lineal de Excel, en el que ha sido metido el array de 50000 elementos sin perder los mínimos y los máximos.
Fig. 3. Gráfico lineal en Excel. El tamaño del array de datos es de 50000 elementos.
En la librería estándar, los desarrolladores del terminal ofrecen las clases para crear varios tipos de gráficos. Se ubican en el directorio <carpeta del terminal>\MQLX\Include\Canvas\Charts. Vamos a nombrar estas clases en la lista:
- CChartCanvas – clase base para la creación de uno de tres tipos de gráficos.
- CLineChart – clase derivada de CChartCanvas para crear el gráfico lineal.
- CHistogramChart – clase derivada de CChartCanvas para crear el histograma.
- CPieChart – clase derivada de CChartCanvas para crear el diagrama circular.
Para que podamos usar ahora mismo los gráficos en las interfaces gráficas de nuestras aplicaciones MQL, como solución intermedia usaremos lo que ofrece la librería estándar. Algunos métodos de estas clases serán modificados, algunos serán sustituidos por completo. Además, añadiremos las posibilidades adicionales. Posteriormente, en el proceso de la escritura de los futuros artículos, intentaremos crear la librería adicional para dibujar los gráficos de mayor calidad.
Mejora de las clases de la biblioteca estándar
Como ya hemos mencionado antes, los archivos con las clases necesarias se encuentran en el directorio: <carpeta del terminal>\MQLX\Include\Canvas\Charts. Vamos a crear en el directorio de nuestra librería (<carpeta del terminal>\MQLX\Include\EasyAndFastGUI\Canvas ) la carpeta Charts y copiaremos ahí los archivos ChartCanvas.mqh y LineChart.mqh desde la librería estándar.
Primero modificaremos la clase base CChartCanvas. Ahora su clase base es CCanvas. Antes, la copia de la clase CCanvas de la librería estándar ha sido adaptada para nuestra librería recibiendo el nombre CCustomCanvas. Ahora hay que hacerla como la clase base para CChartCanvas. Abajo se muestran las líneas que han sido modificadas en el archivo ChartCanvas.mqh.
//+------------------------------------------------------------------+ //| ChartCanvas.mqh | //| Copyright 2009-2016, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "..\CustomCanvas.mqh" ... //+------------------------------------------------------------------+ //| Class CChartCanvas | //| Usage: base class for graphical charts | //+------------------------------------------------------------------+ class CChartCanvas : public CCustomCanvas { ...
Me gustaría que el fondo de los gráficos sea de gradiente. Vamos a añadir esta posibilidad. Para poder implementar esta opción, incluiremos el archivo con la clase para trabajar con el color (CColors) en el archivo CustomCanvas.mqh:
//+------------------------------------------------------------------+ //| CustomCanvas.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "..\Colors.mqh" ...
Ahora se puede declarar la instancia de la clase CChartCanvas en esta clase y usarla en nuestro proyecto.
En esta fase del desarrollo, puesto que se plantea escribir desde cero la librería para crear los gráficos, no hay sentido de mostrar la descripción detallada de cómo están organizados todos los métodos de las clases que vamos a usar ahora. Indicaremos brevemente sólo los métodos que han sido modificados y los que han sido añadidos para que Usted pueda hacer comparación personalmente, en caso de necesidad.
Pues bien, vamos a nombrarlos. Para hacer un fondo de gradiente, hacen falta por lo menos dos colores. En la clase CChartCanvas ya existe el método para especificar el color del fondo: CChartCanvas::ColorBackground(). Añadimos el método parecido CChartCanvas::ColorBackground2() para establecer el segundo color. Necesitamos el método especial para inicializar el array con los colores de gradiente, igual que en la clase CElements (el método InitColorArray(). Para dibujar el gradiente, hay que cambiar el método CChartCanvas::DrawBackground(). Si antes era suficiente llenar el fondo con un color simplemente llamando a CCustomCanvas::Erase() y pasándole el color necesario como argumento, ahora primero hay que (1) declarar el array y establecerle el tamaño igual al alto del lienzo, (2) inicializarlo con los colores de gradiente, (3) dibujar en el ciclo cada línea usando los colores desde este array. Para resetar los arrays y eliminar los objetos, hay que añadir otro método más CChartCanvas::DeleteAll() a la clase.
Todas las demás modificaciones en la clase CChartCanvas están relacionadas con los métodos donde se calculan y se establecen los márgenes, el fondo, tamaños de los marcadores en la descripción de las series y el estilo de la cuadrícula. Usted puede estudiar más detalladamente el código de todas estas adiciones y modificaciones en los archivos adjuntos al artículo, comparándolas con la versión inicial desde la librería estándar.
A continuación, veremos las modificaciones en la clase CLineChart. Su clase base es CChartCanvas. Aquí también es necesario crear el método para la eliminación de los objetos, CLineChart::DeleteAll(). Aquí las modificaciones principales están relacionadas con el dibujo de datos en el gráfico a través del método CLineChart::DrawData(). En primer lugar, ha recibido tres métodos adionales: CLineChart::CheckLimitWhile(), CLineChart::CalculateVariables() y CLineChart::CalculateArray(), en los que se realizan los cálculos. Y lo más importante es que ha sido corregido el algoritmo de compresión los datos mostrados en el gráfico. En la versión que se propone, la compresión se realiza de tal manera que si el ancho del área asignada del gráfico es superado por el ancho del array de los píxeles mostrados, este array se divide en el número de partes igual al número de píxeles que salen del lado derecho del gráfico. Luego, cada parte (salvo la primera) se solapa sobre la anterior. De esta manera, el saliente del lado derecho se nivela, el primer elemento del array siempre está anclado al lado derecho, así como no se pierden visualmente los datos respecto a los mínimos y los máximos.
Ahora hay que crear la clase para la creación del elemento «Gráfico lineal» semejante a las clases de otros controles de la librería que han sido considerados en los artículos anteriores de la serie.
Desarrollo de la clase CLineGraph
En primer lugar, hay que crear el nuevo tipo del objeto en el archivo Objects.mqh. Será la clase CLineChartObject parecida a todas las clases en el archivo. Su clase base será CLineChart que es necesario incluir en el archivo Objects.mqh:
//+------------------------------------------------------------------+ //| Objects.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "..\Canvas\Charts\LineChart.mqh" ...
Luego, creamos el archivo LineGraph.mqh con la clase CLineGraph y lo incluimos en el motor de la librería (WndContainer.mqh): La clase CLineGraph debe contener los métodos virtuales estándar, igual que todos los demás controles de la librería:
//+------------------------------------------------------------------+ //| Clase para crear el gráfico lineal | //+------------------------------------------------------------------+ class CLineGraph : public CElement { private: //--- Puntero al formulario al que está adjuntado el control CWindow *m_wnd; public: //--- Guarda el puntero del formulario void WindowPointer(CWindow &object) { m_wnd=::GetPointer(object); } //--- public: //--- Manejador de eventos del gráfico virtual void OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) {} //--- Temporizador virtual void OnEventTimer(void) {} //--- Desplazamiento del control virtual void Moving(const int x,const int y); //--- (1) Mostrar, (2) ocultar, (3) resetear, (4) eliminar virtual void Show(void); virtual void Hide(void); virtual void Reset(void); virtual void Delete(void); };
Las propiedades que estarán disponibles para el ajuste de la apariencia del gráfico lineal son las siguientes:
- Colores del gradiente del fondo
- Color del borde
- Color de la cuadrícula
- Color del texto
- Número de dígitos después de la coma (para los valores en la escala vertical)
class CLineGraph : public CElement { private: //--- Colores del gradiente color m_bg_color; color m_bg_color2; //--- Color del borde color m_border_color; //--- Color de la cuadrícula color m_grid_color; //--- Color del texto color m_text_color; //--- Número de dígitos después de la coma int m_digits; //--- public: //--- Número de dígitos después de la coma void SetDigits(const int digits) { m_digits=::fabs(digits); } //--- Dos colores para el gradiente void BackgroundColor(const color clr) { m_bg_color=clr; } void BackgroundColor2(const color clr) { m_bg_color2=clr; } //--- Colores del (1) borde, (2) cuadrícula (3) y texto void BorderColor(const color clr) { m_border_color=clr; } void GridColor(const color clr) { m_grid_color=clr; } void TextColor(const color clr) { m_text_color=clr; } };
Para crear el gráfico lineal, vamos a necesitar un método privado (private) y uno público (public):
class CLineGraph : public CElement { public: //--- Métodos para crear el elemento bool CreateLineGraph(const long chart_id,const int subwin,const int x,const int y); //--- private: bool CreateGraph(void); };
Y por último, para el trabajo con el elemento «Gráfico lineal» después de su creación, vamos a usar los métodos que no permitirán lo siguiente:
- establecer el número máximo de las series en el gráfico;
- establecer el mínimo/máximo de la escala vertical y el número de líneas de la cuadrícula;
- agregar una línea de datos;
- actualizar una línea de datos;
- eliminar una línea de datos.
class CLineGraph : public CElement { public: //--- Número máximo de las líneas de datos void MaxData(const int total) { m_line_chart.MaxData(total); } //--- Establecer los parámetros de la escala vertical void VScaleParams(const double max,const double min,const int num_grid); //--- Añadir una línea en el gráfico void SeriesAdd(double &data[],const string descr,const color clr); //--- Actualizar línea en el gráfico void SeriesUpdate(const uint pos,const double &data[],const string descr,const color clr); //--- Eliminar línea del gráfico void SeriesDelete(const uint pos); };
Este mínimo es suficiente para poder usar los gráficos lineales en sus aplicaciones MQL. Ahora crearemos la aplicación que permitirá probar cómo funciona todo eso.
Escribimos la aplicación para la prueba del gráfico lineal
Para la prueba podemos hacer la copia del EA que hemos usado en este artículo para probar el «Indicador de progreso». Eliminamos de ahí todos los controles, salvo la barra de estado. Vamos a aclarar qué controles tenemos que crear en la interfaz gráfica para manejar el gráfico lineal.
Implementamos el modo en el que va a realizarse la adición y la eliminación automática de datos en los arrays de la serie. Este proceso va a controlarse en el temporizador de la clase personalizada CProgram. Hagamos que la velocidad de este proceso también pueda controlarse indicando en el campo de edición el retardo en milisegundos (parámetro Delay). Introduciremos para este modo dos controles más en cuyos campos de edición podremos especificar el rango, es decir el número mínimo (parámetro Min. limit size) y máximo (parámetro Max. limit size) de elementos en los arrays. Si activamos este modo, los arrays van a aumentarse a un elemento con cada evento del temporizador y después de eso empezarán a reducirse hasta llegar al tamaño mínimo establecido. Después de eso, todo se repite. Además, en el campo de edición del control separado va a mostrarse el tamaño actual de los arrays (parámetro Size of series) que también puede manejarse manualmente.
El número de series (filas de datos) se puede ajustar seleccionando de la lista desplegable de 1 serie hasta 24 (parámetro Number of series). Los datos van a calcularse a través de las fórmulas trigonométricas usando los valores devueltos de las funciones matemáticas del seno y coseno. Añadimos los elementos que permitirán controlar los parámetros que participan en los cálculos. Aquí será el (1) ratio del incremento (parámetro Increment ratio) y (2) desplazamiento de cada serie respecto a la fila anterior de datos (parámetro Offset series). Cambiando el ratio del incremento, se puede obtener diferentes visualizaciones de las series. El parámetro Increment ratio tendrá su casilla de verificación (checkbox) mediante la cual se podrá habilitar el cambio automático al siguiente valor, cuando se finalice el ciclo del incremento/reducción del tamaño de la serie, si este modo está activado. Aquí el incremento del ratio va a realizarse hasta que no lleguemos a la limitación máxima en el propio control. Una vez alcanzada la limitación, el contador dará la vuelta para la reducción de los valores del parámetro Increment ratio. En otras palabras, el contador va a dar la vuelta en cuanto se alcance el valor mínimo o máximo.
Para que el experimento sea aún más interesante, vamos a implementar el modo que permitirá hacer la animación de las series «corrientes», así como añadiremos el parámetro adicional que controle el paso del desplazamiento de las series (Run speed). Aparte de eso, añadiremos a la interfaz gráfica el indicador de progreso. Si esta activado el modo de adición y reducción automática de los datos a los arrays de las series, este indicador va a mostrar qué es lo que queda hasta la finalización de este proceso.
Pues bien. Declaramos las instancias de las clases de los controles necesarios para la creación de la interfaz gráfica del programa, así como los métodos para su creación y los márgenes desde el punto extremo del formulario:
class CProgram : public CWndEvents { private: //--- Controles CSpinEdit m_delay_ms; CComboBox m_series_total; CCheckBoxEdit m_increment_ratio; CSpinEdit m_offset_series; CSpinEdit m_min_limit_size; CCheckBoxEdit m_max_limit_size; CCheckBoxEdit m_run_speed; CSpinEdit m_series_size; CLineGraph m_line_chart; CProgressBar m_progress_bar; //--- private: //--- Controles para manejar el gráfico lineal #define SPINEDIT1_GAP_X (7) #define SPINEDIT1_GAP_Y (25) bool CreateSpinEditDelay(const string text); #define COMBOBOX1_GAP_X (7) #define COMBOBOX1_GAP_Y (50) bool CreateComboBoxSeriesTotal(const string text); #define CHECKBOX_EDIT1_GAP_X (161) #define CHECKBOX_EDIT1_GAP_Y (25) bool CreateCheckBoxEditIncrementRatio(const string text); #define SPINEDIT2_GAP_X (161) #define SPINEDIT2_GAP_Y (50) bool CreateSpinEditOffsetSeries(const string text); #define SPINEDIT3_GAP_X (330) #define SPINEDIT3_GAP_Y (25) bool CreateSpinEditMinLimitSize(const string text); #define CHECKBOX_EDIT2_GAP_X (330) #define CHECKBOX_EDIT2_GAP_Y (50) bool CreateCheckBoxEditMaxLimitSize(const string text); #define CHECKBOX_EDIT3_GAP_X (501) #define CHECKBOX_EDIT3_GAP_Y (25) bool CreateCheckBoxEditRunSpeed(const string text); #define SPINEDIT4_GAP_X (501) #define SPINEDIT4_GAP_Y (50) bool CreateSpinEditSeriesSize(const string text); //--- Gráfico lineal #define LINECHART1_GAP_X (5) #define LINECHART1_GAP_Y (75) bool CreateLineChart(void); //--- Indicador de progreso #define PROGRESSBAR1_GAP_X (5) #define PROGRESSBAR1_GAP_Y (364) bool CreateProgressBar(void); };
En los artículos anteriores, ya hemos considerado todos los métodos para la creación de los controles en la clase personalizada. Por eso aquí mostraremos sólo el código del método para la creación del gráfico lineal. Pero antes de eso, hay que crear los métodos para el trabajo con los arrays y para el cálculo de datos para ellos. Declaramos la estructura Series con el array para mostrar los datos data[] y con el array auxiliar data_temp[] para los cálculos previos. Además, necesitamos los arrays donde van a almacenarse los colores y descripciones de las series.
class CProgram : public CWndEvents { private: //--- Estructura de las series en el gráfico struct Series { double data[]; // array de datos a mostrar double data_temp[]; // array auxiliar para los cálculos }; Series m_series[]; //--- (1) Nombres y (2) colores de las series string m_series_name[]; color m_series_color[]; };
Usando el método CProgram::ResizeDataArrays() se puede establecer nuevo tamaño para los arrays. El número actual de las series y sus tamaños se obtienen desde los controles:
class CProgram : public CWndEvents { private: //--- Establecer nuevo tamaño para las series void ResizeDataArrays(void); }; //+------------------------------------------------------------------+ //| Establecer nuevo tamaño para los arrays | //+------------------------------------------------------------------+ void CProgram::ResizeDataArrays(void) { int total =(int)m_series_total.ButtonText(); int size_of_series =(int)m_series_size.GetValue(); //--- for(int s=0; s<total; s++) { //--- Establecer nuevo tamaño para los arrays ::ArrayResize(m_series[s].data,size_of_series); ::ArrayResize(m_series[s].data_temp,size_of_series); } }
Después de establecer el tamaño, hay que inicializar el array para los cálculos con datos nuevos. Para eso aplicamos el método CProgram::InitArrays(). En los cálculos preliminares, vamos a utilizar los parámetros desde los campos de edición de los controles Offset series e Increment ratio. Para el modo de las series «corrientes» vamos a necesitar el contador m_run_speed_counter. Durante la llamada al método CProgram::ShiftLineChartSeries() en el temporizador, este contador va a aumentarse por el valor desde el campo de edición del elemento Run Speed, si el checkbox de este elemento está activado.
class CProgram : public CWndEvents { private: //--- Contador de la velocidad de la serie «corriente» double m_run_speed_counter; //--- Inicialización de los arrays auxiliares para los cálculos void InitArrays(void); //--- Desplazamiento de la serie del gráfico lineal (gráfico corriente) void ShiftLineChartSeries(void); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CProgram::CProgram(void) : m_run_speed_counter(0.0) { //--- ... } //+------------------------------------------------------------------+ //| Desplazamiento de la serie del gráfico lineal | //+------------------------------------------------------------------+ void CProgram::ShiftLineChartSeries(void) { if(m_run_speed.CheckButtonState()) m_run_speed_counter+=m_run_speed.GetValue(); } //+------------------------------------------------------------------+ //| Inicialización de los arrays auxiliares para los cálculos | //+------------------------------------------------------------------+ void CProgram::InitArrays(void) { int total=(int)m_series_total.ButtonText(); //--- for(int s=0; s<total; s++) { int size_of_series=::ArraySize(m_series[s].data_temp); //--- for(int i=0; i<size_of_series; i++) { if(i==0) { if(s>0) m_series[s].data_temp[i]=m_series[s-1].data_temp[i]+m_offset_series.GetValue(); else m_series[s].data_temp[i]=m_run_speed_counter; } else m_series[s].data_temp[i]=m_series[s].data_temp[i-1]+(int)m_increment_ratio.GetValue(); } } }
Como ejemplo, hagamos el cálculo de las series usando tres fórmulas que se puede eligir en los parámetros externos del EA. Para eso declaramos la enumeración ENUM_FORMULA. Pasaremos también el ajuste de los parámetros del color de la serie a los parámetros externos (véase el código de abajo).
//--- Enumeración de las funciones enum ENUM_FORMULA { FORMULA_1=0, // Formula 1 FORMULA_2=1, // Formula 2 FORMULA_3=2 // Formula 3 }; //--- Parámetros externos input ENUM_FORMULA Formula =FORMULA_1; // Formula input color ColorSeries_01 =clrRed; // Color series 01 input color ColorSeries_02 =clrDodgerBlue; // Color series 02 input color ColorSeries_03 =clrWhite; // Color series 03 input color ColorSeries_04 =clrYellow; // Color series 04 input color ColorSeries_05 =clrMediumPurple; // Color series 05 input color ColorSeries_06 =clrMagenta; // Color series 06
La puesta del tamaño inicial de los arrays de la serie, así como la inicialización de los arrays de las descripciones y del color de las series se realiza en el constructor de la clase CProgram:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CProgram::CProgram(void) : m_run_speed_counter(0.0) { //--- Establecer el tamaño de los arrays de las series int number_of_series=24; ::ArrayResize(m_series,number_of_series); ::ArrayResize(m_series_name,number_of_series); ::ArrayResize(m_series_color,number_of_series); //--- Inicialización del array de los nombres de las series for(int i=0; i<number_of_series; i++) m_series_name[i]="Series "+string(i+1); //--- Inicialización de arrays del color de las series m_series_color[0] =m_series_color[6] =m_series_color[12] =m_series_color[18] =ColorSeries_01; m_series_color[1] =m_series_color[7] =m_series_color[13] =m_series_color[19] =ColorSeries_02; m_series_color[2] =m_series_color[8] =m_series_color[14] =m_series_color[20] =ColorSeries_03; m_series_color[3] =m_series_color[9] =m_series_color[15] =m_series_color[21] =ColorSeries_04; m_series_color[4] =m_series_color[10] =m_series_color[16] =m_series_color[22] =ColorSeries_05; m_series_color[5] =m_series_color[11] =m_series_color[17] =m_series_color[23] =ColorSeries_06; }
Para el cálculo de las series según la fórmula especificada en los parámetros externos, aplicaremos el método CProgram::CalculateSeries():
class CProgram : public CWndEvents { private: //--- Calcular las series void CalculateSeries(void); }; //+------------------------------------------------------------------+ //| Calcula las series | //+------------------------------------------------------------------+ void CProgram::CalculateSeries(void) { int total=(int)m_series_total.ButtonText(); //--- for(int s=0; s<total; s++) { int size_of_series=::ArraySize(m_series[s].data_temp); //--- for(int i=0; i<size_of_series; i++) { m_series[s].data_temp[i]+=m_offset_series.GetValue(); //--- switch(Formula) { case FORMULA_1 : m_series[s].data[i]=::sin(m_series[s].data_temp[i])-::cos(m_series[s].data_temp[i]); break; case FORMULA_2 : m_series[s].data[i]=::sin(m_series[s].data_temp[i]-::cos(m_series[s].data_temp[i])); break; case FORMULA_3 : m_series[s].data[i]=::sin(m_series[s].data_temp[i]*10)-::cos(m_series[s].data_temp[i]); break; } } } }
Después de que los datos calculados de las series hayan sido introducidos en los arrays, se puede (1) añadirlos al gráfico usando el método CProgram::AddSeries() o si han sido añadidos antes, se puede, (2) actualizarlos usando el método CProgram::UpdateSeries().
class CProgram : public CWndEvents { private: //--- Añadir las series al gráfico void AddSeries(void); //--- Actualizar las series en el gráfico void UpdateSeries(void); }; //+------------------------------------------------------------------+ //| Calcula y coloca las series en el diagrama | //+------------------------------------------------------------------+ void CProgram::AddSeries(void) { int total=(int)m_series_total.ButtonText(); for(int s=0; s<total; s++) m_line_chart.SeriesAdd(m_series[s].data,m_series_name[s],m_series_color[s]); } //+------------------------------------------------------------------+ //| Calcula y actualiza las series en el diagrama | //+------------------------------------------------------------------+ void CProgram::UpdateSeries(void) { int total=(int)m_series_total.ButtonText(); for(int s=0; s<total; s++) m_line_chart.SeriesUpdate(s,m_series[s].data,m_series_name[s],m_series_color[s]); }
Inmediatamente después de la creación del gráfico lineal, (1) se establece el tamaño para los arrayas de las series y (2) se realiza la inicialización de los arrays auxiliares. Después de eso, las series (3) se calculan y (4) se añaden al gráfico.
//+------------------------------------------------------------------+ //| Crea el gráfico lineal | //+------------------------------------------------------------------+ bool CProgram::CreateLineChart(void) { //--- Guardamos el puntero a la ventana m_line_chart.WindowPointer(m_window1); //--- Coordenadas int x=m_window1.X()+LINECHART1_GAP_X; int y=m_window1.Y()+LINECHART1_GAP_Y; //--- Establecemos las propiedades antes de la creación m_line_chart.XSize(630); m_line_chart.YSize(280); m_line_chart.BorderColor(clrSilver); m_line_chart.VScaleParams(2,-2,4); m_line_chart.MaxData(int(m_series_total.ButtonText())); //--- Creamos el control if(!m_line_chart.CreateLineGraph(m_chart_id,m_subwin,x,y)) return(false); //--- (1) Establecemos el tamaño para los arrays y (2) los inicializamos ResizeDataArrays(); InitArrays(); //--- (1) Calculamos y (2) añadimos las series al gráfico CalculateSeries(); AddSeries(); //--- Añadimos el puntero al control a la base CWndContainer::AddToElementsArray(0,m_line_chart); return(true); }
Habrá que repetir muy a menudo la misma secuencia de acciones, pero con la actualización de la serie, con la interacción con los controles para el gráfico lineal. Por eso escribiremos el método adicional CProgram::RecalculatingSeries() que facilitará el código debido a la llamada sólo a un método en vez de cuatro:
class CProgram : public CWndEvents { private: //--- Recálculo de las series en el gráfico void RecalculatingSeries(void); }; //+------------------------------------------------------------------+ //| Recálculo de las series en el gráfico | //+------------------------------------------------------------------+ void CProgram::RecalculatingSeries(void) { //--- (1) Establecemos el tamaño para los arrays y (2) los inicializamos ResizeDataArrays(); InitArrays(); //--- (1) Calculamos y (2) actualizamos las series CalculateSeries(); UpdateSeries(); }
En esta fase del desarrollo, si cargamos la aplicación en el gráfico, veremos el siguiente resultado:
Fig. 4. Prueba del control «Gráfico lineal».
Si el checkbox del parámetro Max. limit size está activado, se inicia el modo del cambio automático de los arrays de las series en el método CProgram::AutoResizeLineChartSeries(). Este algoritmo ya ha sido descrito al detalle al principio de esta sección, por eso para estudiar el código de este método (véase el código de abajo), bastará con comentarios detallados.
class CProgram : public CWndEvents { private: //--- Cambio automático del tamaño de las series del gráfico lineal void AutoResizeLineChartSeries(void); }; //+------------------------------------------------------------------+ //| Cambio automático del tamaño de las series del gráfico lineal | //+------------------------------------------------------------------+ void CProgram::AutoResizeLineChartSeries(void) { //--- Salir el aumento del array de las series por el temporizador está desactivado if(!m_max_limit_size.CheckButtonState()) return; //--- Para indicar la dirección del cambio del tamaño de los arrays static bool resize_direction=false; //--- Si llegamos al mínimo del tamaño del array if((int)m_series_size.GetValue()<=m_min_limit_size.GetValue()) { //--- Cambiamos la dirección hacia el aumento del array resize_direction=false; //--- Si hace falta cambiar el valor X if(m_increment_ratio.CheckButtonState()) { //--- Para indicar la dirección del contador del ratio del incremento static bool increment_ratio_direction=true; //--- Si el contador está hacia el aumento if(increment_ratio_direction) { //--- Si hemos llegado a la limitación máxima, cambiamos la dirección del contador a la contraria if(m_increment_ratio.GetValue()>=m_increment_ratio.MaxValue()-1) increment_ratio_direction=false; } //--- Si el contador está hacia la reducción else { //--- Si hemos llegado a la limitación mínima, cambiamos la dirección del contador a la contraria if(m_increment_ratio.GetValue()<=m_increment_ratio.MinValue()+1) increment_ratio_direction=true; } //--- Obtenemos el valor actual del parámetro "Increment ratio" y lo cambiamos según la dirección especificada int increase_value=(int)m_increment_ratio.GetValue(); m_increment_ratio.ChangeValue((increment_ratio_direction)? ++increase_value : --increase_value); } } //--- Cambiamos la dirección hacia la disminución del array si hemos llegado hacia el máximo if((int)m_series_size.GetValue()>=m_max_limit_size.GetValue()) resize_direction=true; //--- Si el indicador de progreso está activado, mostramos el proceso if(m_progress_bar.IsVisible()) { if(!resize_direction) m_progress_bar.Update((int)m_series_size.GetValue(),(int)m_max_limit_size.GetValue()); else m_progress_bar.Update(int(m_max_limit_size.GetValue()-m_series_size.GetValue()),(int)m_max_limit_size.GetValue()); } //--- Cambiamos el tamaño del array según la dirección int size_of_series=(int)m_series_size.GetValue(); m_series_size.ChangeValue((!resize_direction)? ++size_of_series : --size_of_series); //--- Establecemos nuevo tamaño par los arrays ResizeDataArrays(); }
Como ya hemos mencionado antes, la animación de los gráficos va a realizarse en el temporizador. Colocamos todas las acciones necesarias en el método CProgram::UpdateLineChartByTimer(). El programa abandona el método si (1) el formulario está plegado, si (2) los modos en lo que hace falta actualizar las series por el temporizador están desactivados. Además de eso, otro obstáculo puede ser el retardo en el campo de eición Delay. Si todas las comprobaciones han sido superadas, se realizan los cálculos necesarios para los modos activados y se actualizan las series en el gráfico lineal.
class CProgram : public CWndEvents { private: //--- Actualización del gráfico lineal por el temporizador void UpdateLineChartByTimer(void); }; //+------------------------------------------------------------------+ //| Actualización por el temporizador | //+------------------------------------------------------------------+ void CProgram::UpdateLineChartByTimer(void) { //--- Salir si el formulario está plegado o se encuentra en el proceso de desplazamiento if(m_window1.IsMinimized()) return; //--- Salir si el modo de animación está desactivado if(!m_max_limit_size.CheckButtonState() && !m_run_speed.CheckButtonState()) return; //--- Retardo static int count=0; if(count<m_delay_ms.GetValue()) { count+=TIMER_STEP_MSC; return; } count=0; //--- Si la opción «Series corrientes» está activada, vamos a desplazar el primer valor de la serie ShiftLineChartSeries(); //--- Si el control del tamaño de los arrays de las series está activado por el temporizador AutoResizeLineChartSeries(); //--- Inicializamos los arrays InitArrays(); //--- (1) Calculamos y (2) actualizamos las series CalculateSeries(); UpdateSeries(); }
Ahora veremos brevemente cómo está organizado el manejador de eventos CProgram::OnEvent() de la aplicación. Para la introducción momentánea de los cambios visuales en el gráfico lineal vamos a usar los siguientes controles.
- Parámetro Number of series (lista combinada (combobox). Cada vez que se elige nuevo valor en este control, el número de series en el gráfico lineal va a cambiarse. Abajo se muestra el bloque del código para el procesamiento de este evento:
... //--- Evento de la selección del elemento en el combobox if(id==CHARTEVENT_CUSTOM+ON_CLICK_COMBOBOX_ITEM) { //--- Obtenemos nueva cantidad de las series m_line_chart.MaxData((int)m_series_total.ButtonText()); //--- (1) Establecemos el tamaño para los arrays y (2) los inicializamos ResizeDataArrays(); InitArrays(); //--- (1) Calculamos, (2) añadimos al gráfico y (3) actualizamos las series CalculateSeries(); AddSeries(); UpdateSeries(); return; } ...
Por ejemplo, por defecto, en el combobox se establece la visualización de seis series (valor 6). Vamos a cambiarlo por 3. El resultado se muestra en la captura de pantalla de abajo:
Fig. 5. Prueba del cambio del número de las series en el gráfico lineal.
- Parámetros Max. limit size (checkbox con campo de edición) y Size of series (campo de edición). Al pinchar en estos controles se genera el evento con el identificador ON_CLICK_LABEL. Si se hace clic en el control Size of series, el valor en el campo de edición se reduce al mínimo. El clic en el control Max. limit size va a pasar el estado de su checkbox al contrario. Del estado del checkbox depende si se muestra o se oculta el indicador de progreso que se muestra cuando se activa el modo del cambio automático de los tamaños de las series en el gráfico lineal.
... //--- Evento del clic en la etiqueta de texto del control if(id==CHARTEVENT_CUSTOM+ON_CLICK_LABEL) { //--- Si es el mensaje del control 'Size of series' if(sparam==m_series_size.LabelText()) { //--- Recálculo de las series en el gráfico RecalculatingSeries(); return; } //--- Si es el mensaje del control 'Max. Limit Size' if(sparam==m_max_limit_size.LabelText()) { //--- Mostrar u ocultar el indicador de progreso dependiendo del estado del checkbox del control 'Max. limit size' if(m_max_limit_size.CheckButtonState()) m_progress_bar.Show(); else m_progress_bar.Hide(); //--- return; } } ...
En la captura de pantalla de abajo se muestra cómo se ve eso en proceso:
Fig. 6. Prueba del gráfico lineal en modo del cambio automático de los tamaños de las series.
- Al introducir los valores en los campos de edición de los controles Increment ratio, Offset series y Size of series se invoca el método CProgram::RecalculatingSeries() para el recálculo de las series (véase el código de abajo).
... //--- Evento de introducción del valor nuevo en el campo de edición if(id==CHARTEVENT_CUSTOM+ON_END_EDIT) { //--- Si es el mensaje del control 'Increment ratio' o 'Offset series' o 'Size of series' if(sparam==m_increment_ratio.LabelText() || sparam==m_offset_series.LabelText() || sparam==m_series_size.LabelText()) { //--- Recálculo de las series en el gráfico RecalculatingSeries(); return; } return; } //--- Eventos del clic en los botones conmutadores del campo de edición if(id==CHARTEVENT_CUSTOM+ON_CLICK_INC || id==CHARTEVENT_CUSTOM+ON_CLICK_DEC) { //--- Si es el mensaje del control 'Increment ratio' o 'Offset series' o 'Size of series' if(sparam==m_increment_ratio.LabelText() || sparam==m_offset_series.LabelText() || sparam==m_series_size.LabelText()) { //--- Recálculo de las series en el gráfico RecalculatingSeries(); return; } return; } ...
En la captura de pantalla de abajo se muestra otro ejemplo. Intente establecer los mismos parámetros en su copia para ver cómo funciona eso en el modo de animación.
Fig. 7. Prueba del modo «Series corrientes».
Una vez introducidos los cambios en las copias de las clases de la librería estándar, los arrays multielementos caben correctamente en el área de las series del gráfico lineal. En la captura de pantalla de abajo se muestra el ejemplo cuando el tamaño de las series es igual a mil elementos (véase el parámetro Size of series).
Fig. 8. Prueba de la serie con una gran cantidad de datos.
La aplicación para las pruebas está hecha. Usted puede descargar el archivo de este EA al final del artículo y probar todo por sí mismo.
Conclusión
En este artículo han sido demostradas las clases del código para la creación de los controles de la interfaz como el «Indicador de progreso» y «Gráfico lineal».
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. Algunos fragmentos de este esquema representan una solución temporal, y según vaya desarrollando la librería, en el esquema van a introducirse ciertos cambios.
Fig. 9. Estructura de la librería en la fase actual del desarrollo.
Con eso hemos terminado la parte principal de la serie de artículos sobre el desarrollo de la librería Easy And Fast para la creación de las interfaces gráficas en el entorno de los terminales de trading MetaTrader. En los siguientes artículos se explicará cómo trabajar con esta librería. Se mostrarán distintos ejemplos, adiciones y actualizaciones. Cualquiera que desee puede unirse al desarrollo de este proyecto. Si le ha gustado el resultado, por favor, pruebe la librería en sus proyectos, avise sobre los errores encontrados y haga sus preguntas.
Más abajo puede descargar el material de la novena parte de la serie para poder probar cómo funciona todo eso. Si le surgen algunas preguntas sobre el uso del material de estos archivos, puede dirigirse a la descripción detallada del proceso de desarrollo de la librería en uno de los artículos listados más abajo, o bien hacer su pregunta en los comentarios para el artículo.
Lista de artículos (capítulos) de la novena parte:
- Interfaces gráficas IX: Interfaces gráficas IX: Control «Paleta para seleccionar el color» (Capítulo 1)
- Interfaces gráficas IX: Elementos «Indicador de progreso» y «Gráfico lineal» (Capítulo 2)
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/2580
- 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