English 中文 Español Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Создай свои графические панели на MQL5

Создай свои графические панели на MQL5

MetaTrader 5Примеры | 11 января 2012, 14:22
9 423 27
MetaQuotes
MetaQuotes

Введение

В составе Стандартной Библиотеки появился новый набор классов. Эти классы предназначены для самостоятельного создания диалогов управления и панелей индикации в составе MQL5-программ.

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

Разработанные классы позволяют следующие варианты их использования:
  1. Панель индикации в отдельном подокне чарта;
  2. Панель управления экспертом;
  3. Панель управления пользовательским индикатором.

В данной статье мы покажем как легко и просто можно создавать собственные панели индикации в отдельном подокне чарта на основе классов Стандартной Библиотеки.


Что нам предлагает Стандартная Библиотека

Стандартная Библиотека предоставляет разработчикам следующие готовые элементы управления:

1. Простые элементы управления:

Элемент управления Применение Реализация на основе встроенного объекта Файл Стандартной Библиотеки

Кнопка с текстом

обеспечение взаимодействия "мышки" с MQL-программой

"Кнопка"  <Controls\Button.mqh>

Кнопка с картинкой

обеспечение взаимодействия "мышки" с MQL-программой

"Графическая метка"  <Controls\BmpButton.mqh>

Поле ввода

ввод или отображение (в режиме "Только чтение") текстовой информации

"Поле ввода"  <Controls\Edit.mqh>

Надпись

отображение вспомогательной текстовой информации

"Текстовая метка"  <Controls\Label.mqh>

Панель

вспомогательный элемент (визуальное группирование элементов управления)

"Прямоугольная метка"  <Controls\Panel.mqh>

Картинка

декоративный элемент

 "Графическая метка"  <Controls\Picture.mqh>



2. Комбинированные элементы управления:


Элемент управления Применение Реализация на основе элементов Файл Стандартной Библиотеки

Список

просмотр списка

"Прямоугольник", "Кнопка с картинкой" и "Поле ввода"  <Controls\List.mqh>

Поле с выпадающим списком

выбор из выпадающего списка

"Поле ввода", "Кнопка с картинкой" и "Список"  <Controls\ComboBox.mqh>

Поле инкремента/декремента

перебор значений

"Поле ввода" и "Кнопка с картинкой"  <Controls\SpinEdit.mqh>

Радиокнопка

переключатель "Кнопка с картинкой" и "Надпись"  <Controls\RadioButton.mqh>
 Группа радиокнопок  редактирование поля типа enum  "Прямоугольник" и "Радиокнопка"  <Controls\RadioGroup.mqh>

Чекбокс

опция выбора

"Кнопка с картинкой" и "Надпись"  <Controls\CheckBox.mqh>

Группа чекбоксов

редактирование набора флагов

 "Прямоугольник" и "Чекбокс"  <Controls\CheckGroup.mqh>
 Диалог  форма диалога  "Прямоугольник", "Кнопка с картинкой" и "Поле ввода"  <Controls\Dialog.mqh>



Создаём панель индикации

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

  • в числовом виде,
  • в виде текста,
  • в виде цвета
  • и так далее.

Мы с вами детально пройдем все необходимые шаги и создадим графическую панель следующего вида:



Для создания панели-индикатора нам понадобятся два файла:

  1. Включаемый файл с описанием класса индикаторной панели.
  2. Файл с исходным кодом индикатора.

Заготовки для этих файлов можно получить при помощи Мастера MQL5. Создадим в общей папке индикаторов (MQL5\Indicators) отдельную папку MyIndicators, а в ней вложенную подпапку MyPanel. Процесс создания папок мы здесь рассматривать не будем, он хорошо описан в справке.


Описание класса

Итак, рабочую папку мы уже создали. Теперь найдём её в окне "Навигатор" и щёлкнем по ней правой кнопкой мышки. В появившемся меню выберем пункт "Новый файл". Из предложенных Мастером MQL5 вариантов продолжения выберем "Новый класс", нажмём кнопку "Далее >". Заполняем диалог описания класса, как показано на рисунке:



Жмём кнопку "Готово". В результате наших действий мы получили следующий код:

//+------------------------------------------------------------------+
//|                                                  PanelDialog.mqh |
//|                        Copyright 2011, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CPanelDialog : public CAppDialog
  {
private:

public:
                     CPanelDialog();
                    ~CPanelDialog();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CPanelDialog::CPanelDialog()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CPanelDialog::~CPanelDialog()
  {
  }
//+------------------------------------------------------------------+


Добавим включаемый файл <Controls\Dialog.mqh> из состава Стандартной Библиотеки с описанием базового класса CAppDialog и комментарии.

//+------------------------------------------------------------------+
//|                                                  PanelDialog.mqh |
//|                        Copyright 2011, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#include <Controls\Dialog.mqh>
//+------------------------------------------------------------------+
//| Класс CPanelDialog                                               |
//| Назначение: главный диалог приложения                            |
//+------------------------------------------------------------------+
class CPanelDialog : public CAppDialog
  {
private:

public:
                     CPanelDialog(void);
                    ~CPanelDialog(void);
  };
//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CPanelDialog::CPanelDialog(void)
  {
  }
//+------------------------------------------------------------------+
//| Деструктор                                                       |
//+------------------------------------------------------------------+
CPanelDialog::~CPanelDialog(void)
  {
  }
//+------------------------------------------------------------------+

Мы получили описание класса, который позволит нам использовать в своём индикаторе диалоговое окно. Наш диалог пока пустой, позже мы наполним его элементами управления. А сейчас займёмся индикатором.


Исходный код индикатора

Для создания индикатора также воспользуемся Мастером MQL5. Наши действия будут аналогичны тем, которые мы произвели при создании описателя класса. Единственное отличие - из предложенных Мастером MQL5 вариантов продолжения выберем "Пользовательский индикатор". Для создания индикатора нужно пройти три диалога.

В первом диалоге нужно ввести имя индикатора:



Во втором диалоге нужно установить галочку "OnChartEvent" (обязательно) и галочку "OnTimer":



В третьем диалоге нужно установить галочку "Индикатор в отдельном окне" (обязательно):



Нажимаем кнопку "Готово". Получаем код:

//+------------------------------------------------------------------+
//|                                               PanelIndicator.mq5 |
//|                        Copyright 2011, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,

                const double &price[])
  {
//---
   
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
//---
   
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//---
   
  }
//+------------------------------------------------------------------+


Теперь в нашу "заготовку" добавляем:

  • недостающие описания свойств индикатора;
  • включаемый файл с описанием класса нашего диалога;
  • глобальную переменную - объект класса нашего диалога;
  • код создания диалога, запуска приложения и создания таймера в тело функции OnInit();
  • функцию OnDeinit(), а в ней размещаем код уничтожения диалога и таймера;
  • в код вызова обработчика событий чарта функцию OnChartEvent(...);
  • комментарии.

Получили готовый к использованию индикатор:

//+------------------------------------------------------------------+
//|                                               PanelIndicator.mq5 |
//|                        Copyright 2011, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_plots               0
#property indicator_buffers             0
#property indicator_minimum             0.0
#property indicator_maximum             0.0
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "PanelDialog.mqh"
//+------------------------------------------------------------------+
//| Глобальные переменные                                            |
//+------------------------------------------------------------------+
CPanelDialog ExtDialog;
//+------------------------------------------------------------------+
//| Инициализация                                                    |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- создаём диалог приложения
   if(!ExtDialog.Create(0,"Panel Indicator",0,0,0,0,130))
     return(-1);
//--- запускаем приложение
   if(!ExtDialog.Run())
     return(-2);
//--- создаём таймер
   EventSetTimer(1);
//--- удача
   return(0);
  }
//+------------------------------------------------------------------+
//| Деинициализация                                                  |
//+------------------------------------------------------------------+
int OnDeinit()
  {
//--- удаляем диалог
   ExtDialog.Destroy();
//--- удаляем таймер
   EventKillTimer();
  }
//+------------------------------------------------------------------+
//| Итерация                                                         |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
//---
   
//--- возвращаем значение prev_calculated для следующего вызова
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Обработчик события таймера                                       |
//+------------------------------------------------------------------+
void OnTimer()
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Обработчик события чарта                                         |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- обрабатываем событие
   ExtDialog.ChartEvent(id,lparam,dparam,sparam);
  }
//+------------------------------------------------------------------+


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

Но и пустой диалог уже наделил наш индикатор определёнными свойствами:

  • при создании индикатор подстроил высоту подокна под высоту диалога;
  • ширина диалога всегда равна ширине чарта;
  • индикатор умеет минимизировать и восстанавливать собственное подокно.

