Скачать MetaTrader 5

Графические интерфейсы XI: Интеграция графической стандартной библиотеки (build 16)

4 августа 2017, 13:06
Anatoli Kazharski
27
1 598

Содержание


Введение

Более подробно о том, для чего предназначена эта библиотека, можно прочитать в самой первой статье: Графические интерфейсы I: Подготовка структуры библиотеки (Глава 1). В конце статей каждой части представлен список глав со ссылками, там же есть возможность загрузить к себе на компьютер полную версию библиотеки на текущей стадии разработки. Файлы нужно разместить по тем же директориям, как они расположены в архиве.

Во второй главе девятой части серии был продемонстрирован пример того, как можно интегрировать в библиотеку класс для создания линейных графиков. Это было временным решением, так как возможностей этой части стандартной библиотеки сильно не хватало. Недавно разработчики MQL5 представили новую версию графической библиотеки для создания научных графиков (класс CGraphic). Некоторые функции этого класса описаны в статье Визуализируй это! Графическая библиотека в MQL5 как аналог plot из R. В этой статье я представлю обновление библиотеки для создания графических интерфейсов с новым элементом для создания графиков. Теперь визуализировать данные различных типов можно будет ещё проще.


Изменения в схеме библиотеки

До сих пор мы использовали для рисования копию класса CCanvas. В связи с недавно проведённым глобальным рефакторингом кода библиотеки теперь в этой копии нет необходимости. Её можно заменить оригинальной версией из стандартной библиотеки. Это уменьшило объём библиотеки ещё приблизительно на 10% и почти на 40% относительно версии, которая была представлена до рефакторинга.

Для создания графиков теперь будет использоваться класс CGraphic, поэтому подключим файл Graphic.mqh к файлу Objects.mqh. Так как файл с классом CCanvas подключен к одному из файлов, подключенных, в свою очередь, к файлу Graphic.mqh, то он также станет доступным для всей библиотеки.

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

Класс CLineChart переименован в CGraph. Изменения также коснулись и его внутреннего содержания. Теперь в этом классе есть только методы для управления общими свойствами и состояниями элемента. 

class CGraph : public CElement
  {
public:
   //--- Обработчик событий графика
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- Перемещение элемента
   virtual void      Moving(const bool only_visible=true);
   //--- Управление
   virtual void      Show(void);
   virtual void      Hide(void);
   virtual void      Reset(void);
   virtual void      Delete(void);
   //--- Применение последних изменений
   virtual void      Update(const bool redraw=false);
   //---
private:
   //--- Изменение размеров
   void              Resize(const int width,const int height);
   //--- Изменить ширину по правому краю окна
   virtual void      ChangeWidthByRightWindowSide(void);
   //--- Изменить высоту по нижнему краю окна
   virtual void      ChangeHeightByBottomWindowSide(void);
  };

Свойствами же графика можно управлять, получив с помощью метода CGraphic::GetGraphicPointer() указатель на экземпляр класса CGraphic:

class CGraph : public CElement
  {
private:
   //--- Объекты для создания элемента
   CGraphic          m_graph;
   //---
public:
   //--- Возвращает указатель на график
   CGraphic         *GetGraphicPointer(void) { return(::GetPointer(m_graph)); }
  };

К классу CGraphic подключены дополнительные классы для управления свойствами осей (CAxis) и кривых (CCurve) графика. Для генерации цвета кривых предназначен класс CColorGenerator. Все эти классы содержатся в отдельных файлах, подключенных к файлу Graphic.mqh:

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

Файл с классом CCanvas подключен к файлу Curve.mqh, и отсюда он будет доступен во всей библиотеке.

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

Все вышеописанные взаимосвязи между файлами и классами отображены на рисунке:

 Рис. 1. Взаимосвязи между классами стандартной и разрабатываемой библиотеки.

Рис. 1. Взаимосвязи между классами стандартной и разрабатываемой библиотеки.

Таким образом, для библиотеки и файлов приложения, в котором она используется, автоматически становятся доступными классы стандартной библиотеки для работы с массивами и файлами. Ниже продемонстрирую несколько тестовых MQL-приложений, чтобы лучше разобраться в новых возможностях.

Приложение для теста свойств графика

В первом тестовом приложении реализуем графический интерфейс с элементами для управления некоторыми свойствами графика типа CGraphic. В верхней части формы расположим элемент типа CTabs. В данном случае это группа из четырёх вкладок. Сразу под рабочей областью вкладок будет график с двумя кривыми, значения которых сгенерируем случайным образом.

На первой вкладке (Background) создадим элементы управления следующими свойствами графика:

  • Цвет фона
  • Главный текст графика (отображается в верхней части)
  • Вспомогательный текст графика (отображается в нижней части)
  • Цвет главного текста
  • Цвет вспомогательного текста
  • Размер шрифта главного текста
  • Размер шрифта вспомогательного текста

Для установки и получения этих свойств в классе CGraphic есть соответствующие публичные методы:

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

Вот как это выглядит:

 Рис. 2. Элементы первой вкладки (Background) тестового MQL-приложения.

Рис. 2. Элементы первой вкладки (Background) тестового MQL-приложения.


На второй вкладке (Indents & history) расположим элементы для установки следующих свойств:

  • Отступы (слева, справа, сверху, снизу)
  • Ширина легенды
  • Размер шрифта легенды
  • Размер маркеров легенды
  • Общие отступы между всеми элементами графика
  • Размер отметок на шкалах осей графика

Для получения и установки этих свойств можно воспользоваться методами CGraphic, которые показаны в листинге кода ниже:

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

Вот как это выглядит в графическом интерфейсе тестового MQL-приложения:

 Рис. 3. Элементы второй вкладки (Indents & history) тестового MQL-приложения.

Рис. 3. Элементы второй вкладки (Indents & history) тестового MQL-приложения.


На третьей вкладке (Grid) находятся элементы управления для установки свойств сетки:

  • Цвет линий сетки
  • Цвет нулевой линии осей
  • Цвет фона сетки
  • Отрисовка точек в узлах сетки
  • Радиус точек
  • Цвет точек

Для получения и установки этих свойств в классе CGraphic есть соответствующие методы:

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

Вот как это выглядит в итоге:

 Рис. 4. Элементы третьей вкладки (Grid) тестового MQL-приложения.

Рис. 4. Элементы третьей вкладки (Grid) тестового MQL-приложения.


На четвёртой вкладке (Axes) расположились элементы, с помощью которых можно изменять свойства осей графика. Переключаться для настройки той или иной оси можно радио-кнопками в левой части рабочей области вкладок, которые отделены от других элементов вкладки Axes разделительной линией.

Перечислим свойства, которые будут доступны для изменения:

  • Автомасштабирование
  • Минимальное значение оси
  • Максимальное значение оси
  • Значение допуска для минимума оси
  • Значение допуска для максимума оси
  • Размер цифр на оси
  • Максимальная отображаемая длина цифр на оси
  • Размер шрифта для имени оси
  • Начальное значение шага по оси
  • Максимальное количество цифр на оси
  • Имя оси
  • Цвет текста имени оси

В листинге ниже показаны названия методов класса CAxis для получения и изменения вышеописанных свойств:

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

Вот что в итоге получилось:

 Рис. 5. Элементы четвёртой вкладки (Axes) тестового MQL-приложения.

Рис. 5. Элементы четвёртой вкладки (Axes) тестового MQL-приложения.


В конце статьи вы можете загрузить к себе на компьютер это тестовое приложение для более подробного изучения.


Приложение для теста свойств кривых графика

Для тестов некоторых свойств кривых графика типа CGraphic было написано отдельное MQL-приложение. В верхней части его формы расположены элементы для управления свойствами кривых графика, а сразу за ними — два графика типа CGraphic (элемент CGraph). На первом графике будут отображаться серии со случайными данными, а на втором — их производные, которые для примера будут рассчитываться по формуле индикатора Momentum.

Перечислим элементы для управления свойствами кривых графика.

  • Чекбокс Animate предназначен для запуска автоматического поступления данных на график.
  • Поле ввода Array size — текущее количество элементов в массиве данных, отображаемых на графике.
  • Кнопка Random генерирует случайные последовательности данных в сериях на графике.
  • Поле ввода Period — значение переменной для расчёта индикатора Momentum.
  • Комбо-бокс Curve type — выбор типа кривых на графике.
  • Комбо-бокс Point type — выбор типа точек данных, по которым строятся кривые.

В пользовательском классе приложения (CProgram) реализованы методы, связанные с вышеперечисленными элементами управления и выполняют следующие задачи:

  • Установка размера массивам данных для вывода на график
  • Инициализация массивов данными
  • Обновление графиков для отображения последних изменений
  • Добавление одного элемента в конец массивов
  • Удаление одного элемента в конце массивов
  • Обновление графиков по таймеру
  • Анимация графиков с автоматическим поступлением новых данных

Методы, в которых всё это реализовано, показаны в листинге кода ниже. Подробнее с кодом этих методов можно ознакомиться, скачав файлы в конце статьи.

class CProgram : public CWndEvents
  {
protected:
   //--- Массивы данных для вывода на графики
   double            data1[];
   double            data2[];
   //---
   double            data3[];
   double            data4[];
   //---
private:
   //--- Установка нового размера массивам
   void              ResizeGraph1Arrays(void);
   void              ResizeGraph2Arrays(void);
   void              ResizeGraph1Arrays(const int new_size);
   void              ResizeGraph2Arrays(const int new_size);
   //--- Инициализация массивов
   void              InitGraph1Arrays(void);
   void              InitGraph2Arrays(void);
   //--- Обнуление массивов
   void              ZeroGraph1Arrays(void);
   void              ZeroGraph2Arrays(void);
   //--- Устанавливает случайное значение по указанному индексу
   void              SetGraph1Value(const int index);
   void              SetGraph2Value(const int index);
   //--- Обновить серии на графике
   void              UpdateGraph(void);
   void              UpdateGraph1(void);
   void              UpdateGraph2(void);
   
   //--- Перерасчёт серий на графике
   void              RecalculatingSeries(void);
   //--- Добавляет одно значение в конец массивов
   void              AddValue(void);
   //--- Удаляет одно значение в конце массивов
   void              DeleteValue(void);

   //--- Обновление графика по таймеру
   void              UpdateGraphByTimer(void);
   //--- Анимация серий графика
   void              AnimateGraphSeries(void);
  };

Ниже показано, как это выглядит:

 Рис. 6. Графический интерфейс приложения для тестирования свойств кривых графика.

Рис. 6. Графический интерфейс приложения для тестирования свойств кривых графика.


В конце статьи вы можете загрузить к себе на компьютер это тестовое приложение для более подробного изучения.

Приложение с анимированным графиком гипоциклоиды

В одной из своих книг по программированию на VBA в среде программы Microsoft Excel Джон Уокенбах предоставляет читателям компакт-диск с файлами для тестов. В одном из файлов реализована диаграмма, в которой генерируется бесконечное количество гипоциклоид. 

Википедия даёт такое определение:

Гипоциклоида (от греческих слов ὑπό — под, внизу и κύκλος — круг, окружность) — плоская кривая, образуемая точкой окружности, катящейся по внутренней стороне другой окружности без скольжения.

Определение Джона Уокенбаха в своей книге:

Гипоциклоида — это траектория точки, находящейся на окружности, которая движется внутри другой окружности.

Реализуем такое же приложение на MQL, а для управления параметрами добавим графический интерфейс. Рассмотрим подробнее, как это устроено.

Для генерации новой гипоциклоиды используются три параметра, по которым инициализируются числовые последовательности с указанным шагом. Затем по значениям в этих последовательностях осуществляются расчёты для получения координат точек на графике. После этого результаты нормализуются.

В пользовательском классе объявим несколько массивов для расчёта последовательностей и полей для расчёта среднего и стандартного отклонения:

//+------------------------------------------------------------------+
//|                                                      Program.mqh |
//|                        Copyright 2017, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include <Math\Stat\Stat.mqh>
#include <EasyAndFastGUI\WndEvents.mqh>
#include <EasyAndFastGUI\TimeCounter.mqh>
//+------------------------------------------------------------------+
//| Класс для создания приложения                                    |
//+------------------------------------------------------------------+
class CProgram : public CWndEvents
  {
protected:
...
   //--- Массивы данных для расчётов
   double            a_inc[];
   double            b_inc[];
   double            t_inc[];
   double            x_source[];
   double            y_source[];
   //--- Массивы данных для вывода на график
   double            x_norm[];
   double            y_norm[];
   //--- Для расчёта среднего и стандартного отклонения
   double            x_mean;
   double            y_mean;
   double            x_sdev;
   double            y_sdev;
...
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CProgram::CProgram(void) : x_mean(0),
                           y_mean(0),
                           x_sdev(0),
                           y_sdev(0)
  {
...
  }

Значения будут рассчитываться в методе CProgram::InitArrays(). Здесь в первом цикле рассчитываются исходные данные. Затем получаются среднее и стандартное отклонения, во втором цикле нормализуются данные. Размер массивам устанавливается с помощью метода CProgram::ResizeArrays(). Значение для размера массивов берется из элемента управления «Поле ввода» (CTextEdit) графического интерфейса приложения.

class CProgram : public CWndEvents
  {
private:
   //--- Установить новый размер массивам
   void              ResizeArrays(void);
   //--- Инициализация вспомогательных массивов для расчётов
   void              InitArrays(void);
  };
//+------------------------------------------------------------------+
//| Изменяет размер массивов                                         |
//+------------------------------------------------------------------+
void CProgram::ResizeArrays(void)
  {
   int array_size =::ArraySize(x_norm);
   int new_size   =(int)m_array_size.GetValue();
//--- Выйти, если размер не изменился
   if(array_size==new_size)
      return;
//--- Установить новый размер
   ::ArrayResize(a_inc,new_size);
   ::ArrayResize(b_inc,new_size);
   ::ArrayResize(t_inc,new_size);
   ::ArrayResize(x_source,new_size);
   ::ArrayResize(y_source,new_size);
   ::ArrayResize(x_norm,new_size);
   ::ArrayResize(y_norm,new_size);
  }
//+------------------------------------------------------------------+
//| Инициализация массивов                                           |
//+------------------------------------------------------------------+
void CProgram::InitArrays(void)
  {
//--- Изменить размеры массивов
   ResizeArrays();
//--- Рассчитаем значения по формулам
   int total=(int)m_array_size.GetValue();
   for(int i=0; i<total; i++)
     {
      if(i<1)
        {
         a_inc[i] =1+(double)m_animate.GetValue();
         b_inc[i] =1+(double)m_animate.GetValue();
         t_inc[i] =1+(double)m_animate.GetValue();
        }
      else
        {
         a_inc[i] =a_inc[i-1]+(double)m_a_inc.GetValue();
         b_inc[i] =b_inc[i-1]+(double)m_b_inc.GetValue();
         t_inc[i] =t_inc[i-1]+(double)m_t_inc.GetValue();
        }
      //---
      double a=a_inc[i];
      double b=b_inc[i];
      double t=t_inc[i];
      //---
      x_source[i] =(a-b)*cos(t)+b*cos((a/b-1)*t);
      y_source[i] =(a-b)*sin(t)+b*sin((a/b-1)*t);
     }
//--- Рассчитаем среднее
   x_mean=MathMean(x_source);
   y_mean=MathMean(y_source);
//--- Рассчитаем стандартное отклонение
   x_sdev=MathStandardDeviation(x_source);
   y_sdev=MathStandardDeviation(y_source);
//--- Корректировка для предотвращения деления на ноль
   x_sdev =(x_sdev==0)? 1 : x_sdev;
   y_sdev =(y_sdev==0)? 1 : y_sdev;
//--- Нормализуем данные
   for(int i=0; i<total; i++)
     {
      x_norm[i] =(x_source[i]-x_mean)/x_sdev;
      y_norm[i] =(y_source[i]-y_mean)/y_sdev;
     }
  }

В классе CGraphic есть методы, которые позволяют добавить в рабочую область созданного графика дополнительные засечки на шкалы осей, линии и текст.

В случае нашего примера, с помощью метода CProgram::TextAdd() будем выводить в левом верхнем углу диаграммы значения среднего и стандартного отклонения для X- и Y-последовательностей. Для получения координат крайней точки (левый верхний угол) диаграммы используются методы CGraphic::ScaleX() и CGraphic::ScaleY(), которые предназначены для масштабирования реальных значений графика в пиксельные координаты. Здесь в качестве реальных значений подаются минимум по оси X и максимум по оси Y

class CProgram : public CWndEvents
  {
private:
   //--- Добавляет текст на график
   void              TextAdd(void);
  };
//+------------------------------------------------------------------+
//| Добавляет текст на график                                        |
//+------------------------------------------------------------------+
void CProgram::TextAdd(void)
  {
//--- Получим указатель графика
   CGraphic *graph=m_graph1.GetGraphicPointer();
//---  
   int  x     =graph.ScaleX(graph.XAxis().Min())+50;
   int  y     =graph.ScaleY(graph.YAxis().Max())+10;
   int  y2    =y+20;
   uint clr   =::ColorToARGB(clrBlack);
   uint align =TA_RIGHT;
//---
   string str[8];
   str[0] ="x mean:";
   str[1] ="y mean:";
   str[2] =::DoubleToString(x_mean,2);
   str[3] =::DoubleToString(y_mean,2);
   str[4] ="x sdev:";
   str[5] ="y sdev:";
   str[6] =::DoubleToString(x_sdev,2);
   str[7] =::DoubleToString(y_sdev,2);
//--- Рассчитываем координаты и выводим текст на график
   int l_x=0,l_y=0;
   for(int i=0; i<8; i++)
     {
      if(i<2)
         l_x=x;
      else if(i<6)
         l_x=(i%2==0)? l_x+50 : l_x;
      else
         l_x=(i%2==0)? l_x+60 : l_x;
      //---
      l_y=(i%2==0)? y : y2;
      //---
      graph.TextAdd(l_x,l_y,str[i],clr,align);
     }
  }

После того, как на график установлены все необходимые данные, нужно его перерисовать для отображения последних изменений. Для этого используется метод CProgram::UpdateSeries(). Здесь сначала проверяем, есть ли на графике серии. Если есть, то устанавливаем рассчитанные в последний раз данные. Кроме этого, с помощью элементов управления графического интерфейса приложения устанавливаем свойства для кривой. Здесь это (1) сглаживание кривой, (2) тип точек и (3) тип кривой. Наносить текст на график нужно уже после того, как были установлены и нарисованы все остальные свойства и данные. В самом конце нужно обязательно обновить график, чтобы увидеть результат.

class CProgram : public CWndEvents
  {
private:
   //--- Устанавливает и обновляет серии на графике
   void              UpdateSeries(void);
  };
//+------------------------------------------------------------------+
//| Устанавливает и обновляет серии на графике                       |
//+------------------------------------------------------------------+
void CProgram::UpdateSeries(void)
  {
//--- Получим указатель графика
   CGraphic *graph=m_graph1.GetGraphicPointer();
//--- Обновим все серии графика новыми данными
   int total=graph.CurvesTotal();
   if(total>0)
     {
      //--- Получим указатель кривой
      CCurve *curve=graph.CurveGetByIndex(0);
      //--- Установить массивы данных
      curve.Update(x_norm,y_norm);
      //--- Получим значения свойств кривой
      ENUM_CURVE_TYPE curve_type =(ENUM_CURVE_TYPE)m_curve_type.GetListViewPointer().SelectedItemIndex();
      ENUM_POINT_TYPE point_type =(ENUM_POINT_TYPE)m_point_type.GetListViewPointer().SelectedItemIndex();
      //--- Установить свойства
      curve.LinesSmooth(m_line_smooth.IsPressed());
      curve.PointsType(point_type);
      curve.Type(curve_type);
     }
//--- Применить 
   graph.Redraw(true);
//--- Вывести текст
   TextAdd();
//--- Обновить график
   graph.Update();
  }

Для расчёта и применения полученных результатов одним вызовом используется метод CProgram::RecalculatingSeries():

class CProgram : public CWndEvents
  {
private:
   //--- Перерасчёт серий на графике
   void              RecalculatingSeries(void);
  };
//+------------------------------------------------------------------+
//| Перерасчёт серий на графике                                      |
//+------------------------------------------------------------------+
void CProgram::RecalculatingSeries(void)
  {
//--- Рассчитаем значения и инициализируем массивы
   InitArrays();
//--- Обновим серии
   UpdateSeries();
  }

Построенная по этим формулам диаграмма будет выглядеть интереснее, если станет анимированной. Чтобы привести в движение рассчитываемые последовательности, нужно изменять их начальное значение. Этого можно добиться, вводя значения посредством поля ввода или запустив процесс в автоматическом режиме. В автоматическом режиме приращение или убавление значения в этом поле ввода осуществляется в методе CProgram::AnimateGraphSeries(). Он вызывается в методе CProgram::UpdateGraphByTimer(), вызов которого, в свою очередь, осуществляется в таймере приложения.

class CProgram : public CWndEvents
  {
private:
   //--- Обновление графика по таймеру
   void              UpdateGraphByTimer(void);
   //--- Анимация серий графика
   void              AnimateGraphSeries(void);
  };
//+------------------------------------------------------------------+
//| Таймер                                                           |
//+------------------------------------------------------------------+
void CProgram::OnTimerEvent(void)
  {
   CWndEvents::OnTimerEvent();
//--- Обновление графика по таймеру
   if(m_counter1.CheckTimeCounter())
     {
      UpdateGraphByTimer();
     }
...
  }
//+------------------------------------------------------------------+
//| Обновление графика по таймеру                                    |
//+------------------------------------------------------------------+
void CProgram::UpdateGraphByTimer(void)
  {
//--- Выйти, если (1) форма свёрнута или (2) отключена анимация
   if(m_window.IsMinimized() || !m_animate.IsPressed())
      return;
//--- Анимация серий графика
   AnimateGraphSeries();
//--- Обновить массивы и серии на графике
   RecalculatingSeries();
  }
//+------------------------------------------------------------------+
//| Анимация серий графика                                           |
//+------------------------------------------------------------------+
void CProgram::AnimateGraphSeries(void)
  {
//--- Для указания направления изменения размера массивов
   static bool counter_direction=false;
//--- Переключим направление, если дошли до минимума
   if((double)m_animate.GetValue()<=(double)m_animate.MinValue())
      counter_direction=false;
//--- Переключим направление, если дошли до максимума
   if((double)m_animate.GetValue()>=(double)m_animate.MaxValue())
      counter_direction=true;
//--- Изменяем размер массива по направлению
   string value="";
   if(!counter_direction)
      value=string((double)m_animate.GetValue()+m_animate.StepValue());
   else
      value=string((double)m_animate.GetValue()-m_animate.StepValue());
//--- Установить новое значение и обновить поле ввода
   m_animate.SetValue(value,false);
   m_animate.GetTextBoxPointer().Update(true);
  }

В итоге получился результат, показанный ниже:

 Рис. 7. Демонстрация анимированного гипоциклоида.

Рис. 7. Демонстрация анимированного гипоциклоида.


В конце статьи вы можете загрузить к себе на компьютер это тестовое приложение для более подробного изучения.

Новая версия тестового приложения из предыдущих обновлений

Тестовое приложение, которое было продемонстрировано в статье Графические интерфейсы IX: Элементы "Индикатор выполнения" и "Линейный график" (Глава 2), было обновлено в соответствии с изменениями в данном обновлении.

Ниже показано, как выглядит новая версия этого MQL-приложения с обновлённым графическим интерфейсом:

 Рис. 8. Новая версия тестового приложения из предыдущих обновлений.

Рис. 8. Новая версия тестового приложения из предыдущих обновлений.


В конце статьи вы можете загрузить к себе на компьютер это тестовое приложение для более подробного изучения.

Заключение

В этой статье в разрабатываемую библиотеку для создания графических интерфейсов была интегрирована часть стандартной библиотеки для построения научных графиков. Все продемонстрированные примеры вы можете загрузить к себе на компьютер в конце статьи для более подробного изучения исходных кодов.

На текущем этапе разработки библиотеки её общая схема выглядит так:

 Рис. 9. Структура библиотеки на текущей стадии разработки.

Рис. 9. Структура библиотеки на текущей стадии разработки.


Представленный код библиотеки бесплатный. Вы можете использовать его в своих проектах, в том числе и коммерческих, писать статьи и выполнять заказы.

Если у вас есть вопросы по использованию материала из статьи, можете задать их в комментариях.
Прикрепленные файлы |
Последние комментарии | Перейти к обсуждению на форуме трейдеров (27)
Oleksii Chepurnyi
Oleksii Chepurnyi | 27 дек 2017 в 17:15
Artem Virskiy:

Если использовать метод Update() для экземпляра CTable - программа компилируется, но не запускается, пишет "Invalid EX5 file (4)".


Кто нибудь успешно сделал ячейки-кнопки?

Ячейки-кнопки делал, но менять картинку задачи небыло...

У метода Update() по-умолчанию перерисовка установлена false. С перерисовкой нужно использовать Update(true)

Artem Virskiy
Artem Virskiy | 27 дек 2017 в 17:45

Добавил в библиотеке одну строчку и заработало! Ура, товарищи!

Желтым выделено что добавил. Куда добавил:

void CTable::DrawImage(const int column_index,const int row_index)

  {

//--- Расчёт координат

   int x =m_columns[column_index].m_x+m_columns[column_index].m_image_x_offset;

   int y =m_rows[row_index].m_y+m_columns[column_index].m_image_y_offset;

//--- Выбранная картинка в ячейке и её размеры

   int  selected_image =m_columns[column_index].m_rows[row_index].m_selected_image;

   uint image_height   =m_columns[column_index].m_rows[row_index].m_images[selected_image].Height();

   uint image_width    =m_columns[column_index].m_rows[row_index].m_images[selected_image].Width();

//--- Рисуем

   for(uint ly=0,i=0; ly<image_height; ly++)

     {

      for(uint lx=0; lx<image_width; lx++,i++)

        {

         //--- Если нет цвета, перейти к следующему пикселю

         if(m_columns[column_index].m_rows[row_index].m_images[selected_image].Data(i)<1)

            continue;

         //--- Получаем цвет нижнего слоя (фона ячейки) и цвет указанного пикселя картинки

         uint background  =(row_index==m_selected_item)? m_selected_row_color : m_canvas.PixelGet(x+lx,y+ly);

         uint pixel_color =m_columns[column_index].m_rows[row_index].m_images[selected_image].Data(i);

         //--- Смешиваем цвета

         uint foreground=::ColorToARGB(m_clr.BlendColors(background,pixel_color));

         //--- Рисуем пиксель наслаиваемого изображения

         m_table.PixelSet(x+lx,y+ly,foreground);

        }

     }

   m_table.Update(true);

  }


Artem Virskiy
Artem Virskiy | 27 дек 2017 в 17:49
Oleksii Chepurnyi:

Ячейки-кнопки делал, но менять картинку задачи небыло...

У метода Update() по-умолчанию перерисовка установлена false. С перерисовкой нужно использовать Update(true)


Так это ясный перец!

Я всегда за правду, когда мне fasle я сразу чую :)

Pavel Kolchin
Pavel Kolchin | 28 дек 2017 в 17:57

как изменить пункты комбобокса после его создания?

Andrii Djola
Andrii Djola | 21 янв 2018 в 18:46

а у меня такая проблема, когда выбираю пункт combobox, то не нажимается любой item на CheckBoxList

Глубокие нейросети (Часть II). Разработка и выбор предикторов Глубокие нейросети (Часть II). Разработка и выбор предикторов

Во второй статье из серии о глубоких нейросетях рассматриваются трансформация и выбор предикторов в процессе подготовки данных для обучения модели.

TradeObjects: Автоматизация торговли на основе графических объектов в MetaTrader TradeObjects: Автоматизация торговли на основе графических объектов в MetaTrader

В статье рассматривается простой подход к созданию системы автоматической торговли по линейной разметке графика. Предложен готовый эксперт, использующий стандартные свойства объектов MetaTrader 4 и 5 и поддерживающий основные торговые операции.

Универсальный торговый эксперт: Индикатор CUnIndicator и работа с отложенными ордерами (часть 9) Универсальный торговый эксперт: Индикатор CUnIndicator и работа с отложенными ордерами (часть 9)

В статье описана работа с индикаторами через универсальный класс CUnIndicator. Кроме того, рассмотрены новые методы работы с отложенными ордерами. Обратите внимание: с этого момента структура проекта CStrategy существенно изменена. Теперь все его файлы располагаются в единой директории для удобства пользователей.

Глубокие нейросети (Часть III). Выбор примеров и уменьшение размерности Глубокие нейросети (Часть III). Выбор примеров и уменьшение размерности

Эта статья продолжает серию публикаций о глубоких нейросетях. Рассматривается выбор примеров (удаление шумовых), уменьшение размерности входных данных и разделение набора на train/val/test в процессе подготовки данных для обучения.