English Русский 中文 Deutsch 日本語 Português
preview
DoEasy. Elementos de control (Parte 27): Seguimos trabajando en el objeto WinForms "ProgressBar"

DoEasy. Elementos de control (Parte 27): Seguimos trabajando en el objeto WinForms "ProgressBar"

MetaTrader 5Ejemplos | 6 marzo 2023, 08:53
504 0
Artyom Trishkin
Artyom Trishkin

Contenido


Concepto

Vamos a continuar desarrollando el control ProgressBar (barra de progreso), que comenzamos a tratar en el último artículo. En nuestro caso, este elemento constará de dos componentes: el sustrato y la barra de progreso. En el futuro, además de la barra de progreso, podremos colocar otros elementos de diseño en el sustrato: textos, imágenes, etc. Por el momento, este control será un objeto WinForms estático. Hoy desarrollaremos una funcionalidad con la que podremos controlar programáticamente la barra de progreso de este objeto. Así, haremos posible usarlo en nuestros programas para indicar el progreso de aquellas operaciones que requieran un número significativo de acciones idénticas.

Si prestamos atención a las barras de progreso similares en el entorno de Windows, notaremos que mientras se rellena la barra de progreso, esta se ve recorrida por destellos u otros efectos visuales. Aparte de "dar vida" a la interfaz del programa, haciéndola más atractiva, los efectos visuales en la barra de progreso también ejercen como indicación adicional de la finalización del proceso. Si el número de operaciones que muestra la barra de progreso resulta lo suficientemente grande, la barra de progreso no se rellenará en cada operación completada, sino después de un cierto número de ellas. Por consiguiente, incluso si la barra de progreso está llena, las operaciones aún podrían proseguir. En tal situación, si los efectos visuales continúan mostrándose en la barra de progreso, esto indicará un proceso que aún no se ha completado. Una vez se complete el proceso, la barra de progreso ya no mostrará efectos visuales.

Por lo general, al finalizar el proceso, el control ProgressBar también se ocultará, por lo que la indicación descrita anteriormente no resultará visible, pero si creamos la barra de progreso en una ventana aparte, que, una vez completadas todas las operaciones, deba cerrarse manualmente, la indicación de finalización/no finalización del proceso se podrá observar mediante los efectos visuales en la barra de progreso. O, si hay varias barras de progreso al mismo tiempo que muestran un proceso, pero dividido en subprocesos. Luego, mostraremos las barras de progreso de los procesos ya completados hasta que se complete el proceso general, y en los controles ProgressBar que muestran los subprocesos, también podremos observar esta doble indicación: si la barra de progreso está completamente llena, pero los efectos visuales continúan mostrándose en ella, entonces este subproceso aún no se habrá completado al 100%.

La implementación de la funcionalidad de efectos visuales para el control ProgressBar nos permitirá usar su concepto en otros controles de la biblioteca. Cada objeto que deba tener efectos visuales se colocará en la lista de elementos activos. La biblioteca verá esta lista y llamará a los controladores de eventos del temporizador de cada uno de los objetos ubicados en esta lista a su vez en el temporizador. Será precisamente en los propios objetos, en su manejador del temporizador, donde se implementará la funcionalidad necesaria.

Además, hoy mejoraremos ligeramente el dibujado de los iconos predefinidos. En el último artículo, hablamos del extraño comportamiento de las líneas suavizadas al dibujarse estas con transparencia; de hecho, todas las primitivas gráficas dibujadas con los métodos de suavizado de la biblioteca estándar se dibujan de manera anormal si se configuran como transparentes. Es decir, no podemos implementar una aparición o desaparición suave de tales líneas, pero podemos recurrir a un truco: si establecemos la opacidad del color en un cierto valor para la línea, por ejemplo, de 128 a 255, entonces se deberá dibujar con el método de suavizado. Si la opacidad se establece de 127 a 0, dibujaremos dicha línea usando el método habitual sin suavizado. Este enfoque dotará al efecto visual de una sensación de aparición/desaparición suave de las primitivas gráficas dibujadas, mientras que la semitransparencia ocultará la "aspereza" de las líneas dibujadas sin suavizado.


Mejorando las clases de la biblioteca

Al crear un objeto resaltado en el último artículo, partimos de la suposición de que se creará usando como base un objeto de sombra; es decir, se derivará de él para utilizar los métodos de desenfoque disponibles en esta clase. Sin embargo, como ha demostrado la práctica, este enfoque no se ha justificado: además del hecho de que complica la adición de efectos visuales a cualquier objeto, el uso de un destello como heredero de un objeto de sombra limitará su uso en el marco de un concepto ya creado.

Por lo tanto, haremos lo siguiente: el objeto resaltado se heredará de la clase de objeto básico de todos los objetos WinForms de la biblioteca CWinFormBase y estará en la lista de objetos auxiliares. Luego, podremos añadirlo fácilmente con solo una línea de código en la clase donde debe usarse, y ya habremos escrito los manejadores para los objetos activos. Este enfoque nos ahorrará la necesidad de añadir a diferentes clases de la biblioteca funcionalidad idéntica a la funcionalidad de procesamiento de objetos ocultos. Simplemente utilizaremos los métodos preparados para añadir objetos vinculados y procesarlos.

En el archivo \MQL5\Include\DoEasy\Defines.mqh, desplazaremos la línea de identificación del objeto resaltado de la sección de objetos gráficos a la sección de controles auxiliares de la biblioteca:

//+------------------------------------------------------------------+
//| The list of graphical element types                              |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_ELEMENT_TYPE
  {
   GRAPH_ELEMENT_TYPE_STANDARD,                       // Standard graphical object
   GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED,              // Extended standard graphical object
   GRAPH_ELEMENT_TYPE_SHADOW_OBJ,                     // Shadow object
                                                                    
   GRAPH_ELEMENT_TYPE_ELEMENT,                        // Element
   GRAPH_ELEMENT_TYPE_FORM,                           // Form
   GRAPH_ELEMENT_TYPE_WINDOW,                         // Window
   //--- WinForms
   GRAPH_ELEMENT_TYPE_WF_UNDERLAY,                    // Panel object underlay
   GRAPH_ELEMENT_TYPE_WF_BASE,                        // Windows Forms Base
   //--- 'Container' object types are to be set below
   GRAPH_ELEMENT_TYPE_WF_CONTAINER,                   // Windows Forms container base object
   GRAPH_ELEMENT_TYPE_WF_PANEL,                       // Windows Forms Panel
   GRAPH_ELEMENT_TYPE_WF_GROUPBOX,                    // Windows Forms GroupBox
   GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,                 // Windows Forms TabControl
   GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER,             // Windows Forms SplitContainer
   //--- 'Standard control' object types are to be set below
   GRAPH_ELEMENT_TYPE_WF_COMMON_BASE,                 // Windows Forms base standard control
   GRAPH_ELEMENT_TYPE_WF_LABEL,                       // Windows Forms Label
   GRAPH_ELEMENT_TYPE_WF_BUTTON,                      // Windows Forms Button
   GRAPH_ELEMENT_TYPE_WF_CHECKBOX,                    // Windows Forms CheckBox
   GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON,                 // Windows Forms RadioButton
   GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX,           // Base list object of Windows Forms elements
   GRAPH_ELEMENT_TYPE_WF_LIST_BOX,                    // Windows Forms ListBox
   GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX,            // Windows Forms CheckedListBox
   GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX,             // Windows Forms ButtonListBox
   GRAPH_ELEMENT_TYPE_WF_TOOLTIP,                     // Windows Forms ToolTip
   GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,                // Windows Forms ProgressBar
   //--- Auxiliary elements of WinForms objects
   GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM,               // Windows Forms ListBoxItem
   GRAPH_ELEMENT_TYPE_WF_TAB_HEADER,                  // Windows Forms TabHeader
   GRAPH_ELEMENT_TYPE_WF_TAB_FIELD,                   // Windows Forms TabField
   GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL,       // Windows Forms SplitContainerPanel
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON,                // Windows Forms ArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP,             // Windows Forms UpArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN,           // Windows Forms DownArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT,           // Windows Forms LeftArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT,          // Windows Forms RightArrowButton
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX,        // Windows Forms UpDownArrowButtonsBox
   GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX,        // Windows Forms LeftRightArrowButtonsBox
   GRAPH_ELEMENT_TYPE_WF_SPLITTER,                    // Windows Forms Splitter
   GRAPH_ELEMENT_TYPE_WF_HINT_BASE,                   // Windows Forms HintBase
   GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT,              // Windows Forms HintMoveLeft
   GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT,             // Windows Forms HintMoveRight
   GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP,                // Windows Forms HintMoveUp
   GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN,              // Windows Forms HintMoveDown
   GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR,            // Windows Forms BarProgressBar
   GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ,                   // Glare object
  };
//+------------------------------------------------------------------+


Para dibujar efectos visuales (en este caso, los destellos), deberemos establecer una lista de estilos de efectos visuales:

//+------------------------------------------------------------------+
//| List of visual effect styles                                     |
//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_VISUAL_EFF_STYLE
  {
   CANV_ELEMENT_VISUAL_EFF_STYLE_RECTANGLE,           // Rectangle
   CANV_ELEMENT_VISUAL_EFF_STYLE_PARALLELOGRAMM,      // Parallelogram
  };
//+------------------------------------------------------------------+

Por ahora, implementaremos solo dos tipos de destellos: un rectángulo regular y un paralelogramo. A continuación, podremos dibujar cualquier número de formas de destellos predefinidas y usarlas para diseñar efectos visuales. No tendrán por qué ser obligatoriamente primitivas gráficas: pueden ser imágenes superpuestas a objetos como efectos, especialmente porque estas imágenes pueden estar animadas, lo cual implementaremos más adelante como ejemplo.

En el archivo \MQL5\Include\DoEasy\Data.mqh, añadiremos los índices de los nuevos mensajes de la biblioteca :

//--- CForm
   MSG_FORM_OBJECT_TEXT_NO_SHADOW_OBJ_FIRST_CREATE_IT,// No shadow object. Create it using the CreateShadowObj() method
   MSG_FORM_OBJECT_TEXT_NO_GLARE_OBJ_FIRST_CREATE_IT, // No glare object. We must first create it using the CreateGlareObj() method
   MSG_FORM_OBJECT_ERR_FAILED_CREATE_SHADOW_OBJ,      // Failed to create new shadow object
   MSG_FORM_OBJECT_ERR_FAILED_CREATE_GLARE_OBJ,       // Failed to create new glare object
   MSG_FORM_OBJECT_ERR_FAILED_CREATE_PC_OBJ,          // Failed to create new pixel copier object
   MSG_FORM_OBJECT_PC_OBJ_ALREADY_IN_LIST,            // Pixel copier object with ID already present in the list 

y los mensajes de texto correspondientes a los nuevos índices añadidos:

