Interfaces gráficas XI: Integración de la librería gráfica estándar (build 16)

Anatoli Kazharski | 8 septiembre, 2017


Contenido


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 el segundo capítulo de la novena parte de la serie Interfaces gráficas IX: Controles «Indicador de progreso» y «Gráfico lineal» (Capítulo 2), fue demostrado cómo se podía integrar la clase para crear los gráficos lineales en la librería. Fue una solución temporal porque las posibilidades de esta parte de la librería estándar no eran suficientes. Desde hace poco tiempo, fue presentada la nueva versión de la librería gráfica para el diseño de los gráficos científicos (clase CGraphic). La descripción de algunas funciones de esta clase fue presentada en el artículo ¡Visualice esto! la biblioteca gráfica en MQL5 como un análogo de plot en el lenguaje R. En esta actualización de la librería para la creación de las interfaces gráficas será presentada la versión con nuevo control para crear los gráficos. Ahora, será aún más fácil visualizar los datos de diferentes tipos.


Cambios en el esquema de la librería

Antes, en esta librería se usaba la copia de la clase CCanvas destinada para el dibujado. Debido a la refactorización global del código de la librería realizada hace poco, ahora esta copia no es necesaria y se puede eliminarla, sustituyendo por la versión original desde la librería original. Eso redujo el volumen de la librería aproximadamente en un 10%, y casi en un 40% respecto a la versión que había presentada antes de la refactorización en los artículos Interfaces gráficas XI: Refactorización del código de la librería (build 14.1) e Interfaces gráficas XI: Controles dibujados (build 14.2)

Ahora, para crear los gráficos, va a usarse la clase CGraphic, por eso incluiremos el archivo Graphic.mqh en el archivo Objects.mqh. Puesto que el archivo con la clase CCanvas está incluido en uno de los archivos que están incluidos en el archivo Graphic.mqh, él también estará disponible para toda la librería.

//+------------------------------------------------------------------+
//|                                                      Objects.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include "Enums.mqh"
#include "Defines.mqh"
#include "Fonts.mqh"
#include "Colors.mqh"
#include <Graphics\Graphic.mqh>
#include <ChartObjects\ChartObjectSubChart.mqh>
...

La clase CLineChart fue renombrado en CGraph. Su contenido también fue cambiado. Ahora, en esta clase hay sólo los métodos para gestionar las propiedades generales y los estados del control. 

class CGraph : public CElement
  {
public:
   //--- Manejador de eventos del gráfico
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- Desplazamiento del control
   virtual void      Moving(const bool only_visible=true);
   //--- Gestión
   virtual void      Show(void);
   virtual void      Hide(void);
   virtual void      Reset(void);
   virtual void      Delete(void);
   //--- Aplicación de últimos cambios
   virtual void      Update(const bool redraw=false);
   //---
private:
   //--- Cambio de tamaños
   void              Resize(const int width,const int height);
   //--- Cambiar el ancho por el lado derecho de la ventana
   virtual void      ChangeWidthByRightWindowSide(void);
   //--- Cambiar el alto por el borde inferior de la ventana
   virtual void      ChangeHeightByBottomWindowSide(void);
  };

Se puede gestionar las propiedades del gráfico si obtenemos el puntero a la instancia de la clase CGraphic a través el método CGraphic::GetGraphicPointer() :

class CGraph : public CElement
  {
private:
   //--- Objetos para crear el control
   CGraphic          m_graph;
   //---
public:
   //--- Devuelve el puntero al gráfico
   CGraphic         *GetGraphicPointer(void) { return(::GetPointer(m_graph)); }
  };

Las clases adicionales están incluidos en la clase CGraphic, sirven para gestionar las propiedades de los ejes (CAxis) y curvas (CCurve) del gráfico. La clase CColorGenerator sirve para generar el color de las curvas. Todas estas clases se encuentran en unos archivos separados que están incluidos en el archivo Graphic.mqh:

//+------------------------------------------------------------------+
//|                                                      Graphic.mqh |
//|                   Copyright 2016-2017, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
#include "Curve.mqh"
#include "Axis.mqh"
#include "ColorGenerator.mqh"
...

El archivo con la clase CCanvas está incluido en el archivo Curve.mqh, y desde aquí estará disponible para toda la librería.

//+------------------------------------------------------------------+
//|                                                        Curve.mqh |
//|                   Copyright 2016-2017, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include <Object.mqh>
#include <Canvas\Canvas.mqh>
...