Да будет индикация

Для того чтобы наша панель начала отображать какую-либо информацию нам нужно определиться с ответами на три вопроса:

  1. Какую информацию мы хотим отображать?
  2. Какие дополнительные элементы отображения и/или управления для этого необходимо расположить в нашем диалоге?
  3. Как эти дополнительные элементы будут между собой взаимодействовать?

Ещё один немаловажный фактор - наш диалог должен быть красивым и удобным в использовании.  Это не влияет на работоспособность диалога, но показывает нашу заботу о пользователе будущей MQL5-программы.

   Шаг 1. Какую информацию мы хотим отображать?

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

Значения параметров будут задаваться следующим образом:

  • Значением уровня "красного" будет число в диапазоне от 0 до 255, которое будет изменяться случайным образом при каждом возникновении события Calculate;
  • Значением уровня "зелёного" будет число в диапазоне от 0 до 255, которое будет изменяться случайным образом при каждом возникновении события Timer;
  • Значением уровня "синего" будет число в диапазоне от 0 до 255, которое будет изменяться "вручную" при помощи специального элемента управления.

Кстати, значения этих уровней мы тоже отобразим в нашем индикаторе.


   Шаг 2. Какие дополнительные элементы нам понадобятся?

  1. Для отображения цвета мы будем использовать элемент "Панель".
  2. Для отображения уровней "красного" и "зелёного" будем использовать элементы "Поле ввода" в режиме "Только чтение".
  3. Для управления уровнем "синего" будем использовать элемент "Поле (+/-)". Этот же элемент будет отображать значение уровня.
  4. Оба элемента "Поле ввода" и элемент "Поле (+/-)" снабдим поясняющими надписями, для которых будем использовать, соответственно, элементы "Надпись".

Добавим включаемые файлы из состава Стандартной Библиотеки, в описание класса необходимые элементы и переменные для хранения параметров, снабдив их комментариями.

Получим:

//+------------------------------------------------------------------+
//|                                                  PanelDialog.mqh |
//|                        Copyright 2011, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#include <Controls\Dialog.mqh>
#include <Controls\Panel.mqh>
#include <Controls\Edit.mqh>
#include <Controls\Label.mqh>
#include <Controls\SpinEdit.mqh>
//+------------------------------------------------------------------+
//| Класс CPanelDialog                                               |
//| Назначение: главный диалог приложения                            |
//+------------------------------------------------------------------+
class CPanelDialog : public CAppDialog
  {
private:
   //--- дополнительные элементы
   CPanel            m_color;                         // объект для отображения цвета
   CLabel            m_label_red;                     // объект надпись для "красного"
   CEdit             m_field_red;                     // объект индикации значения "красного"
   CLabel            m_label_green;                   // объект надпись для "зелёного"
   CEdit             m_field_green;                   // объект индикации значения "зелёного"
   CLabel            m_label_blue;                    // объект надпись для "синего"
   CSpinEdit         m_edit_blue;                     // объект управления значением "синего"
   //--- значения параметров
   int               m_red;                           // значение "красного"
   int               m_green;                         // значение "зелёного"
   int               m_blue;                          // значение "синего"

public:
                     CPanelDialog(void);
                    ~CPanelDialog(void);
  };
//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CPanelDialog::CPanelDialog(void)
  {
  }
//+------------------------------------------------------------------+
//| Деструктор                                                       |
//+------------------------------------------------------------------+
CPanelDialog::~CPanelDialog(void)
  {
  }
//+------------------------------------------------------------------+


   Шаг 3. Как дополнительные элементы диалога будут между собой взаимодействовать?
Принцип взаимодействия между элементами диалога будет предельно прост: "Изменение любого параметра (уровней "красного", "зелёного" и "синего") должно отображаться на диалоге". Реализацией алгоритмов взаимодействия мы займёмся позже, а сейчас самое время перейти к созданию диалога.


Немного о красоте

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

Использование предопределённых именованных констант даёт следующие преимущества:

  • не нужно запоминать конкретные числовые значения, которые мы будем использовать в том или ином случае. Правильно подобранные имена констант предоставят нам быстрый доступ к ним через "Интеллектуальный подстановщик имён";
  • при последующем изменении значений констант не нужно искать и заменять многочисленные включения числовых значений. Достаточно изменить только описание константы.

Предлагается использовать следующие константы:

//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+
//--- отступы и зазоры
#define INDENT_LEFT                         (11)      // отступ слева  (с учётом толщины рамки)
#define INDENT_TOP                          (11)      // отступ сверху (с учётом толщины рамки)
#define INDENT_RIGHT                        (11)      // отступ справа (с учётом толщины рамки)
#define INDENT_BOTTOM                       (11)      // отступ снизу  (с учётом толщины рамки)
#define CONTROLS_GAP_X                      (10)      // зазор по X
#define CONTROLS_GAP_Y                      (10)      // зазор по Y
//--- для надписей
#define LABEL_WIDTH                         (50)      // размер по X
//--- для едитов
#define EDIT_WIDTH                          (50)      // размер по X
#define EDIT_HEIGHT                         (20)      // размер по Y
//--- для базовых цветов (RGB)
#define BASE_COLOR_MIN                      (0)       // минимальное значение для цветовой компоненты
#define BASE_COLOR_MAX                      (255)     // максимальное значение для цветовой компоненты


Наполняем панель индикации

Ранее мы создали класс индикаторной панели, теперь для того чтобы наполнить требуемой функциональностью, нам нужно совершить следующие действия:

1. Переопределить метод Create(...) родительского класса. Первоначально наш метод будет выглядеть так:

//+------------------------------------------------------------------+
//| Создание                                                         |
//+------------------------------------------------------------------+
bool CPanelDialog::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- вызываем метод родительского класса
   if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2))                           return(false);
//--- здесь нужно создать дополнительные элементы

//--- удача
   return(true);
  }


2. Создать дополнительные элементы.

Тут сделаем небольшое лирическое отступление. Конечно, можно разместить код создания всех дополнительных элементов прямо в теле метода Create(...), но в результате мы рискуем получить большую нечитабельную "портянку".

Поэтому разделим создание на функционально законченные куски-методы:

  • bool CreateColor(void)  - создание цветной панели,
  • bool CreateRed(void)    - создание элемента индикации "Red" с поясняющей надписью,
  • bool CreateGreen(void) - создание элемента индикации "Green" с поясняющей надписью,
  • bool CreateBlue(void)    - создание элемента управления "Blue" с поясняющей надписью.

Эти методы мы последовательно вызовем из Create(...):

//+------------------------------------------------------------------+
//| Создание                                                         |
//+------------------------------------------------------------------+
bool CPanelDialog::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- вызываем метод родительского класса
   if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2))                           return(false);

//--- создаём дополнительные элементы
   if(!CreateColor())                                                               return(false);
   if(!CreateRed())                                                                 return(false);
   if(!CreateGreen())                                                               return(false);
   if(!CreateBlue())                                                                return(false);
//--- удача
   return(true);
  }


Создание элементов

Мы не будем рассматривать создание каждого дополнительного элемента, а рассмотрим подробнее один метод - bool CreateBlue(void).

Он выглядит следующим образом:

//+------------------------------------------------------------------+
//| Создание элемента управления "Blue" с поясняющей надписью        |
//+------------------------------------------------------------------+
bool CPanelDialog::CreateBlue(void)
  {
//--- координаты
   int x1=INDENT_LEFT;
   int y1=INDENT_TOP+2*(EDIT_HEIGHT+CONTROLS_GAP_Y);
   int x2=x1+EDIT_WIDTH;
   int y2=y1+EDIT_HEIGHT;
//--- создаём надпись
   if(!m_label_blue.Create(m_chart_id,m_name+"LabelBlue",m_subwin,x1,y1+1,x2,y2))   return(false);
   if(!m_label_blue.Text("Blue"))                                                   return(false);
   if(!Add(m_label_blue))                                                           return(false);
//--- корректируем координаты
   x1+=LABEL_WIDTH+CONTROLS_GAP_X;
   x2=x1+EDIT_WIDTH;
//--- создаём элемент
   if(!m_edit_blue.Create(m_chart_id,m_name+"Blue",m_subwin,x1,y1,x2,y2))           return(false);
   if(!Add(m_edit_blue))                                                           return(false);
   m_edit_blue.MinValue(BASE_COLOR_MIN);
   m_edit_blue.MaxValue(BASE_COLOR_MAX);
   m_edit_blue.Value(m_blue);
//--- удача
   return(true);
  }

Есть две тонкости:

  1. Элемент создаётся с относительными координатами, т.е. смещение задаётся относительно верхнего левого угла того контейнера (комбинированного элемента), в который элемент добавится после создания.
  2. После создания элемент обязательно должен быть добавлен в какой-либо контейнер при помощи метода Add(...). В нашем случае таким контейнером является диалог.


Изменение параметров

Для изменения цвета "цветной панели" добавим метод void SetColor(void);

Для того чтобы иметь возможность изменять параметры (уровни базовых цветов) извне, добавим три публичных метода:

  • void SetRed(const int value)     - изменяет уровень "красного" и отражает изменение на индикаторе,
  • void SetGreen(const int value)  - изменяет уровень "зелёного" и отражает изменение на индикаторе,
  • void SetBlue(const int value)    - изменяет уровень "синего" и отражает изменение на индикаторе.

Фраза "отражает изменение на индикаторе", означает что новое значение уровня базового цвета отображается в виде числа в соответствующем элементе и "цветная панель" изменяет свой цвет.

Для примера приведем код одного из методов:

//+------------------------------------------------------------------+
//| Установка значения "Red"                                         |
//+------------------------------------------------------------------+
void CPanelDialog::SetRed(const int value)
  {
//--- проверяем
   if(value<0 || value>255) return;
//--- сохраняем
   m_red=value;
//--- устанавливаем
   m_field_red.Text(IntegerToString(value));
//--- устанавливаем цвет панели
   SetColor();
  }

Как мы условились выше:

  • Значение уровня "красного" будет изменяться случайным образом при каждом возникновении события Calculate;
  • Значение уровня "зелёного" будет изменяться случайным образом при каждом возникновении события Timer;

Добавим соответствующий код в исходный текст нашего индикатора:

//+------------------------------------------------------------------+
//|                                               PanelIndicator.mq5 |
//|                        Copyright 2011, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2011, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_plots               0
#property indicator_buffers             0
#property indicator_minimum             0.0
#property indicator_maximum             0.0
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "PanelDialog.mqh"
//+------------------------------------------------------------------+
//| Глобальные переменные                                            |
//+------------------------------------------------------------------+
CPanelDialog ExtDialog;
//+------------------------------------------------------------------+
//| Инициализация                                                    |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- создаём диалог приложения
   if(!ExtDialog.Create(0,"Panel Indicator",0,0,0,0,130))
      return(-1);
//--- запускаем приложение
   if(!ExtDialog.Run())
      return(-2);
//--- создаём таймер
   EventSetTimer(1);
//--- удача
   return(0);
  }
//+------------------------------------------------------------------+
//| Деинициализация                                                  |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- уничтожаем диалог
   ExtDialog.Destroy();
//--- уничтожаем таймер
   EventKillTimer();
  }
//+------------------------------------------------------------------+
//| Итерация                                                         |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
//--- изменяем свойство диалога
   ExtDialog.SetRed(MathRand()%256);
//--- возвращаем значение prev_calculated для следующего вызова
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Обработчик события таймера                                       |
//+------------------------------------------------------------------+
void OnTimer()
  {
//--- изменяем свойство диалога
   ExtDialog.SetGreen(MathRand()%256);
  }
//+------------------------------------------------------------------+
//| Обработчик события чарта                                         |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- обрабатываем событие
   ExtDialog.ChartEvent(id,lparam,dparam,sparam);
  }
//+------------------------------------------------------------------+


Обработка событий

Всё взаимодействие диалога с терминалом и взаимодействие между элементами управления диалога построено на механизме событий. Рассматривать работу этого механизма мы не будем, мы будем его просто использовать.

События условно можно разделить на две группы:

  • "Внутренние" события, которые обрабатываются, минуя очередь событий терминала;
  • "Внешние" события, которые обрабатываются через очередь событий терминала.

Мы будем обрабатывать оба типа событий.

Из "внутренних" событий нам нужно обработать только событие изменения размера диалога. Для этого перегрузим метод OnResize() родительского класса. Наш метод будет простым, так как высота диалога не изменяется, а при изменении ширины диалога нам нужно изменить только ширину цветовой панели:

//+------------------------------------------------------------------+
//| Обработчик изменения размеров                                    |
//+------------------------------------------------------------------+
bool CPanelDialog::OnResize(void)
  {
//--- вызываем метод родительского класса
   if(!CAppDialog::OnResize()) return(false);
//--- измененяем ширину цветовой панели
   m_color.Width(ClientAreaWidth()-(INDENT_RIGHT+LABEL_WIDTH+CONTROLS_GAP_X+EDIT_WIDTH+CONTROLS_GAP_X+INDENT_LEFT));
//--- удача
   return(true);
  }

Перечень "внешних" событий у нас тоже ограничится одним - это будет событие изменения элемента управления уровнем "синего". Требования к обработчику "внешнего" события минимальные: обработчик должен быть методом класса без параметров типа void.

Опишем обработчик этого события:

//+------------------------------------------------------------------+
//| Обработчик события изменения уровня "синего"                     |
//+------------------------------------------------------------------+
void CPanelDialog::OnChangeBlue(void)
  {
//--- сохраняем
   m_blue=m_edit_blue.Value();
//--- устанавливаем цвет панели
   SetColor();
  }

Как видите, ничего сложного нет.

Для того чтобы наш диалог смог обрабатывать "внешние" события нужно перегрузить метод родительского класса:

virtual bool  OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);

Теперь немного о "мистическом". Если вы уже открыли файл PanelDialog.mqh в редакторе, то тела метода OnEvent(...) вы не обнаружите.

Пусть вас это не смущает - всё дело в том, что для описания обработки "внешних" событий создан набор макросов (см. файл Стандартной Библиотеки <Controls\Defines.mqh>).

Наш обработчик событий выглядит следующим образом:

//+------------------------------------------------------------------+
//| Обработка событий                                                |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CPanelDialog)
   ON_EVENT(ON_CHANGE,m_edit_blue,OnChangeBlue)
EVENT_MAP_END(CAppDialog)

Этот непонятный на первый взгляд "псевдокод" выполняет следующие действия:

  • При получении события ON_CHANGE от элемента управления m_edit_blue вызывается метод OnChangeBlue и обработка события завершается (с кодом возврата true);
  • При получении любого другого события управление передаётся в метод родительского класса.

Заключение

В этой статье мы рассмотрели процесс создания панели-индикатора на базе классов Стандартной Библиотеки.

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

Более сложные примеры из стандартной поставки вы можете посмотреть в следующих папках своего терминала:

  • Experts\Examples\Controls\
  • Indicators\Examples\Panels\ChartPanel\
  • Indicators\Examples\Panels\SimplePanel\