//--- CForm
   {"Отсутствует объект тени. Необходимо сначала его создать при помощи метода CreateShadowObj()","There is no shadow object. You must first create it using the CreateShadowObj() method"},
   {"Отсутствует объект блика. Необходимо сначала его создать при помощи метода CreateGlareObj()","There is no glare object. You must first create it using the CreateGlareObj() method"},
   {"Не удалось создать новый объект для тени","Failed to create new object for shadow"},
   {"Не удалось создать новый объект для блика","Failed to create new object for glare"},
   {"Не удалось создать новый объект-копировщик пикселей","Failed to create new pixel copier object"},
   {"В списке уже есть объект-копировщик пикселей с идентификатором ","There is already a pixel copier object in the list with ID "},

Después desplazaremos el índice del mensaje "Objeto de destello" al grupo de objetos estándar de WinForms:

   MSG_GRAPH_ELEMENT_TYPE_ELEMENT,                    // Element
   MSG_GRAPH_ELEMENT_TYPE_SHADOW_OBJ,                 // Shadow object
                                                                    
   MSG_GRAPH_ELEMENT_TYPE_FORM,                       // Form
   MSG_GRAPH_ELEMENT_TYPE_WINDOW,                     // Window

   MSG_GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR,        // BarProgressBar control
   MSG_GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,            // ProgressBar control
   MSG_GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ,               // Glare object
//---
   MSG_GRAPH_ELEMENT_TYPE_WF_WRONG_TYPE_PASSED,       // Incorrect control type

Y de la misma forma trasladaremos el texto de este mensaje:

   {"Элемент","Element"},
   {"Объект тени","Shadow object"},
                     
   {"Форма","Form"},
   {"Окно","Window"},

   {"Элемент управления BarProgressBar","Control element \"BarProgressBar\""},
   {"Элемент управления ProgressBar","Control element \"ProgressBar\""},
   {"Объект блика","Glare object"},
   
   {"Передан не правильный тип элемента управления","Wrong control type passed"},


En el archivo \MQL5\Include\DoEasy\GraphINI.mqh, donde se indican los parámetros de varios esquemas de color, añadiremos una propiedad para especificar el valor de opacidad para el destello. Luego aumentaremos el número total de propiedades a 12:

//+------------------------------------------------------------------+
//| List of form style parameter indices                             |
//+------------------------------------------------------------------+
enum ENUM_FORM_STYLE_PARAMS
  {
   //--- CForm
   FORM_STYLE_FRAME_GLARE_OPACITY,              // Glare opacity
   FORM_STYLE_FRAME_SHADOW_OPACITY,             // Shadow opacity
   FORM_STYLE_FRAME_SHADOW_BLUR,                // Shadow blur
   FORM_STYLE_DARKENING_COLOR_FOR_SHADOW,       // Form shadow color darkening
   FORM_STYLE_FRAME_SHADOW_X_SHIFT,             // Shadow X axis shift
   FORM_STYLE_FRAME_SHADOW_Y_SHIFT,             // Shadow Y axis shift
   FORM_STYLE_GRADIENT_V,                       // Vertical gradient filling flag
   FORM_STYLE_GRADIENT_C,                       // Cyclic gradient filling flag
   //--- CPanel
   FORM_STYLE_FRAME_WIDTH_LEFT,                 // Panel frame width to the left
   FORM_STYLE_FRAME_WIDTH_RIGHT,                // Panel frame width to the right
   FORM_STYLE_FRAME_WIDTH_TOP,                  // Panel frame width on top
   FORM_STYLE_FRAME_WIDTH_BOTTOM,               // Panel frame width below
  };
#define TOTAL_FORM_STYLE_PARAMS        (12)     // Number of form style parameters
//+------------------------------------------------------------------+


Y escribiremos los valores predeterminados para la opacidad de los destellos en el array de propiedades para los dos esquemas de color actualmente disponibles en la biblioteca:

//+------------------------------------------------------------------+
//| Array containing form style parameters                           |
//+------------------------------------------------------------------+
int array_form_style[TOTAL_FORM_STYLES][TOTAL_FORM_STYLE_PARAMS]=
  {
//--- "Flat form" style parameters
   {
      //--- CForm
      40,                                       // Glare opacity
      80,                                       // Shadow opacity
      4,                                        // Shadow blur
      80,                                       // Form shadow color darkening
      2,                                        // Shadow X axis shift
      2,                                        // Shadow Y axis shift
      false,                                    // Vertical gradient filling flag
      false,                                    // Cyclic gradient filling flag
      //--- CPanel
      3,                                        // Panel frame width to the left
      3,                                        // Panel frame width to the right
      3,                                        // Panel frame width on top
      3,                                        // Panel frame width below
   },
//--- "Embossed form" style parameters
   {
      //--- CForm
      40,                                       // Glare opacity
      80,                                       // Shadow opacity
      4,                                        // Shadow blur
      80,                                       // Form shadow color darkening
      2,                                        // Shadow X axis shift
      2,                                        // Shadow Y axis shift
      true,                                     // Vertical gradient filling flag
      false,                                    // Cyclic gradient filling flag
      //--- CPanel
      3,                                        // Panel frame width to the left
      3,                                        // Panel frame width to the right
      3,                                        // Panel frame width on top
      3,                                        // Panel frame width below
   },
  };
//+------------------------------------------------------------------+

En el futuro, modificaremos radicalmente y complementaremos la composición de estos valores de propiedad para diferentes esquemas de color cuando comencemos a implementarlos.


Como hemos cambiado hoy la categoría del elemento gráfico de destello, en el archivo \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh de la clase de objeto básico de todos los objetos gráficos de la biblioteca, en el método que retorna la descripción del tipo del elemento gráfico, trasladaremos el procesamiento del tipo "objeto de destello" transmitido al método desde su ubicación anterior al lugar correspondiente al tipo:

//+------------------------------------------------------------------+
//| Return the description of the graphical element type             |
//+------------------------------------------------------------------+
string CGBaseObj::TypeElementDescription(const ENUM_GRAPH_ELEMENT_TYPE type)
  {
   return
     (
      type==GRAPH_ELEMENT_TYPE_STANDARD                  ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD)                 :
      type==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED)        :
      type==GRAPH_ELEMENT_TYPE_ELEMENT                   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_ELEMENT)                  :
      type==GRAPH_ELEMENT_TYPE_SHADOW_OBJ                ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_SHADOW_OBJ)               :
                                                                                                                            
      type==GRAPH_ELEMENT_TYPE_FORM                      ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_FORM)                     :
      type==GRAPH_ELEMENT_TYPE_WINDOW                    ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WINDOW)                   :
      //--- WinForms
      type==GRAPH_ELEMENT_TYPE_WF_UNDERLAY               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_UNDERLAY)              :
      type==GRAPH_ELEMENT_TYPE_WF_BASE                   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BASE)                  :
      //--- Containers
      type==GRAPH_ELEMENT_TYPE_WF_CONTAINER              ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CONTAINER)             :
      type==GRAPH_ELEMENT_TYPE_WF_GROUPBOX               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_GROUPBOX)              :
      type==GRAPH_ELEMENT_TYPE_WF_PANEL                  ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_PANEL)                 :
      type==GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL)           :
      type==GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER        ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER)       :
      //--- Standard controls
      type==GRAPH_ELEMENT_TYPE_WF_COMMON_BASE            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_COMMON_BASE)           :
      type==GRAPH_ELEMENT_TYPE_WF_LABEL                  ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LABEL)                 :
      type==GRAPH_ELEMENT_TYPE_WF_CHECKBOX               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKBOX)              :
      type==GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON)           :
      type==GRAPH_ELEMENT_TYPE_WF_BUTTON                 ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON)                :
      type==GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX      ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ELEMENTS_LIST_BOX)     :
      type==GRAPH_ELEMENT_TYPE_WF_LIST_BOX               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX)              :
      type==GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM          ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM)         :
      type==GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX       ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX)      :
      type==GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX        ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX)       :
      type==GRAPH_ELEMENT_TYPE_WF_TOOLTIP                ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TOOLTIP)               :
      type==GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR           ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR)          :
      //--- Auxiliary control objects
      type==GRAPH_ELEMENT_TYPE_WF_TAB_HEADER             ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_HEADER)            :
      type==GRAPH_ELEMENT_TYPE_WF_TAB_FIELD              ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_TAB_FIELD)             :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON           ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON)          :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP        ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP)       :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN      ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN)     :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT      ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT)     :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT     ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT)    :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX)  :
      type==GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX   ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX)  :
      type==GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL  ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER_PANEL) :
      type==GRAPH_ELEMENT_TYPE_WF_SPLITTER               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_SPLITTER)              :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_BASE              ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_BASE)             :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_LEFT)        :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT        ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_RIGHT)       :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP           ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_UP)          :
      type==GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_HINT_MOVE_DOWN)        :
      type==GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR       ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR)      :
      type==GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ              ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ)             :
      "Unknown"
     );
  }  
//+------------------------------------------------------------------+

Técnicamente, no existe diferencia en el orden de verificación, pero por el bien del orden, lo desplazaremos al lugar "correcto". Por cierto, como es obvio, existe una diferencia desde el punto de vista técnico en la velocidad de acceso, ya que la verificación if-else se organiza aquí y, si algún tipo se verifica con más frecuencia que otros, pero se encuentra más cerca del final de la lista de comprobaciones, el tiempo de acceso aumentará. Pero esto se da solo en teoría en el marco de los ciclos del procesador. No obstante, para que el acceso a cualquier elemento sea el mismo, deberemos realizar todas estas comprobaciones en los métodos de la biblioteca usando el operador switch, cosa que realizaremos posteriormente al final de la creación de la biblioteca.

Antes planeábamos (y ya lo hicimos en el último artículo) que el objeto de destello se heredara del objeto de sombra. La razón radicaba en la presencia de métodos de desenfoque gaussiano en los métodos del objeto de sombra. Ahora hemos abandonado esta idea, porque, en primer lugar, no necesitamos usar el desenfoque para implementar destellos, y en segundo lugar, porque usar un objeto de sombra como principal para un objeto de destello complicará enormemente su adición a otros elementos gráficos como parte de los objetos adjuntos a ellos. Por lo tanto, el objeto de destello será un objeto WinForms auxiliar independiente. No obstante, para que podamos usar el desenfoque en aquellos objetos en los que sea necesario (y no solo en el objeto de sombra), transferiremos los métodos de desenfoque del objeto de sombra al objeto de elemento gráfico; desde allí, estos métodos estarán disponibles para todos los elementos gráficos de la biblioteca.

Ahora, quitaremos del archivo \MQL5\Include\DoEasy\Objects\Graph\ShadowObj.mqh la inclusión de la biblioteca AlgLib, eliminaremos la declaración de los métodos para el desenfoque y borraremos los códigos de implementación para estos dos métodos:

//+------------------------------------------------------------------+
//|                                                    ShadowObj.mqh |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "GCnvElement.mqh"
#include <Math\Alglib\alglib.mqh>
//+------------------------------------------------------------------+
//| Shadows object class                                             |
//+------------------------------------------------------------------+
class CShadowObj : public CGCnvElement
  {
private:
   color             m_color;                         // Shadow color
   uchar             m_opacity;                       // Shadow opacity
   uchar             m_blur;                          // Blur
//--- Draw the object shadow form
   void              DrawShadowFigureRect(const int w,const int h);
protected:
//--- Gaussian blur
   bool              GaussianBlur(const uint radius);
//--- Return the array of weight ratios
   bool              GetQuadratureWeights(const double mu0,const int n,double &weights[]);
//--- Protected constructor with object type, chart ID and subwindow
                     CShadowObj(const ENUM_GRAPH_ELEMENT_TYPE type,
                                CGCnvElement *main_obj,CGCnvElement *base_obj,
                                const long chart_id,
                                const int subwindow,
                                const string descript,
                                const int x,
                                const int y,
                                const int w,
                                const int h);
public:


En el archivo \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh, escribiremos la conexión de la biblioteca AlgLib y, en la sección protegida, declararemos los métodos para implementar el desenfoque:

//+------------------------------------------------------------------+
//|                                                  GCnvElement.mqh |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "GBaseObj.mqh"
#include <Math\Alglib\alglib.mqh>
//+------------------------------------------------------------------+
//| Class of the graphical element object                            |
//+------------------------------------------------------------------+
class CGCnvElement : public CGBaseObj
  {
protected:
   CGCnvElement     *m_element_main;                           // Pointer to the initial parent element within all the groups of bound objects
   CGCnvElement     *m_element_base;                           // Pointer to the parent element within related objects of the current group
   CCanvas           m_canvas;                                 // CCanvas class object
   CPause            m_pause;                                  // Pause class object
   bool              m_shadow;                                 // Shadow presence
   color             m_chart_color_bg;                         // Chart background color
   uint              m_duplicate_res[];                        // Array for storing resource data copy
   color             m_array_colors_bg[];                      // Array of element background colors
   color             m_array_colors_bg_dwn[];                  // Array of control background colors when clicking on the control
   color             m_array_colors_bg_ovr[];                  // Array of control background colors when hovering the mouse over the control
   bool              m_gradient_v;                             // Vertical gradient filling flag
   bool              m_gradient_c;                             // Cyclic gradient filling flag
   int               m_init_relative_x;                        // Initial relative X coordinate
   int               m_init_relative_y;                        // Initial relative Y coordinate
   color             m_array_colors_bg_init[];                 // Array of element background colors (initial color)

//--- Create (1) the object structure and (2) the object from the structure
   virtual bool      ObjectToStruct(void);
   virtual void      StructToObject(void);
//--- Copy the color array to the specified background color array
   void              CopyArraysColors(color &array_dst[],const color &array_src[],const string source);
   
//--- Return the number of graphical elements (1) by type, (2) by name and type
   int               GetNumGraphElements(const ENUM_GRAPH_ELEMENT_TYPE type) const;
   int               GetNumGraphElements(const string name,const ENUM_GRAPH_ELEMENT_TYPE type) const;
//--- Create and return the graphical element name by its type
   string            CreateNameGraphElement(const ENUM_GRAPH_ELEMENT_TYPE type);
   
//--- Gaussian blur
   bool              GaussianBlur(const uint radius);
//--- Return the array of weight ratios
   bool              GetQuadratureWeights(const double mu0,const int n,double &weights[]);

private:


Fuera del cuerpo de la clase, añadiremos la implementación de los métodos de desenfoque eliminados de la clase CShadowObj:

//+------------------------------------------------------------------+
//| Gaussian blur                                                    |
//| https://www.mql5.com/en/articles/1612#chapter4                   |
//+------------------------------------------------------------------+
bool CGCnvElement::GaussianBlur(const uint radius)
  {
//---
   int n_nodes=(int)radius*2+1;
//--- Read graphical resource data. If failed, return false
   if(!CGCnvElement::ResourceStamp(DFUN))
      return false;
//--- Check the blur amount. If the blur radius exceeds half of the width or height, return 'false'
   if((int)radius>=this.Width()/2 || (int)radius>=this.Height()/2)
     {
      ::Print(DFUN,CMessage::Text(MSG_SHADOW_OBJ_IMG_SMALL_BLUR_LARGE));
      return false;
     }
     
//--- Decompose image data from the resource into a, r, g, b color components
   int  size=::ArraySize(this.m_duplicate_res);
//--- arrays for storing A, R, G and B color components
//--- for horizontal and vertical blur
   uchar a_h_data[],r_h_data[],g_h_data[],b_h_data[];
   uchar a_v_data[],r_v_data[],g_v_data[],b_v_data[];
   
//--- Change the size of component arrays according to the array size of the graphical resource data
   if(::ArrayResize(a_h_data,size)==-1)
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
      ::Print(DFUN_ERR_LINE,": \"a_h_data\"");
      return false;
     }
   if(::ArrayResize(r_h_data,size)==-1)
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
      ::Print(DFUN_ERR_LINE,": \"r_h_data\"");
      return false;
     }
   if(::ArrayResize(g_h_data,size)==-1)
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
      ::Print(DFUN_ERR_LINE,": \"g_h_data\"");
      return false;
     }
   if(ArrayResize(b_h_data,size)==-1)
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
      ::Print(DFUN_ERR_LINE,": \"b_h_data\"");
      return false;
     }
   if(::ArrayResize(a_v_data,size)==-1)
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
      ::Print(DFUN_ERR_LINE,": \"a_v_data\"");
      return false;
     }
   if(::ArrayResize(r_v_data,size)==-1)
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
      ::Print(DFUN_ERR_LINE,": \"r_v_data\"");
      return false;
     }
   if(::ArrayResize(g_v_data,size)==-1)
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
      ::Print(DFUN_ERR_LINE,": \"g_v_data\"");
      return false;
     }
   if(::ArrayResize(b_v_data,size)==-1)
     {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
      ::Print(DFUN_ERR_LINE,": \"b_v_data\"");
      return false;
     }
//--- Declare the array for storing blur weight ratios and,
//--- if failed to get the array of weight ratios, return 'false'
   double weights[];
   if(!this.GetQuadratureWeights(1,n_nodes,weights))
      return false;
      
//--- Set components of each image pixel to the color component arrays
   for(int i=0;i<size;i++)
     {
      a_h_data[i]=GETRGBA(this.m_duplicate_res[i]);
      r_h_data[i]=GETRGBR(this.m_duplicate_res[i]);
      g_h_data[i]=GETRGBG(this.m_duplicate_res[i]);
      b_h_data[i]=GETRGBB(this.m_duplicate_res[i]);
     }

//--- Blur the image horizontally (along the X axis)
   uint XY; // Pixel coordinate in the array
   double a_temp=0.0,r_temp=0.0,g_temp=0.0,b_temp=0.0;
   int coef=0;
   int j=(int)radius;
   //--- Loop by the image width
   for(int Y=0;Y<this.Height();Y++)
     {
      //--- Loop by the image height
      for(uint X=radius;X<this.Width()-radius;X++)
        {
         XY=Y*this.Width()+X;
         a_temp=0.0; r_temp=0.0; g_temp=0.0; b_temp=0.0;
         coef=0;
         //--- Multiply each color component by the weight ratio corresponding to the current image pixel
         for(int i=-1*j;i<j+1;i=i+1)
           {
            a_temp+=a_h_data[XY+i]*weights[coef];
            r_temp+=r_h_data[XY+i]*weights[coef];
            g_temp+=g_h_data[XY+i]*weights[coef];
            b_temp+=b_h_data[XY+i]*weights[coef];
            coef++;
           }
         //--- Save each rounded color component calculated according to the ratios to the component arrays
         a_h_data[XY]=(uchar)::round(a_temp);
         r_h_data[XY]=(uchar)::round(r_temp);
         g_h_data[XY]=(uchar)::round(g_temp);
         b_h_data[XY]=(uchar)::round(b_temp);
        }
      //--- Remove blur artifacts to the left by copying adjacent pixels
      for(uint x=0;x<radius;x++)
        {
         XY=Y*this.Width()+x;
         a_h_data[XY]=a_h_data[Y*this.Width()+radius];
         r_h_data[XY]=r_h_data[Y*this.Width()+radius];
         g_h_data[XY]=g_h_data[Y*this.Width()+radius];
         b_h_data[XY]=b_h_data[Y*this.Width()+radius];
        }
      //--- Remove blur artifacts to the right by copying adjacent pixels
      for(int x=int(this.Width()-radius);x<this.Width();x++)
        {
         XY=Y*this.Width()+x;
         a_h_data[XY]=a_h_data[(Y+1)*this.Width()-radius-1];
         r_h_data[XY]=r_h_data[(Y+1)*this.Width()-radius-1];
         g_h_data[XY]=g_h_data[(Y+1)*this.Width()-radius-1];
         b_h_data[XY]=b_h_data[(Y+1)*this.Width()-radius-1];
        }
     }

//--- Blur vertically (along the Y axis) the image already blurred horizontally
   int dxdy=0;
   //--- Loop by the image height
   for(int X=0;X<this.Width();X++)
     {
      //--- Loop by the image width
      for(uint Y=radius;Y<this.Height()-radius;Y++)
        {
         XY=Y*this.Width()+X;
         a_temp=0.0; r_temp=0.0; g_temp=0.0; b_temp=0.0;
         coef=0;
         //--- Multiply each color component by the weight ratio corresponding to the current image pixel
         for(int i=-1*j;i<j+1;i=i+1)
           {
            dxdy=i*(int)this.Width();
            a_temp+=a_h_data[XY+dxdy]*weights[coef];
            r_temp+=r_h_data[XY+dxdy]*weights[coef];
            g_temp+=g_h_data[XY+dxdy]*weights[coef];
            b_temp+=b_h_data[XY+dxdy]*weights[coef];
            coef++;
           }
         //--- Save each rounded color component calculated according to the ratios to the component arrays
         a_v_data[XY]=(uchar)::round(a_temp);
         r_v_data[XY]=(uchar)::round(r_temp);
         g_v_data[XY]=(uchar)::round(g_temp);
         b_v_data[XY]=(uchar)::round(b_temp);
        }
      //--- Remove blur artifacts at the top by copying adjacent pixels
      for(uint y=0;y<radius;y++)
        {
         XY=y*this.Width()+X;
         a_v_data[XY]=a_v_data[X+radius*this.Width()];
         r_v_data[XY]=r_v_data[X+radius*this.Width()];
         g_v_data[XY]=g_v_data[X+radius*this.Width()];
         b_v_data[XY]=b_v_data[X+radius*this.Width()];
        }
      //--- Remove blur artifacts at the bottom by copying adjacent pixels
      for(int y=int(this.Height()-radius);y<this.Height();y++)
        {
         XY=y*this.Width()+X;
         a_v_data[XY]=a_v_data[X+(this.Height()-1-radius)*this.Width()];
         r_v_data[XY]=r_v_data[X+(this.Height()-1-radius)*this.Width()];
         g_v_data[XY]=g_v_data[X+(this.Height()-1-radius)*this.Width()];
         b_v_data[XY]=b_v_data[X+(this.Height()-1-radius)*this.Width()];
        }
     }
     
//--- Set the twice blurred (horizontally and vertically) image pixels to the graphical resource data array
   for(int i=0;i<size;i++)
      this.m_duplicate_res[i]=ARGB(a_v_data[i],r_v_data[i],g_v_data[i],b_v_data[i]);
//--- Display the image pixels on the canvas in a loop by the image height and width from the graphical resource data array
   for(int X=0;X<this.Width();X++)
     {
      for(uint Y=radius;Y<this.Height()-radius;Y++)
        {
         XY=Y*this.Width()+X;
         this.m_canvas.PixelSet(X,Y,this.m_duplicate_res[XY]);
        }
     }
//--- Done
   return true;
  }
//+------------------------------------------------------------------+
//| Return the array of weight ratios                                |
//| https://www.mql5.com/en/articles/1612#chapter3_2                 |
//+------------------------------------------------------------------+
bool CGCnvElement::GetQuadratureWeights(const double mu0,const int n,double &weights[])
  {
   CAlglib alglib;
   double  alp[];
   double  bet[];
   ::ArrayResize(alp,n);
   ::ArrayResize(bet,n);
   ::ArrayInitialize(alp,1.0);
   ::ArrayInitialize(bet,1.0);
//---
   double out_x[];
   int    info=0;
   alglib.GQGenerateRec(alp,bet,mu0,n,info,out_x,weights);
   if(info!=1)
     {
      string txt=(info==-3 ? "internal eigenproblem solver hasn't converged" : info==-2 ? "Beta[i]<=0" : "incorrect N was passed");
      ::Print("Call error in CGaussQ::GQGenerateRec: ",txt);
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+

Escribimos estos métodos hace mucho tiempo, y ya los hemos analizado en artículos anteriores: simplemente los transferiremos de una clase a otra.


Ya hemos dicho antes que debemos mejorar los métodos para dibujar las primitivas gráficas y las imágenes predefinidas. Como los métodos para dibujar primitivas con suavizado no funcionan muy bien al dibujar líneas semitransparentes, deberemos reemplazar el dibujado de primitivas suavizadas por otras sin suavizar si la transparencia de la línea supera el umbral establecido, a saber, la mitad del valor total de opacidad.

Echemos un vistazo a los métodos actualizados. Si el valor de opacidad de la primitiva gráfica dibujada supera el 127, la primitiva se dibujará con el método de suavizado. De lo contrario, dibujaremos usando el método sin suavizado:

//+------------------------------------------------------------------+
//| Draw the Info icon                                               |
//+------------------------------------------------------------------+
void CGCnvElement::DrawIconInfo(const int coord_x,const int coord_y,const uchar opacity)
  {
   int x=coord_x+8;
   int y=coord_y+8;
   this.DrawCircleFill(x,y,7,C'0x00,0x77,0xD7',opacity);
   if(opacity>127)
      this.DrawCircleWu(x,y,7.5,C'0x00,0x3D,0x8C',opacity);
   else
      this.DrawCircle(x,y,8,C'0x00,0x3D,0x8C',opacity);
   this.DrawRectangle(x,y-5,x+1,y-4, C'0xFF,0xFF,0xFF',opacity);
   this.DrawRectangle(x,y-2,x+1,y+4,C'0xFF,0xFF,0xFF',opacity);
  }
//+------------------------------------------------------------------+
//| Draw the Warning icon                                            |
//+------------------------------------------------------------------+
void CGCnvElement::DrawIconWarning(const int coord_x,const int coord_y,const uchar opacity)
  {
   int x=coord_x+8;
   int y=coord_y+1;
   this.DrawTriangleFill(x,y,x+8,y+14,x-8,y+14,C'0xFC,0xE1,0x00',opacity);
   if(opacity>127)
      this.DrawTriangleWu(x,y,x+8,y+14,x-7,y+14,C'0xFF,0xB9,0x00',opacity);
   else
      this.DrawTriangle(x,y,x+8,y+14,x-7,y+14,C'0xFF,0xB9,0x00',opacity);
   this.DrawRectangle(x,y+5,x+1,y+9,  C'0x00,0x00,0x00',opacity);
   this.DrawRectangle(x,y+11,x+1,y+12,C'0x00,0x00,0x00',opacity);
  }
//+------------------------------------------------------------------+
//| Draw the Error icon                                              |
//+------------------------------------------------------------------+
void CGCnvElement::DrawIconError(const int coord_x,const int coord_y,const uchar opacity)
  {
   int x=coord_x+8;
   int y=coord_y+8;
   this.DrawCircleFill(x,y,7,C'0xF0,0x39,0x16',opacity);
   if(opacity>127)
     {
      this.DrawCircleWu(x,y,7.5,C'0xA5,0x25,0x12',opacity);
      this.DrawLineWu(x-3,y-3,x+3,y+3,C'0xFF,0xFF,0xFF',opacity);
      this.DrawLineWu(x+3,y-3,x-3,y+3,C'0xFF,0xFF,0xFF',opacity);
     }
   else
     {
      this.DrawCircle(x,y,8,C'0xA5,0x25,0x12',opacity);
      this.DrawLine(x-3,y-3,x+3,y+3,C'0xFF,0xFF,0xFF',opacity);
      this.DrawLine(x+3,y-3,x-3,y+3,C'0xFF,0xFF,0xFF',opacity);
     }
  }
//+------------------------------------------------------------------+

Obviamente, esto solo ocultará el problema, aunque el uso aquí está justificado: cuando la opacidad disminuye de 255 a 128, la línea suavizada parece más o menos normal, pero luego comienza a romperse y desmoronarse en píxeles en lugar de volverse más transparente. Si remplazamos el dibujado de una línea por el dibujado sin suavizado, esto resultará completamente invisible aquí: la línea ya es semitransparente y en este estado no se notará su transición, que resultará claramente visible si dibujamos una línea sin suavizar completamente opaca.

El resto de los métodos de dibujado de formas predefinidos de esta clase también utilizarán métodos de dibujado suavizado, pero aún no hemos probado cómo quedará todo al cambiar la transparencia. Si también hay artefactos visuales en esos métodos, los mejoraremos exactamente de la misma forma.


Como ahora el objeto resaltado no es un tipo aislado separado (como un objeto de sombra), sino que pertenece a la lista de objetos WinForms auxiliares, la inclusión del archivo de clase de este objeto ahora deberá realizarse en el archivo de la clase del objeto de panel. Este objeto será un objeto contenedor al que estarán conectados los archivos de todos los objetos WinForms de la biblioteca.
Desde el archivo \MQL5\Include\DoEasy\Objects\Graph\WForms\WinFormBase.mqh, eliminaremos la línea de conexión de la clase del objeto de destello:

//+------------------------------------------------------------------+
//|                                                  WinFormBase.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "GlareObj.mqh"
#include "..\Form.mqh"
#include "..\..\..\Services\Select.mqh"
//+------------------------------------------------------------------+

y escribiremos la inclusión del archivo de la clase de objeto de destello en el archivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Panel.mqh de la clase de objeto de panel:

//+------------------------------------------------------------------+
//|                                                        Panel.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Container.mqh"
#include "..\Helpers\TabField.mqh"
#include "..\Helpers\ArrowUpButton.mqh"
#include "..\Helpers\ArrowDownButton.mqh"
#include "..\Helpers\ArrowLeftButton.mqh"
#include "..\Helpers\ArrowRightButton.mqh"
#include "..\Helpers\ArrowUpDownBox.mqh"
#include "..\Helpers\ArrowLeftRightBox.mqh"
#include "..\Helpers\HintMoveLeft.mqh"
#include "..\Helpers\HintMoveRight.mqh"
#include "..\Helpers\HintMoveUp.mqh"
#include "..\Helpers\HintMoveDown.mqh"
#include "GroupBox.mqh"
#include "TabControl.mqh"
#include "SplitContainer.mqh"
#include "..\..\WForms\Common Controls\ListBox.mqh"
#include "..\..\WForms\Common Controls\CheckedListBox.mqh"
#include "..\..\WForms\Common Controls\ButtonListBox.mqh"
#include "..\..\WForms\Common Controls\ToolTip.mqh"
#include "..\..\WForms\Common Controls\ProgressBar.mqh"
#include "..\..\WForms\GlareObj.mqh"
//+------------------------------------------------------------------+
//| Panel object class of WForms controls                            |
//+------------------------------------------------------------------+
class CPanel : public CContainer

Ahora el objeto destacado estará disponible en todos los objetos contenedores, a partir de los cuales podremos crearlo y conectarlo a las listas de objetos adjuntos en cualquier otro elemento gráfico de la biblioteca. Y como cualquier ventana de interfaz independiente es a priori un contenedor, podremos añadir un objeto de destello (y posteriormente un objeto de efecto visual) a cualquier control adjunto a esta ventana.


En el archivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Containers\Container.mqh de la clase del objeto contenedor básico, en su método para establecer los parámetros del objeto adjunto, añadiremos un bloque de código al final para establecer los parámetros del objeto de destello recién creado:

//+------------------------------------------------------------------+
//| Set parameters for the attached object                           |
//+------------------------------------------------------------------+
void CContainer::SetObjParams(CWinFormBase *obj,const color colour)
  {
//--- Set the text color of the object to be the same as that of the base container
   obj.SetForeColor(this.ForeColor(),true);
//--- If the created object is not a container, set the same group for it as the one for its base object
   if(obj.TypeGraphElement()<GRAPH_ELEMENT_TYPE_WF_CONTAINER || obj.TypeGraphElement()>GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER)
      obj.SetGroup(this.Group());
//--- Depending on the object type
   switch(obj.TypeGraphElement())
     {
      //--- For the Container, Panel and GroupBox WinForms objects
      case GRAPH_ELEMENT_TYPE_WF_CONTAINER            :
      case GRAPH_ELEMENT_TYPE_WF_PANEL                :
      case GRAPH_ELEMENT_TYPE_WF_GROUPBOX             :
        obj.SetBorderColor(obj.BackgroundColor(),true);
        break;
      //--- For "Label", "CheckBox" and "RadioButton" WinForms objects

      //---...
      //---...

      //--- For ProgressBar WinForms object
      case GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR         :
        obj.SetBackgroundColor(CLR_DEF_CONTROL_PROGRESS_BAR_BACK_COLOR,true);
        obj.SetBorderColor(CLR_DEF_CONTROL_PROGRESS_BAR_BORDER_COLOR,true);
        obj.SetForeColor(CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR,true);
        obj.SetBorderStyle(FRAME_STYLE_SIMPLE);
        break;
      //--- For GlareObj WinForms object
      case GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ            :
        obj.SetBackgroundColor(CLR_CANV_NULL,true);
        obj.SetBorderColor(CLR_CANV_NULL,true);
        obj.SetForeColor(CLR_CANV_NULL,true);
        obj.SetBorderStyle(FRAME_STYLE_NONE);
        break;
      default:
        break;
     }
   obj.Crop();
  }
//+------------------------------------------------------------------+

Después de crear cualquiera de los controles gráficos adjuntos al contenedor, llamaremos a este método, que se encargará de establecer las propiedades mínimas para que el objeto funcione correctamente.


Vamos a mejorar la clase de objeto de destello en el archivo \MQL5\Include\DoEasy\Objects\Graph\WForms\GlareObj.mqh.

La clase heredará ahora de la clase de objeto WinForms básico. Ahora incluiremos su archivo en el archivo de la clase de objeto de destello y cambiaremos la clase padre:

//+------------------------------------------------------------------+
//|                                                     GlareObj.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "WinFormBase.mqh"
//+------------------------------------------------------------------+
//| Glare object class                                               |
//+------------------------------------------------------------------+
class CGlareObj : public CWinFormBase
  {
   //---...


En la sección privada, declararemos una variable para almacenar el estilo de destello y cambiaremos los métodos de dibujado del destello, declarando otro más:

class CGlareObj : public CWinFormBase
  {
private:
   ENUM_CANV_ELEMENT_VISUAL_EFF_STYLE m_style;        // Glare style
   color             m_color;                         // Glare color
   uchar             m_opacity;                       // Glare opacity
   uchar             m_blur;                          // Blur
//--- Draw the object glare form
   void              DrawGlareFigure(void);
   void              DrawFigureRectangle(void);
   void              DrawFigureParallelogram(void);
protected:

El estilo de destello determinará qué método llamar para dibujarlo. En este caso, el método DrawGlareFigure() llamará a los métodos de dibujado correspondientes al estilo establecido. No habrá parámetros formales en los métodos de dibujado del destello: por el momento, no hay nada que especificar para la correcta representación de una imagen en un elemento gráfico con un tamaño predefinido. El color y la opacidad del destello a dibujar se establecerán de antemano usando los métodos apropiados. El desenfoque aún no se utilizará.

En la sección pública de la clase, declararemos los métodos para dibujar un destello, redibujarlo, configurar la opacidad e inicializar las propiedades, y escribiremos los métodos para configurar y obtener algunas propiedades del objeto:

public:
//--- Constructor indicating the main and base objects, chart ID and subwindow
                     CGlareObj(CGCnvElement *main_obj,CGCnvElement *base_obj,
                               const long chart_id,
                               const int subwindow,
                               const string descript,
                               const int x,
                               const int y,
                               const int w,
                               const int h);

//--- Supported object properties (1) integer, (2) real and (3) string ones
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property) { return true; }
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE property)  { return true; }
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_STRING property)  { return true; }
   
//--- Draw the object glare
   void              Draw(void);
//--- Redraw the object
   virtual void      Redraw(bool redraw);
//--- Set the element opacity
   virtual void      SetOpacity(const uchar value,const bool redraw=false);
//--- Initialize the properties
   void              Initialize(void);

//+------------------------------------------------------------------+
//| Methods of simplified access to object properties                |
//+------------------------------------------------------------------+
//--- (1) Set and (2) return the glare color
   void              SetColor(const color colour)                             { this.m_color=colour;     }
   color             Color(void)                                        const { return this.m_color;     }
//--- (1) Set and (2) return the opacity of the drawn glare
   void              SetOpacityDraw(const uchar opacity)                      { this.m_opacity=opacity;  }
   uchar             OpacityDraw(void)                                  const { return this.m_opacity;   }
//--- (1) Set and (2) return the glare blur
   void              SetBlur(const uchar blur)                                { this.m_blur=blur;        }
   uchar             Blur(void)                                         const { return this.m_blur;      }
//--- (1) Set and (2) return the glare visual effect
   void              SetVisualEffectStyle(const ENUM_CANV_ELEMENT_VISUAL_EFF_STYLE style)
                       { this.m_style=style;                                                             }
   ENUM_CANV_ELEMENT_VISUAL_EFF_STYLE VisualEffectStyle(void)           const { return this.m_style;     }
//--- Set the glare visual effect style as (1) rectangle and (2) parallelogram
   void              SetStyleRectangle(void)
                       { this.SetVisualEffectStyle(CANV_ELEMENT_VISUAL_EFF_STYLE_RECTANGLE);             }
   void              SetStyleParallelogramm(void)
                       { this.SetVisualEffectStyle(CANV_ELEMENT_VISUAL_EFF_STYLE_PARALLELOGRAMM);        }
  };
//+------------------------------------------------------------------+

En el bloque de métodos para el acceso simplificado a las propiedades de los objetos, escribiremos los métodos en los que los valores trasmitidos al método simplemente se establecerán en variables privadas; los valores registrados se retornarán desde estas variables.

En los constructores de clases, reemplazaremos la inicialización de la clase principal CShadowObj con CWinFormBase y añadiremos la llamada al método de inicialización de variables:

//+------------------------------------------------------------------+
//| Protected constructor with an object type,                       |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CGlareObj::CGlareObj(const ENUM_GRAPH_ELEMENT_TYPE type,
                     CGCnvElement *main_obj,CGCnvElement *base_obj,
                     const long chart_id,
                     const int subwindow,
                     const string descript,
                     const int x,
                     const int y,
                     const int w,
                     const int h) : CWinFormBase(type,main_obj,base_obj,chart_id,subwindow,descript,x-OUTER_AREA_SIZE,y-OUTER_AREA_SIZE,w+OUTER_AREA_SIZE*2,h+OUTER_AREA_SIZE*2)
  {
//--- Set the specified graphical element type for the object and assign the library object type to the current object
   this.SetTypeElement(type);
   this.m_type=OBJECT_DE_TYPE_GGLARE;
   this.Initialize();
  }
//+------------------------------------------------------------------+
//| Constructor indicating the main and base objects,                |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CGlareObj::CGlareObj(CGCnvElement *main_obj,CGCnvElement *base_obj,
                     const long chart_id,
                     const int subwindow,
                     const string descript,
                     const int x,
                     const int y,
                     const int w,
                     const int h) : CWinFormBase(GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
   this.m_type=OBJECT_DE_TYPE_GGLARE; 
   this.Initialize();
  }
//+------------------------------------------------------------------+

Ahora, en lugar de escribir en cada constructor las líneas de inicialización para cada variable (lo mismo para ambos constructores), simplemente llamaremos al método de inicialización de la propiedad del objeto:

//+------------------------------------------------------------------+
//| Initialize the properties                                        |
//+------------------------------------------------------------------+
void CGlareObj::Initialize(void)
  {
   CGCnvElement::SetBackgroundColor(CLR_CANV_NULL,true);
   CGCnvElement::SetActive(false);
   this.SetBlur(4);
   this.SetColor(clrWhite);
   this.SetOpacity(127);
   this.m_shadow=false;
   this.SetVisibleFlag(false,false);
   this.SetVisualEffectStyle(CANV_ELEMENT_VISUAL_EFF_STYLE_RECTANGLE);
   this.SetDisplayState(CANV_ELEMENT_DISPLAY_STATE_NORMAL);
   this.SetDisplayed(false);
  }
//+------------------------------------------------------------------+

El método establecerá los valores predeterminados para las propiedades del objeto. Estableceremos el desenfoque en 4, y el color de destello en blanco. La opacidad será de 127: un objeto semitransparente, sin sombra (lo cual es natural). Asimismo, estableceremos la bandera de invisibilidad del objeto, y el tipo de efecto visual será un destello rectangular. El estado de la visualización será el normal (el objeto estará oculto), mientras que la bandera de visualización estará reseteada: el objeto no deberá mostrarse en el objeto principal visualizado.


Método que establece la opacidad de un elemento:

//+------------------------------------------------------------------+
//| Set the element opacity                                          |
//+------------------------------------------------------------------+
void CGlareObj::SetOpacity(const uchar value,const bool redraw=false)
  {
   CGCnvElement::SetOpacity(0,false);
   this.SetOpacityDraw(value>(uchar)CLR_DEF_SHADOW_OPACITY ? (uchar)CLR_DEF_SHADOW_OPACITY : value);
   this.m_canvas.Update(redraw);
  }
//+------------------------------------------------------------------+

El método será idéntico al método del objeto de sombra del mismo nombre. Aquí, el sustrato sobre el que se dibuja el destello se establecerá en transparencia total, y el color utilizado para dibujar la imagen resaltada será el valor transmitido al método. En este caso, si el valor de opacidad transmitido al método es mayor que el valor de opacidad de la sombra por defecto, entonces se usará el valor predeterminado. De lo contrario, se transmitirá al método. Esto se hace para que el objeto nunca quede completamente opaco. El valor predeterminado para la sombra lo usaremos simplemente porque resultará bastante adecuado para estos fines.

Método para dibujar destellos:

//+------------------------------------------------------------------+
//| Draw the object glare                                            |
//+------------------------------------------------------------------+
void CGlareObj::Draw(void)
  {
   if(!this.IsVisible())
      return;
//--- Draw the glare
   this.DrawGlareFigure();
  }
//+------------------------------------------------------------------+

Si el objeto está oculto, no necesitaremos dibujar nada; saldremos del método.
Luego llamaremos al método en el que se selecciona el método de dibujado:

//+------------------------------------------------------------------+
//| Draw the object glare form                                       |
//+------------------------------------------------------------------+
void CGlareObj::DrawGlareFigure()
  {
   switch(this.VisualEffectStyle())
     {
      case CANV_ELEMENT_VISUAL_EFF_STYLE_RECTANGLE       : this.DrawFigureRectangle();       break;
      case CANV_ELEMENT_VISUAL_EFF_STYLE_PARALLELOGRAMM  : this.DrawFigureParallelogram();   break;
      default:
        break;
     }
  }
//+------------------------------------------------------------------+

Según el tipo de destello dibujado, llamaremos al método correspondiente que ya dibujará en realidad la forma del destello.


Método que dibuja la forma rectangular del destello de un objeto:

//+------------------------------------------------------------------+
//| Draw the rectangle object glare form                             |
//+------------------------------------------------------------------+
void CGlareObj::DrawFigureRectangle(void)
  {
   CGCnvElement::DrawRectangleFill(0,0,this.Width()-1,this.Height()-1,this.m_color,this.OpacityDraw());
   CGCnvElement::Update();
  }
//+------------------------------------------------------------------+

Simplemente dibujaremos un rectángulo relleno sobre todo el tamaño del objeto con el color del objeto y la opacidad de dibujado establecida.


Método que dibuja la forma del destello del objeto como un paralelogramo:

//+------------------------------------------------------------------+
//| Draw the shape of the object glare as a parallelogram            |
//+------------------------------------------------------------------+
void CGlareObj::DrawFigureParallelogram(void)
  {
   int array_x[]={6,this.Width()-1,this.Width()-1-6,0};
   int array_y[]={0,0,this.Height()-1,this.Height()-1};
   CGCnvElement::DrawPolygonFill(array_x,array_y,this.m_color,this.OpacityDraw());
   CGCnvElement::Update();
  }
//+------------------------------------------------------------------+

Aquí se dibujará un rectángulo "biselado". Primero, declararemos los arrays de coordenadas X e Y de los vértices del polígono, y luego, utilizando los vértices indicados en los conjuntos, dibujaremos un polígono relleno en forma de rectángulo biselado: un paralelogramo con el color y la opacidad de dibujado establecidos para el objeto.


Si simplemente dibujamos un objeto una vez y luego lo desplazamos, notaremos que cuando el objeto movido va más allá de los límites de la barra de progreso (en este caso, el objeto se desplazará precisamente a lo largo de él), el objeto de destello permanecerá visible. Visualmente, parecerá que el destello "salta" fuera de la barra de progreso y se dibuja en el panel al que está vinculado el control ProgressBar. Para evitar esto, deberemos redibujar el objeto recortando aquellas partes que vayan más allá de su contenedor. Para lograr esto, necesitaremos un método para redibujar el objeto.

Método virtual que redibuja un objeto:

//+------------------------------------------------------------------+
//| Redraw the object                                                |
//+------------------------------------------------------------------+
void CGlareObj::Redraw(bool redraw)
  {
   CGCnvElement::Erase(false);
   this.Draw();
   this.Crop();
   this.Update(redraw);
  }
//+------------------------------------------------------------------+

Aquí, primero borraremos completamente el objeto con su relleno de color y opacidad. Como hemos establecido el color de fondo del objeto en transparente y el valor de opacidad es cero, el objeto se volverá completamente transparente. A continuación, llamaremos al método anteriormente analizado para dibujar la forma de destello establecida, cortaremos las áreas del objeto que sobresalgan del contenedor y actualizaremos el lienzo.

Ahora bastará con llamar a este método al mover el objeto a lo largo de la barra de progreso y el destello no irá más allá de su contenedor.


Vamos a desarrollar la funcionalidad del objeto ProgressBar para controlar su barra de progreso.

Para mostrar el progreso de un determinado proceso, necesitaremos aumentar la anchura del objeto BarProgressBar en una cantidad igual al paso de aumento de la barra de progreso. En general, todos los valores de anchura de la barra de progreso serán relativos, como porcentaje de la anchura del objeto ProgressBar y como porcentaje del valor máximo del parámetro Value de la barra de progreso. La barra de progreso podrá cambiar sus valores dentro de los límites establecidos en los parámetros Minimum y Maximum. Pero estos valores no deberán establecerse en píxeles, sino como porcentaje de la anchura del objeto. Entonces, un Value de 100 tendría 100 píxeles de anchura cuando ProgressBar tuviera la misma anchura, pero sería 200 cuando ProgressBar tuviera 200 píxeles de anchura. En este caso, si el valor de Value es 50, se corresponderá con los valores de 50 y 100 píxeles para los anteriores valores de anchura de objeto.

Para que podamos controlar simplemente los valores de la barra de progreso, haremos que antes de usar el objeto, antes de iniciar el ciclo cuyo proceso deberá mostrarse, establezcamos los valores mínimo y máximo de los límites y establezcamos el paso en el que se deberá aumentar el valor de la anchura de la barra de progreso. Y luego, tras completar la siguiente acción en el ciclo rastreado, solo necesitaremos llamar al método PerformStep() del objeto ProgressBar, y la anchura de la barra de progreso aumentará según el paso de aumento especificado. Por consiguiente, siempre deberemos actuar así: conociendo el número de iteraciones del ciclo monitoreado, asignaremos los valores necesarios al objeto ProgressBar y, dentro del ciclo, al completarse la siguiente iteración, llamaremos al método PerformStep(), lo cual hará que la barra de progreso cambie en un paso.

En el archivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Common Controls\ProgressBar.mqh, en su sección privada, declararemos las variables para almacenar los valores de la barra de progreso, así como el método para calcular la anchura de la barra de progreso:

//+------------------------------------------------------------------+
//| ArrowLeftRightBox object class of WForms controls                |
//+------------------------------------------------------------------+
class CProgressBar : public CContainer
  {
private:
   int               m_progress_bar_max;  // Maximum progress bar width
   int               m_value_by_max;      // Value relative to Maximum
   int               m_steps_skipped;     // Number of skipped steps of increasing the width of the progress bar
//--- Create a new graphical object
   virtual CGCnvElement *CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                          const int element_num,
                                          const string descript,
                                          const int x,
                                          const int y,
                                          const int w,
                                          const int h,
                                          const color colour,
                                          const uchar opacity,
                                          const bool movable,
                                          const bool activity);
//--- Create the progress bar object
   void              CreateProgressBar(void);
//--- Calculate the progress bar width
   int               CalculateProgressBarWidth(void);
//--- Initialize the element properties
   void              Initialize(void);

protected:


La implementación del método público SetValue() se sacará del cuerpo de la clase, dejando en el cuerpo solo su declaración:

//--- (1) Set and (2) return the progress bar increment to redraw it
   void              SetStep(const int value)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STEP,value);
                        CBarProgressBar *bar=this.GetProgressBar();
                        if(bar!=NULL)
                           bar.SetStep(value);
                       }
   int               Step(void)                          const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_STEP);    }
   
//--- (1) Set and (2) return the current value of the progress bar in the range from Min to Max
   void              SetValue(const int value);
   int               Value(void)                         const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE);   }
   
//--- (1) Set and (2) return the upper bound of the ProgressBar operating range
   void              SetMaximum(const int value)               { this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MAXIMUM,value);       }
   int               Maximum(void)                       const { return (int)this.GetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_MAXIMUM); }


A continuación, declararemos algunos métodos más:

//--- Return the pointer to the progress bar object
   CBarProgressBar  *GetProgressBar(void)                { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR,0);     }

//--- Resets the progress bar values to the set minimum
   void              ResetProgressBar(void)              { this.SetValue(this.Minimum());    }
//--- Set the element width
   virtual bool      SetWidth(const int width);
//--- Initialize values for handling in PerformStep
   void              SetValuesForProcessing(const int minimum,const int maximum,const int step,const int steps_skipped);
//--- Increase the current position of the progress bar by the step value
   void              PerformStep();

//--- Supported object properties (1) integer, (2) real and (3) string ones
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property) { return true; }
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE property)  { return true; }
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_STRING property)  { return true; }

El método ResetProgressBar() se implementará directamente en el cuerpo de la clase y establecerá el valor Value de la barra de progreso en el valor escrito en la propiedad Minimum. Esto permitirá devolver la barra de progreso a su estado original.

En el método de inicialización del valor predeterminado , estableceremos el valor de la barra de progreso en el 50% de la anchura del objeto, calcularemos la anchura máxima en píxeles para el objeto BarProgressBar, que será la barra de progreso, y calcularemos el valor como un porcentaje de la anchura del objeto.:

//+------------------------------------------------------------------+
//| Initialize the element properties                                |
//+------------------------------------------------------------------+
void CProgressBar::Initialize(void)
  {
   this.SetBorderSizeAll(1);
   this.SetBorderStyle(FRAME_STYLE_SIMPLE);
   this.SetBackgroundColor(CLR_DEF_CONTROL_PROGRESS_BAR_BACK_COLOR,true);
   this.SetBorderColor(CLR_DEF_CONTROL_PROGRESS_BAR_BORDER_COLOR,true);
   this.SetForeColor(CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR,true);
   this.SetMarqueeAnimationSpeed(10);
   this.SetMaximum(100);
   this.SetMinimum(0);
   this.SetValue(50);
   this.SetStep(10);
   this.SetStyle(CANV_ELEMENT_PROGRESS_BAR_STYLE_CONTINUOUS);
   this.m_progress_bar_max=this.Width()-this.BorderSizeLeft()-this.BorderSizeRight();
   this.m_value_by_max=this.Value()*100/this.Maximum();
   this.m_steps_skipped=0;
  }
//+------------------------------------------------------------------+

La anchura máxima del objeto de la barra de progreso no deberá superar la anchura del sustrato menos el tamaño de su marco a la izquierda y a la derecha, para que la barra de progreso pueda caber dentro del marco dibujado en el fondo sin salirse de sus límites.


En el método que crea el objeto de la barra de progreso, estableceremos la anchura del objeto creado a partir del valorcalculado por el método CalculateProgressBarWidth()y añadiremos la creación de un objeto de destello:

//+------------------------------------------------------------------+
//| Create the progress bar object                                   |
//+------------------------------------------------------------------+
void CProgressBar::CreateProgressBar(void)
  {
//--- Set the length of the progress bar equal to the object Value()
//--- The height of the progress bar is equal to the height of the object minus the top and bottom frame sizes
   int w=this.CalculateProgressBarWidth();
   int h=this.Height()-this.BorderSizeTop()-this.BorderSizeBottom();
//--- Create the progress bar object
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR,0,0,w,h,clrNONE,255,false,false);
//--- Create the glare object
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ,0,0,w,h,CLR_CANV_NULL,0,true,false);
//--- Add the current CProgressBar object to the list of active elements of the collection
   this.AddObjToListActiveElements();
  }
//+------------------------------------------------------------------+

La lógica del método se explica con detalle en los comentarios al código. Aquí todo es simple: calcularemos la anchura de la barra de progreso y crearemos un objeto con la anchura calculada. El valor predeterminado supondrá el 50 % de la anchura del objeto ProgressBar. Luego crearemos un objeto de destello y añadiremos el objeto actual a la lista de objetos activos. Todos los objetos que se encuentran en la lista de elementos activos se procesarán en el temporizador de la biblioteca y podrán realizar algunas acciones independientes que se implementan en el controlador del temporizador de estos objetos. Para este control, el temporizador procesará la aparición de un destello en la barra de progreso.


Método que inicializa los valores para su procesamiento en PerformStep:

//+------------------------------------------------------------------+
//| Initialize values for handling in PerformStep                    |
//+------------------------------------------------------------------+
void CProgressBar::SetValuesForProcessing(const int minimum,const int maximum,const int step,const int steps_skipped)
  {
   this.SetMinimum(minimum<0 ? 0 : minimum);
   this.SetMaximum(maximum<this.Minimum() ? this.Minimum() : maximum);
   this.SetStep(step<0 ? 0 : step);
   this.m_steps_skipped=steps_skipped;
  }
//+------------------------------------------------------------------+

Aquí, los valores de los límites mínimo y máximo se transmitirán al método, dentro del cual podremos cambiar el valor de Value, el paso de cambio y la cantidad de pasos omitidos. A continuación, verificaremos la corrección de los valores mínimo, máximo y de paso, y escribiremos en la variable el valor de los pasos omitidos. Como el valor de la barra de progreso se calcula como un porcentaje de la anchura del objeto ProgressBar, lo más probable es que no usemos el valor de los pasos omitidos; esto quedará claro a partir de una serie de pruebas de objetos una vez que estén listos. En esta implementación, dicho valor no se usará en ninguna parte.

Método que establece el valor actual de la barra de progreso:

//+------------------------------------------------------------------+
//| Set the current value of the progress bar                        |
//+------------------------------------------------------------------+
void CProgressBar::SetValue(const int value)
  {
//--- Correct the value passed to the method and set it to the object property
   int v=(value<this.Minimum() ? this.Minimum() : value>this.Maximum() ? this.Maximum() : value);
   this.SetProperty(CANV_ELEMENT_PROP_PROGRESS_BAR_VALUE,v);
//--- Get the progress bar object
   CBarProgressBar *bar=this.GetProgressBar();
   if(bar!=NULL)
     {
      //--- Set 'value' for the progress bar
      bar.SetValue(v);
      //--- Calculate the width of the progress bar object
      int w=this.CalculateProgressBarWidth();
      //--- If the calculated width is greater than the maximum possible value, set the maximum width
      if(w>this.m_progress_bar_max)
         w=this.m_progress_bar_max;
      //--- If the width is less than 1, then
      if(w<1)
        {
         //--- hide the progress bar and redraw the chart to display changes immediately
         bar.Hide();
         ::ChartRedraw(bar.ChartID());
        }
      //--- If the width value is not less than 1
      else
        {
         //--- If the progress bar is hidden, display it and
         if(!bar.IsVisible())
            bar.Show();
         //--- change its size according to the received width
         bar.Resize(w,bar.Height(),true);
        }
     }
  }
//+------------------------------------------------------------------+

La lógica del método se detalla en los comentarios al código. Dicho de forma más clara: si al calcular la anchura de un objeto, su anchura resulta menor a un píxel, ese valor no se podrá establecer para el objeto. Por lo tanto, le asignaremos una anchura de un píxel y ocultaremos el objeto, simulando así una barra de progreso de anchura cero.


Método que aumenta la posición actual de la barra de progreso en el valor del paso:

//+------------------------------------------------------------------------+
//| Increase the current position of the progress bar by the step value    |
//+------------------------------------------------------------------------+
void CProgressBar::PerformStep(void)
  {
   this.SetValue(this.Value()+this.Step());
  }
//+------------------------------------------------------------------+

Aquí simplemente estableceremos la propiedad Value del objeto en su valor pasado, más el valor de incremento del paso de este valor especificado anteriormente. Por lo tanto, con cada llamada subsiguiente a este método, el valor de Value aumentará en el incremento registrado en Step.


Método que calcula la anchura de la barra de progreso:

//+------------------------------------------------------------------+
//| Calculate the width of the progress bar                          |
//+------------------------------------------------------------------+
int CProgressBar::CalculateProgressBarWidth(void)   

  {
   this.m_value_by_max=this.Value()*100/this.Maximum();
   return this.m_progress_bar_max*this.m_value_by_max/100;
  }
//+------------------------------------------------------------------+

Aquí, primero calcularemos el valor de Value como un porcentaje del máximo posible y luego retornaremos la anchura relativa de la anchura máxima de la barra de progreso como un porcentaje del valor calculado anteriormente.


Método que establece la nueva anchura del objeto:

//+------------------------------------------------------------------+
//| Set a new width                                                  |
//+------------------------------------------------------------------+
bool CProgressBar::SetWidth(const int width)
  {
   if(!CGCnvElement::SetWidth(width))
      return false;
   this.m_progress_bar_max=this.Width()-this.BorderSizeLeft()-this.BorderSizeRight();
   CBarProgressBar *bar=this.GetProgressBar();
   if(bar==NULL)
      return false;
   int w=this.CalculateProgressBarWidth();
   bar.SetWidth(w);
   return true;
  }
//+------------------------------------------------------------------+

Aquí, primero configuraremos la nueva anchura de relleno, luego calcularemos la anchura máxima de la barra de progreso en píxeles.
A continuación, obtendremos la anchura relativa en tanto por ciento para la barra de progreso y la configuraremos en el objeto CBarProgressBar, que es la barra de progreso.


Todos los efectos visuales que aparecerán en la barra de progreso deberán procesarse en el temporizador del objeto BarProgressBar. El resaltado deberá aparecer a lo largo de la barra de progreso después de una pausa. Y así constantemente. Pausa - destello - pausa - destello, etc. Para implementar este comportamiento, necesitaremos establecer algunos valores para las propiedades que ya tienen los objetos. Estas propiedades las establecimos para implementar la aparición/desaparición suave de pistas en artículos anteriores. Para un objeto de destello, estas propiedades también resultarán adecuadas.

Ahora, incluiremos en el archivo \MQL5\Include\DoEasy\Objects\Graph\WForms\Helpers\BarProgressBar.mqh de la clase de objeto de barra de progreso el archivo de objeto de destello, y en su sección privada, escribiremos los métodos para establecer/retornar el retraso y declararemos el método de inicialización de las propiedades de los objetos:

//+------------------------------------------------------------------+
//|                                               BarProgressBar.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\WinFormBase.mqh"
#include "..\GlareObj.mqh"
//+------------------------------------------------------------------+
//| BarProgressBar object class of the ProgressBar control           |
//+------------------------------------------------------------------+
class CBarProgressBar : public CWinFormBase
  {
private:
//--- (1) Set and (2) return a pause before displaying the effect
   void              SetShowDelay(const long delay)               { this.SetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY,delay);             }
   ulong             ShowDelay(void)                              { return this.GetProperty(CANV_ELEMENT_PROP_TOOLTIP_RESHOW_DELAY);            }
//--- Initialize the properties
   void              Initialize(void);
protected:


En ambos constructores de las clases, en lugar de las propias líneas para establecer las propiedades, escribiremos una llamada al método para establecerlas:

//+------------------------------------------------------------------+
//| Protected constructor with an object type,                       |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CBarProgressBar::CBarProgressBar(const ENUM_GRAPH_ELEMENT_TYPE type,
                                 CGCnvElement *main_obj,CGCnvElement *base_obj,
                                 const long chart_id,
                                 const int subwindow,
                                 const string descript,
                                 const int x,
                                 const int y,
                                 const int w,
                                 const int h) : CWinFormBase(type,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
//--- Set the specified graphical element type for the object and assign the library object type to the current object
   this.SetTypeElement(type);
   this.m_type=OBJECT_DE_TYPE_GWF_HELPER;
   this.Initialize();
  }
//+------------------------------------------------------------------+
//| Constructor indicating the main and base objects,                |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CBarProgressBar::CBarProgressBar(CGCnvElement *main_obj,CGCnvElement *base_obj,
                                 const long chart_id,
                                 const int subwindow,
                                 const string descript,
                                 const int x,
                                 const int y,
                                 const int w,
                                 const int h) : CWinFormBase(GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
   this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_BAR_PROGRESS_BAR);
   this.m_type=OBJECT_DE_TYPE_GWF_HELPER;
   this.Initialize();
  }
//+------------------------------------------------------------------+


Luego transmitiremos las líneas de inicialización de las propiedades eliminadas de los constructores al método de inicialización:

//+------------------------------------------------------------------+
//| Initialize the properties                                        |
//+------------------------------------------------------------------+
void CBarProgressBar::Initialize(void)
  {
   this.SetPaddingAll(0);
   this.SetMarginAll(0);
   this.SetBorderSizeAll(0);
   this.SetBackgroundColor(CLR_DEF_CONTROL_PROGRESS_BAR_BAR_COLOR,true);
   this.SetBorderColor(CLR_DEF_CONTROL_PROGRESS_BAR_BAR_COLOR,true);
   this.SetForeColor(CLR_DEF_CONTROL_PROGRESS_BAR_FORE_COLOR,true);
   this.SetShowDelay(2000);
  }
//+------------------------------------------------------------------+

Además de las líneas trasladadas, aquí hemos añadido la posibilidad de ajustar un retraso de dos segundos entre la aparición de los destellos en la barra de progreso.

Antes, en el temporizador de los objetos, simplemente enviábamos el valor de la función GetTickCount() al gráfico en un comentario.
Ahora escribiremos un manejador completo.

Manejador de eventos del temporizador:

//+------------------------------------------------------------------+
//| Timer                                                            |
//+------------------------------------------------------------------+
void CBarProgressBar::OnTimer(void)
  {
   CWinFormBase *base=this.GetBase();
   if(base==NULL)
      return;
   CWinFormBase *glare=base.GetElementByType(GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ,0);
   if(glare==NULL)
      return;
      
//--- If the object is in the normal state (hidden)
   if(glare.DisplayState()==CANV_ELEMENT_DISPLAY_STATE_NORMAL)
     {
      //--- set the state of waiting for fading in to the object (in our case, waiting for a shift along the progress bar),
      //--- set the waiting duration and set the countdown time
      glare.SetDisplayState(CANV_ELEMENT_DISPLAY_STATE_WAITING_FADE_IN);
      this.m_pause.SetWaitingMSC(this.ShowDelay());
      this.m_pause.SetTimeBegin();
      //--- If the right edge of the glare object is to the right of the left edge of the progress bar object
      if(glare.RightEdge()>=this.CoordX())
        {
         //--- Hide the glare object and move it beyond the right edge of the progress bar
         glare.Hide();
         if(glare.Move(this.CoordX()-glare.Width(),this.CoordY()))
           {
            //--- Set the relative coordinates of the glare object
            glare.SetCoordXRelative(glare.CoordX()-this.CoordX());
            glare.SetCoordYRelative(glare.CoordY()-this.CoordY());
            //--- and its visibility scope equal to the entire object
            glare.SetVisibleArea(0,0,glare.Width(),glare.Height());
           }
        }
      return;
     }
//--- If the object is in the state of waiting for fading in (in our case, waiting for a shift along the progress bar)
   if(glare.DisplayState()==CANV_ELEMENT_DISPLAY_STATE_WAITING_FADE_IN)
     {
      //--- If the waiting time has not yet passed, leave
      if(this.m_pause.Passed()<this.ShowDelay())
         return;
      //--- Set the state of the object being in the process of shifting along the progress bar and
      //---  the process start countdown time
      glare.SetDisplayState(CANV_ELEMENT_DISPLAY_STATE_PROCESS_FADE_IN);
      this.m_pause.SetTimeBegin();
      return;
     }
//--- If the object is in the state of a shift along the progress bar
   if(glare.DisplayState()==CANV_ELEMENT_DISPLAY_STATE_PROCESS_FADE_IN)
     {
      //--- If the glare object X coordinate still has not gone beyond the right edge of the progress bar
      if(glare.CoordX()<=this.RightEdge())
        {
         //--- set the display flag and show the object
         if(!glare.Displayed())
            glare.SetDisplayed(true);
         if(!glare.IsVisible())
            glare.Show();
         //--- bring the highlight object to the foreground
         glare.BringToTop();
         //--- Shift the highlight by 16 pixels to the right
         if(glare.Move(glare.CoordX()+16,this.CoordY()))
           {
            //--- Set the relative coordinates of the highlight object and redraw it
            glare.SetCoordXRelative(glare.CoordX()-this.CoordX());
            glare.SetCoordYRelative(glare.CoordY()-this.CoordY());
            glare.Redraw(true);
           }
         return;
        }
      //--- Set the completion state of the shift along the progress bar
      glare.SetDisplayState(CANV_ELEMENT_DISPLAY_STATE_COMPLETED_FADE_IN);
     }
//--- If the object is in the state of completion of shifting along the progress bar
   if(glare.DisplayState()==CANV_ELEMENT_DISPLAY_STATE_COMPLETED_FADE_IN)
     {
      //--- set the object to its normal state (invisible),
      //--- hide the object and set its invisibility flag
      glare.SetDisplayState(CANV_ELEMENT_DISPLAY_STATE_NORMAL);
      glare.Hide();
      glare.SetDisplayed(false);
      return;
     }
  }
//+------------------------------------------------------------------+

La lógica del método se describe por entero en los comentarios del código y es idéntica al manejador de eventos del temporizador que escribimos para los objetos de pista en el artículo anterior. Esta es solo una versión más simplificada. En los objetos de pista, esperaremos a que el objeto comience a aparecer (aquí también), luego el objeto aparecerá (aquí recorrerá la barra de progreso), después esperará la desaparición y se desvanecerá (aquí no). Esa es la única diferencia. Pero para que el destello aparezca y se extienda a lo largo de la barra de progreso, usaremos los mismos valores de la enumeración ENUM_CANV_ELEMENT_DISPLAY_STATE que usamos en los objetos de pista:

//+------------------------------------------------------------------+
//| List of control display states                                   |
//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_DISPLAY_STATE
  {
   CANV_ELEMENT_DISPLAY_STATE_NORMAL,                 // Normal
   CANV_ELEMENT_DISPLAY_STATE_WAITING_FADE_IN,        // Wait for fading in
   CANV_ELEMENT_DISPLAY_STATE_PROCESS_FADE_IN,        // Fading in
   CANV_ELEMENT_DISPLAY_STATE_COMPLETED_FADE_IN,      // Fading in end
   CANV_ELEMENT_DISPLAY_STATE_WAITING_FADE_OUT,       // Wait for fading out
   CANV_ELEMENT_DISPLAY_STATE_PROCESS_FADE_OUT,       // Fading out
   CANV_ELEMENT_DISPLAY_STATE_COMPLETED_FADE_OUT,     // Fading out end
   CANV_ELEMENT_DISPLAY_STATE_COMPLETED,              // Complete processing
  };
//+------------------------------------------------------------------+

Es decir, las constantes de esta enumeración serán lo suficientemente universales como para procesar diferentes eventos en objetos distintos. Si fuera necesario, podremos añadir constantes a esta lista para describir algunos otros eventos que puedan darse posteriormente.

Ya estamos preparados para las pruebas.


Simulación

Para la prueba, tomaremos el asesor experto del artículo anterior y lo guardaremos en la nueva carpeta \MQL5\Experts\TestDoEasy\Part127\ con el nuevo nombre TestDoEasy127.mq5.

Previamente hemos creado un objeto ProgressBar estático en el segundo panel del control SplitContainer ubicado en la primera pestaña de TabControl. Ahora necesitaremos obtener el puntero al objeto ProgressBar; a continuación, en un ciclo, aumentaremos su tamaño para ver cómo cambia esto el tamaño relativo de la barra de progreso, establecido en el 50% de la anchura de ProgressBar. Luego, nuevamente, en un ciclo, aumentaremos el valor de la barra de progreso usando el método PerformStep. Para que el método funcione, estableceremos los parámetros necesarios de antemano: mínimo = 0, máximo = 350, paso = 1. Una vez completados ambos ciclos, obtendremos el puntero al objeto de destello y estableceremos los parámetros de visualización para él.

Para implementar todo esto, al final del controlador OnInit(), escribiremos el siguiente bloque de código:

//--- Display and redraw all created panels
   for(int i=0;i<FORMS_TOTAL;i++)
     {
      //--- Get the panel object
      pnl=engine.GetWFPanel("WinForms Panel"+(string)i);
      if(pnl!=NULL)
        {
         //--- display and redraw the panel
         pnl.Show();
         pnl.Redraw(true);
         //--- Get the TabControl object from the panel
         CTabControl *tc=pnl.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,0);
         //--- Get the SplitContainer object from the first tab of the TabControl object
         CSplitContainer *sc=tc.GetTabElementByType(0,GRAPH_ELEMENT_TYPE_WF_SPLIT_CONTAINER,0);
         //--- Get the second panel from the SplitContainer object
         CSplitContainerPanel *scp=sc.GetPanel(1);
         //--- Get the ProgressBar object from the received panel
         CProgressBar *pb=scp.GetElementByType(GRAPH_ELEMENT_TYPE_WF_PROGRESS_BAR,0);
         //--- Wait for 1/10 of a second
         Sleep(100);
         //--- Get the width of the ProgressBar object
         int w=pb.Width();
         //--- In the loop, increase the width of the ProgressBar by 180 pixels with a delay of 1/50
         for(int n=0;n<180;n++)
           {
            Sleep(20);
            pb.Resize(w+n,pb.Height(),true);
           }
         //--- Set the values for PerformStep of the ProgressBar object
         pb.SetValuesForProcessing(0,350,1,0);
         //--- Reset ProgressBar to minimum
         pb.ResetProgressBar();
         //--- Wait for 1/5 second
         Sleep(200);
         //--- In the loop from the minimum to the maximum value of ProgressBar
         for(int n=0;n<=pb.Maximum();n++)
           {
            //--- call the method for increasing the progress bar by a given step with a wait of 1/5 second
            pb.PerformStep();
            Sleep(20);
           }
         //--- Get the glare object
         CGlareObj *gl=pb.GetElementByType(GRAPH_ELEMENT_TYPE_WF_GLARE_OBJ,0);
         if(gl!=NULL)
           {
            //--- Set the glare type - rectangle, opacity 40, color - white
            gl.SetStyleRectangle();
            gl.SetOpacity(40);
            gl.SetColor(clrWhite);
           }
        }
     }
        
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+


Luego compilaremos y ejecutaremos el asesor experto en el gráfico:


En el primer ciclo, veremos cómo aumenta la anchura del objeto ProgressBar, y después, la anchura de la barra de progreso también aumentará proporcionalmente. Al mismo tiempo, el valor de Value se establecerá inicialmente en 50 y permanecerá así, ya que la anchura de la barra de progreso se establecerá en valores relativos.

En el segundo ciclo, llamaremos cada vez al método PerformStep, que en cada paso incrementará el valor Value (relativo) en el valor del paso de incremento. Aunque hay 350 incrementos, podemos ver que la barra de progreso crecerá más de 1 píxel a la vez. Esto se debe a que todos los valores son relativos y se calcularán como un porcentaje de la anchura de ProgressBar. Y esto será simplemente normal y correcto: resulta imposible incrementar un píxel en cada paso, si la anchura de ProgressBar es de 100 píxeles y el número de pasos es 1000, entonces obtendremos 10 pasos de incremento por píxel, y se omitirán al calcular valores relativos.

Cuando se complete el ciclo de incremento de la barra de progreso, veremos un destello cada dos segundos. Para la demostración, esto resultará suficiente, pero para el aspecto normal, no. En primer lugar, el destello debería recorrer la barra de progreso en movimiento, y no la barra completada; en segundo lugar, el "efecto" ha resultado demasiado simple. Pero todo esto será corregido y mejorado gradualmente.

Los íconos de los objetos de pista ahora desaparecen con normalidad, sin resultar visibles todo el tiempo hasta el final del proceso de desvanecimiento, y desaparecer junto con el objeto solo en el último paso con la opacidad 0.


¿Qué es lo próximo?

En el próximo artículo, continuaremos trabajando en los objetos de la biblioteca WinForms y el objeto ProgressBar.

Volver al contenido

*Artículos de esta serie:

 
DoEasy. Elementos de control (Parte 20): El objeto WinForms SplitContainer
DoEasy. Elementos de control (Parte 21): Elemento de control SplitContainer. Separador de paneles
DoEasy. Elementos de control (Parte 22): SplitContainer. Cambiando las propiedades del objeto creado
DoEasy. Elementos de control (Parte 23): mejorando los objetos WinForms TabControl y SplitContainer
DoEasy. Elementos de control (Parte 24): El objeto auxiliar WinForms "Pista"
DoEasy. Elementos de control (Parte 25): El objeto WinForms «Tooltip»
DoEasy. Elementos de control (Parte 26): Mejoramos el objeto WinForms "ToolTip" y comenzamos a desarrollar "ProgressBar"

Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/11764

Archivos adjuntos |
MQL5.zip (4524.46 KB)
Indicadores no lineales Indicadores no lineales
En este artículo, intentaremos analizar algunas formas de construir indicadores no lineales, así como su uso en el trading. Existen bastantes indicadores en la plataforma comercial MetaTrader que utilizan enfoques no lineales.
Redes neuronales: así de sencillo (Parte 33): Regresión cuantílica en el aprendizaje Q distribuido Redes neuronales: así de sencillo (Parte 33): Regresión cuantílica en el aprendizaje Q distribuido
Continuamos explorando el aprendizaje Q distribuido. Hoy analizaremos este enfoque desde un ángulo diferente. Vamos a hablar de la posibilidad de utilizar la regresión cuantílica para resolver el problema de la previsión de los movimientos de precio.
Algoritmos de optimización de la población: Colonia de abejas artificiales (Artificial Bee Colony - ABC) Algoritmos de optimización de la población: Colonia de abejas artificiales (Artificial Bee Colony - ABC)
Hoy estudiaremos el algoritmo de colonia de abejas artificiales. Asimismo, complementaremos nuestros conocimientos con nuevos principios para el estudio de los espacios funcionales. En este artículo hablaremos sobre mi interpretación de la versión clásica del algoritmo.
DoEasy. Elementos de control (Parte 26): Mejoramos el objeto WinForms "ToolTip" y comenzamos a desarrollar "ProgressBar" DoEasy. Elementos de control (Parte 26): Mejoramos el objeto WinForms "ToolTip" y comenzamos a desarrollar "ProgressBar"
En este artículo, completaremos el desarrollo del control ToolTip y comenzaremos a desarrollar el objeto WinForms ProgressBar. A medida que trabajemos en los objetos, desarrollaremos una funcionalidad universal para animar los controles y sus componentes.