En la imagen de abajo se muestran todas las conexiones mencionadas entre los archivos y las clases:

 Fig. 1. Conexiones entre las clases de la librería estándar y librería en desarrollo.

Fig. 1. Conexiones entre las clases de la librería estándar y librería en desarrollo.


De esta manera, para la librería y los archivos de la aplicación donde se utiliza, automáticamente se hacen disponibles las clases de la librería estándar para el trabajo con los arrays y los archivos. Luego, en este artículo serán demostradas varias aplicaciones MQL de prueba para comprender mejor qué posibilidades están disponibles ahora.


Aplicación para la prueba de las propiedades del gráfico

En la primera aplicación de prueba, implementamos la interfaz gráfica con los controles para manejar algunas propiedades del gráfico tipo CGraphic. Colocaremos el control tipo CTabs en la parte superior del formulario. En este caso, es el grupo de cuatro pestañas. Debajo del área de trabajo de las pestañas, estará ubicado el gráfico con dos curvas cuyos valores serán generados de forma aleatoria.

En la primera pestaña (Background), vamos a crear los controles que permitirán manejar las siguientes propiedades del gráfico:

  • Color del fondo.
  • Texto principal del gráfico (se muestra en la parte superior).
  • Texto auxiliar del gráfico (se muestra en la parte inferior).
  • Color del texto principal.
  • Color del texto auxiliar.
  • Tamaño de la fuente del texto principal.
  • Tamaño de la fuente del texto auxiliar.

Para establecer y obtener estas propiedades, la clase CGraphic contiene los métodos públicos correspondientes:

//+------------------------------------------------------------------+
//| Structure CBackground                                            |
//| Usage: background on a two-dimensional graphics                  |
//+------------------------------------------------------------------+
struct CBackground
  {
   uint              clr;
   uint              clr_main;
   uint              clr_sub;
   string            main;
   string            sub;
   int               size_main;
   int               size_sub;
  };
//+------------------------------------------------------------------+
//| Class CGraphic                                                   |
//| Usage: class for drawing two-dimensional graphics                |
//+------------------------------------------------------------------+
class CGraphic
  {
protected:
   //--- element of graphic
   CBackground       m_background;           // background
   //---
public:
   //--- gets the background properties
   uint              BackgroundColor(void)       const { return(m_background.clr);       }
   uint              BackgroundMainColor(void)   const { return(m_background.clr_main);  }
   uint              BackgroundSubColor(void)    const { return(m_background.clr_sub);   }
   string            BackgroundMain(void)        const { return(m_background.main);      }
   string            BackgroundSub(void)         const { return(m_background.sub);       }
   int               BackgroundMainSize(void)    const { return(m_background.size_main); }
   int               BackgroundSubSize(void)     const { return(m_background.size_sub);  }
   //--- sets the background properties
   void              BackgroundColor(const uint clr)      { m_background.clr=clr;        }
   void              BackgroundMainColor(const uint clr)  { m_background.clr_main=clr;   }
   void              BackgroundSubColor(const uint clr)   { m_background.clr_sub=clr;    }
   void              BackgroundMain(const string main)    { m_background.main=main;      }
   void              BackgroundSub(const string sub)      { m_background.sub=sub;        }
   void              BackgroundMainSize(const int size)   { m_background.size_main=size; }
   void              BackgroundSubSize(const int size)    { m_background.size_sub=size;  }
  };

A continuación, se puede observar como es:

 Fig. 2. Los controles de la primera pestaña (Background) de la aplicación MQL de prueba.

Fig. 2. Los controles de la primera pestaña (Background) de la aplicación MQL de prueba.


En la segunda pestaña (Indents & history), ubicaremos los controles para establecer las siguientes propiedades:

  • Márgenes (izquierda, derecha, arriba, abajo).
  • Ancho de la leyenda.
  • Tamaño de la fuente de la leyenda.
  • Tamaño de los marcadores de la leyenda.
  • Márgenes generales entre todos los controles del gráfico.
  • Tamaño de las marcas en las escalas de los ejes del gráfico. 

Para obtener y establecer estas propiedades, se puede usar los métodos CGraphic que se muestran en el listado del código de abajo:

//+------------------------------------------------------------------+
//| Structure CCurveHistory                                          |
//| Usage: history of curves on a two-dimensional graphics           |
//+------------------------------------------------------------------+
struct CCurveHistory
  {
   int               name_width;
   int               name_size;
   int               symbol_size;
   int               count_total;
   int               count_points;
   int               count_lines;
   int               count_histogram;
   int               count_custom;
  };
