English 中文 Español Deutsch 日本語 Português
Графические интерфейсы X: Элемент "Стандартный график" (build 4)

Графические интерфейсы X: Элемент "Стандартный график" (build 4)

MetaTrader 5Примеры | 13 октября 2016, 10:26
5 114 51
Anatoli Kazharski
Anatoli Kazharski

Содержание


Введение

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

Рассмотрим ещё один элемент интерфейса, который нельзя было не включить в разрабатываемую библиотеку. При запуске торгового терминала перед пользователем открываются ценовые графики, и хотелось бы получить инструмент, который позволил бы более удобно ими управлять. Ранее я написал статью Рецепты MQL5 - Наблюдение за несколькими таймфреймами в одном окне, в которой был продемонстрирован один из возможных вариантов подобного инструмента. На этот раз напишем класс для создания элемента, который можно будет легко и просто использовать в графическом интерфейсе своих MQL-приложений. В отличие от предыдущей версии, представленной по ссылке выше, в этой реализации содержимое в объектах-графиках можно будет прокручивать по горизонтали, как вы уже привыкли это делать в главном окне графика.

Кроме этого, продолжим оптимизировать код библиотеки для снижения потребления ресурсов процессора. Обо всём этом подробнее читайте далее в статье.

 

Разработка класса для создания элемента «Стандартный график»

Перед тем, как начать разработку класса CStandardChart для создания элемента «Стандартный график», в файл Object.mqh нужно добавить базовый класс CSubChart с дополнительными свойствами (см. листинг кода ниже), как это было сделано ранее для всех типов графических объектов, которые используются при создании элементов библиотеки. Базовым классом для класса CSubChart будет класс стандартной библиотеки — CChartObjectSubChart

//+------------------------------------------------------------------+
//|                                                      Objects.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
...
//--- Список классов в файле для быстрого перехода (Alt+G)
...
class CSubChart;
//+------------------------------------------------------------------+
//| Класс с дополнительными свойствами для объекта Sub Chart         |
//+------------------------------------------------------------------+
class CSubChart : public CChartObjectSubChart
  {
protected:
   int               m_x;
   int               m_y;
   int               m_x2;
   int               m_y2;
   int               m_x_gap;
   int               m_y_gap;
   int               m_x_size;
   int               m_y_size;
   bool              m_mouse_focus;
   //---
public:
                     CSubChart(void);
                    ~CSubChart(void);
   //--- Координаты
   int               X(void)                      { return(m_x);           }
   void              X(const int x)               { m_x=x;                 }
   int               Y(void)                      { return(m_y);           }
   void              Y(const int y)               { m_y=y;                 }
   int               X2(void)                     { return(m_x+m_x_size);  }
   int               Y2(void)                     { return(m_y+m_y_size);  }
   //--- Отступы от крайней точки (xy)
   int               XGap(void)                   { return(m_x_gap);       }
   void              XGap(const int x_gap)        { m_x_gap=x_gap;         }
   int               YGap(void)                   { return(m_y_gap);       }
   void              YGap(const int y_gap)        { m_y_gap=y_gap;         }
   //--- Размеры
   int               XSize(void)                  { return(m_x_size);      }
   void              XSize(const int x_size)      { m_x_size=x_size;       }
   int               YSize(void)                  { return(m_y_size);      }
   void              YSize(const int y_size)      { m_y_size=y_size;       }
   //--- Фокус
   bool              MouseFocus(void)             { return(m_mouse_focus); }
   void              MouseFocus(const bool focus) { m_mouse_focus=focus;   }
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CSubChart::CSubChart(void) : m_x(0),
                             m_y(0),
                             m_x2(0),
                             m_y2(0),
                             m_x_gap(0),
                             m_y_gap(0),
                             m_x_size(0),
                             m_y_size(0),
                             m_mouse_focus(false)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CSubChart::~CSubChart(void)
  {
  }

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

  • координаты и размеры
  • символ, таймфрейм и масштаб
  • отображение ценовой и временной шкал.  
//+------------------------------------------------------------------+
//|                                          ChartObjectSubChart.mqh |
//|                   Copyright 2009-2013, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "ChartObject.mqh"
//+------------------------------------------------------------------+
//| Class CChartObjectSubChart.                                      |
//| Purpose: Class of the "SubChart" object of chart.                |
//|          Derives from class CChartObject.                        |
//+------------------------------------------------------------------+
class CChartObjectSubChart : public CChartObject
  {
public:
                     CChartObjectSubChart(void);
                    ~CChartObjectSubChart(void);
   //--- methods of access to properties of the object
   int               X_Distance(void) const;
   bool              X_Distance(const int X) const;
   int               Y_Distance(void) const;
   bool              Y_Distance(const int Y) const;
   ENUM_BASE_CORNER  Corner(void) const;
   bool              Corner(const ENUM_BASE_CORNER corner) const;
   int               X_Size(void) const;
   bool              X_Size(const int size) const;
   int               Y_Size(void) const;
   bool              Y_Size(const int size) const;
   string            Symbol(void) const;
   bool              Symbol(const string symbol) const;
   int               Period(void) const;
   bool              Period(const int period) const;
   int               Scale(void) const;
   bool              Scale(const int scale) const;
   bool              DateScale(void) const;
   bool              DateScale(const bool scale) const;
   bool              PriceScale(void) const;
   bool              PriceScale(const bool scale) const;
   //--- change of time/price coordinates is blocked
   bool              Time(const datetime time) const { return(false); }
   bool              Price(const double price) const { return(false); }
   //--- method of creating object
   bool              Create(long chart_id,const string name,const int window,
                            const int X,const int Y,const int sizeX,const int sizeY);
   //--- method of identifying the object
   virtual int       Type(void) const { return(OBJ_CHART); }
   //--- methods for working with files
   virtual bool      Save(const int file_handle);
   virtual bool      Load(const int file_handle);
  };

Теперь можно создать файл StandardChart.mqh с классом CStandardChart, в базовом содержании которого сразу можно определить стандартные для всех элементов библиотеки методы управления (см. листинг кода ниже). Так как здесь будет реализовываться режим горизонтальной прокрутки, то нам понадобится ярлык для курсора мыши, который будет подсказывать пользователю, что режим включен и данные в объекте-графике будут смещаться при перемещении курсора мыши по горизонтали. Для смены ярлыка подключим файл Pointer.mqh с классом CPointer, который был рассмотрен ранее в статье Графические интерфейсы VIII: Элемент "Древовидный список" (Глава 2). В качестве картинки для ярлыка курсора мыши возьмем копию той, что включается при горизонтальной прокрутке главного графика (чёрная двухсторонняя стрелка с белым контуром). Два варианта этой картинки (чёрная и синяя стрелки) можно загрузить к себе на компьютер в архиве в конце статьи. 

Соответственно, перечисление указателей для курсора мыши (ENUM_MOUSE_POINTER) дополнено ещё одним идентификатором (MP_X_SCROLL): 

//+------------------------------------------------------------------+
//| Перечисление типов указателей                                    |
//+------------------------------------------------------------------+
enum ENUM_MOUSE_POINTER
  {
   MP_CUSTOM     =0,
   MP_X_RESIZE   =1,
   MP_Y_RESIZE   =2,
   MP_XY1_RESIZE =3,
   MP_XY2_RESIZE =4,
   MP_X_SCROLL   =5
  };

Кроме этого, к файлу Pointer.mqh нужно подключить ресурсы с картинками для этого типа указателя, а switch-конструкцию в методе CPointer::SetPointerBmp() расширить ещё одним case-блоком

//+------------------------------------------------------------------+
//|                                                      Pointer.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
//--- Ресурсы
...
#resource "\\Images\\EasyAndFastGUI\\Controls\\pointer_x_scroll.bmp"
#resource "\\Images\\EasyAndFastGUI\\Controls\\pointer_x_scroll_blue.bmp"
//+------------------------------------------------------------------+
//| Установка картинок для указателя по типу указателя               |
//+------------------------------------------------------------------+
void CPointer::SetPointerBmp(void)
  {
   switch(m_type)
     {
      case MP_X_RESIZE :
         m_file_on  ="Images\\EasyAndFastGUI\\Controls\\pointer_x_rs_blue.bmp";
         m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_x_rs.bmp";
         break;
      case MP_Y_RESIZE :
         m_file_on  ="Images\\EasyAndFastGUI\\Controls\\pointer_y_rs_blue.bmp";
         m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_y_rs.bmp";
         break;
      case MP_XY1_RESIZE :
         m_file_on  ="Images\\EasyAndFastGUI\\Controls\\pointer_xy1_rs_blue.bmp";
         m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_xy1_rs.bmp";
         break;
      case MP_XY2_RESIZE :
         m_file_on  ="Images\\EasyAndFastGUI\\Controls\\pointer_xy2_rs_blue.bmp";
         m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_xy2_rs.bmp";
         break;
      case MP_X_SCROLL :
         m_file_on  ="Images\\EasyAndFastGUI\\Controls\\pointer_x_scroll_blue.bmp";
         m_file_off ="Images\\EasyAndFastGUI\\Controls\\pointer_x_scroll.bmp";
         break;
     }
//--- Если указан пользовательский тип (MP_CUSTOM)
   if(m_file_on=="" || m_file_off=="")
      ::Print(__FUNCTION__," > Для указателя курсора должны быть установлены обе картинки!");
  }

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

//+------------------------------------------------------------------+
//|                                                StandardChart.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
#include "Window.mqh"
#include "Pointer.mqh"
//+------------------------------------------------------------------+
//| Класс для создания стандартного графика                          |
//+------------------------------------------------------------------+
class CStandardChart : public CElement
  {
private:
   //--- Указатель на форму, к которой элемент присоединён
   CWindow          *m_wnd;
   //---
public:
   //--- Сохраняет указатель формы
   void              WindowPointer(CWindow &object)          { m_wnd=::GetPointer(object); }
   //---
public:
   //--- Обработчик события графика
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- Перемещение элемента
   virtual void      Moving(const int x,const int y,const bool moving_mode=false);
   //--- (1) Показ, (2) скрытие, (3) сброс, (4) удаление
   virtual void      Show(void);
   virtual void      Hide(void);
   virtual void      Reset(void);
   virtual void      Delete(void);
   //--- (1) Установка, (2) сброс приоритетов на нажатие левой кнопки мыши
   virtual void      SetZorders(void);
   virtual void      ResetZorders(void);
   //---
private:
   //--- Изменить ширину по правому краю окна
   virtual void      ChangeWidthByRightWindowSide(void);
   //--- Изменить высоту по нижнему краю окна
   virtual void      ChangeHeightByBottomWindowSide(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CStandardChart::CStandardChart(void)
  {
//--- Сохраним имя класса элемента в базовом классе
   CElement::ClassName(CLASS_NAME);
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CStandardChart::~CStandardChart(void)
  {
  }

Если же значение третьего аргумента метода Moving() задаётся как true, то при вызове метода элемент будет перемещён принудительно, независимо от того, находится ли сейчас форма в режиме перемещения. В некоторых случаях это существенно сокращает потребление ресурсов процессора. 

Чтобы узнать, находится ли сейчас форма в режиме перемещения, в класс CWindow добавлен метод ClampingAreaMouse(), возвращающий область, в которой была зажата левая кнопка мыши: 

//+------------------------------------------------------------------+
//| Класс создания формы для элементов управления                    |
//+------------------------------------------------------------------+
class CWindow : public CElement
  {
public:
   //--- Возвращает область, где была зажата левая кнопка мыши
   ENUM_MOUSE_STATE  ClampingAreaMouse(void)                           const { return(m_clamping_area_mouse);          }
  };

Метод CWindow::ClampingAreaMouse() доступен через указатель формы в каждом прикрепленном к ней элементе. Чтобы всё работало так, как было описано выше, в метод Moving() каждого элемента добавляется блок кода, как показано в следующем листинге (см. фрагмент, выделенный жёлтым маркером). Для примера этот метод показан из класса CSimpleButton (сокращённая версия). 

//+------------------------------------------------------------------+
//| Перемещение элементов                                            |
//+------------------------------------------------------------------+
void CSimpleButton::Moving(const int x,const int y,const bool moving_mode=false)
  {
//--- Выйти, если элемент скрыт
   if(!CElement::IsVisible())
      return;
//--- Если управление передано окну, определим его положение
   if(!moving_mode)
      if(m_wnd.ClampingAreaMouse()!=PRESSED_INSIDE_HEADER)
         return;
//--- Если привязка справа
//--- Если привязка слева
//--- Если привязка снизу
//--- Если привязка сверху
//--- Обновление координат графических объектов
   m_button.X_Distance(m_button.X());
   m_button.Y_Distance(m_button.Y());
  }

Пример использования метода Moving() можно посмотреть в листинге ниже, где показан код метода CSimpleButton::Show(). В этом случае обновление координат элемента нужно осуществить принудительно, поэтому в третьем аргументе передано значение true. Соответствующие изменения коснулись всех классов библиотеки, где используется метод Moving().  

//+------------------------------------------------------------------+
//| Показывает кнопку                                                |
//+------------------------------------------------------------------+
void CSimpleButton::Show(void)
  {
//--- Выйти, если элемент уже видим
   if(CElement::IsVisible())
      return;
//--- Сделать видимыми все объекты
   m_button.Timeframes(OBJ_ALL_PERIODS);
//--- Состояние видимости
   CElement::IsVisible(true);
//--- Обновить положение объектов
   Moving(m_wnd.X(),m_wnd.Y(),true);
  }

К оптимизации кода разрабатываемой библиотеки мы ещё вернёмся в этой статье, а пока продолжим рассматривать код элемента «Стандартный график».

Сделаем так, чтобы можно было создавать массив объектов-графиков, размещённых в ряд. Поэтому нужно объявить динамические массивы для объектов, которые представляют графики, а также для некоторых свойств, таких как: (1) идентификатор графика, (2) символ и (3) таймфрейм. Перед созданием элемента «Стандартный график» нужно воспользоваться методом CStandardChart::AddSubChart(), в который нужно передать символ и таймфрейм графика. В самом начале этого метода, перед тем, как добавить элемент в массивы и инициализировать их переданными значениями, стоит проверка символа с помощью метода CStandardChart::CheckSymbol().  

class CStandardChart : public CElement
  {
private:
   //--- Объекты для создания элемента
   CSubChart         m_sub_chart[];
   //--- Свойства графиков:
   long              m_sub_chart_id[];
   string            m_sub_chart_symbol[];
   ENUM_TIMEFRAMES   m_sub_chart_tf[];
   //---
public:
   //--- Добавляет график с указанными свойствами до создания
   void              AddSubChart(const string symbol,const ENUM_TIMEFRAMES tf);
   //---
private:
   //--- Проверка символа
   bool              CheckSymbol(const string symbol);
  };

В методе CStandardChart::CheckSymbol() сначала проверяется, есть ли указанный символ в окне «Обзор рынка». Если символ не найден, то далее осуществляется попытка найти его в общем списке символов. Если символ найден, то его нужно обязательно добавить в окно «Обзор рынка», иначе не получится создать объект-график с этим символом (будет создан объект-график с символом из главного окна графика). 

В случае успеха метод CStandardChart::CheckSymbol() возвращает true. Если же указанный символ не найден, то метод вернёт false и объект-график не будет добавлен (массивы останутся прежнего размера), а в журнал выведется сообщение об этом.  

//+------------------------------------------------------------------+
//| Добавляет график                                                 |
//+------------------------------------------------------------------+
void CStandardChart::AddSubChart(const string symbol,const ENUM_TIMEFRAMES tf)
  {
//--- Проверим, есть ли такой символ на сервере
   if(!CheckSymbol(symbol))
     {
      ::Print(__FUNCTION__," > Символа "+symbol+" нет на сервере!");
      return;
     }
//--- Увеличим размер массивов на один элемент
   int array_size=::ArraySize(m_sub_chart);
   int new_size=array_size+1;
   ::ArrayResize(m_sub_chart,new_size);
   ::ArrayResize(m_sub_chart_id,new_size);
   ::ArrayResize(m_sub_chart_symbol,new_size);
   ::ArrayResize(m_sub_chart_tf,new_size);
//--- Сохраним значения переданных параметров
   m_sub_chart_symbol[array_size] =symbol;
   m_sub_chart_tf[array_size]     =tf;
  }
//+------------------------------------------------------------------+
//| Проверка наличия символа                                         |
//+------------------------------------------------------------------+
bool CStandardChart::CheckSymbol(const string symbol)
  {
   bool flag=false;
//--- Проверим символ в окне "Обзор рынка"
   int symbols_total=::SymbolsTotal(true);
   for(int i=0; i<symbols_total; i++)
     {
      //--- Если такой символ есть, остановим цикл
      if(::SymbolName(i,true)==symbol)
        {
         flag=true;
         break;
        }
     }
//--- Если символа нет в окне "Обзор рынка", то ...
   if(!flag)
     {
      //--- ... попробуем найти его в общем списке
      symbols_total=::SymbolsTotal(false);
      for(int i=0; i<symbols_total; i++)
        {
         //--- Если такой символ есть, то...
         if(::SymbolName(i,false)==symbol)
           {
            //--- ... поместим его в окно "Обзор рынка" и остановим цикл
            ::SymbolSelect(symbol,true);
            flag=true;
            break;
           }
        }
     }
//--- Вернуть результат поиска
   return(flag);
  }

Для создания элемента «Стандартный график» понадобятся три метода: один главный публичный метод (public) и два приватных (private), один из которых относится к ярлыку для курсора мыши в режиме горизонтальной прокрутки. В качестве вспомогательного метода в класс добавлен публичный метод CStandardChart::SubChartsTotal() для получения количества объектов-графиков. 

class CStandardChart : public CElement
  {
private:
   //--- Объекты для создания элемента
   CSubChart         m_sub_chart[];
   CPointer          m_x_scroll;
   //---
public:
   //--- Методы для создания стандартного графика
   bool              CreateStandardChart(const long chart_id,const int subwin,const int x,const int y);
   //---
private:
   bool              CreateSubChart(void);
   bool              CreateXScrollPointer(void);
   //---
public:
   //--- Возвращает размер массива графиков
   int               SubChartsTotal(void)              const { return(::ArraySize(m_sub_chart)); }
  };

Рассмотрим метод CStandardChart::CreateSubCharts() для создания объектов-графиков. Здесь в самом начале стоит проверка на количество добавленных в массивы графиков перед созданием элемента. Если не добавили ни одного, то программа выйдет из метода, выведя сообщение с подсказкой в журнал.

Если графики добавлены, то далее осуществляется расчёт ширины для каждого объекта. Общую ширину элемента пользователь должен указать самостоятельно в пользовательском классе разрабатываемого MQL-приложения. Если нужно создать более одного графика, то, чтобы узнать ширину для каждого объекта, достаточно общую ширину элемента разделить на количество графиков.

Далее в цикле создаются объекты. При этом учитывается позиционирование элемента (точки привязки к одной из сторон формы). Эта тема подробно рассматривалась в предыдущей статье, поэтому больше не будем на ней останавливаться. 

После создания объекта-графика получаем и сохраняем в массиве идентификатор созданного графика, устанавливаем и сохраняем его свойства. 

//+------------------------------------------------------------------+
//| Создаёт графики                                                  |
//+------------------------------------------------------------------+
bool CStandardChart::CreateSubCharts(void)
  {
//--- Получим количество графиков
   int sub_charts_total=SubChartsTotal();
//--- Если нет ни одного графика в группе, сообщить об этом
   if(sub_charts_total<1)
     {
      ::Print(__FUNCTION__," > Вызов этого метода нужно осуществлять, "
              "когда в группе есть хотя бы один график! Воспользуйтесь методом CStandardChart::AddSubChart()");
      return(false);
     }
//--- Рассчитаем координаты и размер
   int x=m_x;
   int x_size=(sub_charts_total>1)? m_x_size/sub_charts_total : m_x_size;
//--- Создадим указанное количество графиков
   for(int i=0; i<sub_charts_total; i++)
     {
      //--- Формирование имени объекта
      string name=CElement::ProgramName()+"_sub_chart_"+(string)i+"__"+(string)CElement::Id();
      //--- Расчёт координаты X
      x=(i>0)?(m_anchor_right_window_side)? x-x_size+1 :  x+x_size-1 : x;
      //--- Корректировка ширины последнего графика
      if(i+1>=sub_charts_total)
         x_size=m_x_size-(x_size*(sub_charts_total-1)-(sub_charts_total-1));
      //--- Установим кнопку
      if(!m_sub_chart[i].Create(m_chart_id,name,m_subwin,x,m_y,x_size,m_y_size))
         return(false);
      //--- Получим и сохраним идентификатор созданного графика
      m_sub_chart_id[i]=m_sub_chart[i].GetInteger(OBJPROP_CHART_ID);
      //--- Установим свойства
      m_sub_chart[i].Symbol(m_sub_chart_symbol[i]);
      m_sub_chart[i].Period(m_sub_chart_tf[i]);
      m_sub_chart[i].Z_Order(m_zorder);
      m_sub_chart[i].Tooltip("\n");
      //--- Сохраним размеры
      m_sub_chart[i].XSize(x_size);
      m_sub_chart[i].YSize(m_y_size);
      //--- Отступы от крайней точки
      m_sub_chart[i].XGap((m_anchor_right_window_side)? x : x-m_wnd.X());
      m_sub_chart[i].YGap((m_anchor_bottom_window_side)? m_y : m_y-m_wnd.Y());
      //--- Сохраним указатель объекта
      CElement::AddToArray(m_sub_chart[i]);
     }
//---
   return(true);
  }

Уже после создания элемента «Стандартный график» можно в любой момент изменить любое свойство объектов-графиков, которые в нём содержатся, через указатель, который можно получить с помощью метода CStandardChart::GetSubChartPointer(). Если случайно передать неправильный индекс, то он будет скорректирован, чтобы предотвратить выход из диапазона массива. 

class CStandardChart : public CElement
  {
public:
   //--- Возвращает указатель на объект-график по указанному индексу
   CSubChart        *GetSubChartPointer(const uint index);
  };
//+------------------------------------------------------------------+
//| Возвращает указатель на график по указанному индексу             |
//+------------------------------------------------------------------+
CSubChart *CStandardChart::GetSubChartPointer(const uint index)
  {
   uint array_size=::ArraySize(m_sub_chart);
//--- Если нет ни одного графика, сообщить об этом
   if(array_size<1)
     {
      ::Print(__FUNCTION__," > Вызов этого метода нужно осуществлять, "
              "когда в группе есть хотя бы один график!");
     }
//--- Корректировка в случае выхода из диапазона
   uint i=(index>=array_size)? array_size-1 : index;
//--- Вернуть указатель
   return(::GetPointer(m_sub_chart[i]));
  }

Создание ярлыка для курсора мыши осуществляется только в том случае, если включен режим горизонтальной прокрутки данных в объектах-графиках. Включить его нужно перед созданием элемента, используя метод CStandardChart::XScrollMode().  

class CStandardChart : public CElement
  {
private:
   //--- Режим горизонтальной прокрутки
   bool              m_x_scroll_mode;
   //---
public:
   //--- Режим горизонтальной прокрутки
   void              XScrollMode(const bool mode) { m_x_scroll_mode=mode; }
  };
//+------------------------------------------------------------------+
//| Создаёт указатель курсора горизонтальной прокрутки               |
//+------------------------------------------------------------------+
bool CStandardChart::CreateXScrollPointer(void)
  {
//--- Выйти, если горизонтальная прокрутка не нужна
   if(!m_x_scroll_mode)
      return(true);
//--- Установка свойств
   m_x_scroll.XGap(0);
   m_x_scroll.YGap(-20);
   m_x_scroll.Id(CElement::Id());
   m_x_scroll.Type(MP_X_SCROLL);
//--- Создание элемента
   if(!m_x_scroll.CreatePointer(m_chart_id,m_subwin))
      return(false);
//---
   return(true);
  }

Подытожим сказанное выше. Если режим горизонтальной прокрутки включен, то для его работы используется метод CStandardChart::HorizontalScroll(), который будет вызываться в обработчике событий элемента по приходу события CHARTEVENT_MOUSE_MOVE. В случае, если левая кнопка мыши нажата, то в зависимости от того, нажата ли она только что или это уже повторные вызовы метода в процессе горизонтальной прокрутки, осуществляется расчёт пройденного расстояния курсора мыши в пикселях от точки нажатия. Здесь же: 

  • Блокируется форма.
  • Рассчитываются координаты для ярлыка курсора мыши.
  • Осуществляется показ ярлыка.

Рассчитанное значение для сдвига данных в объектах-графиках может быть только отрицательным, так как смещение будет производиться относительно последнего бара – метод ::ChartNavigate() со значением CHART_END (во втором аргументе) из перечисления ENUM_CHART_POSITION. Если значение сдвига получилось положительное, программа выйдет из метода. Если проверка пройдена, то запоминаем текущее значение сдвига для следующей итерации. Затем отключаем режимы «Автопрокрутка» (CHART_AUTOSCROLL) и «Сдвиг от правого края графика» (CHART_SHIFT) у всех объектов-графиков и осуществляем сдвиг по рассчитанному значению.

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

class CStandardChart : public CElement
  {
private:
   //--- Переменные, связанные с горизонтальной прокруткой графика
   int               m_prev_x;
   int               m_new_x_point;
   int               m_prev_new_x_point;
   //---
private:
   //--- Горизонтальная прокрутка
   void              HorizontalScroll(void);
  };
//+------------------------------------------------------------------+
//| Горизонтальная прокрутка графика                                 |
//+------------------------------------------------------------------+
void CStandardChart::HorizontalScroll(void)
  {
//--- Выйти, если отключена горизонтальная прокрутка графиков
   if(!m_x_scroll_mode)
      return;
//--- Если кнопка мыши нажата
   if(m_mouse.LeftButtonState())
     {
      //--- Запомним текущие координаты X курсора
      if(m_prev_x==0)
        {
         m_prev_x      =m_mouse.X()+m_prev_new_x_point;
         m_new_x_point =m_prev_new_x_point;
        }
      else
         m_new_x_point=m_prev_x-m_mouse.X();
      //--- Заблокировать форму
      if(!m_wnd.IsLocked())
        {
         m_wnd.IsLocked(true);
         m_wnd.IdActivatedElement(CElement::Id());
        }
      //--- Обновить координаты указателя и сделать его видимым
      int l_x=m_mouse.X()-m_x_scroll.XGap();
      int l_y=m_mouse.Y()-m_x_scroll.YGap();
      m_x_scroll.Moving(l_x,l_y);
      //--- Показать указатель
      m_x_scroll.Show();
      //--- Установить флаг видимости
      m_x_scroll.IsVisible(true);
     }
   else
     {
      m_prev_x=0;
      //--- Разблокировать форму
      if(m_wnd.IdActivatedElement()==CElement::Id())
        {
         m_wnd.IsLocked(false);
         m_wnd.IdActivatedElement(WRONG_VALUE);
        }
      //--- Скрыть указатель
      m_x_scroll.Hide();
      //--- Установить флаг видимости
      m_x_scroll.IsVisible(false);
      return;
     }
//--- Выйти, если положительное значение
   if(m_new_x_point>0)
      return;
//--- Запомнить текущее положение
   m_prev_new_x_point=m_new_x_point;
//--- Применить ко всем графикам
   int symbols_total=SubChartsTotal();
//--- Отключим автопрокрутку и сдвиг от правого края
   for(int i=0; i<symbols_total; i++)
     {
      if(::ChartGetInteger(m_sub_chart_id[i],CHART_AUTOSCROLL))
         ::ChartSetInteger(m_sub_chart_id[i],CHART_AUTOSCROLL,false);
      if(::ChartGetInteger(m_sub_chart_id[i],CHART_SHIFT))
         ::ChartSetInteger(m_sub_chart_id[i],CHART_SHIFT,false);
     }
//--- Сброс последней ошибки
   ::ResetLastError();
//--- Сместим графики
   for(int i=0; i<symbols_total; i++)
      if(!::ChartNavigate(m_sub_chart_id[i],CHART_END,m_new_x_point))
         ::Print(__FUNCTION__," > error: ",::GetLastError());
  }

Для обнуления вспомогательных переменных режима горизонтальной прокрутки данных в объектах-графиках будет использоваться метод CStandardChart::ZeroHorizontalScrollVariables(). Также нам может понадобиться программно перейти к последнему бару. Для этого воспользуемся публичным методом CStandardChart::ResetCharts()

class CStandardChart : public CElement
  {
public:
   //--- Сброс графиков
   void              ResetCharts(void);
   //---
private:
   //--- Обнуление переменных горизонтальной прокрутки
   void              ZeroHorizontalScrollVariables(void);
  };
//+------------------------------------------------------------------+
//| Сброс графиков                                                   |
//+------------------------------------------------------------------+
void CStandardChart::ResetCharts(void)
  {
   int sub_charts_total=SubChartsTotal();
   for(int i=0; i<sub_charts_total; i++)
      ::ChartNavigate(m_sub_chart_id[i],CHART_END);
//--- Обнулить вспомогательные переменные для горизонтальной прокрутки графиков
   ZeroHorizontalScrollVariables();
  }
//+------------------------------------------------------------------+
//| Обнуление переменных горизонтальной прокрутки                    |
//+------------------------------------------------------------------+
void CStandardChart::ZeroHorizontalScrollVariables(void)
  {
   m_prev_x           =0;
   m_new_x_point      =0;
   m_prev_new_x_point =0;
  }

Вполне возможно, что может возникнуть необходимость отследить событие нажатия левой кнопкой мыши на объекте-графике элемента «Стандартный график». Поэтому добавим в файл Defines.mqh новый идентификатор ON_CLICK_SUB_CHART

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
...
//--- Идентификаторы событий
...
#define ON_CLICK_SUB_CHART         (28) // Нажатие на объекте-графике

Для определения нажатия на объекте-графике реализуем метод CStandardChart::OnClickSubChart(). Если проверки по имени и идентификатору прошли успешно (см. листинг ниже), то генерируется сообщение с (1) идентификатором ON_CLICK_SUB_CHART события, (2) идентификатором элемента, (3) индексом объекта-графика и (4) названием символа. 

class CStandardChart : public CElement
  {
private:
   //--- Обработка нажатия на графике
   bool              OnClickSubChart(const string clicked_object);
  };
//+------------------------------------------------------------------+
//| Обработка нажатия на кнопку                                      |
//+------------------------------------------------------------------+
bool CStandardChart::OnClickSubChart(const string clicked_object)
  {
//--- Выйдем, если нажатие было не на пункте меню
   if(::StringFind(clicked_object,CElement::ProgramName()+"_sub_chart_",0)<0)
      return(false);
//--- Получим идентификатор и индекс из имени объекта
   int id=CElement::IdFromObjectName(clicked_object);
//--- Выйти, если идентификатор не совпадает
   if(id!=CElement::Id())
      return(false);
//--- Получим индекс
   int group_index=CElement::IndexFromObjectName(clicked_object);
//--- Отправить сигнал об этом
   ::EventChartCustom(m_chart_id,ON_CLICK_SUB_CHART,CElement::Id(),group_index,m_sub_chart_symbol[group_index]);
   return(true);
  }

Допустим, что Вам нужен другой способ для навигации по объектам-графикам, подобно тому, как это реализовано для главного графика средствами терминала. Если в торговом терминале MetaTrader нажать клавишу «Space» или «Enter», то в левом нижнем углу графика активируется поле ввода (см. скриншот ниже). Это своего рода командная строка, в которую можно ввести дату для быстрого перехода к ней на графике. Также с помощью этой командной строки можно изменить символ и таймфрейм графика.  

 Рис. 1. Командная строка графика в левом нижнем углу.

Рис. 1. Командная строка графика в левом нижнем углу.


Кстати, в последнем на текущий момент обновлении торгового терминала (build 1455) разработчиками была добавлена новая возможность по управлению командной строкой:

8. MQL5: Добавлено свойство CHART_QUICK_NAVIGATION для включения/отключения строки быстрой навигации на графике. Для изменения и получения состояния свойства используйте функции ChartSetInteger и ChartGetInteger.

Строка вызывается нажатием клавиши Enter или Space. При помощи нее можно быстро переместиться к заданной дате на графике, переключить символ и таймфрейм. Если ваша MQL5-программа обрабатывает нажатия клавиш Enter или Space, отключите свойство CHART_QUICK_NAVIGATION, чтобы терминал не перехватывал эти события. При этом останется возможность вызова строки навигации двойным кликом мыши.

… 

В рамках графического интерфейса всё можно сделать ещё удобнее и проще. В библиотеке Easy And Fast уже есть элемент «Календарь» (класс CCalendar). Навигацию по главному графику и объектам-графикам можно реализовать, просто выбирая дату в календаре. Упростим всё до вызова всего лишь одного метода с одним аргументом. Значением этого аргумента будет дата, к которой нужно сместить график. Назовём этот метод CStandardChart::SubChartNavigate(), и в листинге ниже вы можете ознакомиться с кодом его текущей версии.

В начале метода у главного графика отключаем режимы «Автопрокрутка графика» и «Смещение от правого края графика». Затем, если переданная в метод дата больше, чем начало текущего дня, то просто переходим к последнему бару и выходим из метода. Если же дата меньше, то нужно рассчитать, на какое количество баров будет смещение влево. Сначала расчёт делается для главного графика:

  • Получаем общее количество доступных баров по текущему символу и таймфрейму от начала текущего дня до указанной даты.
  • Получаем количество видимых на графике баров.
  • Получаем количество баров от начала текущего дня + два бара в качестве дополнительного отступа.
  • Рассчитываем количество баров для сдвига от последнего бара.

После этого производится смещение главного графика, и всё повторяется для объектов-графиков. 

class CStandardChart : public CElement
  {
public:
   //--- Переход к указанной дате
   void              SubChartNavigate(const datetime date);
  };
//+------------------------------------------------------------------+
//| Переход к указанной дате                                         |
//+------------------------------------------------------------------+
void CStandardChart::SubChartNavigate(const datetime date)
  {
//--- (1) Текущая дата на графике и (2) только что выбранная в календаре
   datetime current_date  =::StringToTime(::TimeToString(::TimeCurrent(),TIME_DATE));
   datetime selected_date =date;
//--- Отключим автопрокрутку и сдвиг от правого края
   ::ChartSetInteger(m_chart_id,CHART_AUTOSCROLL,false);
   ::ChartSetInteger(m_chart_id,CHART_SHIFT,false);
//--- Если выбранная в календаре дата больше, чем текущая
   if(selected_date>=current_date)
     {
      //--- Перейти к текущей дате на всех графиках
      ::ChartNavigate(m_chart_id,CHART_END);
      ResetCharts();
      return;
     }
//--- Получим количество баров от указанной даты
   int  bars_total    =::Bars(::Symbol(),::Period(),selected_date,current_date);
   int  visible_bars  =(int)::ChartGetInteger(m_chart_id,CHART_VISIBLE_BARS);
   long seconds_today =::TimeCurrent()-current_date;
   int  bars_today    =int(seconds_today/::PeriodSeconds())+2;
//--- Установим отступ от правого края всех графиков
   m_prev_new_x_point=m_new_x_point=-((bars_total-visible_bars)+bars_today);
   ::ChartNavigate(m_chart_id,CHART_END,m_new_x_point);
//---
   int sub_charts_total=SubChartsTotal();
   for(int i=0; i<sub_charts_total; i++)
     {
      //--- Отключим автопрокрутку и сдвиг от правого края
      ::ChartSetInteger(m_sub_chart_id[i],CHART_AUTOSCROLL,false);
      ::ChartSetInteger(m_sub_chart_id[i],CHART_SHIFT,false);
      //--- Получим количество баров от указанной даты
      bars_total   =::Bars(m_sub_chart[i].Symbol(),(ENUM_TIMEFRAMES)m_sub_chart[i].Period(),selected_date,current_date);
      visible_bars =(int)::ChartGetInteger(m_sub_chart_id[i],CHART_VISIBLE_BARS);
      bars_today   =int(seconds_today/::PeriodSeconds((ENUM_TIMEFRAMES)m_sub_chart[i].Period()))+2;
      //--- Отступ от правого края графика
      m_prev_new_x_point=m_new_x_point=-((bars_total-visible_bars)+bars_today);
      ::ChartNavigate(m_sub_chart_id[i],CHART_END,m_new_x_point);
     }
  }

Разработка класса CStandardChart для создания элемента «Стандартный график» завершена. Теперь напишем приложение, чтобы посмотреть, как это работает. 


Приложение для теста элемента

Для теста можно взять эксперта из предыдущей статьи. Удалим из него все элементы, кроме главного меню, статусной строки и вкладок. Сделаем так, чтобы на каждой вкладке была своя группа объектов-графиков. Каждая группа будет содержать в себе какую-то определённую валюту, поэтому у каждой вкладки будет соответствующее описание:

  • Первая вкладка – EUR (Euro — евро).
  • Вторая вкладка – GBP (Great Britain Pound – британский фунт).
  • Третья вкладка – AUD (Australian Dollar – австралийский доллар).
  • Четвёртая вкладка – CAD (Canadian Dollar – канадский доллар).
  • Пятая вкладка – JPY (Japanese Yen – японская иена).

Объекты-графики будут располагаться строго в рабочей области вкладок и автоматически изменяться в размерах при изменении размеров формы. Правая граница рабочей области вкладок будет всегда иметь отступ в 173 пикселя от правого края формы. Это пространство заполним элементами управления для установки таких свойств, как:

  • Показ временной шкалы (Date time).
  • Показ ценовой шкалы (Price scale).
  • Изменение таймфрейма графика (Timeframes).
  • Навигация по данным графика посредством календаря.

Для примера достаточно привести код создания только для одного элемента «Стандартный график» (CStandardChart). Помним, что по умолчанию режим горизонтальной прокрутки графиков отключен, поэтому если прокрутка нужна, можно воспользоваться методом CStandardChart::XScrollMode(). Для добавления графиков в группу используется метод CStandardChart::AddSubChart().

//+------------------------------------------------------------------+
//| Класс для создания приложения                                    |
//+------------------------------------------------------------------+
class CProgram : public CWndEvents
  {
protected:
   //--- Стандартный график
   CStandardChart    m_sub_chart1;
   //---
protected:
   //--- Стандартный график
   bool              CreateSubChart1(const int x_gap,const int y_gap);
  };
//+------------------------------------------------------------------+
//| Создаёт стандартный график 1                                     |
//+------------------------------------------------------------------+
bool CProgram::CreateSubChart1(const int x_gap,const int y_gap)
  {
//--- Сохраним указатель на окно
   m_sub_chart1.WindowPointer(m_window);
//--- Закрепить за 1-ой вкладкой
   m_tabs.AddToElementsArray(0,m_sub_chart1);
//--- Координаты
   int x=m_window.X()+x_gap;
   int y=m_window.Y()+y_gap;
//--- Установим свойства перед созданием
   m_sub_chart1.XSize(600);
   m_sub_chart1.YSize(200);
   m_sub_chart1.AutoXResizeMode(true);
   m_sub_chart1.AutoYResizeMode(true);
   m_sub_chart1.AutoXResizeRightOffset(175);
   m_sub_chart1.AutoYResizeBottomOffset(25);
   m_sub_chart1.XScrollMode(true);
//--- Добавим графики
   m_sub_chart1.AddSubChart("EURUSD",PERIOD_D1);
   m_sub_chart1.AddSubChart("EURGBP",PERIOD_D1);
   m_sub_chart1.AddSubChart("EURAUD",PERIOD_D1);
//--- Создадим элемент управления
   if(!m_sub_chart1.CreateStandardChart(m_chart_id,m_subwin,x,y))
      return(false);
//--- Добавим объект в общий массив групп объектов
   CWndContainer::AddToElementsArray(0,m_sub_chart1);
   return(true);
  }

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

 Рис. 2. Тест элемента «Стандартный график».

Рис. 2. Тест элемента «Стандартный график».


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

 

Оптимизация таймера и обработчика событий движка библиотеки

До этого тесты библиотеки Easy And Fast проводились мной только в операционной системе Windows 7 x64. После перехода на Windows 10 x64 было обнаружено, что потребление ресурсов процессора существенно увеличивается. Даже в состоянии покоя, когда не осуществляется взаимодействие с графическим интерфейсом, процессы библиотеки потребляли до 10% ресурсов процессора. На скриншотах ниже показано потребление ресурсов процессора до и после загрузки тестового MQL-приложения на график.

Рис. 3. Потребление ресурсов процессора до загрузки тестового MQL-приложения на график.

Рис. 3. Потребление ресурсов процессора до загрузки тестового MQL-приложения на график.


Рис. 4. Потребление ресурсов процессора после загрузки тестового MQL-приложения на график.

Рис. 4. Потребление ресурсов процессора после загрузки тестового MQL-приложения на график.


Оказалось, что всё дело в таймере движка библиотеки, где обновление графика осуществляется каждые 16 мс (см. листинг кода ниже):

//+------------------------------------------------------------------+
//| Таймер                                                           |
//+------------------------------------------------------------------+
void CWndEvents::OnTimerEvent(void)
  {
//--- Если массив пуст, выйдем  
   if(CWndContainer::WindowsTotal()<1)
      return;
//--- Проверка событий всех элементов по таймеру
   CheckElementsEventsTimer();
//--- Перерисуем график
   m_chart.Redraw();
  }

Если к этому прибавить ещё перемещение курсора мыши в области графика и активное взаимодействие с графическим интерфейсом MQL-приложения, то потребление вырастет ещё больше. Задача состоит в том, чтобы ограничить работу таймера движка библиотеки и исключить перерисовку графика по приходу события перемещения курсора мыши. Каким образом это можно сделать?

В методе CWndEvents::ChartEventMouseMove() удалим строку, отвечающую за перерисовку графика (выделено красным): 

//+------------------------------------------------------------------+
//| Событие CHARTEVENT MOUSE MOVE                                    |
//+------------------------------------------------------------------+
void CWndEvents::ChartEventMouseMove(void)
  {
//--- Выйти, если это не событие перемещения курсора
   if(m_id!=CHARTEVENT_MOUSE_MOVE)
      return;
//--- Перемещение окна
   MovingWindow();
//--- Установка состояния графика
   SetChartState();
//--- Перерисуем график
   m_chart.Redraw();
  }

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

Для реализации задуманного нужно внести некоторые дополнения в класс CMouse. Добавим в него счётчик вызовов системного таймера и метод CMouse::GapBetweenCalls(), который будет возвращать разницу между вызовами события перемещения курсора мыши. 

class CMouse
  {
private:
   //--- Счётчик вызовов
   ulong             m_call_counter;
   //---
public:   
   //--- Возвращает (1) сохранённое при последнем вызове значение счётчика и (2) разницу между вызовами обработчика события перемещения курсора мыши
   ulong             CallCounter(void)     const { return(m_call_counter);                  }
   ulong             GapBetweenCalls(void) const { return(::GetTickCount()-m_call_counter); }
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CMouse::CMouse(void) : m_call_counter(::GetTickCount())
  {
  }

Логика здесь проста. Как только курсор мыши начинает перемещаться, в обработчике событий класса CMouse запоминаем текущее значение системного таймера

//+------------------------------------------------------------------+
//| Обработка событий перемещения курсора мыши                       |
//+------------------------------------------------------------------+
void CMouse::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Обработка события перемещения курсора
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      //--- Координаты и состояние левой кнопки мыши
      m_x                 =(int)lparam;
      m_y                 =(int)dparam;
      m_left_button_state =(bool)int(sparam);
      //--- Сохраним значение счётчика вызовов
      m_call_counter=::GetTickCount();
      //--- Получим местоположение курсора
      if(!::ChartXYToTimePrice(0,m_x,m_y,m_subwin,m_time,m_level))
         return;
      //--- Получим относительную координату Y
      if(m_subwin>0)
         m_y=m_y-m_chart.SubwindowY(m_subwin);
     }
  }

А в таймере движка библиотеки (класс CWndEvents) нужно поставить условие, что если курсор мыши находится в состоянии покоя уже более 500 мс, то не нужно перерисовывать график. Левая кнопка мыши при этом должна быть отжата, чтобы не столкнуться с тем, что ускоренная прокрутка элементов работает только в течение 500 мс. 

//+------------------------------------------------------------------+
//| Таймер                                                           |
//+------------------------------------------------------------------+
void CWndEvents::OnTimerEvent(void)
  {
//--- Выйти, если курсор мыши в состоянии покоя (разница между вызовами >500 ms) и левая кнопка мыши отжата
   if(m_mouse.GapBetweenCalls()>500 && !m_mouse.LeftButtonState())
      return;
//--- Если массив пуст, выйдем  
   if(CWndContainer::WindowsTotal()<1)
      return;
//--- Проверка событий всех элементов по таймеру
   CheckElementsEventsTimer();
//--- Перерисуем график
   m_chart.Redraw();
  }

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


Оптимизация элементов «Древовидный список» и «Файловый навигатор»

Обнаружилось, что при большом количестве элементов в древовидном списке (CTreeView), а также файловом навигаторе (CFileNavigator), где используется этот тип списков, инициализация была очень долгой. Чтобы решить эту проблему, при добавлении элемента в массивы в функции ::ArrayResize() в качестве третьего параметра нужно указывать резервный размер для массива. 

Цитата из справки к функции ::ArrayResize():

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

Для сравнения ниже показаны результаты тестов с различными значениями резервного размера массивов в древовидном списке. Количество файлов для теста более 15 000.

 Рис. 5. Результаты тестов формирования массивов с резервным значением размера.

Рис. 5. Результаты тестов формирования массивов с резервным значением размера.


Установим резервный размер для массивов древовидного списка равный 10 000. Соответствующие изменения были внесены в классы CTreeView и CFileNavigator.

 

Новые ярлыки для папок и файлов в файловом навигаторе

Добавлены новые ярлыки для папок и файлов файлового навигатора (класс CFileNavigator) аналогичные тем, что установлены в файловом навигаторе операционной системы Windows 10. Их лаконичный дизайн больше подходит для графических интерфейсов разрабатываемой библиотеки, но в случае необходимости у Вас есть возможность установить свои версии.

 Рис. 6. Новые ярлыки папок и файлов в файловом навигаторе.

Рис. 6. Новые ярлыки папок и файлов в файловом навигаторе. 

 

Эти картинки доступны для скачивания в архиве в конце статьи.

 

Заключение

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

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

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


В следующих статьях серии о графических интерфейсах продолжим развивать библиотеку Easy And Fast. Библиотека будет расширена дополнительными элементами управления, которые могут понадобится в разрабатываемых MQL-приложениях. Существующие элементы будут развиваться и дополняться новыми возможностями. 

Ниже вы можете загрузить к себе на компьютер четвёртую версию (build 4) библиотеки Easy And FastПри наличии интереса вы можете поспособствовать более быстрому развитию этого проекта, если будете предлагать свои варианты решения некоторых задач в комментариях к статьям или в личной переписке. 

Прикрепленные файлы |
Последние комментарии | Перейти к обсуждению на форуме трейдеров (51)
Konstantin
Konstantin | 21 апр. 2017 в 13:32
Планируется ли в следующем обновлении внесение функционала для изменения расположения графиков? В текущей версии идет горизонтальное расположение, но есть необходимость располагать их по вертикали, мы об этом с вами разговаривали.
Anatoli Kazharski
Anatoli Kazharski | 21 апр. 2017 в 13:47
Konstantin:
Планируется ли в следующем обновлении внесение функционала для изменения расположения графиков? В текущей версии идет горизонтальное расположение, но есть необходимость располагать их по вертикали, мы об этом с вами разговаривали.

И я Вам ответил, что сделаю.

В следующем обновлении нет, так как сейчас идёт большая работа над другими частями в рамках второго этапа развития. В общем, не могу сказать точно, когда именно. В одном из следующих.

Konstantin
Konstantin | 21 апр. 2017 в 14:04
Anatoli Kazharski:

И я Вам ответил, что сделаю.

В следующем обновлении нет, так как сейчас идёт большая работа над другими частями в рамках второго этапа развития. В общем, не могу сказать точно, когда именно. В одном из следующих.


да ответили, но в каком обновлении я не уточнял, поэтому и спросил ))
Anatoli Kazharski
Anatoli Kazharski | 21 апр. 2017 в 14:09
Konstantin:

да ответили, но в каком обновлении я не уточнял, поэтому и спросил ))

