![Рисование стрелочных индикаторов с использованием класса CCanvas](https://c.mql5.com/i/articles/overlay.png)
Рисование стрелочных индикаторов с использованием класса CCanvas
Содержание
- Введение
- 1. Координаты и привязка
- 2. Элементы стрелочного прибора
- 3. Функции
- 3.1. GaugeCreate
- 3.2. GaugeSetCaseParameters
- 3.3. GaugeSetScaleParameters
- 3.4. GaugeSetMarkParameters
- 3.5. GaugeSetMarkLabelFont
- 3.6. GaugeSetLegendParameters
- 3.7. GaugeSetRangeParameters
- 3.8. GaugeSetNeedleParameters
- 3.9. GaugeRedraw
- 3.10. GaugeNewValue
- 3.11. GaugeDelete
- 3.12. GaugeCalcLocation
- 3.13. GaugeRelocation
- 4. Перечисления
- 4.1. ENUM_CASE_BORDER_STYLE
- 4.2. ENUM_CASE_STYLE
- 4.3. ENUM_GAUGE_LEGEND
- 4.4. ENUM_MARK_STYLE
- 4.5. ENUM_MUL_SCALE
- 4.6. ENUM_NCENTER_STYLE
- 4.7. ENUM_NEEDLE_FILL
- 4.8. ENUM_REL_MODE
- 4.9. ENUM_SCALE_STYLE
- 4.10. ENUM_SIZE
- 5. Макроподстановки
- 6. Доработка класса CCanvas
- 7. Примеры применения
- 8. Оценка ресурсоемкости
- Заключение
Введение
Все началось со знакомства с классом 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. Строение стрелочного прибора
Слой шкалы содержит:
- бордюр (1)
- отметки шкалы (5, 6, 7)
- подписи отметок шкалы (4)
- выделенные диапазоны (2, 12)
- надписи (3, 10, 11)
Надписи различают по назначению:
- описание прибора (3)
- единицы измерения (11)
- текущее значение (10)
- множитель подписей шкалы (не показан)
Отметки шкалы разделяют на:
- основные (7)
- средние (5)
- малые (6)
Подписи есть только у основных отметок, шаг которых устанавливается числовым значением. Шаг средних отметок рассчитывается исходя из заданного количества средних отметок между соседними основными. Шаг малых отметок рассчитывается исходя из заданного количества малых отметок между соседними средними. Допускается отсутствие малых и средних отметок на шкале.
Слой стрелки содержит:
- стрелку (8)
- центр стрелки (9)
На рисунке 3 показаны размеры некоторых элементов стрелочного прибора:
- d — размер прибора, который соответствует диаметру внешней линии контура прибора
- b — размер бордюра
- g — размер пространства между бордюром и элементами шкалы
- c — размер центра стрелки.
Важно. Единственный размер, который задают в пикселях — это диаметр прибора (на рисунке 3 — размер d). Все остальные размеры элементов и шрифтов задают в условных единицах и их величина рассчитывается как процент от диаметра. Это сделано для простоты масштабирования. Достаточно изменить диаметр, и все остальные размеры будут пропорционально пересчитаны. Коэффициенты для расчета перечислены в разделе "макроподстановки" и могут быть изменены пользователем по желанию.
Форма корпуса стрелочного прибора может быть двух видов: круг и сектор. Форма в виде сектора наиболее удобна для применения в том случае, если величина угла диапазона шкалы составляет менее 180 градусов.
Рис.4. Форма стрелочного прибора
На рисунке 4 показан круглый стрелочный прибор (a) и два стрелочных прибора в форме сектора (b, c). Для установки нужной формы корпуса используется функция GaugeSetCaseParameters().
Самый важный элемент стрелочного прибора. От ее вида зависит удобство восприятия данных оператором. Шкала не должна выглядеть загроможденной, но при этом должна быть достаточно информативной. Выбор крайних значений шкалы, а также шаг основных отметок, требует особого внимания. Функция 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, обратный порядок
Настройка отметок шкалы заключается в выборе их размера и способа выравнивания.
Выравнивание может быть:
- по внутреннему краю шкалы
- по внешнему краю шкалы
- по центру
На рисунке 8 показаны примеры выравнивания отметок шкалы:
- a — выравнивание по центру
- b — по внутреннему краю
- c — по внешнему краю
Для настройки используется функция GaugeSetMarkParameters().
Расположение подписей отметок относится к настройкам шкалы и устанавливается при помощи функции GaugeSetScaleParameters().
На рисунке 8(a) приведен пример расположения подписей с внутренней стороны шкалы, на рисунке 8(b) и 8(c) — с внешней.
Для того чтобы подписи отметок не занимали слишком много места на шкале, рекомендуется использовать множитель — коэффициент, на который будут разделены все отображаемые значения подписей. Множитель может принимать значения от 0.0001 до 10000. На рисунке 4(c) используется множитель 100, что позволило использовать в подписях отметок однозначные числа вместо трехзначных. На рисунке 1 для индикатора ATR используется множитель 0.0001, что дало возможность не использовать точку и нули в подписях отметок. Множитель устанавливается при помощи функции GaugeSetScaleParameters().
Рис.8. Расположение отметок и подписей
Надписи предназначены для отображения дополнительной информации и бывают четырех типов:
- описание прибора
- единицы измерения
- текущее значение
- множитель
Любую из надписей можно скрыть. По умолчанию отображается только описание прибора.
Расположение надписи устанавливается при помощи угла и радиуса. Угол задают в градусах, и его величина равняется углу между лучом, исходящим из центра прибора вертикально вверх и воображаемым отрезком, соединяющим центр прибора с центром надписи. Радиус задают в условных величинах. Он может принимать значения от 0 до 10, где 0 соответствует радиусу центра стрелки, а значение 10 соответствует внешнему радиусу шкалы.
На рисунке 9 приведен пример расположения надписей.
- Надпись "Profit" (описание прибора) имеет координаты: угол 0 и радиус 3.
- Надпись "0.00" (текущее значение) имеет координаты: угол 225 и радиус 4.
- Надпись "USD" (единицы измерения) имеет координаты: угол 215 и радиус 8.
Для настройки надписей используется функция GaugeSetLegendParameters().
Рис.9. Координаты надписей
Важно. Надписи не закреплены на шкале и их угол не связан с углом поворота шкалы.
Выделенные диапазоны данных — неотъемлемый элемент любого стрелочного прибора. Они помогают определить, что величина приняла аварийные значения или вошла в какой-то особый диапазон. При помощи функции GaugeSetRangeParameters() можно установить до четырех выделенных диапазонов. Для этого нужно задать крайние значения и цвет выделения. На рисунке 1 у индикатора Profit выделены два диапазона: от 200 до 400 зеленый, сигнализирующий о том, что пора фиксировать прибыль, и от -200 до -400 оранжевый, предупреждающий о большой просадке.
При помощи функции GaugeSetNeedleParameters() настраивается размер центра стрелки и тип закрашивания. Тип закрашивания влияет на ресурсоемкость индикатора, так как слой стрелки полностью перерисовывается при каждом обновлении показаний. На рисунке 10 показаны примеры закрашивания.
- закрашенная стрелка с использованием алгоритма сглаживания (a)
- закрашенная стрелка без использования алгоритма сглаживания (b)
- контур стрелки с использованием алгоритма сглаживания без закрашивания (c)
Рис.10. Способы закрашивания стрелки
Преимущества и недостатки каждого из способов рассмотрим в разделах, касающихся доработки класса CCanvas и оценки ресурсоемкости.
3. Функции
В таблице 1 приведен список функций для рисования стрелочных приборов и настройки их внешнего вида.
Функция | Действие |
---|---|
GaugeCalcLocation | Считает координаты центра стрелочного прибора |
GaugeCreate | Создает стрелочный прибор |
GaugeDelete | Удаляет стрелочный прибор |
GaugeNewValue | Обновляет положение стрелки и отображаемую величину |
GaugeRedraw | Перерисовывает стрелочный прибор |
GaugeRelocation | Изменяет расположение объектов прибора на графике |
GaugeSetCaseParameters | Устанавливает параметры корпуса стрелочного прибора |
GaugeSetLegendParameters | Устанавливает параметры надписи |
GaugeSetMarkLabelFont | Устанавливает шрифт подписей отметок шкалы |
GaugeSetMarkParameters | Устанавливает параметры отметок шкалы |
GaugeSetNeedleParameters | Устанавливает параметры стрелки |
GaugeSetRangeParameters | Устанавливает параметры диапазонов |
GaugeSetScaleParameters | Устанавливает параметры шкалы |
Табл.1. Список функций
Рассмотрим каждую функцию более детально. В том порядке, в котором их рекомендуется вызывать при инициализации.
Создает стрелочный прибор.
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.
Устанавливает параметры корпуса стрелочного прибора.
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.
Устанавливает параметры шкалы.
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] Отображение линии шкалы.
Устанавливает параметры отметок шкалы.
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 малые отметки отображаться не будут.
Устанавливает шрифт подписей отметок шкалы.
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] Цвет шрифта.
Устанавливает параметры надписи.
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] Цвет шрифта.
Устанавливает параметры выделенного диапазона.
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] Каким цветом выделить диапазон.
Устанавливает параметры стрелки.
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.
Перерисовывает стрелочный прибор. Функцию необходимо вызывать после изменения каких-либо параметров для применения изменений.
void GaugeRedraw( GAUGE_STR &g // ссылка на структуру стрелочного прибора );
Параметры
g
[in] Ссылка на структуру стрелочного прибора.
Обновляет положение стрелки и отображаемую величину.
void GaugeNewValue( GAUGE_STR &g, // ссылка на структуру стрелочного прибора double v // значение переменной );
Параметры
g
[in] Ссылка на структуру стрелочного прибора.
v
[in] Текущее значение переменной.
Удаляет графические объекты, составляющие стрелочный прибор. Функцию необходимо вызывать из обработчика OnDeinit().
void GaugeDelete( GAUGE_STR &g // ссылка на структуру стрелочного прибора );
Параметры
g
[in] Ссылка на структуру стрелочного прибора.
Выполняет расчет координат объектов стрелочного прибора. При отключенном относительном расположении вернет всегда одни и те же координаты. В противном случае координаты могут отличаться от предыдущих значений, если опорный объект изменил свое расположение или размер.
bool GaugeCalcLocation( GAUGE_STR& g // ссылка на структуру стрелочного прибора );
Параметры
g
[in] Ссылка на структуру стрелочного прибора.
Возвращаемое значение
Вернет true в том случае, если полученные значения координат отличаются от предыдущих. В противном случае вернет false. Если функция вернула true, необходимо вызвать функцию 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_10000 | 10000 | х10k |
MUL_1000 | 1000 | х1k |
MUL_100 | 100 | х100 |
MUL_10 | 10 | х10 |
MUL_1 | 1 | Не отображается |
MUL_01 | 0.1 | /10 |
MUL_001 | 0.01 | /100 |
MUL_0001 | 0.001 | /1k |
MUL_00001 | 0.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. Внешний вид стрелочного прибора после дополнительной настройки
Рассмотрим более сложный пример — индикатор 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() могут вызвать зависания терминала. Поэтому рекомендуется использовать компромиссный способ рисования стрелки (сглаживание без закрашивания).
![Поиск условий входа в рынок с помощью поддержки, сопротивления и ценового действия](https://c.mql5.com/2/19/avatar.png)
![Как обезопасить себя и своего эксперта при торговле на Московской бирже](https://c.mql5.com/2/18/MOEX.png)
![Способ построения уровней сопротивления и поддержки средствами MQL5](https://c.mql5.com/2/19/avatar__1.png)
![Как правильно выбрать торговый сигнал для подписки. Пошаговое руководство](https://c.mql5.com/2/18/signals__1.png)
![MQL5 - Язык торговых стратегий для клиентского терминала MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Побольше бы таких статей. Спасибо за интересный и качественный материал!
Не могу понять что делаю не так но у меня ни один из предоставленных кодов не работает ...
Обьекты создаются где то за пределами но самих приборов нет ...
Может ли автор детально проконсультировать по своему труду ?
Если кто замучился понимать, как указать десятичную дробь для главного значения, то вот в этой функции:
"1" - это и есть количество знаков после запятой.
А само значение указывается через функцию: