Скачать MetaTrader 5

Рисование стрелочных индикаторов с использованием класса CCanvas

19 июня 2015, 10:37
Serhii Shevchuk
8
3 948

Содержание

Введение

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

Стрелочные индикаторы

Рис.1. Стрелочные приборы


1. Координаты и привязка

Существует два типа расположения прибора на графике — абсолютное и относительное.

При абсолютном расположении координаты представляют собой дистанции в пикселях по осям X и Y от угла привязки.

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

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

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

Относительное расположение

Рис.2. Относительное расположение стрелочных приборов

Разберем по порядку настройки каждого прибора:

  • Прибор "gg01": относительное расположение отключено. Смещение по горизонтали 40, по вертикали 40.
  • Прибор "gg02": относительное расположение — горизонтальное, опорный объект — "gg01". Смещение от локального начала координат (точка A) по горизонтали 15, по вертикали 0.
  • Прибор "gg03": относительное расположение — вертикальное, опорный объект — "gg01". Смещение от локального начала координат (точка B) по горизонтали 0, по вертикали 15.
  • Прибор "gg04": относительное расположение — вертикальное, опорный объект — "gg02". Смещение от локального начала координат (точка C) по горизонтали 50, по вертикали 15.

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

Тип расположения и координаты устанавливаются при помощи функции GaugeCreate().


2. Элементы стрелочного прибора

Стрелочный прибор состоит из двух графических объектов. Один объект называется слоем шкалы, другой — слоем стрелки. Оба графических объекта имеют одинаковые координаты, слой стрелки расположен поверх слоя шкалы. Имя прибора, которое задают во входных параметрах, служит префиксом для имен обоих объектов. Например, если имя прибора "Gauge01", то слой шкалы получит имя "Gauge01_s", а слой стрелки — "Gauge01_n".

На рисунке 3 показано строение стрелочного прибора.

Рис.3. Строение стрелочного прибора

Рис.3. Строение стрелочного прибора

Слой шкалы содержит:

  • бордюр (1)
  • отметки шкалы (5, 6, 7)
  • подписи отметок шкалы (4)
  • выделенные диапазоны (2, 12)
  • надписи (3, 10, 11)

Надписи различают по назначению:

  • описание прибора (3)
  • единицы измерения (11)
  • текущее значение (10)
  • множитель подписей шкалы (не показан)

Отметки шкалы разделяют на:

  • основные (7)
  • средние (5)
  • малые (6)

Подписи есть только у основных отметок, шаг которых устанавливается числовым значением. Шаг средних отметок рассчитывается исходя из заданного количества средних отметок между соседними основными. Шаг малых отметок рассчитывается исходя из заданного количества малых отметок между соседними средними. Допускается отсутствие малых и средних отметок на шкале.

Слой стрелки содержит:

  • стрелку (8)
  • центр стрелки (9)


2.1. Размеры

На рисунке 3 показаны размеры некоторых элементов стрелочного прибора:

  • d — размер прибора, который соответствует диаметру внешней линии контура прибора
  • b — размер бордюра
  • g — размер пространства между бордюром и элементами шкалы
  • c — размер центра стрелки.

Важно. Единственный размер, который задают в пикселях — это диаметр прибора (на рисунке 3 — размер d). Все остальные размеры элементов и шрифтов задают в условных единицах и их величина рассчитывается как процент от диаметра. Это сделано для простоты масштабирования. Достаточно изменить диаметр, и все остальные размеры будут пропорционально пересчитаны. Коэффициенты для расчета перечислены в разделе "макроподстановки" и могут быть изменены пользователем по желанию.


2.2. Форма корпуса

Форма корпуса стрелочного прибора может быть двух видов: круг и сектор. Форма в виде сектора наиболее удобна для применения в том случае, если величина угла диапазона шкалы составляет менее 180 градусов.

Форма стрелочного прибора

Рис.4. Форма стрелочного прибора

На рисунке 4 показан круглый стрелочный прибор (a) и два стрелочных прибора в форме сектора (b, c). Для установки нужной формы корпуса используется функция GaugeSetCaseParameters().


2.3. Шкала

Самый важный элемент стрелочного прибора. От ее вида зависит удобство восприятия данных оператором. Шкала не должна выглядеть загроможденной, но при этом должна быть достаточно информативной. Выбор крайних значений шкалы, а также шаг основных отметок, требует особого внимания. Функция GaugeSetScaleParameters() позволяет настроить диапазон шкалы, ее поворот, и значения крайних точек (минимум и максимум). Минимум может быть как слева (прямой порядок), так и справа (обратный порядок).

Диапазон шкалы — это угол, образованный двумя радиус-векторами крайних точек шкалы. Пример показан на рисунке 5.

Диапазон шкалы

Рис.5. Диапазон шкалы

Поворот шкалы — это угол отклонения биссектрисы угла диапазона шкалы от луча, исходящего из центра прибора вертикально вверх. Пример на рисунке 6.

Угол поворота шкалы

Рис.6. Угол поворота шкалы

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

Минимум и максимум шкалы — важные параметры, которые следует выбирать исходя из диапазона допустимых значений отображаемой величины. Для удобства допускается отсутствие отметки нуля на шкале. Если величина изменяется в диапазоне от 400 до 600, нет смысла рисовать шкалу от нуля. На рисунке 7 показаны некоторые примеры установки минимума и максимума.

Минимум и максимум шкалы

Рис.7. Минимум и максимум шкалы

  • a) значения от 0 до 500, прямой порядок
  • b) значения от -200 до 400, прямой порядок
  • c) значения от -400 до 0, прямой порядок
  • d) значения от 500 до 0, обратный порядок
  • e) значения от 200 до 800, прямой порядок
  • f) значения от 0 до -800, обратный порядок


2.4. Отметки на шкале

Настройка отметок шкалы заключается в выборе их размера и способа выравнивания.

Выравнивание может быть:

  • по внутреннему краю шкалы
  • по внешнему краю шкалы
  • по центру

На рисунке 8 показаны примеры выравнивания отметок шкалы:

  • a — выравнивание по центру
  • b — по внутреннему краю
  • c — по внешнему краю

Для настройки используется функция GaugeSetMarkParameters().

Расположение подписей отметок относится к настройкам шкалы и устанавливается при помощи функции GaugeSetScaleParameters().

На рисунке 8(a) приведен пример расположения подписей с внутренней стороны шкалы, на рисунке 8(b) и 8(c) — с внешней.

Для того чтобы подписи отметок не занимали слишком много места на шкале, рекомендуется использовать множитель — коэффициент, на который будут разделены все отображаемые значения подписей. Множитель может принимать значения от 0.0001 до 10000. На рисунке 4(c) используется множитель 100, что позволило использовать в подписях отметок однозначные числа вместо трехзначных. На рисунке 1 для индикатора ATR используется множитель 0.0001, что дало возможность не использовать точку и нули в подписях отметок. Множитель устанавливается при помощи функции GaugeSetScaleParameters().

Расположение отметок и подписей

Рис.8. Расположение отметок и подписей


2.5. Надписи

Надписи предназначены для отображения дополнительной информации и бывают четырех типов:

  • описание прибора
  • единицы измерения
  • текущее значение
  • множитель

Любую из надписей можно скрыть. По умолчанию отображается только описание прибора.

Расположение надписи устанавливается при помощи угла и радиуса. Угол задают в градусах, и его величина равняется углу между лучом, исходящим из центра прибора вертикально вверх и воображаемым отрезком, соединяющим центр прибора с центром надписи. Радиус задают в условных величинах. Он может принимать значения от 0 до 10, где 0 соответствует радиусу центра стрелки, а значение 10 соответствует внешнему радиусу шкалы.

На рисунке 9 приведен пример расположения надписей.

  • Надпись "Profit" (описание прибора) имеет координаты: угол 0 и радиус 3.
  • Надпись "0.00" (текущее значение) имеет координаты: угол 225 и радиус 4.
  • Надпись "USD" (единицы измерения) имеет координаты: угол 215 и радиус 8.

Для настройки надписей используется функция GaugeSetLegendParameters().

Координаты надписей

Рис.9. Координаты надписей

Важно. Надписи не закреплены на шкале и их угол не связан с углом поворота шкалы.


2.6. Выделенные диапазоны

Выделенные диапазоны данных — неотъемлемый элемент любого стрелочного прибора. Они помогают определить, что величина приняла аварийные значения или вошла в какой-то особый диапазон. При помощи функции GaugeSetRangeParameters() можно установить до четырех выделенных диапазонов. Для этого нужно задать крайние значения и цвет выделения. На рисунке 1 у индикатора Profit выделены два диапазона: от 200 до 400 зеленый, сигнализирующий о том, что пора фиксировать прибыль, и от -200 до -400 оранжевый, предупреждающий о большой просадке.


2.7. Стрелка

При помощи функции GaugeSetNeedleParameters() настраивается размер центра стрелки и тип закрашивания. Тип закрашивания влияет на ресурсоемкость индикатора, так как слой стрелки полностью перерисовывается при каждом обновлении показаний. На рисунке 10 показаны примеры закрашивания.

  • закрашенная стрелка с использованием алгоритма сглаживания (a)
  • закрашенная стрелка без использования алгоритма сглаживания (b)
  • контур стрелки с использованием алгоритма сглаживания без закрашивания (c)

Способы закрашивания стрелки

Рис.10. Способы закрашивания стрелки

Преимущества и недостатки каждого из способов рассмотрим в разделах, касающихся доработки класса CCanvas и оценки ресурсоемкости.


3. Функции

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

Функция
Действие
GaugeCalcLocation
Считает координаты центра стрелочного прибора
GaugeCreate
Создает стрелочный прибор
GaugeDelete
Удаляет стрелочный прибор
GaugeNewValue
Обновляет положение стрелки и отображаемую величину
GaugeRedraw
Перерисовывает стрелочный прибор
GaugeRelocation
Изменяет расположение объектов прибора на графике
GaugeSetCaseParameters
Устанавливает параметры корпуса стрелочного прибора
GaugeSetLegendParameters
Устанавливает параметры надписи
GaugeSetMarkLabelFont
Устанавливает шрифт подписей отметок шкалы
GaugeSetMarkParameters
Устанавливает параметры отметок шкалы
GaugeSetNeedleParameters
Устанавливает параметры стрелки
GaugeSetRangeParameters
Устанавливает параметры диапазонов
GaugeSetScaleParameters
Устанавливает параметры шкалы

Табл.1. Список функций

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


3.1. GaugeCreate

Создает стрелочный прибор.

bool GaugeCreate(
   string name,              // имя прибора
   GAUGE_STR &g,             // ссылка на структуру стрелочного прибора
   int x,                    // отступ по горизонтали от угла привязки
   int y,                    // отступ по вертикали от угла привязки
   int size,                 // размер стрелочного прибора
   string ObjRel,            // имя графического объекта, относительно которого задано расположение
   ENUM_REL_MODE rel_mode,   // относительное расположение
   ENUM_BASE_CORNER corner,  // угол привязки
   bool back,                // объекты на заднем плане
   uchar scale_transparency, // прозрачность шкалы
   uchar needle_transparency // прозрачность стрелки 
 );

Параметры

 name

   [in]  Имя стрелочного прибора. Используется, как префикс к именам графических объектов, составляющих стрелочный прибор.

 g

   [out]  Ссылка на структуру стрелочного прибора.

 x

   [in]  Дистанция в пикселях по оси X от угла привязки. При относительном расположении — дистанция от локального начала координат.

 y

   [in]  Дистанция в пикселях по оси Y от угла привязки. При относительном расположении — дистанция от локального начала координат.

 size

   [in]  Размер стрелочного прибора. Представлен, как диаметр корпуса.

 ObjRel

   [in]  Имя другого стрелочного прибора, относительно которого задано расположение. Актуально только в том случае, если задано относительное расположение.

 rel_mode

   [in]  Способ относительного расположения. Значение может быть одним из значений перечисления ENUM_REL_MODE.

 corner

   [in]  Угол графика для привязки графического объекта. Значение может быть одним из значений перечисления ENUM_BASE_CORNER.

 back

   [in]  Объекты на заднем плане.

 scale_transparency

   [in]  Степень прозрачности шкалы. Может принимать значения от 0 до 255.

 needle_transparency

   [in]  Степень прозрачности стрелки. Может принимать значения от 0 до 255.

Возвращаемое значение

  Вернет true в том случае, если объекты слоя шкалы и слоя стрелки были созданы, иначе вернет false.


3.2. GaugeSetCaseParameters

Устанавливает параметры корпуса стрелочного прибора.

void GaugeSetCaseParameters(
   GAUGE_STR &g,                  // ссылка на структуру стрелочного прибора
   ENUM_CASE_STYLE style,         // стиль корпуса
   color ccol,                    // цвет корпуса
   ENUM_CASE_BORDER_STYLE bstyle, // стиль бордюра
   color bcol,                    // цвет бордюра
   ENUM_SIZE border_gap_size      // размер пространства между бордюром и элементами шкалы
);

Параметры

 g

   [out]  Ссылка на структуру стрелочного прибора.

 style

   [in]  Стиль корпуса. Значение может быть одним из значений перечисления ENUM_CASE_STYLE.

 ccol

   [in]  Цвет корпуса.

 bstyle

   [in]  Стиль бордюра. Значение может быть одним из значений перечисления ENUM_CASE_BORDER_STYLE.

 bcol

   [in]  Цвет бордюра.

 gap_size

   [in]  Размер области между внутренней линией бордюра и ближайшим элементом шкалы (на рисунке 3 размер "g"). Значение может быть одним из значений перечисления ENUM_SIZE.


3.3. GaugeSetScaleParameters

Устанавливает параметры шкалы.

void GaugeSetScaleParameters(
   GAUGE_STR &g,           // ссылка на структуру стрелочного прибора
   int range,              // диапазон шкалы
   int rotation,           // угол поворота
   double min,             // минимальное значение (левое)
   double max,             // максимальное значение (правое)
   ENUM_MUL_SCALE mul,     // множитель подписей шкалы
   ENUM_SCALE_STYLE style, // стиль шкалы
   color col,              // цвет шкалы
   bool display_arc        // показывать линию шкалы
);

Параметры

 g

   [out]  Ссылка на структуру стрелочного прибора.

 range

   [in]  Диапазон шкалы. Задают как угол, образованный двумя радиус-векторами крайних отметок шкалы. Может принимать значения от 30 до 320 градусов (рисунок 5).

 rotation

   [in]  Угол поворота шкалы (рисунок 6).

 min

   [in]  Минимальное значение шкалы при прямой нумерации.

 max

   [in]  Максимальное значение шкалы при прямой нумерации.

 mul

   [in]  Множитель подписей шкалы. Значение может быть одним из значений перечисления ENUM_MUL_SCALE.

 style

   [in]  Стиль шкалы. Значение может быть одним из значений перечисления ENUM_SCALE_STYLE.

 col

   [in]  Цвет шкалы.

 display_arc=false

   [in]  Отображение линии шкалы.


3.4. GaugeSetMarkParameters

Устанавливает параметры отметок шкалы.

void GaugeSetMarkParameters(  
   GAUGE_STR &g,          // ссылка на структуру стрелочного прибора
   ENUM_MARK_STYLE style, // стиль отметок шкалы
   ENUM_SIZE size,        // размер отметок
   double major_tmi,      // шаг основных отметок
   int middle_tmarks,     // количество средних отметок между соседними основными
   int minor_tmarks       // количество малых отметок между соседними средними
);

Параметры

 g

   [out]  Ссылка на структуру стрелочного прибора.

 style

   [in]  Стиль отметок шкалы. Значение может быть одним из значений перечисления ENUM_MARK_STYLE.

 size

   [in]  Размер отметок. Значение может быть одним из значений перечисления ENUM_SIZE.

 major_tmi

   [in]  Шаг основных отметок шкалы. Основные отметки сопровождаются подписями соответствующих значений.

 middle_tmarks

   [in]  Количество средних отметок шкалы между соседними основными отметками. Значение может быть любым положительным числом. Ограничений по величине нет. При значении 0 средние отметки отображаться не будут.

 minor_tmarks

   [in]  Количество малых отметок шкалы между соседними средними отметками (или основными, если средние не отображаются). Значение может быть любым положительным числом. Ограничений по величине нет. При значении 0 малые отметки отображаться не будут.


3.5. GaugeSetMarkLabelFont

Устанавливает шрифт подписей отметок шкалы.

void GaugeSetMarkLabelFont(
   GAUGE_STR &g,        // ссылка на структуру стрелочного прибора
   ENUM_SIZE font_size, // размер шрифта 
   string font,         // шрифт
   bool italic,         // курсив
   bool bold,           // жирный
   color col            // цвет
);

Параметры

 g

   [out]  Ссылка на структуру стрелочного прибора.

 font_size

   [in]  Размер шрифта подписей отметок шкалы. Значение может быть одним из значений перечисления ENUM_SIZE.

 font

   [in]  Шрифт.

 italic

   [in]  Курсив.

 bold

   [in]  Жирный.

 col

   [in]  Цвет шрифта.


3.6. GaugeSetLegendParameters

Устанавливает параметры надписи.

void GaugeSetLegendParameters(
   GAUGE_STR &g,         // ссылка на структуру стрелочного прибора
   ENUM_GAUGE_LEGEND gl, // тип надписи
   bool enable,          // показывать надпись
   string str,           // строка (или дополнительный параметр)
   int radius,           // координаты - радиус
   double angle,         // координаты - угол
   uint font_size,       // размер шрифта
   string font,          // шрифт
   bool italic,          // курсив
   bool bold,            // жирный
   color col             // цвет
);

Параметры

 g

   [out]  Ссылка на структуру стрелочного прибора

 gl

   [in]  Тип надписи. Значение может быть одним из значений перечисления ENUM_GAUGE_LEGEND.

 enable

   [in]  Отображать надпись.

 str

   [in]  Для надписей типа LEGEND_DESCRIPTION или LEGEND_UNITS это отображаемая строка. Для надписи типа LEGEND_MUL данный параметр игнорируется. Для надписи типа LEGEND_VALUE это количество знаков после запятой. Может принимать значения от "0" до "8". Любые другие значения воспринимаются, как "0". Например, строка "2" означает два знака после запятой. Строка "hello" означает 0 знаков после запятой.

 radius

   [in]  Радиус. Расстояние в условных единицах от центра стрелочного прибора до центра надписи (рисунок 9).

 angle

   [in]  Угловая координата. Ее величина равняется углу между лучом, исходящим из центра прибора вертикально вверх и воображаемым отрезком, соединяющим центр прибора с центром надписи (рисунок 9).

 font_size

   [in]  Размер шрифта надписи.

 font

   [in]  Шрифт.

 italic

   [in]  Курсив.

 bold

   [in]  Жирный.

 col

   [in]  Цвет шрифта.


3.7. GaugeSetRangeParameters

Устанавливает параметры выделенного диапазона.

void GaugeSetRangeParameters(
   GAUGE_STR &g, // ссылка на структуру стрелочного прибора
   int index,    // индекс диапазона
   bool enable,  // отображать диапазон
   double start, // начальное значение
   double end,   // конечное значение
   color col     // цвет
);

Параметры

 g

   [out]  Ссылка на структуру стрелочного прибора.

 index

   [in]  Индекс диапазона. Может принимать значения от 0 до 3.

 enable

   [in]  Отображать диапазон.

 start

   [in]  Начальное значение.

 end

   [in]  Конечное значение.

 col

   [in]  Каким цветом выделить диапазон.


3.8. GaugeSetNeedleParameters

Устанавливает параметры стрелки.

void GaugeSetNeedleParameters(
   GAUGE_STR &g,                     // ссылка на структуру стрелочного прибора
   ENUM_NCENTER_STYLE ncenter_style, // стиль центра стрелки
   color ncenter_col,                // цвет центра стрелки
   color needle_col,                 // цвет стрелки
   ENUM_NEEDLE_FILL needle_fill      // способ закрашивания стрелки
);

Параметры

 g

   [out]  Ссылка на структуру стрелочного прибора.

 ncenter_style

   [in]  Стиль центра стрелки. Значение может быть одним из значений перечисления ENUM_NCENTER_STYLE.

 ncenter_col

   [in]  Цвет центра стрелки.

 needle_col

   [in]  Цвет стрелки.

 needle_fill

   [in]  Способ закрашивания стрелки. Значение может быть одним из значений перечисления ENUM_NEEDLE_FILL.


3.9. GaugeRedraw

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

void GaugeRedraw(
   GAUGE_STR &g       // ссылка на структуру стрелочного прибора
); 

Параметры

 g

   [in]  Ссылка на структуру стрелочного прибора.


3.10. GaugeNewValue

Обновляет положение стрелки и отображаемую величину.

void GaugeNewValue(
   GAUGE_STR &g,     // ссылка на структуру стрелочного прибора
   double v          // значение переменной
);

Параметры

 g

   [in]  Ссылка на структуру стрелочного прибора.

 v

   [in]  Текущее значение переменной.


3.11. GaugeDelete

Удаляет графические объекты, составляющие стрелочный прибор. Функцию необходимо вызывать из обработчика OnDeinit().

void GaugeDelete(
   GAUGE_STR &g      // ссылка на структуру стрелочного прибора
);

Параметры

 g

   [in]  Ссылка на структуру стрелочного прибора.


3.12. GaugeCalcLocation

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

bool GaugeCalcLocation( 
   GAUGE_STR& g         // ссылка на структуру стрелочного прибора
);

Параметры

 g

   [in]  Ссылка на структуру стрелочного прибора.

Возвращаемое значение

  Вернет true в том случае, если полученные значения координат отличаются от предыдущих. В противном случае вернет false. Если функция вернула true, необходимо вызвать функцию GaugeRelocation(), чтобы применить рассчитанные параметры.


3.13. GaugeRelocation

Размещает графические объекты, составляющие стрелочный прибор, в заданном месте графика. Необходимо вызывать в том случае, если указано относительное расположение, и функция GaugeCalcLocation() вернула результат true.

void GaugeRelocation(
   GAUGE_STR &g       // ссылка на структуру стрелочного прибора
);

Параметры

 g

   [in]  Ссылка на структуру стрелочного прибора.


4. Перечисления

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

Перечисление
Описание
ENUM_CASE_BORDER_STYLEСтиль бордюра
ENUM_CASE_STYLE
Стиль корпуса
ENUM_GAUGE_LEGEND
Тип надписи
ENUM_MARK_STYLE
Стиль отметок шкалы
ENUM_MUL_SCALE
Множитель подписей отметок шкалы
ENUM_NCENTER_STYLEСтиль центра стрелки
ENUM_NEEDLE_FILLСпособ заливки стрелки
ENUM_REL_MODEСпособ относительного расположения
ENUM_SCALE_STYLEСтиль шкалы
ENUM_SIZEРазмер

Табл.2. Список перечислений


4.1. ENUM_CASE_BORDER_STYLE

Стиль бордюра. Значения перечислены в таблице 3.

Идентификатор
Описание
CASE_BORDER_NONE
Нет бордюра
CASE_BORDER_THINТонкий бордюр
CASE_BORDER_THICK
Толстый бордюр

Табл.3. Значения ENUM_CASE_BORDER_STYLE

4.2. ENUM_CASE_STYLE

Стиль корпуса. Значения перечислены в таблице 4.

Идентификатор
Описание
CASE_ROUND
Круглый корпус
CASE_SECTOR
Корпус в виде сектора

Табл.4. Значения ENUM_CASE_STYLE

4.3. ENUM_GAUGE_LEGEND

Тип надписи. Значения перечислены в таблице 5.

Идентификатор
 Описание
LEGEND_DESCRIPTIONОписание стрелочного прибора
LEGEND_UNITSЕдиницы измерения
LEGEND_MULМножитель подписей шкалы
LEGEND_VALUEТекущее значение переменной

Табл.5. Значения ENUM_GAUGE_LEGEND

4.4. ENUM_MARK_STYLE

Стиль отметок шкалы. Значения перечислены в таблице 6.

Идентификатор
 Описание
MARKS_INNERВыравнивание отметок по внутреннему краю
MARKS_MIDDLEВыравнивание отметок по центру
MARKS_OUTERВыравнивание отметок по внешнему краю

Табл.6. Значения ENUM_MARK_STYLE

4.5. ENUM_MUL_SCALE

Множитель подписей отметок шкалы. Значения перечислены в таблице 7.

 ИдентификаторЗначение
Отображение
MUL_1000010000
 х10k
MUL_10001000
 х1k
MUL_100100
 х100
MUL_1010
 х10
MUL_11
 Не отображается
MUL_010.1
 /10
MUL_0010.01
 /100
MUL_00010.001
 /1k
MUL_000010.0001
 /10k

Табл.7. Значения ENUM_MUL_SCALE

4.6. ENUM_NCENTER_STYLE

Стиль центра стрелки. Значения перечислены в таблице 8.

Идентификатор
Описание
NDCS_NONEНе отображать центр стрелки
NDCS_SMALLОтображать малый
NDCS_LARGEОтображать крупный

Табл.8. Значения ENUM_NCENTER_STYLE

4.7. ENUM_NEEDLE_FILL

Способ закрашивания стрелки. Значения перечислены в таблице 9.

 Идентификатор Описание
NEEDLE_FILLЗакрашивать стрелку без сглаживания границ
NEEDLE_FILL_AAЗакрашивать стрелку со сглаживанием границ
NEEDLE_NOFILL_AAНе закрашивать стрелку со сглаживанием границ

Табл.9. Значения ENUM_NEEDLE_FILL

4.8. ENUM_REL_MODE

Способ относительного расположения. Значения перечислены в таблице 10.

 Идентификатор Описание
RELATIVE_MODE_NONEОтносительное расположение отключено
RELATIVE_MODE_HORГоризонтальное
RELATIVE_MODE_VERTВертикальное
RELATIVE_MODE_DIAGПо диагонали

Табл.10. Значения ENUM_REL_MODE

4.9. ENUM_SCALE_STYLE

Стиль шкалы. Значения перечислены в таблице 11.

Идентификатор
 Описание
SCALE_INNERПодписи отметок с внутренней стороны шкалы
SCALE_OUTERПодписи отметок с наружной стороны шкалы

Табл.11. Значения ENUM_SCALE_STYLE

4.10. ENUM_SIZE

Размер. Значения перечислены в таблице 12.

Идентификатор
 Описание
SIZE_SMALLМалый
SIZE_MIDDLEСредний
SIZE_LARGEКрупный

Табл.12. Значения ENUM_SIZE


5. Макроподстановки

Коэффициенты для размеров:

#define DIAM_TO_NDCSL_RATIO   5   //диаметр центра стрелки (small) в процентах от диаметра корпуса
#define DIAM_TO_NDCSB_RATIO   7.5 //диаметр центра стрелки (large) в процентах от диаметра корпуса
//---
#define DIAM_TO_BD_SIZE_S     2 //ширина бордюра (small) в процентах от диаметра корпуса
#define DIAM_TO_BD_SIZE_B     5 //ширина бордюра (large) в процентах от диаметра корпуса
//---
#define DIAM_TO_BD_GAP_S      2.0 //зазор от бордюра корпуса до внутренних элементов прибора (small) в процентах от диаметра корпуса
#define DIAM_TO_BD_GAP_M      3.0 //зазор от бордюра корпуса до внутренних элементов прибора (middle) в процентах от диаметра корпуса
#define DIAM_TO_BD_GAP_L      7.0 //зазор от бордюра корпуса до внутренних элементов прибора (large) в процентах от диаметра корпуса
//---
#define DIAM_TO_MSZ_MJ_S      3.3 //размер отметок major шкалы (small) в процентах от диаметра корпуса
#define DIAM_TO_MSZ_MD_S      2.3 //размер отметок middle шкалы (small) в процентах от диаметра корпуса
#define DIAM_TO_MSZ_MN_S      1.3 //размер отметок minor шкалы (small) в процентах от диаметра корпуса
//---
#define DIAM_TO_MSZ_MJ_M      6.5 //размер отметок major шкалы (middle) в процентах от диаметра корпуса
#define DIAM_TO_MSZ_MD_M      4.8 //размер отметок middle шкалы (middle) в процентах от диаметра корпуса
#define DIAM_TO_MSZ_MN_M      3.0 //размер отметок minor шкалы (middle) в процентах от диаметра корпуса
//---
#define DIAM_TO_MSZ_MJ_L      10.0 //размер отметок major шкалы (large) в процентах от диаметра корпуса
#define DIAM_TO_MSZ_MD_L      7.5  //размер отметок middle шкалы (large) в процентах от диаметра корпуса
#define DIAM_TO_MSZ_MN_L      5.0  //размер отметок minor шкалы (large) в процентах от диаметра корпуса
//---
#define DIAM_TO_MFONT_SZ_S    4   //размер шрифта подписей отметок шкалы (small) в процентах от диаметра корпуса
#define DIAM_TO_MFONT_SZ_M    6.5 //размер шрифта подписей отметок шкалы (middle) в процентах от диаметра корпуса
#define DIAM_TO_MFONT_SZ_L    10  //размер шрифта подписей отметок шкалы (large) в процентах от диаметра корпуса

Цвета по умолчанию:

#define DEF_COL_SCALE      clrBlack
#define DEF_COL_MARK_FONT  clrBlack
#define DEF_COL_CASE       clrMintCream
#define DEF_COL_BORDER     clrLightSteelBlue
#define DEF_COL_LAB        clrDarkGray
#define DEF_COL_NCENTER    clrLightSteelBlue
#define DEF_COL_NEEDLE     clrDimGray


6. Доработка класса CCanvas


6.1. Рисование отрезка со сглаживанием

Метод LineAA позволяет рисовать отрезок с использованием алгоритма сглаживания. Но при рисовании отметок шкалы, расположенных по кругу, появляется одна проблема. При пересчете координат начала и конца отрезка из полярной системы координат в прямоугольную, получаются дробные числа, которые округляются до целых, и расположение меток становится "кривым", что показано на рисунке 11(b).

Поэтому был добавлен метод LineAA2, который отличается от LineAA только тем, что тип входных параметров x1, y1, x2, y2 изменен на double. Это позволяет передать дробные значения координат и избавиться от описанной проблемы, что видно на рисунке 11(с).

//+------------------------------------------------------------------+
//| Draw line with antialiasing (with style) v.2                     |
//+------------------------------------------------------------------+
void CCanvas::LineAA2(const double x1,const double y1,const double x2,const double y2,const uint clr,const uint style)
  {
//--- line is out of image boundaries
   if((x1<0 && x2<0) || (y1<0 && y2<0))
      return;
   if(x1>=m_width && x2>=m_width)
      return;
   if(y1>=m_height && y2>=m_height)
      return;
//--- check
   if(x1==x2 && y1==y2)
     {
      PixelSet(int(x1),int(y1),clr);
      return;
     }
//--- set the line style
   if(style!=UINT_MAX)
      LineStyleSet(style);
//--- preliminary calculations
   double dx=x2-x1;
   double dy=y2-y1;
   double xy=sqrt(dx*dx+dy*dy);
   double xx=x1;
   double yy=y1;
   uint   mask=1<<m_style_idx;
//--- set pixels
   dx/=xy;
   dy/=xy;
   do
     {
      if((m_style&mask)==mask)
         PixelSetAA(xx,yy,clr);
      xx+=dx;
      yy+=dy;
      mask<<=1;
      if(mask==0x1000000)
         mask=1;
     }
   while(fabs(x2-xx)>=fabs(dx) && fabs(y2-yy)>=fabs(dy));
  } 

На рисунке 11 для сравнения показаны примеры рисования отметок шкалы разными методами:

Методы рисования отметок шкалы

Рис. 11. Рисование отметок шкалы разными методами (увеличение 200%)


6.2. Закрашивание области со сглаженными границами

Метод Fill предназначен для закрашивания области, ограниченной отрезками, нарисованными без использования алгоритма сглаживания. Если попробовать закрасить им область, ограниченную отрезками, нарисованными методом LineAA, то область будет закрашена не полностью, что видно на рисунке 12(a).

Закрашивание области со сглаженными границами

Рис.12. Закрашивание области со сглаженными границами (увеличение 200%)

В связи с этим был добавлен метод Fill2. Разница в том, что он закрашивает не цвет фона, а любой цвет, который отличается от цвета отрезков, ограничивающих область. Это позволяет закрасить полутона, которые не закрашивает метод Fill. На рисунке 12(b) приведен пример.

//+------------------------------------------------------------------+
//| Fill closed region with color (v.2)                              |
//+------------------------------------------------------------------+
void CCanvas::Fill2(int x,int y,const uint clr)
  {
//--- check
   if(x<0 || x>=m_width || y<0 || y>=m_height)
      return;
//---
   int  index=y*m_width+x;
   uint old_clr=m_pixels[index];
//--- check if replacement is necessary
   if(old_clr==clr)
      return;
//--- use pseudo stack to emulate deeply-nested recursive calls
   int  stack[];
   uint count=1;
   int  idx;
   int  total=ArraySize(m_pixels);
//--- allocate memory for stack
   if(ArrayResize(stack,total)==-1)
      return;
   stack[0]=index;
   m_pixels[index]=clr;
   for(uint i=0;i<count;i++)
     {
      index=stack[i];
      x=index%m_width;
      //--- left adjacent point
      idx=index-1;
      if(x>0 && m_pixels[idx]!=clr)
        {
         stack[count]=idx;
         if(m_pixels[idx]==old_clr)
            count++;
         m_pixels[idx]=clr;
        }
      //--- top adjacent point
      idx=index-m_width;
      if(idx>=0 && m_pixels[idx]!=clr)
        {
         stack[count]=idx;
         if(m_pixels[idx]==old_clr)
            count++;
         m_pixels[idx]=clr;
        }
      //--- right adjacent point
      idx=index+1;
      if(x<m_width-1 && m_pixels[idx]!=clr)
        {
         stack[count]=idx;
         if(m_pixels[idx]==old_clr)
            count++;
         m_pixels[idx]=clr;
        }
      //--- bottom adjacent point
      idx=index+m_width;
      if(idx<total && m_pixels[idx]!=clr)
        {
         stack[count]=idx;
         if(m_pixels[idx]==old_clr)
            count++;
         m_pixels[idx]=clr;
        }
     }
//--- deallocate memory
   ArrayFree(stack);
  }  

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

   1) Сначала весь холст (слой стрелки) закрашиваем цветом, которым должна быть закрашена стрелка:

n.canvas.Fill(10, 10, ColorToARGB(n.needle.c, n.transparency));

   2) Затем рисуем стрелку, состоящую из трех отрезков, методом LineAA2:

n.canvas.LineAA2(db_xbuf[0], db_ybuf[0], db_xbuf[1], db_ybuf[1], 0);
n.canvas.LineAA2(db_xbuf[1], db_ybuf[1], db_xbuf[2], db_ybuf[2], 0);
n.canvas.LineAA2(db_xbuf[2], db_ybuf[2], db_xbuf[0], db_ybuf[0], 0);

  3) После этого закрашиваем область вокруг стрелки прозрачным цветом методом Fill2:

n.canvas.Fill2(10, 10, 0);

Описанный способ не самый оптимальный, но позволяет красиво рисовать стрелку.

Методы закрашивания стрелки

Рис.13. Стрелки, закрашенные разными методами

На рисунке 13 показаны стрелки, закрашенные разными методами.

  • a) Стрелка состоящая из трех отрезков, нарисованных методом LineAA2 и закрашенная методом Fill2.
  • b) Стрелка, нарисованная методом FillTriangle.
  • c) Стрелка состоящая из трех отрезков, нарисованных методом LineAA2 без закрашивания.