//+------------------------------------------------------------------+
//| Class CGraphic                                                   |
//| Usage: class for drawing two-dimensional graphics                |
//+------------------------------------------------------------------+
class CGraphic
  {
protected:
   //--- element of graphic
   CCurveHistory     m_history;              // history
   //---
public:
   //--- gets or sets indents
   int               IndentUp(void)               const { return(m_up0);     }
   void              IndentUp(const int up)             { m_up0=up;          }
   int               IndentDown(void)             const { return(m_down0);   }
   void              IndentDown(const int down)         { m_down0=down;      }
   int               IndentLeft(void)             const { return(m_left0);   }
   void              IndentLeft(const int left)         { m_left0=left;      }
   int               IndentRight(void)            const { return(m_right0);  }
   void              IndentRight(const int right)       { m_right0=right;    }
   //--- gets or sets gap 
   int               GapSize(void)           const { return(m_gap); }
   void              GapSize(const int size)       { m_gap=size;    }
   //--- gets or sets major mark size
   int               MajorMarkSize(void)           const { return(m_mark_size); }
   void              MajorMarkSize(const int size)       { m_mark_size=size;    }
   //--- gets the curve history properties
   int               HistoryNameWidth(void)            const { return(m_history.name_width);  }
   int               HistoryNameSize(void)             const { return(m_history.name_size);   }
   int               HistorySymbolSize(void)           const { return(m_history.symbol_size); }
   //--- sets the curve history properties
   void              HistoryNameWidth(const int width) { m_history.name_width=width; }
   void              HistoryNameSize(const int size)   { m_history.name_size=size;   }
   void              HistorySymbolSize(const int size) { m_history.symbol_size=size; }
  };

Abajo se muestra cómo se ve eso en la interfaz gráfica de una aplicación MQL:

 Fig. 3. Controles de la segunda pestaña (Indents & history) de la aplicación MQL de prueba.

Fig. 3. Controles de la segunda pestaña (Indents & history) de la aplicación MQL de prueba.


En la tercera pestaña (Grid), se encuentran los controles para establecer las propiedades de la cuadrícula que se listan a continuación:

  • Color de las líneas de la cuadrícula.
  • Color de línea cero de los ejes.
  • Color del fondo de la cuadrícula.
  • Dibujado de los puntos en los nodos de la cuedrícula.
  • Radio de los puntos.
  • Color de los puntos.

Para obtener y establecer estas propiedades, en la clase CGraphic hay métodos correspondientes (véase el código de abajo):

//+------------------------------------------------------------------+
//| Structure CGrid                                                  |
//| Usage: grid on a two-dimensional graphics                        |
//+------------------------------------------------------------------+
struct CGrid
  {
   uint              clr_line;
   uint              clr_background;
   uint              clr_circle;
   uint              clr_axis_line;
   uint              clr_frame;
   int               r_circle;
   bool              has_circle;
  };
//+------------------------------------------------------------------+
//| Class CGraphic                                                   |
//| Usage: class for drawing two-dimensional graphics                |
//+------------------------------------------------------------------+
class CGraphic
  {
protected:
   //--- element of graphic
   CGrid             m_grid;                 // grid
   //---
public:
   //--- gets the grid properties
   uint              GridLineColor(void)        const { return(m_grid.clr_line);       }
   uint              GridAxisLineColor(void)    const { return(m_grid.clr_axis_line);  }
   uint              GridBackgroundColor(void)  const { return(m_grid.clr_background); }
   int               GridCircleRadius(void)     const { return(m_grid.r_circle);       }
   uint              GridCircleColor(void)      const { return(m_grid.clr_circle);     }
   bool              GridHasCircle(void)        const { return(m_grid.has_circle);     }
   //--- sets the grid properties
   void              GridLineColor(const uint clr)        { m_grid.clr_line=clr;       }
   void              GridAxisLineColor(const uint clr)    { m_grid.clr_axis_line=clr;  }
   void              GridBackgroundColor(const uint clr)  { m_grid.clr_background=clr; }
   void              GridCircleRadius(const int r)        { m_grid.r_circle=r;         }
   void              GridCircleColor(const uint clr)      { m_grid.clr_circle=clr;     }
   void              GridHasCircle(const bool has)        { m_grid.has_circle=has;     }
  };

Pues, es lo que veremos al final:

 Fig. 4. Controles de la tercera pestaña (Grid) de la aplicación MQL.

Fig. 4. Controles de la tercera pestaña (Grid) de la aplicación MQL.


En la cuarta pestaña (Axes), se ubicarán los controles a través de los cuales se puede cambiar las propiedades de los ejes del gráfico. Para cambiar entre los ejes, se usarán los botones de opción en la parte izquierda del área de trabajo de las pestañas, que se encuentra separada de los demás controles de la pestaña Axes con una línea separadora.

Las propiedades que estarán disponibles para el ajuste son las siguientes:

  • Redimensionamiento automático.
  • Valor mínimo del eje.
  • Valor máximo del eje.
  • Valor del suplemento para el mínimo del eje.
  • Valor del suplemento para el máximo del eje.
  • Tamaño de los números en el eje.
  • Longitud máxima de los números a mostrar en el eje.
  • Tamaño de la fuente para el nombre del eje.
  • Valor inicial del paso en el eje.
  • Cantidad máximo de los números en el eje.
  • Nombre del eje.
  • Color del texto del nombre del eje.

Abajo se muestran los nombres de los métodos de la clase CAxis para obtener y modificar las propiedades descritas:

//+------------------------------------------------------------------+
//| Class CAxis                                                      |
//| Usage: class for create axes on a two-dimensional graphics       |
//+------------------------------------------------------------------+
class CAxis
  {
private:
   double            m_min;
   double            m_max;
   uint              m_clr;
   string            m_name;
   int               m_name_size;
   int               m_values_size;
   int               m_values_width;
   bool              m_auto_scale;
   double            m_default_step;   // length of the default step
   double            m_max_labels;     // the maximum number of marks
   double            m_min_grace;      // "grace" value applied to the minimum data range
   double            m_max_grace;      // "grace" value applied to the maximum data range
   //---
public:
                     CAxis(void);
                    ~CAxis(void);
   //--- properties
   double            Min(void)                  const { return(m_min);  }
   void              Min(const double min)            { m_min=min;      }
   double            Max(void)                  const { return(m_max);  }
   void              Max(const double max)            { m_max=max;      }
   string            Name(void)                 const { return(m_name); }
   void              Name(const string name)          { m_name=name;    }
   //--- default properties 
   uint              Color(void)                        const { return(m_clr);            }
   void              Color(const uint clr)                    { m_clr=clr;                }
   bool              AutoScale(void)                    const { return(m_auto_scale);     }
   void              AutoScale(const bool auto)               { m_auto_scale=auto;        }
   int               ValuesSize(void)                   const { return(m_values_size);    }
   void              ValuesSize(const int size)               { m_values_size=size;       }
   int               ValuesWidth(void)                  const { return(m_values_width);   }
   void              ValuesWidth(const int width)             { m_values_width=width;     }
   int               NameSize(void)                     const { return(m_name_size);      }
   void              NameSize(const int size)                 { m_name_size=size;         }
   double            DefaultStep(void)                  const { return(m_default_step);   }
   void              DefaultStep(const double value)          { m_default_step=value;     }
   double            MaxLabels(void)                    const { return(m_max_labels);     }
   void              MaxLabels(const double value)            { m_max_labels=value;       }
   double            MinGrace(void)                     const { return(m_min_grace);      }
   void              MinGrace(const double value)             { m_min_grace=value;        }
   double            MaxGrace(void)                     const { return(m_max_grace);      }
   void              MaxGrace(const double value)             { m_max_grace=value;        }
  };

Abajo se muestra lo que hemos obtenido al final:

 Fig. 5. Controles de la cuarta pestaña (Axes) de la aplicación MQL de prueba.

Fig. 5. Controles de la cuarta pestaña (Axes) de la aplicación MQL de prueba.


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


Aplicación para probar las propiedades de las curvas del gráfico

Para las pruebas de algunas propiedades de las curvas del gráfico tipo CGraphic, fue escrita una aplicación MQL especial. En la parte superior del formulario de esta aplicación, se encuentran los controles para manejar las propiedades de las curvas del gráfico, y debajo de ellos se ubican dos gráficos tipo CGraphic (control CGraph). En el primer gráfico, veremos las series con datos aleatorios, en el segundo, van a construirse sus derivados, que serán calculados (como ejemplo) según la fórmula del indicador Momentum.