Сначала добавлю возможность работать вот с этими графиками: Визуализируй это! Графическая библиотека в MQL5 как аналог plot из R

А потом расширю возможности класса CStandartChart.

Konstantin
Konstantin | 21 апр. 2017 в 16:37
Anatoli Kazharski:

Сначала добавлю возможность работать вот с этими графиками: Визуализируй это! Графическая библиотека в MQL5 как аналог plot из R

А потом расширю возможности класса CStandartChart.


кстати тоже понадобится эта возможность, нужно будет выводить статданные, поэтому и сделал подвал под таблицей


Универсальный Зигзаг Универсальный Зигзаг
Зигзаг — один из самых популярных индикаторов среди пользователей MetaTrader 5. В статье были проанализированы возможности создания различных вариантов Зигзага. В результате мы получаем универсальный индикатор с широкими возможностями для расширения функциональности, который удобно использовать при разработке торговых советников и других индикаторов.
Основы программирования на MQL5: Глобальные переменные терминала MetaTrader 5 Основы программирования на MQL5: Глобальные переменные терминала MetaTrader 5
Глобальные переменные терминала — незаменимое средство при разработке сложных и надежных экспертов. Освоив работу с глобальными переменными терминала, вы уже не сможете представить себе создание экспертов на MQL5 без их использования.
Торговая стратегия '80-20' Торговая стратегия '80-20'
В статье описывается создание инструментов (индикатора и советника) для исследования торговой стратегии '80-20'. Правила ТС взяты из книги Линды Рашке и Лоуренса Коннорса "Биржевые секреты. Высокоэффективные стратегии краткосрочной торговли". На языке MQL5 формализованы правила этой стратегии, а созданные на ее основе индикатор и советник протестированы на современной истории рынка.
Торговая система 'Turtle Soup' и её модификация 'Turtle Soup Plus One' Торговая система 'Turtle Soup' и её модификация 'Turtle Soup Plus One'
В статье формализованы и запрограммированы правила торговых стратегий Turtle Soup и Turtle Soup Plus One из книги Линды Рашке и Лоуренса Коннорс Street Smarts: High Probability Short-Term Trading Strategies. Описанные в книге стратегии получили достаточно широкое распространение, но важно понимать, что авторы строили их исходя из поведения рынка 15..20-летней давности.