Как видно, стрелка на рисунке 13(b) имеет угловатый вид при небольшом отклонении от углов, кратных 90 градусам. Кроме того, наблюдается смещение стрелки от центра, что обусловлено округлением значений координат вершин при пересчете из полярной системы координат в прямоугольную. Однако при этом данный метод самый экономный с точки зрения ресурсоемкости (этот вопрос рассмотрим позже). Стрелка на рисунке 13(c) является компромиссом между двумя методами, описанными выше. Она состоит из отрезков, нарисованных методом LineAA2, но без закрашивания.


7. Примеры применения

Разберем применение библиотеки стрелочных приборов на нескольких примерах.


7.1. Индикатор текущей прибыли

Начнем с простейшего. Приведенный ниже пример демонстрирует необходимый минимум для добавления стрелочного прибора в советник или индикатор.

//+------------------------------------------------------------------+
//|                                       profit_gauge_indicator.mq5 |
//|                                         Copyright 2015, Decanium |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Decanium"
#property version   "1.00"
#property indicator_plots 0
#property indicator_chart_window

#include <Gauge\gauge_graph.mqh>

input string inp_gauge_name="gg01";                  // Имя индикатора
input int inp_x = 40;                                // Смещение по горизонтали
input int inp_y = 40;                                // Смещение по вертикали
input int inp_size=300;                              // Размер индикатора
input string inp_ObjRel="";                          // Имя базового индикатора при относительном расположении
input ENUM_REL_MODE inp_rel_mode=RELATIVE_MODE_NONE; // Режим относительного расположения
input ENUM_BASE_CORNER inp_corner=CORNER_LEFT_UPPER; // Угол привязки
input bool inp_back=false;                           // Индикатор на заднем плане
input uchar inp_scale_transparency=0;                // Степень прозрачности шкалы, 0..255
input uchar inp_needle_transparency=0;               // Степень прозрачности стрелки, 0..255

//--- объявление структуры стрелочного прибора
GAUGE_STR g0;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- создание стрелочного прибора
   if(GaugeCreate(inp_gauge_name,g0,inp_x,inp_y,inp_size,inp_ObjRel,inp_rel_mode,
      inp_corner,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- рисование стрелочного прибора
   GaugeRedraw(g0);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- удаление стрелочного прибора
   GaugeDelete(g0);
   ChartRedraw();
  }    
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
//--- обновление показаний
   double profit=AccountInfoDouble(ACCOUNT_PROFIT);
   GaugeNewValue(g0,profit);
//---
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//---
   if(id==CHARTEVENT_CHART_CHANGE)
     {
      if(GaugeCalcLocation(g0)==true)
         GaugeRelocation(g0);
     }
  }
//+------------------------------------------------------------------+

Для начала нужно объявить структуру стрелочного прибора. Затем в функции инициализации создать стрелочный прибор при помощи GaugeCreate() и вызвать функцию рисования GaugeRedraw(). Для обновления показаний использовать GaugeNewValue(), которая в данном примере вызывается из обработчика OnCalculate().

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

Внешний вид индикатора, значения параметров по умолчанию

Рис.14. Внешний вид стрелочного прибора по умолчанию

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

input int inp_scale_range=270;   // Диапазон шкалы, 30..320 градусов
input int inp_rotation=45;       // Поворот шкалы, 0..359 градусов

В коде инициализации добавим вызов функции установки параметров шкалы.

//--- установка параметров шкалы и отметок на шкале
   GaugeSetScaleParameters(g0,inp_scale_range,inp_rotation,-200,400,MUL_1,SCALE_INNER,clrBlack);

Здесь, кроме новых входных параметров, будут также установлены:

  • новые значения минимума и максимума (-200 и 400 соответственно)
  • множитель подписей отметок шкалы (MUL_1)
  • стиль шкалы (SCALE_INNER — подписи отметок внутри)
  • цвет отметок шкалы (clrBlack)

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

   GaugeSetMarkParameters(g0,MARKS_INNER,SIZE_MIDDLE,100,1,4);

Выделим на шкале два диапазона данных. Диапазон с индексом 0, который начинается от значения 200 и заканчивается значением 400, будет выделен цветом clrLimeGreen. Диапазон с индексом 1, который начинается от значения -100 и заканчивается значением -200, будет выделен цветом clrCoral.

//--- выделение диапазонов на шкале
   GaugeSetRangeParameters(g0,0,true,200,400,clrLimeGreen);
   GaugeSetRangeParameters(g0,1,true,-100,-200,clrCoral);

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

Описание прибора:

   GaugeSetLegendParameters(g0,LEGEND_DESCRIPTION,true,"Profit",3,0,14,"Arial",false,false);

Отображаемая строка "Profit", радиус 3, угол 0, размер шрифта 14 условных единиц.

Единицы измерения:

   GaugeSetLegendParameters(g0,LEGEND_UNITS,true,"USD",8,215,10,"Arial",true,false);

Отображаемая строка "USD", радиус 8, угол 215, размер шрифта 10 условных единиц.

Текущее значение:

   GaugeSetLegendParameters(g0,LEGEND_VALUE,true,"1",4,225,20,"Arial",true,false);

Здесь строка "1" означает формат отображения — один знак после запятой. Координаты: радиус 4, угол 255. Размер шрифта 20 условных единиц.

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

Индикатор текущей прибыли

Рис.15. Внешний вид стрелочного прибора после дополнительной настройки


7.2. Индикатор Dashboard

Рассмотрим более сложный пример — индикатор Dashboard. Его внешний вид показан на рисунке 1. Индикатор отображает текущую прибыль, спред, уровень свободной маржи в процентах, и текущие значения технических индикаторов ATR, Force Index и RSI.

Для начала объявим массив структур стрелочного прибора. 

//--- объявление массива структур стрелочного прибора
GAUGE_STR gg[6];

После этого создадим и настроим стрелочные приборы.

В левом нижнем углу будет находиться индикатор уровня маржи. Он будет иметь абсолютные координаты, а все остальные индикаторы будут располагаться относительно него или соседнего индикатора.