Éstos son los controles para manejar las propiedades de las curvas del gráfico:

  • Checkbox Animate – sirve para iniciar la llegada automática de los datos al gráfico.
  • Campo de edición Array size – número actual de controles en el array de los datos mostrados en el gráfico.
  • Botón Random – genera las consecuencias aleatorias de los datos en las series en el gráfico.
  • Camos de edición Period – valor de la variable para calcular el indu¡icador Momentum.
  • Combobox Curve type – selección del tipo de las curvas en el gráfico.
  • Combobox Point type – selección del tipo de los puntos de datos a base de los cuales se construyen las curvas en el gráfico.

En la clase personalizada de la aplicación (CProgram), han sido implementados los métodos que están vinculados con los controles arriba mencionados y ejecutan las siguientes tareas:

  • Establecimiento del tamaño para los arrays para mostrar los datos en el gráfico.
  • Inicialización de arrays usando los datos.
  • Actualización de los gráficos para mostrar los últimos cambios.
  • Adición de un control al final de los arrays.
  • Eliminación de un control al final de los arrays.
  • Actualización de los gráficos por el temporizador.
  • Animación de los gráficos con la llegada automática de nuevos datos.

Los métodos donde todo eso está implementado se muestran a continuación. Puede estudiar al detalle el código de estos métodos en los archivos adjuntos al artículo.

class CProgram : public CWndEvents
  {
protected:
   //--- Arrays de los datos para mostrar en los gráficos
   double            data1[];
   double            data2[];
   //---
   double            data3[];
   double            data4[];
   //---
private:
  //--- Establecer el tamaño nuevo de los arrays
   void              ResizeGraph1Arrays(void);
   void              ResizeGraph2Arrays(void);
   void              ResizeGraph1Arrays(const int new_size);
   void              ResizeGraph2Arrays(const int new_size);
   //--- Inicialización de arrays
   void              InitGraph1Arrays(void);
   void              InitGraph2Arrays(void);
  //--- Puesta a cero de arrays
   void              ZeroGraph1Arrays(void);
   void              ZeroGraph2Arrays(void);
   //--- Establece el valor aleatorio según el índice especificado
   void              SetGraph1Value(const int index);
   void              SetGraph2Value(const int index);
   //--- Actualizar las series en el gráfico
   void              UpdateGraph(void);
   void              UpdateGraph1(void);
   void              UpdateGraph2(void);
   
   //--- Recálculo de las series en el gráfico
   void              RecalculatingSeries(void);
   //--- Añade un valor al final de los arrays
   void              AddValue(void);
   //--- Elimina un valor al final de los arrays
   void              DeleteValue(void);

   //--- Actualización del gráfico por el temporizador
   void              UpdateGraphByTimer(void);
   //--- Animación de las series del gráfico
   void              AnimateGraphSeries(void);
  };

Abajo se muestra el resultado:

 Fig. 6. Interfaz gráfica de la aplicación para testear las propiedades de las curvas del gráfico.

Fig. 6. Interfaz gráfica de la aplicación para testear las propiedades de las curvas del gráfico.


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


Aplicación con gráfico animado de hipocicloide

En uno de sus libros de programación en VBA en el ambiente del programa Microsoft Excel, su autor, John Walkenbach, proporciona un CD con archivos para las pruebas a sus lectores. En uno de los archivos, fue implementado el diagrama en la que se generaba una cantidad infinita de hipocicloides. 

Nota: La wikipedia ofrece la siguiente definición:

Hipocicloide (en base de las palabras griegas ὑπό — abajo, debajo y κύκλος — círculo, circunferencia) es una curva plana formada por un punto situado sobre la circunferencia que rueda por el interior de otra circunferencia sin deslizamiento .

John Walkenbach define este concepto manera en sus libro de la siguiente:

Hipocicloide es la trayectoria descrita por un punto situado sobre una circunferencia generatriz que rueda sor el interior de otra circunferencia directriz sin deslizamiento.

Vamos a implementar la misma aplicación en MQL, y añadiremos la interfaz gráfica para manejar los parámetros. Vamos a ver en detalle cómo están organizados todo eso.

Para generar una nueva curva hipocicloide, se usan tres parámetros según los cuales se inicializan las secuencias numéricas con paso indicado. Luego, a base de estos valores en estas secuencias, se realizan los cálculos para obtener las coordenadas de los puntos en el gráfico. Después de eso, los resultados obtenidos se normalizan.

En la clase personalizada, declaramos varios arrays para calcular las secuencias y los campos para el cálculo de la desviación media y estándar:

//+------------------------------------------------------------------+
//|                                                      Program.mqh |
//|                        Copyright 2017, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include <Math\Stat\Stat.mqh>
#include <EasyAndFastGUI\WndEvents.mqh>
#include <EasyAndFastGUI\TimeCounter.mqh>
//+------------------------------------------------------------------+
//| Clase para crear la aplicación                                     |
//+------------------------------------------------------------------+
class CProgram : public CWndEvents
  {
protected:
...
   //--- Arrays de los datos para los cálculos
   double            a_inc[];
   double            b_inc[];
   double            t_inc[];
   double            x_source[];
   double            y_source[];
   //--- Arrays de los datos para mostrar en el gráfico
   double            x_norm[];
   double            y_norm[];
   //--- Para el cálculo de la desviación media y estándar
   double            x_mean;
   double            y_mean;
   double            x_sdev;
   double            y_sdev;
...
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CProgram::CProgram(void) : x_mean(0),
                           y_mean(0),
                           x_sdev(0),
                           y_sdev(0)
  {
...
  }

El cálculo de los valores va a realizarse en el método CProgram::InitArrays(). Aquí, los datos iniciales se calculan en el primer ciclo. Luego, se obtiene la desviación media y estándar, y los datos se normalizan en el segundo ciclo. El tamaño para los arrays se establece a través del método CProgram::ResizeArrays(). El valor para el tamaño de los arrays se coge del control «Campo de edición» (CTextEdit) de la interfaz gráfica de la aplicación.

class CProgram : public CWndEvents
  {
private:
   //--- Establecemos nuevo tamaño para los arrays
   void              ResizeArrays(void);
   //--- Inicialización de los arrays auxiliares para los cálculos
   void              InitArrays(void);
  };
//+------------------------------------------------------------------+
//| Cambia el tamaño de los arrays                                         |
//+------------------------------------------------------------------+
void CProgram::ResizeArrays(void)
  {
   int array_size =::ArraySize(x_norm);
   int new_size   =(int)m_array_size.GetValue();
//--- Salir si el tamaño no ha cambiado
   if(array_size==new_size)
      return;
//--- Establecer nuevo tamaño
   ::ArrayResize(a_inc,new_size);
   ::ArrayResize(b_inc,new_size);
   ::ArrayResize(t_inc,new_size);
   ::ArrayResize(x_source,new_size);
   ::ArrayResize(y_source,new_size);
   ::ArrayResize(x_norm,new_size);
   ::ArrayResize(y_norm,new_size);
  }
//+------------------------------------------------------------------+
//| Inicialización de arrays                                           |
//+------------------------------------------------------------------+
void CProgram::InitArrays(void)
  {
//--- Cambiar los tamaños de los arrays
   ResizeArrays();
//--- Calculamos los valores usando las fórmulas
   int total=(int)m_array_size.GetValue();
   for(int i=0; i<total; i++)
     {
      if(i<1)
        {
         a_inc[i] =1+(double)m_animate.GetValue();
         b_inc[i] =1+(double)m_animate.GetValue();
         t_inc[i] =1+(double)m_animate.GetValue();
        }
      else
        {
         a_inc[i] =a_inc[i-1]+(double)m_a_inc.GetValue();
         b_inc[i] =b_inc[i-1]+(double)m_b_inc.GetValue();
         t_inc[i] =t_inc[i-1]+(double)m_t_inc.GetValue();
        }
      //---
      double a=a_inc[i];
      double b=b_inc[i];
      double t=t_inc[i];
      //---
      x_source[i] =(a-b)*cos(t)+b*cos((a/b-1)*t);
      y_source[i] =(a-b)*sin(t)+b*sin((a/b-1)*t);
     }
//--- Calculamos la media
   x_mean=MathMean(x_source);
   y_mean=MathMean(y_source);
//--- Calculamos la desviación estándar
   x_sdev=MathStandardDeviation(x_source);
   y_sdev=MathStandardDeviation(y_source);
//--- Corrección para evitar la división por cero
   x_sdev =(x_sdev==0)? 1 : x_sdev;
   y_sdev =(y_sdev==0)? 1 : y_sdev;
//--- Normalizamos los datos
   for(int i=0; i<total; i++)
     {
      x_norm[i] =(x_source[i]-x_mean)/x_sdev;
      y_norm[i] =(y_source[i]-y_mean)/y_sdev;
     }
  }

En la clase CGraphic hay métodos que permiten añadir las marcas adicionales para las escalas de los ejes, líneas y el texto en el área de trabajo del gráfico.

En caso de nuestro ejemplo, usaremos el método CProgram::TextAdd() para mostrar las diagramas de los valores de la desviación media y estándar para las secuencias X y Y colocándolas en la esquina superior izquierda. Para obtener las coordenadas del punto extremo (esquina superior izquierda) del diagrama, se usan los métodos CGraphic::ScaleX() y CGraphic::ScaleY(), que sirven para el redimensionamiento de los valores reales del gráfico en las coordenadas de los píxeles. Aquí, como valores reales, se usa el mínimo por el eje X y el máximo por el eje Y

class CProgram : public CWndEvents
  {
private:
   //--- Añade el texto en el gráfico
   void              TextAdd(void);
  };
//+------------------------------------------------------------------+
//| Añade el texto en el gráfico                                        |
//+------------------------------------------------------------------+
void CProgram::TextAdd(void)
  {
//--- Obtenemos el puntero del gráfico
   CGraphic *graph=m_graph1.GetGraphicPointer();
//---  
   int  x     =graph.ScaleX(graph.XAxis().Min())+50;
   int  y     =graph.ScaleY(graph.YAxis().Max())+10;
   int  y2    =y+20;
   uint clr   =::ColorToARGB(clrBlack);
   uint align =TA_RIGHT;
//---
   string str[8];
   str[0] ="x mean:";
   str[1] ="y mean:";
   str[2] =::DoubleToString(x_mean,2);
   str[3] =::DoubleToString(y_mean,2);
   str[4] ="x sdev:";
   str[5] ="y sdev:";
   str[6] =::DoubleToString(x_sdev,2);
   str[7] =::DoubleToString(y_sdev,2);
//--- Calculamos las coordenadas y mostramos el texto en el gráfico
   int l_x=0,l_y=0;
   for(int i=0; i<8; i++)
     {
      if(i<2)
         l_x=x;
      else if(i<6)
         l_x=(i%2==0)? l_x+50 : l_x;
      else
         l_x=(i%2==0)? l_x+60 : l_x;
      //---
      l_y=(i%2==0)? y : y2;
      //---
      graph.TextAdd(l_x,l_y,str[i],clr,align);
     }
  }

Una vez establecidos todos los datos necesarios en el gráfico, hay que redibujarlo para mostrar los últimos cambios. Para eso se utiliza el método CProgram::UpdateSeries(). Aquí, primero, comprobamos si hay series en el gráfico. Si es así, establecemos los datos calculados últimamente. Aparte de eso, establecemos las propiedades para la curva usando los controles de la interfaz gráfica de la aplicación. Aquí es (1) el suavizado de la curva, (2) tipo de los puntos y (3) tipo de la curva. Cabe mencionar que la colocación del texto en el gráfico debe realizarse después de que hayan sido establecidas y dibujadas todas las demás propiedades y datos. Al final del todo, hay que actualizar el gráfico para ver el resultado.

class CProgram : public CWndEvents
  {
private:
   //--- Coloca y actualiza las series en el gráfico
   void              UpdateSeries(void);
  };
//+------------------------------------------------------------------+
//| Coloca y actualiza las series en el gráfico                       |
//+------------------------------------------------------------------+
void CProgram::UpdateSeries(void)
  {
//--- Obtenemos el puntero del gráfico
   CGraphic *graph=m_graph1.GetGraphicPointer();
//--- Actualizamos todas las series del gráfico con nuevos datos
   int total=graph.CurvesTotal();
   if(total>0)
     {
      //--- Obtenemos el puntero de la curva
      CCurve *curve=graph.CurveGetByIndex(0);
       //--- Establecer el array de los datos
      curve.Update(x_norm,y_norm);
      //--- Obtenemos los valores de las propiedades de la curva
      ENUM_CURVE_TYPE curve_type =(ENUM_CURVE_TYPE)m_curve_type.GetListViewPointer().SelectedItemIndex();
      ENUM_POINT_TYPE point_type =(ENUM_POINT_TYPE)m_point_type.GetListViewPointer().SelectedItemIndex();
      //--- Establecer las propiedades
      curve.LinesSmooth(m_line_smooth.IsPressed());
      curve.PointsType(point_type);
      curve.Type(curve_type);
     }
//--- Aplicar
   graph.Redraw(true);
//--- Mostrar el texto
   TextAdd();
//--- Actualizar el gráfico
   graph.Update();
  }

Para el cálculo y aplicación de los resultados obtenidos con una llamada, se usa el método CProgram::RecalculatingSeries():

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)
  {
//--- Calculamos los valores e inicializamos los arrays
   InitArrays();
//--- Actualizamos las series
   UpdateSeries();
  }