Прикрепленные файлы |
panelindicator.mq5 (3.36 KB)
paneldialog.mqh (12.5 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (27)
Evgeniy Scherbina
Evgeniy Scherbina | 11 мар. 2024 в 16:41

Есть у индикатора вот такое свойство:

#property indicator_separate_window

Это свойство нужно, чтобы индикатор был внизу. Ну да, как бы все понятно.

Мне нужно, чтобы была также "диалоговая панель" в основном окне...

И все, приплыли. Из-за вот этого свойства, что бы ты ни лепил, какие бы свойства ни определял, твоя диалоговая панель будет в подокне индикатора. И никогда она не будет в основном окне.

Я три дня сидел разбирался в стандартной библиотеке. Я так и не понял, как можно было создать такое упущение?

Если я не прав, ткните, где я неправильно делаю. Только ради бога не пишите, что нужно указать 0 для свойств окна и подокна.

А пока я создам свою панель, которая будет работать как надо.

Maxim Kuznetsov
Maxim Kuznetsov | 11 мар. 2024 в 17:13
Evgeniy Scherbina #:

Есть у индикатора вот такое свойство:

Это свойство нужно, чтобы индикатор был внизу. Ну да, как бы все понятно.

Мне нужно, чтобы была также "диалоговая панель" в основном окне...

И все, приплыли. Из-за вот этого свойства, что бы ты ни лепил, какие бы свойства ни определял, твоя диалоговая панель будет в подокне индикатора. И никогда она не будет в основном окне.

Я три дня сидел разбирался в стандартной библиотеке. Я так и не понял, как можно было создать такое упущение?

Если я не прав, ткните, где я неправильно делаю. Только ради бога не пишите, что нужно указать 0 для свойств окна и подокна.

А пока я создам свою панель, которая будет работать как надо.

прошерстить код, добавить опцию m_subwin определяющую номер подокна, перехватить события "создание/удаление" подокна чтобы это дело во всём диалоге корректировать если оно не 0, там где ObjectCreate(...) использовать этот m_subwin 

тогда панели и элементы можно создавать в любом окне и подокне

Evgeniy Scherbina
Evgeniy Scherbina | 11 мар. 2024 в 17:17
Maxim Kuznetsov #:

прошерстить код, добавить опцию m_subwin определяющую номер подокна, перехватить события "создание/удаление" подокна чтобы это дело во всём диалоге корректировать если оно не 0, там где ObjectCreate(...) использовать этот m_subwin 

тогда панели и элементы можно создавать в любом окне и подокне

Да да, вот это все добавить, приладить, аллюминиевой проволокой обмотать и скотчем залепить. Нужно простое и правильное решение для всей этой библиотеки. Если такого решения нет, значит, библиотеку нужно переделывать. Я пожалуй сделаю свое диалоговое окно сам.
Stanislav Korotky
Stanislav Korotky | 11 мар. 2024 в 17:29
Evgeniy Scherbina #:
Да да, вот это все добавить, приладить, аллюминиевой проволокой обмотать и скотчем залепить. Нужно простое и правильное решение для всей этой библиотеки. Если такого решения нет, значит, библиотеку нужно переделывать. Я пожалуй сделаю свое диалоговое окно сам.

Это ограничение платформы МетаТрейдер - один индикатор может быть только в главном окне или только в подокне.

Возможные варианты решения и пример одной реализации есть в книге.

Если вкратце - делаете 2 индикатора, и один создаст другой с помощью ChartIndicatorAdd. Они могут обмениваться данными через события, ресуры, буфера и пр.

Evgeniy Scherbina
Evgeniy Scherbina | 11 мар. 2024 в 19:04
Stanislav Korotky #:

Это ограничение платформы МетаТрейдер - один индикатор может быть только в главном окне или только в подокне.

Возможные варианты решения и пример одной реализации есть в книге.

Если вкратце - делаете 2 индикатора, и один создаст другой с помощью ChartIndicatorAdd. Они могут обмениваться данными через события, ресуры, буфера и пр.

Если это слова специалиста, то это именно то, что я хотел услышать. И это значит, что нужно создавать свою панель.

Если это только предположение... то нужно создавать свою панель.

Я жду подсказку, как просто залепить панель в основном окне, если индикатор располагается в подокне.

Простейшие торговые системы с использованием семафорных индикаторов Простейшие торговые системы с использованием семафорных индикаторов
Если разобраться досконально в любой сложной торговой системе, то мы увидим, что в основе её лежит набор простых торговых сигналов. Поэтому начинающему разработчику торговых роботов не стоит сразу же приниматься за написание сложных алгоритмов. В статье приводится пример торговой системы, использующей для осуществления сделок семафорные индикаторы.
Используйте EX5-библиотеки для продвижения своих разработок Используйте EX5-библиотеки для продвижения своих разработок
С помощью сокрытия реализации функций/классов в ex5-файл вы сможете делиться своими ноу-хау алгоритмами с другими программистами, создавать общие проекты и продвигать их в сети. И пока команда MetaQuotes всеми силами приближает возможность прямого наследования классов из ex5‑библиотек, мы реализуем данную возможность уже сейчас.
Создание советников при помощи Expert Advisor Visual Wizard Создание советников при помощи Expert Advisor Visual Wizard
Программа Expert Advisor Visual Wizard предоставляет интуитивно понятную графическую среду с полным набором готовых торговых блоков, которые позволят вам за несколько минут создавать советники. Знания программирования и языка MQL5 не требуется. Подход "click, drag and drop" позволяет вам создавать визуальные представления торговых стратегий и сигналов. Эти торговые диаграммы автоматически анализируются генератором MQL5-кода, который преобразует их в готовые к работе советники. Интерактивная графическая среда упрощает процесс проектирования и избавляет от необходимости написания кода на MQL5.
Объектно-ориентированный подход к построению мультитаймфреймовых и мультивалютных панелей Объектно-ориентированный подход к построению мультитаймфреймовых и мультивалютных панелей
В статье рассказывается об использовании объектно-ориентированного подхода для разработки для создания мультитаймфреймовых и мультивалютных панелей в MetaTrader 5. Основной целью является построение универсальной панели, которая может быть использована для отображения различных типов данных (цены, их изменения, значения индикаторов или текущее состояние условий на покупку и продажу) без изменения кода самой панели.