//--- создание стрелочного прибора gg00, уровень маржи
   if(GaugeCreate("gg00",gg[0],5,-90,240,"",RELATIVE_MODE_NONE,
      CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- установка параметров корпуса
   GaugeSetCaseParameters(gg[0],CASE_SECTOR,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- установка параметров шкалы и отметок на шкале
   GaugeSetScaleParameters(gg[0],120,35,800,2000,MUL_100,SCALE_INNER,clrBlack);
   GaugeSetMarkParameters(gg[0],MARKS_INNER,SIZE_MIDDLE,200,1,4);
   GaugeSetMarkLabelFont(gg[0],SIZE_MIDDLE,"Arial",false,false,DEF_COL_MARK_FONT);
//--- выделение диапазонов на шкале
   GaugeSetRangeParameters(gg[0],0,true,1400,2000,clrLimeGreen);
   GaugeSetRangeParameters(gg[0],1,true,1000,800,clrCoral);
//--- установка текстовых меток
   GaugeSetLegendParameters(gg[0],LEGEND_DESCRIPTION,true,"Margin lvl",4,15,12,"Arial",false,false);
   GaugeSetLegendParameters(gg[0],LEGEND_VALUE,true,"0",3,80,16,"Arial",true,false);
   GaugeSetLegendParameters(gg[0],LEGEND_MUL,true,"",4,55,8,"Arial",true,false);
//--- установка параметров стрелки
   GaugeSetNeedleParameters(gg[0],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

Продолжаем выстраивать нижний ряд. Следующий — индикатор текущей прибыли.

//--- создание стрелочного прибора gg01, текущая прибыль
   if(GaugeCreate("gg01",gg[1],-80,20,320,"gg00",RELATIVE_MODE_HOR,
      CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- установка параметров корпуса
   GaugeSetCaseParameters(gg[1],CASE_SECTOR,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- установка параметров шкалы и отметок на шкале
   GaugeSetScaleParameters(gg[1],200,0,-400,400,MUL_1,SCALE_INNER,clrBlack);
   GaugeSetMarkParameters(gg[1],MARKS_INNER,SIZE_MIDDLE,100,1,4);
   GaugeSetMarkLabelFont(gg[1],SIZE_MIDDLE,"Arial",false,false,DEF_COL_MARK_FONT);
//--- выделение диапазонов на шкале
   GaugeSetRangeParameters(gg[1],0,true,200,400,clrLimeGreen);
   GaugeSetRangeParameters(gg[1],1,true,-200,-400,clrCoral);
//--- установка текстовых меток
   GaugeSetLegendParameters(gg[1],LEGEND_DESCRIPTION,true,"Profit",3,0,16,"Arial",false,false);
   GaugeSetLegendParameters(gg[1],LEGEND_UNITS,true,"USD",3,-90,10,"Arial",true,false);
   GaugeSetLegendParameters(gg[1],LEGEND_VALUE,true,"1",3,90,12,"Arial",true,false);
//--- установка параметров стрелки
   GaugeSetNeedleParameters(gg[1],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

Замыкает нижний ряд индикатор спреда.

//--- создание стрелочного прибора gg02, спред
   if(GaugeCreate("gg02",gg[2],-80,-20,240,"gg01",RELATIVE_MODE_HOR,
      CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- установка параметров корпуса
   GaugeSetCaseParameters(gg[2],CASE_SECTOR,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- установка параметров шкалы и отметок на шкале
   GaugeSetScaleParameters(gg[2],120,-35,60,0,MUL_1,SCALE_INNER,clrBlack);
   GaugeSetMarkParameters(gg[2],MARKS_INNER,SIZE_MIDDLE,10,1,4);
   GaugeSetMarkLabelFont(gg[2],SIZE_MIDDLE,"Arial",false,false,DEF_COL_MARK_FONT);
//--- выделение диапазонов на шкале
   GaugeSetRangeParameters(gg[2],0,true,35,10,clrLimeGreen);
   GaugeSetRangeParameters(gg[2],1,true,50,60,clrCoral);
//--- установка текстовых меток
   GaugeSetLegendParameters(gg[2],LEGEND_DESCRIPTION,true,"Spread",4,-15,14,"Arial",false,false);
   GaugeSetLegendParameters(gg[2],LEGEND_VALUE,true,"0",3,-80,16,"Arial",true,false);
//--- установка параметров стрелки
   GaugeSetNeedleParameters(gg[2],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

Индикатор ATR, левый в верхнем ряду, располагается относительно индикатора свободной маржи.

//--- создание стрелочного прибора gg03, ATR
   if(GaugeCreate("gg03",gg[3],30,0,180,"gg00",RELATIVE_MODE_VERT,
      CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- установка параметров корпуса
   GaugeSetCaseParameters(gg[3],CASE_ROUND,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- установка параметров шкалы и отметок на шкале
   GaugeSetScaleParameters(gg[3],270,45,0.001,0.004,MUL_00001,SCALE_INNER,clrBlack);
   GaugeSetMarkParameters(gg[3],MARKS_INNER,SIZE_LARGE,0.001,9,3);
   GaugeSetMarkLabelFont(gg[3],SIZE_LARGE,"Arial",false,false,DEF_COL_MARK_FONT);
//--- выделение диапазонов на шкале
   GaugeSetRangeParameters(gg[3],0,true,0.002,0.001,clrYellow);
//--- установка текстовых меток
   GaugeSetLegendParameters(gg[3],LEGEND_DESCRIPTION,true,"ATR",7,-140,26,"Arial",false,false);
//GaugeSetLegendParameters(gg[3],LEGEND_UNITS,true,"USD",8,180,5,"Arial",true,false);
   GaugeSetLegendParameters(gg[3],LEGEND_VALUE,true,"5",2,180,14,"Arial",true,false);
   GaugeSetLegendParameters(gg[3],LEGEND_MUL,true,"",2,0,20,"Arial",true,false);
//--- установка параметров стрелки
   GaugeSetNeedleParameters(gg[3],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

Индикатор RSI располагается относительно индикатора спреда, над ним.

//--- создание стрелочного прибора gg04, RSI
   if(GaugeCreate("gg04",gg[4],-30,0,180,"gg02",RELATIVE_MODE_VERT,
      CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- установка параметров корпуса
   GaugeSetCaseParameters(gg[4],CASE_ROUND,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- установка параметров шкалы и отметок на шкале
   GaugeSetScaleParameters(gg[4],270,45,0,100,MUL_10,SCALE_INNER,clrBlack);
   GaugeSetMarkParameters(gg[4],MARKS_INNER,SIZE_LARGE,10,1,4);
   GaugeSetMarkLabelFont(gg[4],SIZE_LARGE,"Arial",false,false,DEF_COL_MARK_FONT);
//--- установка текстовых меток
   GaugeSetLegendParameters(gg[4],LEGEND_DESCRIPTION,true,"RSI",7,-140,26,"Arial",false,false);
   GaugeSetLegendParameters(gg[4],LEGEND_VALUE,true,"2",2,180,16,"Arial",true,false);
   GaugeSetLegendParameters(gg[4],LEGEND_MUL,true,"",2,0,20,"Arial",true,false);
//--- установка параметров стрелки
   GaugeSetNeedleParameters(gg[4],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

Индикатор Force Index — над индикатором текущей прибыли.

//--- создание стрелочного прибора gg05, Force
   if(GaugeCreate("gg05",gg[5],-10,60,180,"gg03",RELATIVE_MODE_HOR,
      CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- установка параметров корпуса
   GaugeSetCaseParameters(gg[5],CASE_ROUND,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- установка параметров шкалы и отметок на шкале
   GaugeSetScaleParameters(gg[5],270,45,-4,4,MUL_1,SCALE_INNER,clrBlack);
   GaugeSetMarkParameters(gg[5],MARKS_INNER,SIZE_LARGE,1,1,4);
   GaugeSetMarkLabelFont(gg[5],SIZE_LARGE,"Arial",false,false,DEF_COL_MARK_FONT);
//--- выделение диапазонов на шкале
   GaugeSetRangeParameters(gg[5],0,true,-1,-4,clrMediumSeaGreen);
   GaugeSetRangeParameters(gg[5],1,true,1,4,clrCrimson);
//--- установка текстовых меток
   GaugeSetLegendParameters(gg[5],LEGEND_DESCRIPTION,true,"Force",7,-140,20,"Arial",false,false);
   GaugeSetLegendParameters(gg[5],LEGEND_VALUE,true,"5",2,180,14,"Arial",true,false);
   GaugeSetLegendParameters(gg[5],LEGEND_MUL,true,"",3,0,10,"Arial",true,false);
//--- установка параметров стрелки
   GaugeSetNeedleParameters(gg[5],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

Рисование стрелочных приборов можно выполнить в цикле.

//--- рисование стрелочных приборов
   for(int i=0; i<6;i++)
     {
      GaugeRedraw(gg[i]);
      GaugeNewValue(gg[i],0);
     }

При наступлении события OnCalculate() пересчитываем текущие значения и вызываем для каждого индикатора функцию GaugeNewValue().

//--- обновление показаний
//--- спред
   GaugeNewValue(gg[2],spread[rates_total-1]);
//--- текущая прибыль   
   double profit=AccountInfoDouble(ACCOUNT_PROFIT);
   GaugeNewValue(gg[1],profit);
//--- уровень маржи
   double margin_level=AccountInfoDouble(ACCOUNT_MARGIN_LEVEL);
   GaugeNewValue(gg[0],margin_level);
//--- индикатор ATR
   calculated=BarsCalculated(handle_ATR);
   if(calculated>0)
     {
      double ival[1];
      if(CopyBuffer(handle_ATR,0,0,1,ival)<0)
         Print("ATR CopyBuffer error");
      else
         GaugeNewValue(gg[3],ival[0]);
     }
//--- индикатор RSI
   calculated=BarsCalculated(handle_RSI);
   if(calculated>0)
     {
      double ival[1];
      if(CopyBuffer(handle_RSI,0,0,1,ival)<0)
         Print("RSI CopyBuffer error");
      else
         GaugeNewValue(gg[4],ival[0]);
     }
//--- индикатор Force Index
   calculated=BarsCalculated(handle_Force);
   if(calculated>0)
     {
      double ival[1];
      if(CopyBuffer(handle_Force,0,0,1,ival)<0)
         Print("Force Index CopyBuffer error");
      else
         GaugeNewValue(gg[5],ival[0]);
     }

Обратите внимание, что в данном примере нет смысла вызывать GaugeRelocation() из события OnChartEvent(). Хотя здесь и используется относительное расположение, но поскольку стрелочные приборы инициализируются все вместе, исключается ситуация, когда изменилось расположение или размер одного из них, и нужно пересчитать координаты другого.


8. Оценка ресурсоемкости

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

//+------------------------------------------------------------------+
//|                                                    test_fill.mq5 |
//|                                         Copyright 2015, Decanium |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Decanium"
#property version   "1.00"

#include <Canvas/Canvas2.mqh>

CCanvas canvas;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   Print("***** start test *****");
//---
   string ObjName="test";
   ObjectDelete(0,ObjName);
   canvas.CreateBitmapLabel(ObjName,10,10,400,400,COLOR_FORMAT_ARGB_NORMALIZE);
//---
   int x[3]={200,185,215};
   int y[3]={70, 250,250};
   int cycles=1000;
   uint col=ColorToARGB(clrRed,255);
   uint c1,c2;
//--- тест заливки со сглаживанием границ
   canvas.Erase();
   c1=GetTickCount();
   for(int i=0; i<cycles; i++)
     {
      canvas.Fill(10, 10, col);
      canvas.LineAA2(x[0], y[0], x[1], y[1], 0);
      canvas.LineAA2(x[1], y[1], x[2], y[2], 0);
      canvas.LineAA2(x[2], y[2], x[0], y[0], 0);
      canvas.Fill2(10, 10, 0);
     }
   c2=GetTickCount();
   canvas.Update(true);
   Print("Filled AA: ",c2-c1," ms, ",cycles," cycles, ",
         DoubleToString(double(c2-c1)/double(cycles),2)," ms per cycle");
//--- тест сглаженного контура без заливки
   canvas.Erase();
   c1=GetTickCount();
   for(int i=0; i<cycles; i++)
     {
      canvas.LineAA2(x[0], y[0], x[1], y[1], col);
      canvas.LineAA2(x[1], y[1], x[2], y[2], col);
      canvas.LineAA2(x[2], y[2], x[0], y[0], col);
     }
   c2=GetTickCount();
   canvas.Update(true);
   Print("Not filled AA: ",c2-c1," ms, ",cycles," cycles, ",
         DoubleToString(double(c2-c1)/double(cycles),2)," ms per cycle");
//--- тест заливки без сглаживания
   canvas.Erase();
   c1=GetTickCount();
   for(int i=0; i<cycles; i++)
     {
      canvas.FillTriangle(x[0],y[0],x[1],y[1],x[2],y[2],col);
      canvas.LineAA2(x[0], y[0], (x[1]+x[2])/2, y[1], col);
     }
   c2=GetTickCount();
   canvas.Update(true);
   Print("Filled: ",c2-c1," ms, ",cycles," cycles, ",
         DoubleToString(double(c2-c1)/double(cycles),2)," ms per cycle");
  }
//+------------------------------------------------------------------+

Скрипт запускает каждый из способов рисования стрелки в цикле по 1000 раз и измеряет затраченное на это время в миллисекундах.

Тест ресурсоемкости

Рис.16. Результаты тестирования ресурсоемкости

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


Заключение

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

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

Прикрепленные файлы |
dashboard.mq5 (11.45 KB)
Canvas2.mqh (85.76 KB)
gauge_graph.mqh (77.25 KB)
Dmitry Deviatko
Dmitry Deviatko | 20 июн 2015 в 19:56
Полностью согласен с предыдущим комментарием. Спасибо за работу! Хоть, пока, и не представляю, куда можно такое впихнуть, но сразу захотелось это использовать :), добавлю в закладки.
Ruslan Khasanov
Ruslan Khasanov | 20 июн 2015 в 20:13

Побольше бы таких статей. Спасибо за интересный и качественный материал!

Vladimir Pastushak
Vladimir Pastushak | 29 июл 2015 в 14:29

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

Обьекты создаются где то за пределами но самих приборов нет ...

Vladimir Pastushak
Vladimir Pastushak | 29 июл 2015 в 14:30
Может ли автор детально проконсультировать по своему труду ?
Serhii Shevchuk
Serhii Shevchuk | 29 июл 2015 в 14:55
Vladimir Pastushak:
Может ли автор детально проконсультировать по своему труду ?
Могу. Напишите максимально подробно, что именно не получается?
Как обезопасить себя и своего эксперта при торговле на Московской бирже Как обезопасить себя и своего эксперта при торговле на Московской бирже

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

Как правильно выбрать торговый сигнал для подписки. Пошаговое руководство Как правильно выбрать торговый сигнал для подписки. Пошаговое руководство

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

Способ построения уровней сопротивления и поддержки средствами MQL5 Способ построения уровней сопротивления и поддержки средствами MQL5

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

Рецепты MQL5 - Пишем свой стакан цен Рецепты MQL5 - Пишем свой стакан цен

Эта статья научит читателей программно работать со стаканом цен, а также подробно опишет принципы работы класса CMarketBook, который органично расширит стандартную библиотеку классов MQL5 и предоставит удобные методы для работы со стаканом.