El diagrama construido a base de estas fórmulas tendrá un aspecto más interesante si estará animado. Para hacer moverse las secuencias calculadas, hay que modificar el valor inicial de estas secuencias. Se puede conseguir eso, introduciendo los valores en los campos de edición o iniciando el proceso en modo automático. En modo automático, el incremento o reducción de los valores en este campo, se realiza en el método CProgram::AnimateGraphSeries(). Este método se invoca dentro del método CProgram::UpdateGraphByTimer(), cuya llamada, a su vez, se realiza en el temporizador de la aplicación.

class CProgram : public CWndEvents
  {
private:
   //--- Actualización del gráfico por el temporizador
   void              UpdateGraphByTimer(void);
   //--- Animación de las series del gráfico
   void              AnimateGraphSeries(void);
  };
//+------------------------------------------------------------------+
//| Temporizador                                                           |
//+------------------------------------------------------------------+
void CProgram::OnTimerEvent(void)
  {
   CWndEvents::OnTimerEvent();
//--- Actualización del gráfico por el temporizador
   if(m_counter1.CheckTimeCounter())
     {
      UpdateGraphByTimer();
     }
...
  }
//+------------------------------------------------------------------+
//| Actualización del gráfico por el temporizador                                    |
//+------------------------------------------------------------------+
void CProgram::UpdateGraphByTimer(void)
  {
//--- Salir si (1) el formulario está minimizado o (2) la animación está desactivada
   if(m_window.IsMinimized() || !m_animate.IsPressed())
      return;
//--- Animación de las series del gráfico
   AnimateGraphSeries();
//--- Actualizar los arrays y las series en el gráfico
   RecalculatingSeries();
  }
