Скачать MetaTrader 5

Графические интерфейсы X: Обновления для библиотеки Easy And Fast (build 3)

19 сентября 2016, 16:05
Anatoli Kazharski
8
2 485

Содержание


Введение

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

В этой статье представлена следующая версия библиотеки Easy And Fast (версия 3). Исправлены некоторые недоработки и добавлены новые возможности. Подробнее читайте далее в статье.

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

 

Список обновлений

1. До сих пор в MQL-приложениях к предыдущим статьям демонстрировалось, как можно реализовать графический интерфейс в подокне индикатора. Конечно же, для некоторых простых приложений такого подхода будет вполне достаточно. Но хотелось бы также иметь возможность создать графический интерфейс в подокне для программы типа «Эксперт». Таким образом, мы бы смогли создавать полноценные торговые панели, полностью отделённые от главного окна графика. Работать в таком режиме будет удобнее: ценовой график и какие-либо важные данные на нём всегда оставались бы открытыми и незагромождёнными графическим интерфейсом приложения. Далее будет подробно описано, как это было реализовано в рамках библиотеки Easy And Fast.

Задача состояла в том, чтобы включение режима «Эксперт в подокне» осуществлялось подключением ресурса с индикатором-пустышкой (SubWindow.ex5) к главному файлу MQL-приложения. Индикатор-пустышка — это просто подокно без расчётной части серий. Но так как сейчас нет возможности узнать средствами MQL, подключен ли индикатор в качестве ресурса, то временно в файл Defines.mqh добавим директиву с идентификатором константы EXPERT_IN_SUBWINDOW. Он нужен для того, чтобы исключить вывод в журнал сообщения об ошибке, которая возникает при попытке получить хэндл индикатора, недоступного по указанной директории.

По умолчанию установлено значение false, то есть режим отключен и графический интерфейс будет создаваться в главном окне графика (см. листинг ниже).

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//--- Режим "Эксперт в окне"
#define EXPERT_IN_SUBWINDOW false
...

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

//+------------------------------------------------------------------+
//|                                                TestLibrary01.mq5 |
//|                        Copyright 2016, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2016, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
//--- Подключение индикатора для режима "Эксперт в подокне"
#resource \\Indicators\\SubWindow.ex5
...

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

Индикатор и эксперт будут обмениваться сообщениями. Дело в том, что индикатор "не знает", какие режимы для построения графического интерфейса выбраны у эксперта. Ему нужно прислать сообщение, если разработчик приложения решил сделать подокно фиксированной высоты, а также значение высоты в пикселях. Для формирования сообщения понадобится ещё один идентификатор события ON_SUBWINDOW_CHANGE_HEIGHT. Этот идентификатор также нужно разместить и в файле Defines.mqh, чтобы он был доступен в приложениях, где используется библиотека для создания графических интерфейсов. 

Чтобы понять, когда эксперту нужно генерировать событие для изменения высоты подокна, после успешной инициализации индикатора при первом автоматическом вызове функции ::OnCalculate(), когда аргумент prev_calculated равен нулю, будем передавать сообщение для эксперта. Для однозначной идентификации, что сообщение пришло от индикатора SubWindow.ex5, кроме идентификатора события ON_SUBWINDOW_CHANGE_HEIGHT, в качестве string-параметра (sparam) будет отправляться имя программы. Сообщение будет отслеживаться в обработчике класса CWindow. Если там все условия будут выполнены, то индикатору будет отправлен ответ с таким же (1) идентификатором (ON_SUBWINDOW_CHANGE_HEIGHT), (2) значением высоты в long-параметре события (lparam) и (3) именем эксперта. По приходу этого сообщения обрабатываться оно будет в функции ::OnChartEvent(). После проверки имени высота подокна устанавливается по переданному значению.  

//+------------------------------------------------------------------+
//|                                                    SubWindow.mq5 |
//|                        Copyright 2016, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2016, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property indicator_separate_window
#property indicator_plots   0
#property indicator_buffers 0
#property indicator_minimum 0.0
#property indicator_maximum 0.0
//--- Имя программы
#define PROGRAM_NAME ::MQLInfoString(MQL_PROGRAM_NAME)
//--- Идентификатор события для изменения высоты подокна эксперта
#define ON_SUBWINDOW_CHANGE_HEIGHT (25)
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit(void)
  {
//--- Короткое имя индикатора
   ::IndicatorSetString(INDICATOR_SHORTNAME,PROGRAM_NAME);
//--- Инициализация прошла успешно
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Деинициализация                                                  |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int    rates_total,
                const int    prev_calculated,
                const int    begin,
                const double &price[])
  {
//--- Если инициализация прошла успешно
   if(prev_calculated<1)
      //--- Отправим сообщение эксперту, чтобы получить от него размер для подокна
      ::EventChartCustom(0,ON_SUBWINDOW_CHANGE_HEIGHT,0,0.0,PROGRAM_NAME);
//---
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int    id,
                  const long   &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Обработка события изменения высоты подокна эксперта
   if(id==CHARTEVENT_CUSTOM+ON_SUBWINDOW_CHANGE_HEIGHT)
     {
      //--- Принимать сообщения только от имени эксперта
      if(sparam==PROGRAM_NAME)
         return;
      //--- Изменить высоту подокна
      ::IndicatorSetInteger(INDICATOR_HEIGHT,(int)lparam);
     }
  }
//+------------------------------------------------------------------+

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

  • сообщение было отправлено самим экспертом для индикатора;
  • эта программа не эксперт;
  • не установлен режим фиксированной высоты подокна эксперта. 

//+------------------------------------------------------------------+
//| Обработчик событий графика                                       |
//+------------------------------------------------------------------+
void CWindow::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
...
//--- Обработка события изменения высоты подокна эксперта
   if(id==CHARTEVENT_CUSTOM+ON_SUBWINDOW_CHANGE_HEIGHT)
     {
      //--- Выйти, если это сообщение было от эксперта
      if(sparam==PROGRAM_NAME)
         return;
      //--- Выйти, если эта программа не эксперт
      if(CElement::ProgramType()!=PROGRAM_EXPERT)
         return;
      //--- Выйти, если не установлен режим фиксированной высоты подокна
      if(!m_height_subwindow_mode)
         return;
      //--- Рассчитать и изменить высоту подокна
      m_subwindow_height=(m_is_minimized)? m_caption_height+3 : m_bg_full_height+3;
      ChangeSubwindowHeight(m_subwindow_height);
      return;
     }
  }

Если все проверки пройдены, то далее рассчитывается высота для подокна с учётом текущего состояния окна (свёрнуто/развёрнуто) и затем вызывается метод CWindow::ChangeSubwindowHeight(), в который тоже было внесено небольшое дополнение (см. следующий листинг кода), смысл которого заключается в том, что если тип программы «Эксперт», то генерируется сообщение для индикатора SubWindow.ex5

//+------------------------------------------------------------------+
//| Изменяет высоту подокна индикатора                               |
//+------------------------------------------------------------------+
void CWindow::ChangeSubwindowHeight(const int height)
  {
//--- Если графический интерфейс не в подокне или программа типа "Скрипт"
   if(CElement::m_subwin<=0 || CElement::m_program_type==PROGRAM_SCRIPT)
      return;
//--- Если нужно изменить высоту подокна
   if(height>0)
     {
      //--- Если программа типа "Индикатор"
      if(CElement::m_program_type==PROGRAM_INDICATOR)
        {
         if(!::IndicatorSetInteger(INDICATOR_HEIGHT,height))
            ::Print(__FUNCTION__," > Не удалось изменить высоту подокна индикатора! Номер ошибки: ",::GetLastError());
        }
      //--- Если программа типа "Эксперт"
      else
        {
         //--- Отправить сообщение индикатору SubWindow.ex5 о том, что размеры окна нужно изменить
         ::EventChartCustom(m_chart_id,ON_SUBWINDOW_CHANGE_HEIGHT,(long)height,0,PROGRAM_NAME);
        }
     }
  }

В движок библиотеки (класс CWndEvents) также нужно внести некоторые дополнения для определения, проверки и корректировки номера подокна, в котором будет размещаться графический интерфейс MQL-приложения типа «Эксперт». В метод CWndEvents::DetermineSubwindow() нужно добавить блок кода для режима “Эксперт в подокне”. В листинге ниже показана сокращённая версия этого метода. Вход в блок происходит по условию, что тип программы «Эксперт». Далее идет проверка на то, включен ли режим «Эксперт в подокне». Если включен, то получаем хэндл индикатора SubWindow.ex5 и, если с этим не возникло проблем, то сначала определяется текущее количество подокон в окне графика. Полученным значением определяется номер подокна индикатора SubWindow.ex5, установка которого осуществляется функцией ::ChartIndicatorAdd(). Затем, если при установке подокна не возникло ошибок, сохраняется (1) номер подокна эксперта, (2) текущее количество подокон и (3) короткое имя индикатора SubWindow.ex5.  

//+------------------------------------------------------------------+
//| Класс для обработки событий                                      |
//+------------------------------------------------------------------+
class CWndEvents : public CWndContainer
  {
protected:
   //--- Хэндл подокна эксперта
   int               m_subwindow_handle;
   //--- Имя подокна эксперта
   string            m_subwindow_shortname;
   //--- Количество подокон на графике после установки подокна эксперта
   int               m_subwindows_total;
  };
//+------------------------------------------------------------------+
//| Определение номера подокна                                       |
//+------------------------------------------------------------------+
void CWndEvents::DetermineSubwindow(void)
  {
//--- Выйдем, если тип программы "Скрипт"
//--- Сброс последней ошибки

//--- Если тип программы эксперт
   if(PROGRAM_TYPE==PROGRAM_EXPERT)
     {
      //--- Выйти, если графический интерфейс эксперта нужен в главном окне
      if(!EXPERT_IN_SUBWINDOW)
         return;
      //--- Получим хэндл индикатора-пустышки (пустое подокно)
      m_subwindow_handle=iCustom(Symbol(),Period(),"::Indicators\\SubWindow.ex5");
      //--- Если такого индикатор нет, сообщить об ошибке в журнал
      if(m_subwindow_handle==INVALID_HANDLE)
         ::Print(__FUNCTION__," > Ошибка при получении хэндла индикатора в директории ::Indicators\\SubWindow.ex5 !");
      //--- Если хэндл получен, значит индикатор есть, подключен к приложению в качестве ресурса,
      //    а это значит, что графический интерфейс приложения нужно поместить в подокно
      else
        {
         //--- Получим количество подокон на графике
         int subwindows_total=(int)::ChartGetInteger(m_chart_id,CHART_WINDOWS_TOTAL);
         //--- Установим подокно для графического интерфейса эксперта
         if(::ChartIndicatorAdd(m_chart_id,subwindows_total,m_subwindow_handle))
           {
            //--- Сохраним номер подокна и текущее количество подокон на графике
            m_subwin           =subwindows_total;
            m_subwindows_total =subwindows_total+1;
            //--- Получим и сохраним короткое имя подокна эксперта
            m_subwindow_shortname=::ChartIndicatorName(m_chart_id,m_subwin,0);
           }
         //--- Если подокно не установилось
         else
            ::Print(__FUNCTION__," > Ошибка при установке подокна эксперта! Номер ошибки: ",::GetLastError());
        }
      //---
      return;
     }
//--- Определение номера окна индикатора
//--- Если не получилось определить номер, выйдем
//--- Если это не главное окно графика
...
  }

Также нужно позаботиться о том, чтобы при добавлении и удалении других индикаторов в подокнах графика номер подокна эксперта корректировался для правильной работы его графического интерфейса. Кроме этого, сделаем так, что, если пользователь удалит подокно эксперта, то эксперт тоже будет удалён, перед этим оставив сообщение в журнале на вкладке «Эксперты» окна «Инструменты» о причине удаления с графика. Для этого реализован метод CWndEvents::CheckExpertSubwindowNumber(). Допуск в этот метод осуществляется по условию, что программа имеет тип «Эксперт». Если проверка пройдена, то далее получаем количество подокон в окне графика. Если оказалось, что количество подокон не изменилось, то программа выходит из метода. 

Далее в цикле нужно найти подокно эксперта и если оно есть — проверить, не изменился ли номер его подокна. Причиной того, что номер подокна изменился, может быть добавление или удаление, какого-нибудь индикатора, который тоже размещался в своём подокне. Если номер подокна изменился, то нужно сохранить его номер и обновить значение во всех элементах главного окна

Если подокно не найдено, то причина может быть только одна: его удалили. В таком случае эксперт тоже удаляется с графика

class CWndEvents : public CWndContainer
  {
private:
   //--- Проверка и обновление номера окна эксперта
   void              CheckExpertSubwindowNumber(void);
  };
//+------------------------------------------------------------------+
//| Проверка и обновление номера окна эксперта                       |
//+------------------------------------------------------------------+
void CWndEvents::CheckExpertSubwindowNumber(void)
  {
//--- Выйти, если это не эксперт
   if(PROGRAM_TYPE!=PROGRAM_EXPERT)
      return;
//--- Получим количество подокон на графике
   int subwindows_total=(int)::ChartGetInteger(m_chart_id,CHART_WINDOWS_TOTAL);
//--- Выйти, если количество подокон и количество индикаторов не изменилось
   if(subwindows_total==m_subwindows_total)
      return;
//--- Сохраним текущее количество подокон
   m_subwindows_total=subwindows_total;
//--- Для проверки наличия подокна эксперта
   bool is_subwindow=false;
//--- Найдём подокно эксперта
   for(int sw=0; sw<subwindows_total; sw++)
     {
      //--- Остановить цикл, если подокно эксперта есть
      if(is_subwindow)
         break;
      //--- Сколько индикаторов в данном окне/подокне
      int indicators_total=::ChartIndicatorsTotal(m_chart_id,sw);
      //--- Переберём все индикаторы в окне 
      for(int i=0; i<indicators_total; i++)
        {
         //--- Получим короткое имя индикатора
         string indicator_name=::ChartIndicatorName(m_chart_id,sw,i);
         //--- Если это не подокно эксперта, перейти к следующему
         if(indicator_name!=m_subwindow_shortname)
            continue;
         //--- Отметим, что подокно эксперта есть
         is_subwindow=true;
         //--- Если номер подокна изменился, то 
         //    нужно сохранить новый номер во всех элементах главной формы
         if(sw!=m_subwin)
           {
            //--- Сохраним номер подокна
            m_subwin=sw;
            //--- Сохраним его также во всех элементах главной формы интерфейса
            int elements_total=CWndContainer::ElementsTotal(0);
            for(int e=0; e<elements_total; e++)
               m_wnd[0].m_elements[e].SubwindowNumber(m_subwin);
           }
         //---
         break;
        }
     }
//--- Если подокно эксперта не обнаружено, удалим эксперта
   if(!is_subwindow)
     {
      ::Print(__FUNCTION__," > Удаление подокна эксперта приводит к удалению эксперта!");
      //--- Удаление эксперта с графика
      ::ExpertRemove();
     }
  }

 

2. В предыдущей версии библиотеки была добавлена возможность включения режима для автоматического изменения ширины формы. Добавим аналогичную возможность для изменения её высоты. Идентификатор события изменения размеров формы ON_WINDOW_CHANGE_SIZE теперь не подходит для решения этой задачи, так как изменение размеров по ширине и высоте будут обрабатываться, как отдельные события. Поэтому в файле Defines.mqh вместо идентификатора ON_WINDOW_CHANGE_SIZE теперь будут два отдельных идентификатора, как показано в листинге кода ниже. Соответствующая замена идентификатора осуществлена в других файлах библиотеки.  

#define ON_WINDOW_CHANGE_XSIZE     (3)  // Изменение размеров окна по оси X
#define ON_WINDOW_CHANGE_YSIZE     (4)  // Изменение размеров окна по оси Y

Для изменения высоты формы добавлен метод CWindow::ChangeWindowHeight(). При вызове метода после изменения размеров формы в самом конце генерируется сообщение о совершённом действии

//+------------------------------------------------------------------+
//| Класс создания формы для элементов управления                    |
//+------------------------------------------------------------------+
class CWindow : public CElement
  {
public:
   //--- Управление размерами
   void              ChangeWindowHeight(const int height);
  };
//+------------------------------------------------------------------+
//| Изменяет высоту окна                                             |
//+------------------------------------------------------------------+
void CWindow::ChangeWindowHeight(const int height)
  {
//--- Если высота не изменилась, выйдем
   if(height==m_bg.YSize())
      return;
//--- Выйти, если окно свёрнуто
   if(m_is_minimized)
      return;
//--- Обновим высоту для фона
   CElement::YSize(height);
   m_bg.YSize(height);
   m_bg.Y_Size(height);
   m_bg_full_height=height;
//--- Сообщение о том, что размеры окна были изменены
   ::EventChartCustom(m_chart_id,ON_WINDOW_CHANGE_YSIZE,(long)CElement::Id(),0,"");
  }

Для того, чтобы высота формы изменялась автоматически, пользователю-разработчику MQL-приложения нужно установить соответствующий режим с помощью метода CElement::AutoYResizeMode(): 

//+------------------------------------------------------------------+
//| Базовый класс элемента управления                                |
//+------------------------------------------------------------------+
class CElement
  {
protected:
   //--- Режим автоматического изменения размеров элемента
   bool              m_auto_yresize_mode;
   //---
public:
   //--- (1) Режим авто-изменения высоты элемента
   bool              AutoYResizeMode(void)                     const { return(m_auto_yresize_mode);          }
   void              AutoYResizeMode(const bool flag)                { m_auto_yresize_mode=flag;             }
  };

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

//--- Событие изменения свойств графика
   if(id==CHARTEVENT_CHART_CHANGE)
     {
      //--- Если кнопка отжата
      if(m_clamping_area_mouse==NOT_PRESSED)
        {
         //--- Получим размеры окна графика
         SetWindowProperties();
         //--- Корректировка координат
         UpdateWindowXY(m_x,m_y);
        }
      //--- Изменить ширину, если включен режим
      if(CElement::AutoXResizeMode())
         ChangeWindowWidth(m_chart.WidthInPixels()-2);
      //--- Изменить высоту, если включен режим
      if(CElement::AutoYResizeMode())
         ChangeWindowHeight(m_chart.HeightInPixels(m_subwin)-3);
      //---
      return;
     }

 

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

  • CTabs – простые вкладки.
  • CIconTabs – вкладки с картинками.
  • CCanvasTable – нарисованная таблица.
  • CLineGraph – линейный график.

Подобно тому, как ранее в класс CElement был добавлен виртуальный метод CElement::ChangeWidthByRightWindowSide() для изменения ширины элемента, добавим соответствующий метод для изменения высоты. Кроме этого, создадим методы для установки отступа от нижнего края формы, аналогичные ранее добавленным для отступа от правого края формы при изменении её ширины. 

class CElement
  {
protected:
   //--- Отступ от правого/нижнего края формы в режиме авто-изменения ширины/высоты элемента
   int               m_auto_xresize_right_offset;
   int               m_auto_yresize_bottom_offset;
   //---
public:
   //--- Получение/установка отступа от нижнего края формы
   int               AutoYResizeBottomOffset(void)             const { return(m_auto_yresize_bottom_offset); }
   void              AutoYResizeBottomOffset(const int offset)       { m_auto_yresize_bottom_offset=offset;  }
   //---
public:
   //--- Изменить высоту по нижнему краю окна
   virtual void      ChangeHeightByBottomWindowSide(void) {}
  };

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

//+------------------------------------------------------------------+
//| Изменить высоту по нижнему краю окна                             |
//+------------------------------------------------------------------+
void CCanvasTable::ChangeHeightByBottomWindowSide(void)
  {
//--- Выйти, если включен режим фиксации к нижнему краю формы  
   if(m_anchor_bottom_window_side)
     return;
//--- Координаты
   int y=0;
//--- Размеры
   int x_size=(m_auto_xresize_mode)? m_wnd.X2()-m_area.X()-m_auto_xresize_right_offset : m_x_size;
   int y_size=m_wnd.Y2()-m_area.Y()-m_auto_yresize_bottom_offset;
//--- Выйти, если размер меньше указанного
   if(y_size<60)
     return;
//--- Установить новый размер фона таблицы
   ChangeMainSize(x_size,y_size);
//--- Рассчитать размеры таблицы
   CalculateTableSize();
//--- Проверка наличия полос прокрутки
   bool is_scrollh=!(m_table_visible_x_size>=m_table_x_size);
   bool is_scrollv=!(m_table_visible_y_size>=m_table_y_size);
//--- Отступ относительно наличия полос прокрутки
   int offset=(is_scrollh || (!is_scrollh && !is_scrollv))? 0 : 2;
//--- Рассчитать и установить новую координату для горизонтальной полосы прокрутки
   y=m_area.Y2()-m_scrollh.ScrollWidth();
   m_scrollh.YDistance(y);
//--- Инициализация горизонтальной полосы прокрутки под новый размер
   m_scrollh.Reinit(m_table_x_size,m_table_visible_x_size);
//--- Рассчитать и изменить высоту вертикальной полосы прокрутки
   m_scrollv.Reinit(m_table_y_size,m_table_visible_y_size-offset);
   m_scrollv.ChangeYSize((is_scrollh)? m_table_visible_y_size+2 : m_table_visible_y_size);
//--- Если вертикальная полоса прокрутки не нужна
   if(!is_scrollv)
     {
      //--- Скрыть вертикальную полосу прокрутки
      m_scrollv.Hide();
      //--- Изменить ширину горизонтальной полосы прокрутки
      m_scrollh.ChangeXSize(m_area.XSize());
     }
   else
     {
      //--- Показать вертикальную полосу прокрутки
      if(CElement::IsVisible())
         m_scrollv.Show();
      //--- Рассчитать и изменить ширину горизонтальной полосы прокрутки
      m_scrollh.ChangeXSize(m_area.XSize()-m_scrollv.ScrollWidth()+1);
     }
//--- Управление видимостью горизонтальной полосы прокрутки
   if(CElement::IsVisible())
     {
      if(!is_scrollh) m_scrollh.Hide();
      else m_scrollh.Show();
     }
//--- Установить новый размер таблице
   ChangeTableSize(m_table_x_size,m_table_y_size,m_table_visible_x_size,m_table_visible_y_size-offset);
//--- Нарисуем таблицу
   DrawTable();
//--- Обновить положение объектов
   Moving(m_wnd.X(),m_wnd.Y());
  }

Для того, чтобы всё это заработало, в классе CWndEvents тоже нужно внести некоторые изменения и дополнения. Так как теперь события изменения размеров (ширина и высота) генерируются с уникальными идентификаторами, то для их обработки нужны два отдельных метода. 

//+------------------------------------------------------------------+
//| Класс для обработки событий                                      |
//+------------------------------------------------------------------+
class CWndEvents : public CWndContainer
  {
private:
   //--- Обработка изменения размеров окна
   bool              OnWindowChangeXSize(void);
   bool              OnWindowChangeYSize(void);
  };
//+------------------------------------------------------------------+
//| Событие ON_WINDOW_CHANGE_XSIZE                                   |
//+------------------------------------------------------------------+
bool CWndEvents::OnWindowChangeXSize(void)
  {
//--- Если сигнал "Изменить размер элементов"
   if(m_id!=CHARTEVENT_CUSTOM+ON_WINDOW_CHANGE_XSIZE)
      return(false);
//--- Индекс активного окна
   int awi=m_active_window_index;
//--- Если идентификаторы окна совпадают
   if(m_lparam!=m_windows[awi].Id())
      return(true);
//--- Изменить ширину всех элементов кроме формы
   int elements_total=CWndContainer::ElementsTotal(awi);
   for(int e=0; e<elements_total; e++)
     {
      //--- Если окно, то перейти к следующему
      if(m_wnd[awi].m_elements[e].ClassName()=="CWindow")
         continue;
      //--- Если включен режим, то подогнать ширину
      if(m_wnd[awi].m_elements[e].AutoXResizeMode())
         m_wnd[awi].m_elements[e].ChangeWidthByRightWindowSide();
     }
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Событие ON_WINDOW_CHANGE_YSIZE                                   |
//+------------------------------------------------------------------+
bool CWndEvents::OnWindowChangeYSize(void)
  {
//--- Если сигнал "Изменить размер элементов"
   if(m_id!=CHARTEVENT_CUSTOM+ON_WINDOW_CHANGE_YSIZE)
      return(false);
//--- Индекс активного окна
   int awi=m_active_window_index;
//--- Если идентификаторы окна совпадают
   if(m_lparam!=m_windows[awi].Id())
      return(true);
//--- Изменить ширину всех элементов кроме формы
   int elements_total=CWndContainer::ElementsTotal(awi);
   for(int e=0; e<elements_total; e++)
     {
      //--- Если окно, то перейти к следующему
      if(m_wnd[awi].m_elements[e].ClassName()=="CWindow")
         continue;
      //--- Если включен режим, то подогнать высоту
      if(m_wnd[awi].m_elements[e].AutoYResizeMode())
         m_wnd[awi].m_elements[e].ChangeHeightByBottomWindowSide();
     }
//---
   return(true);
  }

Вызов методов CWndEvents::OnWindowChangeXSize() и CWndEvents::OnWindowChangeYSize() осуществляется в общем методе CWndEvents::ChartEventCustom() для обработки пользовательских событий. В листинге ниже показана сокращённая версия этого метода. 

//+------------------------------------------------------------------+
//| Событие CHARTEVENT_CUSTOM                                        |
//+------------------------------------------------------------------+
void CWndEvents::ChartEventCustom(void)
  {
//--- Если сигнал свернуть форму
//--- Если сигнал развернуть форму

//--- Если сигнал изменить размер элементов по оси X
   if(OnWindowChangeXSize())
      return;
//--- Если сигнал изменить размер элементов по оси Y
   if(OnWindowChangeYSize())
      return;

//--- Если сигнал на скрытие контекстных меню от пункта инициатора
//--- Если сигнал на скрытие всех контекстных меню

//--- Если сигнал на открытие диалогового окна
//--- Если сигнал на закрытие диалогового окна
//--- Если сигнал на сброс цвета элементов на указанной форме
//--- Если сигнал на сброс приоритетов на нажатие левой кнопкой мыши
//--- Если сигнал на восстановление приоритетов на нажатие левой кнопкой мыши
  }

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

На скриншотах ниже показан пример с графическим интерфейсом MQL-приложения, который создан в главном окне графика. Для окна приложения (CWindow) установлены режимы автоматического изменения ширины и высоты по размеру окна графика. Для элементов «Главное меню» (CMenuBar) и «Строка состояния» (CStatusBar) включен режим автоматического изменения ширины по ширине окна MQL-приложения. Для элементов «Вкладки» (CTabs) и «Нарисованная таблица» (CCanvasTable) установлены режимы автоматического изменения ширины и высоты по размеру формы и указаны отступы от нижнего края MQL-приложения.

Рис. 1. Минимальные размеры окна терминала. Графический интерфейс MQL-приложения с включенными режимами автоизменения размеров. 

Рис. 1. Минимальные размеры окна терминала. Графический интерфейс MQL-приложения с включенными режимами автоизменения размеров.


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

Рис. 2. При изменении размеров окна терминала, размеры графического интерфейса MQL-приложения тоже изменяются. 

Рис. 2. При изменении размеров окна терминала, размеры графического интерфейса MQL-приложения тоже изменяются.


4. Очень часто при проектировании графического интерфейса, в котором размеры формы могут изменяться, может понадобится, чтобы элементы позиционировались в правой или нижней части окна приложения. До этого была возможность просто указать координаты для элемента относительно левой верхней точки формы, к которой он прикрепляется. Теперь на выбор будет четыре варианта для позиционирования элемента относительно формы:

  • Слева и сверху.
  • Сверху и справа.
  • Справа и снизу.
  • Снизу и слева.

Для реализации задуманного добавим в базовый класс элементов (CElement) два метода для установки/получения режимов позиционирования элемента в правой и нижней части формы: 

class CElement
  {
protected:
   //--- Точки привязки элемента в правой и нижней стороне окна
   bool              m_anchor_right_window_side;
   bool              m_anchor_bottom_window_side;
   //---
public:
   //--- Режим (получение/установка) привязки элемента к (1) правому и (2) нижнему краю окна
   bool              AnchorRightWindowSide(void)               const { return(m_anchor_right_window_side);   }
   void              AnchorRightWindowSide(const bool flag)          { m_anchor_right_window_side=flag;      }
   bool              AnchorBottomWindowSide(void)              const { return(m_anchor_bottom_window_side);  }
   void              AnchorBottomWindowSide(const bool flag)         { m_anchor_bottom_window_side=flag;     }
  };

Чтобы всё работало в соответствии с этими режимами, во всех классах элементов библиотеки внесены изменения. В качестве примера приведём код метода создания текстовой метки для элемента «Чекбокс» (CCheckBox). Обратите внимание на строки, где рассчитываются координаты и отступы для графического объекта. 

//+------------------------------------------------------------------+
//| Создаёт текстовую метку чекбокса                                 |
//+------------------------------------------------------------------+
bool CCheckBox::CreateLabel(void)
  {
//--- Формирование имени объекта
   string name=CElement::ProgramName()+"_checkbox_lable_"+(string)CElement::Id();
//--- Координаты
   int x =(m_anchor_right_window_side)? m_x-m_label_x_gap : m_x+m_label_x_gap;
   int y =(m_anchor_bottom_window_side)? m_y-m_label_y_gap : m_y+m_label_y_gap;
//--- Цвет текста относительно состояния
   color label_color=(m_check_button_state) ? m_label_color : m_label_color_off;
//--- Установим объект
   if(!m_label.Create(m_chart_id,name,m_subwin,x,y))
      return(false);
//--- Установим свойства
   m_label.Description(m_label_text);
   m_label.Font(FONT);
   m_label.FontSize(FONT_SIZE);
   m_label.Color(label_color);
   m_label.Corner(m_corner);
   m_label.Anchor(m_anchor);
   m_label.Selectable(false);
   m_label.Z_Order(m_zorder);
   m_label.Tooltip("\n");
//--- Отступы от крайней точки
   m_label.XGap((m_anchor_right_window_side)? x : x-m_wnd.X());
   m_label.YGap((m_anchor_bottom_window_side)? y : y-m_wnd.Y());
//--- Инициализация массива градиента
   CElement::InitColorArray(label_color,m_label_color_hover,m_label_color_array);
//--- Сохраним указатель объекта
   CElement::AddToArray(m_label);
   return(true);
  }

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

//+------------------------------------------------------------------+
//| Перемещение элементов                                            |
//+------------------------------------------------------------------+
void CCheckBox::Moving(const int x,const int y)
  {
//--- Выйти, если элемент скрыт
   if(!CElement::IsVisible())
      return;
//--- Если привязка справа
   if(m_anchor_right_window_side)
     {
      //--- Сохранение координат в полях элемента
      CElement::X(m_wnd.X2()-XGap());
      //--- Сохранение координат в полях объектов
      m_area.X(m_wnd.X2()-m_area.XGap());
      m_check.X(m_wnd.X2()-m_check.XGap());
      m_label.X(m_wnd.X2()-m_label.XGap());
     }
   else
     {
      //--- Сохранение координат в полях объектов
      CElement::X(x+XGap());
      //--- Сохранение координат в полях объектов
      m_area.X(x+m_area.XGap());
      m_check.X(x+m_check.XGap());
      m_label.X(x+m_label.XGap());
     }
//--- Если привязка снизу
   if(m_anchor_bottom_window_side)
     {
      //--- Сохранение координат в полях элемента
      CElement::Y(m_wnd.Y2()-YGap());
      //--- Сохранение координат в полях объектов
      m_area.Y(m_wnd.Y2()-m_area.YGap());
      m_check.Y(m_wnd.Y2()-m_check.YGap());
      m_label.Y(m_wnd.Y2()-m_label.YGap());
     }
   else
     {
      //--- Сохранение координат в полях объектов
      CElement::Y(y+YGap());
      //--- Сохранение координат в полях объектов
      m_area.Y(y+m_area.YGap());
      m_check.Y(y+m_check.YGap());
      m_label.Y(y+m_label.YGap());
     }
//--- Обновление координат графических объектов
   m_area.X_Distance(m_area.X());
   m_area.Y_Distance(m_area.Y());
   m_check.X_Distance(m_check.X());
   m_check.Y_Distance(m_check.Y());
   m_label.X_Distance(m_label.X());
   m_label.Y_Distance(m_label.Y());
  }

Для наглядности на рисунке ниже схематично показаны все возможные сочетания режимов позиционирования и автоматического изменения размеров элемента. Это абстрактный пример, в котором в качестве формы (см. девятый столбец «Result») выступает белый прямоугольник с чёрной жирной рамкой и размером 400 x 400 пикселей, а в качестве элемента — серый прямоугольник размером 200 x 200 пикселей. Для каждого варианта также показаны относительные координаты и размер элемента. Прочерки означают, что в этих случаях размер указывать необязательно (если включен режим автоизменения размера). 

Рис. 3. Таблица с перечислением различных вариантов в сочетании с позиционированием элемента и автоизменением его размеров. 

Рис. 3. Таблица с перечислением различных вариантов в сочетании с позиционированием элемента и автоизменением его размеров.


5. Добавлен идентификатор события ON_CLICK_TAB для генерации события переключения вкладок в классах CTabs и CIconTabs

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
...
#define ON_CLICK_TAB               (27) // Переключение вкладки

Событие с идентификатором ON_CLICK_TAB теперь можно отслеживать в обработчике пользовательского класса, что даёт ещё больше возможностей по управлению внешним видом графического интерфейса. 


6. В методах Show() всех элементов добавлено принудительное обновление координат. Для примера в листинге ниже показан этот метод класса CSimpleButton (выделенная жёлтым маркером строка):

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

В некоторых случаях при активном использовании графического интерфейса MQL-приложения можно было столкнуться с тем, что некоторые его элементы оказывались не на своём месте. Теперь эта проблема устранена.


7. В классе CWindow добавлены методы для получения указателей на кнопки: 

//+------------------------------------------------------------------+
//| Класс создания формы для элементов управления                    |
//+------------------------------------------------------------------+
class CWindow : public CElement
  {
public:
   //--- Возвращает указатели на кнопки формы
   CBmpLabel        *GetCloseButtonPointer(void)                             { return(::GetPointer(m_button_close));   }
   CBmpLabel        *GetRollUpButtonPointer(void)                            { return(::GetPointer(m_button_unroll));  }
   CBmpLabel        *GetUnrollButtonPointer(void)                            { return(::GetPointer(m_button_rollup));  }
   CBmpLabel        *GetTooltipButtonPointer(void)                           { return(::GetPointer(m_button_tooltip)); }
  };

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


8. Исправление в классе CTable. Добавлена проверка в главный метод создания элемента (см. листинг кода ниже). Видимая часть не должна быть больше общей. Теперь, если пользователь ошибётся при установке свойств таблицы, то значения будут исправлены автоматически. 

//+------------------------------------------------------------------+
//| Создаёт таблицу из полей ввода                                   |
//+------------------------------------------------------------------+
bool CTable::CreateTable(const long chart_id,const int subwin,const int x,const int y)
  {
//--- Выйти, если нет указателя на форму
   if(!CElement::CheckWindowPointer(::CheckPointer(m_wnd)))
      return(false);
//--- Видимая часть не должна быть больше общей
   m_visible_rows_total    =(m_visible_rows_total>m_rows_total)? m_rows_total : m_visible_rows_total;
   m_visible_columns_total =(m_visible_columns_total>m_columns_total)? m_columns_total : m_visible_columns_total;
//--- Инициализация переменных
   m_id       =m_wnd.LastId()+1;
   m_chart_id =chart_id;
   m_subwin   =subwin;
   m_x        =x;
   m_y        =y;
   m_x_size   =(m_x_size<1 || m_auto_xresize_mode)? (m_anchor_right_window_side)? m_wnd.X2()-m_x+m_x_size-(m_wnd.X2()-m_x)+1-m_auto_xresize_right_offset : m_wnd.X2()-m_x-m_auto_xresize_right_offset : m_x_size;
   m_y_size   =m_row_y_size*m_visible_rows_total-(m_visible_rows_total-1)+2;
//--- Отступы от крайней точки
   CElement::XGap((m_anchor_right_window_side)? m_x : m_x-m_wnd.X());
   CElement::YGap((m_anchor_bottom_window_side)? m_y : m_y-m_wnd.Y());
//--- Создание таблицы
   if(!CreateArea())
      return(false);
   if(!CreateCells())
      return(false);
   if(!CreateScrollV())
      return(false);
   if(!CreateScrollH())
      return(false);
//--- Скрыть элемент, если окно диалоговое или оно минимизировано
   if(m_wnd.WindowType()==W_DIALOG || m_wnd.IsMinimized())
      Hide();
//---
   return(true);
  }


 

Приложение для теста обновлений

Для теста немного изменим MQL-приложение из предыдущей статьи, чтобы можно было продемонстрировать всё, что было представлено в этой статье. Сделаем эксперта в подокне индикатора “SubWindow”. Размеры главного окна графического интерфейса будут автоматически подстраиваться под размеры подокна. Высота подокна будет свободна для изменения размера вручную. Для этого при вызове метода CWindow::RollUpSubwindowMode() нужно передать значения false (выделено зелёным маркером в листинге ниже). 

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

//+------------------------------------------------------------------+
//| Создаёт форму для элементов управления                           |
//+------------------------------------------------------------------+
bool CProgram::CreateWindow(const string caption_text)
  {
//--- Добавим указатель окна в массив окон
   CWndContainer::AddWindow(m_window);
//--- Координаты
   int x=1;
   int y=1;
//--- Свойства
   m_window.Movable(false);
   m_window.UseRollButton();
   m_window.AutoXResizeMode(true);
   m_window.AutoYResizeMode(true);
   m_window.RollUpSubwindowMode(false,false);
//--- Создание формы
   if(!m_window.CreateWindow(m_chart_id,m_subwin,caption_text,x,y))
      return(false);
//--- Установим всплывающие подсказки
   m_window.GetCloseButtonPointer().Tooltip("Close program");
   m_window.GetUnrollButtonPointer().Tooltip("Unroll");
   m_window.GetRollUpButtonPointer().Tooltip("Roll up");
   return(true);
  }

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

 Рис. 4. Элементы первой вкладки привязаны к правому краю формы.

Рис. 4. Элементы первой вкладки привязаны к правому краю формы.


В листинге ниже показан пример кода метода создания для элемента «Простая кнопка» (CSimpleButton). Для того, чтобы привязать элемент к правому краю, достаточно вызвать метод AnchorRightWindowSide(), передав в него значение true

//+------------------------------------------------------------------+
//| Создаёт простую кнопку 1                                         |
//+------------------------------------------------------------------+
bool CProgram::CreateSimpleButton1(const int x_gap,const int y_gap,string button_text)
  {
//--- Сохраним указатель на окно
   m_simple_button1.WindowPointer(m_window);
//--- Закрепить за 1-ой вкладкой
   m_tabs.AddToElementsArray(0,m_simple_button1);
//--- Координаты
   int x=m_window.X()+x_gap;
   int y=m_window.Y()+y_gap;
//--- Установим свойства перед созданием
   m_simple_button1.ButtonXSize(140);
   m_simple_button1.BackColor(C'255,140,140');
   m_simple_button1.BackColorHover(C'255,180,180');
   m_simple_button1.BackColorPressed(C'255,120,120');
   m_simple_button1.AnchorRightWindowSide(true);
//--- Создание кнопки
   if(!m_simple_button1.CreateSimpleButton(m_chart_id,m_subwin,button_text,x,y))
      return(false);
//--- Добавим указатель на элемент в базу
   CWndContainer::AddToElementsArray(0,m_simple_button1);
   return(true);
  }

За второй вкладкой закрепим только нарисованную таблицу (CCanvasTable), которая при изменении ширины и высоты подокна будет подстраиваться под размеры формы.

 Рис. 5. Нарисованная таблица подстраивающаяся под размеры формы.

Рис. 5. Нарисованная таблица, подстраивающаяся под размеры формы.


Для того, чтобы всё это именно так работало, в методе создания элемента в пользовательском классе с помощью методов AutoXResizeMode() и AutoYResizeMode() нужно включить режимы для автоизменения размеров по горизонтали и вертикали. С помощью методов AutoXResizeRightOffset() и AutoYResizeBottomOffset() можно настроить отступы правой и нижней границ элемента от правого и нижнего краёв формы. В данном случае от правого края задан отступ в 1 пиксель, а от нижнего края — 25 пикселей (см. листинг кода ниже). 

//+------------------------------------------------------------------+
//| Создаёт нарисованную таблицу                                     |
//+------------------------------------------------------------------+
bool CProgram::CreateCanvasTable(const int x_gap,const int y_gap)
  {
#define COLUMNS3_TOTAL 15
#define ROWS3_TOTAL    30
//--- Сохраним указатель на форму
   m_canvas_table.WindowPointer(m_window);
//--- Закрепить за 2-ой вкладкой
   m_tabs.AddToElementsArray(1,m_canvas_table);
//--- Координаты
   int x=m_window.X()+x_gap;
   int y=m_window.Y()+y_gap;
//--- Массив ширины столбцов
   int width[COLUMNS3_TOTAL];
   ::ArrayInitialize(width,70);
   width[0]=100;
   width[1]=90;
//--- Массив выравнивания текста в столбцах
   ENUM_ALIGN_MODE align[COLUMNS3_TOTAL];
   ::ArrayInitialize(align,ALIGN_CENTER);
   align[0]=ALIGN_RIGHT;
   align[1]=ALIGN_RIGHT;
   align[2]=ALIGN_LEFT;
//--- Установим свойства перед созданием
   m_canvas_table.XSize(400);
   m_canvas_table.YSize(200);
   m_canvas_table.TableSize(COLUMNS3_TOTAL,ROWS3_TOTAL);
   m_canvas_table.TextAlign(align);
   m_canvas_table.ColumnsWidth(width);
   m_canvas_table.GridColor(clrLightGray);
   m_canvas_table.AutoXResizeMode(true);
   m_canvas_table.AutoYResizeMode(true);
   m_canvas_table.AutoXResizeRightOffset(1);
   m_canvas_table.AutoYResizeBottomOffset(25);
//--- Заполним таблицу данными
   for(int c=0; c<COLUMNS3_TOTAL; c++)
      for(int r=0; r<ROWS3_TOTAL; r++)
         m_canvas_table.SetValue(c,r,string(c)+":"+string(r));
//--- Создадим элемент управления
   if(!m_canvas_table.CreateTable(m_chart_id,m_subwin,x,y))
      return(false);
//--- Добавим объект в общий массив групп объектов
   CWndContainer::AddToElementsArray(0,m_canvas_table);
   return(true);
  }

На третью вкладку поместим линейный график (CLineGraph), размеры которого также будут подстраиваться под размеры формы:

Рис. 6. Элемент «Линейный график» подстраивающийся под размеры формы. 

Рис. 6. Элемент «Линейный график» подстраивающийся под размеры формы.


На четвёртой и пятой вкладках для демонстрации показана привязка к правому краю многих других элементов библиотеки:

Рис. 7. Элементы на четвёртой вкладке. 

Рис. 7. Элементы на четвёртой вкладке.


Рис. 8. Элементы на пятой вкладке. 

Рис. 8. Элементы на пятой вкладке.


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

 


Заключение

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

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

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


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

Ниже вы можете загрузить к себе на компьютер третью версию библиотеки Easy And Fast. Если у Вас возникают вопросы по использованию материала, предоставленного в этих файлах, то Вы можете обратиться к подробному описанию этой серии статей или задать вопрос в комментариях к статье. 


Прикрепленные файлы |
Pavel Kolchin
Pavel Kolchin | 12 окт 2016 в 14:29
Anatoli Kazharski:

Будет ли мини мануал как пользоваться текущей версией библиотеки без изучения всех предыдущих статей?

Anatoli Kazharski
Anatoli Kazharski | 12 окт 2016 в 14:54
Pavel Kolchin:

Будет ли мини мануал как пользоваться текущей версией библиотеки без изучения всех предыдущих статей?

Да, но только после того, как будет сформирована основная часть библиотеки и весь необходимый функционал. 
Artyom Trishkin
Artyom Trishkin | 12 окт 2016 в 20:23
Anatoli Kazharski:
Да, но только после того, как будет сформирована основная часть библиотеки и весь необходимый функционал. 

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


Имеем кнопку с привязанной к ней подсказкой.

После нажатия на кнопку, она программно делается ButtonState(false)

и вот после этого всплывающая подсказка, которая привязана к этой кнопке, постоянно находится на графике.

Хотел в обработчик OnEvent() Tooltip.mqh внести отслеживания активности элемента, но у элементов нет метода для получения состояния - заблокирован/активен. Есть только IsVisible() и IsDropdown().

Хотел по аналогии с этой проверкой

//--- Выйти, если элемент скрыт
      if(!CElement::IsVisible())
         return;

сделать что-то вроде такого:

//--- Скрыть подсказку и выйти, если элемент заблокирован
      if(!CElement::IsState())
         //--- Скрыть подсказку и выйти
         FadeOutTooltip();
         return;

но увы... Может есть возможность сделать проверку состояния активен/заблокирован для элементов? Может уже есть, да я не туда гляжу?

Впрочем, такое состояние может быть не у каждого элемента... Или у каждого?
Скажешь что-нибудь?
Anatoli Kazharski
Anatoli Kazharski | 12 окт 2016 в 20:40
Artyom Trishkin:

...


Скажешь что-нибудь?

Не могу пока ничего подсказать, так как занимаюсь другими задачами.

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

Artyom Trishkin
Artyom Trishkin | 12 окт 2016 в 20:59
Anatoli Kazharski:

Не могу пока ничего подсказать, так как занимаюсь другими задачами.

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

Хорошо, спасибо.
LifeHack для трейдера: "Тихая" оптимизация или Строим распределения трейдов LifeHack для трейдера: "Тихая" оптимизация или Строим распределения трейдов

Анализ торговой истории и построение HTML графиков распределения результатов торговли в зависимости от времени входа в позицию. Графики отображаются в трех разрезах – по часам, дням недели и месяцам.

Сравнение MQL5 и QLUA - почему торговые операции в MQL5 до 28 раз быстрее? Сравнение MQL5 и QLUA - почему торговые операции в MQL5 до 28 раз быстрее?

Многие трейдеры зачастую не задумываются над тем, как быстро доходит их заявка до биржи, как долго она там исполняется, и когда наконец-то торговый терминал трейдера узнает о результате торговой операции. Мы обещали дать сравнение скорости торговых операций, ведь никто до нас не делал таких замеров с помощью программ на MQL5 и QLUA.

Быстрая оценка сигнала: торговая активность, графики просадки/загрузки и распределения MFE/MAE Быстрая оценка сигнала: торговая активность, графики просадки/загрузки и распределения MFE/MAE

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

Кроссплатформенный торговый советник: Ордера Кроссплатформенный торговый советник: Ордера

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