//+------------------------------------------------------------------+
//| Animación de las series del gráfico                                           |
//+------------------------------------------------------------------+
void CProgram::AnimateGraphSeries(void)
  {
//--- Para indicar la dirección del cambio del tamaño de los arrays
   static bool counter_direction=false;
//--- Si llegamos al mínimo, cambiamos de dirección
   if((double)m_animate.GetValue()<=(double)m_animate.MinValue())
      counter_direction=false;
//--- Si llegamos al máximo, cambiamos de dirección
   if((double)m_animate.GetValue()>=(double)m_animate.MaxValue())
      counter_direction=true;
//--- Cambiamos el tamaño del array según la dirección
   string value="";
   if(!counter_direction)
      value=string((double)m_animate.GetValue()+m_animate.StepValue());
   else
      value=string((double)m_animate.GetValue()-m_animate.StepValue());
//--- Establecer el valor nuevo y actualizar el campo de edición
   m_animate.SetValue(value,false);
   m_animate.GetTextBoxPointer().Update(true);
  }

Al final, obtenemos el siguiente resultado:

 Fig. 7. Demostración del hipocicloide animado.

Fig. 7. Demostración del hipocicloide animado.


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


Nueva versión de la aplicación de prueba desde las actualizaciones anteriores

La aplicación de prueba que había sido presentada en el artículo Interfaces gráficas IX: Controles «Indicador de progreso» y «Gráfico lineal» (Capítulo 2) fue actualizada de acuerdo con los cambios realizados en esta actualización.

A continuación, se muestra la nueva versión de esta aplicación MQL con la interfaz gráfica actualizada:

 Fig. 8. Nueva versión de la aplicación de prueba desde las actualizaciones anteriores.

Fig. 8. Nueva versión de la aplicación de prueba desde las actualizaciones anteriores.


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


Conclusión

En este artículo, la parte de la librería estándar para la construcción de los gráficos científicos ha sido integrada en nuestra librería para la creación de las interfaces gráficas. Puede descargar todos los ejemplos demostrados al final del artículo para estudiarlos más detalladamente.

En esta fase del desarrollo de la librería, su esquema general tiene el siguiente aspecto:

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

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


Este código de la librería se ofrece de forma gratuita. Usted puede utilizarlo en sus proyectos, inclusive de negocio, escribir los artículos y realizar los trabajos de encargo.

Si tiene algunas preguntas respecto al uso del material del artículo, puede hacerlas en los comentarios para el artículo.