- Типы объектов и особенности указания их координат
- Объекты с привязкой ко времени и цене
- Объекты с привязкой к экранным координатам
- Создание объектов
- Удаление объектов
- Поиск объектов
- Обзор функций доступа к свойствам объектов
- Основные свойства объектов
- Координаты времени и цены
- Угол окна привязки и экранные координаты
- Определение точки привязки на объекте
- Управление состоянием объекта
- Приоритет объектов (Z-порядок)
- Настройка отображения объекта: цвет, стиль и рамка
- Настройки шрифта
- Поворот текста на произвольный угол
- Определение ширины и высоты объектов
- Видимость объектов в разрезе таймфреймов
- Назначение кода символа для метки
- Свойства лучей для объектов c прямыми линиями
- Управление нажатым состоянием объекта
- Настройка изображений в объектах-картинках
- Кадрирование (вывод части) изображения
- Свойства поля ввода: выравнивание и "только чтение"
- Ширина канала стандартного отклонения
- Настройка уровней в объектах с их поддержкой
- Дополнительные свойства Ганна, Фибоначчи и Эллиота
- Объект-график
- Перемещение объектов
- Получение времени или цены в заданных точках линий
Настройка отображения объекта: цвет, стиль и рамка
Внешний вид объектов можно менять с помощью множества свойств, изучение которых мы начнем в этом разделе с цвета, стиля, ширины линий и рамок. Другие аспекты форматирования, такие как шрифт, угол наклона и выравнивание текста будут рассмотрены в следующих разделах.
Все свойства из нижеприведенной таблицы имеют типы, совместимые с целыми числами, в связи с чем управляются функциями ObjectGetInteger и ObjectSetInteger.
Идентификатор |
Описание |
Тип свойства |
---|---|---|
OBJPROP_COLOR |
Цвет линии и основного элемента объекта (например, шрифта или заливки) |
color |
OBJPROP_STYLE |
Стиль линии |
ENUM_LINE_STYLE |
OBJPROP_WIDTH |
Толщина линии в пикселях |
int |
OBJPROP_FILL |
Заливка объекта цветом (для OBJ_RECTANGLE, OBJ_TRIANGLE, OBJ_ELLIPSE, OBJ_CHANNEL, OBJ_STDDEVCHANNEL, OBJ_REGRESSION) |
bool |
OBJPROP_BACK |
Объект на заднем плане |
bool |
OBJPROP_BGCOLOR |
Цвет фона для OBJ_EDIT, OBJ_BUTTON, OBJ_RECTANGLE_LABEL |
color |
OBJPROP_BORDER_TYPE |
Тип рамки для прямоугольной панели OBJ_RECTANGLE_LABEL |
ENUM_BORDER_TYPE |
OBJPROP_BORDER_COLOR |
Цвет рамки для поля ввода OBJ_EDIT и кнопки OBJ_BUTTON |
color |
В отличие от большинства объектов с линиями (отдельные вертикальная и горизонтальная, трендовая, циклические, каналы и т.д.), где свойство OBJPROP_COLOR определяет цвет линии, для картинок OBJ_BITMAP_LABEL и OBJ_BITMAP оно определяет цвет рамки, а OBJPROP_STYLE — тип отрисовки рамки.
Перечисление ENUM_LINE_STYLE, используемое для OBJPROP_STYLE, мы уже встречали в главе про индикаторы, в разделе Настройка графических построений.
Следует отличать заливку, выполняемую основным цветом OBJPROP_COLOR, от цвета фона OBJPROP_BGCOLOR. И то, и другое поддерживается разными группами типов объектов — они перечислены в таблице.
Свойство OBJPROP_BACK требует отдельных пояснений. Дело в том, что объекты и индикаторы по умолчанию выводятся поверх графика цен. Пользователь может изменить такое поведение для всего графика целиком, зайдя в диалог Настройка графика и далее закладка Общие, опция График сверху. Этот флажок имеет и программный аналог — свойство CHART_FOREGROUND (см. Режимы отображения графика). Однако иногда желательно убрать на задний план не все объекты, а лишь избранные. Тогда для них можно установить OBJPROP_BACK в true. При этом объект будет перекрываться даже сеткой и разделителями периодов, если они включены на графике.
Когда включен режим заливки OBJPROP_FILL, цвет баров, попадающих внутрь фигуры, зависит от свойства OBJPROP_BACK. По умолчанию, при OBJPROP_BACK равном false, бары, наложившиеся на объект, рисуются инвертированным цветом по отношению к OBJPROP_COLOR (инвертированный цвет получается переключением всех битов в значении цвета на противоположные, например, для 0xFF0080 получается 0x00FF7F). При OBJPROP_BACK равном true, бары рисуются обычным образом, поскольку объект выводится на заднем фоне, "под" графиком (см. пример далее).
Перечисление ENUM_BORDER_TYPE содержит следующие элементы:
Идентификатор |
Внешний вид |
---|---|
BORDER_FLAT |
Плоский |
BORDER_RAISED |
Выпуклый |
BORDER_SUNKEN |
Вогнутый |
Когда рамка плоская (BORDER_FLAT), она отображается линией с цветом, стилем и шириной, согласно свойствам OBJPROP_COLOR, OBJPROP_STYLE, OBJPROP_WIDTH. Выпуклый и вогнутый варианты имитируют объемные фаски по периметру в оттенках OBJPROP_BGCOLOR.
Когда цвет рамки OBJPROP_BORDER_COLOR не задан (по умолчанию, что соответствует clrNone), поле ввода обрамляется линией основного цвета OBJPROP_COLOR, а вокруг кнопки рисуется объемная рамка с фасками в оттенках OBJPROP_BGCOLOR.
Для проверки работы новых свойств рассмотрим скрипт ObjectStyle.mq5. В нем мы создадим 5 прямоугольников типа OBJ_RECTANGLE, то есть с привязкой ко времени и ценам. Они будут равномерно расположены по всей ширине окна, подсвечивая диапазон между максимальной ценой High и минимальной ценой Low в каждом из пяти временных отрезков. Для всех объектов будем настраивать и периодически менять цвет, стиль, толщину линий, а также заполнение и опцию отображения под графиком.
Вновь воспользуемся вспомогательным классом ObjectBuilder, производным от ObjectSelector. В отличие от предыдущего раздела, добавим в ObjectBuilder деструктор, в котором вызовем ObjectDelete.
#include <MQL5Book/ObjectMonitor.mqh>
|
Это позволит возложить на этот класс не только настройку объектов, но и их автоматическое удаление по завершении работы скрипта.
В функции OnStart узнаем количество видимых баров, номер первого бара, а также вычисляем ширину одного прямоугольника в барах.
#define OBJECT_NUMBER 5
|
Зарезервируем под объекты массив умных указателей, чтобы обеспечить вызов деструкторов ObjectBuilder.
AutoPtr<ObjectBuilder> objects[OBJECT_NUMBER]; |
Определим палитру цветов и создадим 5 объектов-прямоугольников.
color colors[OBJECT_NUMBER] = {clrRed, clrGreen, clrBlue, clrMagenta, clrOrange};
|
Здесь для каждого объекта вычисляются координаты двух точек привязки, устанавливаются начальные цвет, стиль и ширина линий.
Далее в бесконечном цикле меняем свойства объектов. При включении ScrollLock анимацию можно приостановить.
const int key = TerminalInfoInteger(TERMINAL_KEYSTATE_SCRLOCK);
|
Вот как это выглядит на графике.
Прямоугольники OBJ_RECTANGLE с разными настройками отображения
Самый левый прямоугольник красного цвета имеет включенный режим заливки и находится на переднем плане — поэтому бары внутри него отображаются контрастным ярко-голубым цветом (clrAqua, широко известным также как Cyan, — это инвертированный clrRed). Прямоугольник фиолетового цвета также имеет заливку, но с опцией заднего плана, поэтому бары в нем отображаются стандартным способом.
Обратите внимание, что оранжевый прямоугольник полностью перекрывает бары в начале и конце своего поддиапазона за счет большой ширины линий и отображения поверх графика.
При включенной заливке ширина линии не учитывается. При ширине границы больше 1 некоторые прерывистые стили линий не применяются.
ObjectShapesDraw
Для второго примера данного раздела вспомним о гипотетической программе рисования фигур, которую мы схематично проектировали в третьей Части, когда изучали ООП. Наш прогресс остановился на том, что в виртуальном методе рисования (а он так и назывался — draw) мы могли лишь вывести сообщение в журнал о том, что рисуем конкретную фигуру. Теперь, после знакомства с графическими объектами, у нас появилась возможность реализовать рисование.
В качестве отправной точки возьмем скрипт Shapes5stats.mq5. Дополненная версия будет называться ObjectShapesDraw.mq5.
Напомним, что помимо базового класса Shape у нас описано несколько классов фигур: Rectangle, Ellipse, Triangle, Square, Circle. Все они удачно ложатся на графические объекты типов OBJ_RECTANGLE, OBJ_ELLIPSE, OBJ_TRIANGLE. Но есть и некоторые нюансы.
Все указанные объекты имеют привязку к координатам времени и цены, в то время как наша программа рисования предполагает унифицированные оси X и Y с точечным позиционированием. В связи с этим нам потребуется особым образом настроить график для рисования и пользоваться функцией ChartXYToTimePrice для пересчета экранных точек во время и цену.
Кроме того, объекты OBJ_ELLIPSE и OBJ_TRIANGLE допускают произвольное вращение (в частности, малый и большой радиус эллипса могут быть повернуты), в то время как OBJ_RECTANGLE всегда имеет свои стороны ориентированными по горизонтали и вертикали. Мы для упрощения примера ограничимся стандартным положением всех фигур.
В принципе, новую реализацию следует рассматривать именно как демонстрацию графических объектов, а не программы рисования. Более правильным подходом для полноценного рисования, лишенного ограничений, которые накладывают графические объекты (поскольку они предназначены в общем-то для других целей — разметки графика), является использование графических ресурсов. Поэтому мы вернемся к переосмыслению программы рисования в главе про ресурсы.
В новом классе Shape избавимся от вложенной структуры Pair с координатами объекта: эта структура служила средством демонстрации нескольких принципов ООП, но сейчас проще вернуть изначальное описание полей int x, y непосредственно в класс Shape. Также мы добавим поле с названием объекта.
class Shape
|
Поле name потребуется для установки свойств графического объекта, а также для его удаления с графика, что логично сделать в деструкторе.
Поскольку разные типы фигур требуют разного количества точек или характерных размеров, добавим в интерфейс Shape в дополнение к виртуальному методу draw еще один — setup:
virtual void setup(const int ¶meters[]) = 0; |
Напомним, что у нас в скрипте реализован вложенный класс Shape::Registrator, который занимался подсчетом количества фигур по типам. Настало время поручить ему кое-что более ответственное — работать в качестве фабрики фигур. Так называемые "фабричные" классы или методы хороши тем, что позволяют унифицированным образом создавать объекты разных классов.
Для этого добавим в Registrator метод для создания фигуры — с параметрами, включающими обязательные координаты первой точки, цвет и массив дополнительных параметров (каждая фигура сможет интерпретировать его по своим правилам, а в перспективе считывать из или записывать в файл).
virtual Shape *create(const int px, const int py, const color back,
|
Метод является абстрактным виртуальным, потому что конкретные типы фигур смогут создавать только производные классы-регистраторы, описываемые в классах-наследниках Shape. Чтобы упростить написание производных классов-регистраторов, введем шаблонный класс MyRegistrator с подходящей для всех случаев реализацией метода create.
template<typename T>
|
Здесь мы вызываем конструктор некоей заранее неизвестной фигуры T, донастраиваем её с помощью вызова setup и возвращаем экземпляр вызывающему коду.
Вот как это используется в классе Rectangle, у которого два дополнительных параметра обозначают ширину и высоту.
class Rectangle : public Shape
|
При создании фигуры её имя будет содержать не только имя класса (typename), но и порядковый номер экзмепляра, подсчитываемый в вызове r.increment().
Другие классы фигур описаны аналогично.
Теперь настало время заглянуть в метод draw для Rectangle. В нем мы переводим пару точек (x,y) и (x + dx, y + dy) в координаты время/цена с помощью ChartXYToTimePrice и создаем объект OBJ_RECTANGLE.
void draw() override
|
Разумеется, не забываем установить цвет OBJPROP_COLOR и заливку OBJPROP_FILL.
Для класса Square практически ничего не требуется менять: достаточно лишь установить равными dx и dy.
Для класса Ellipse два дополнительных параметра dx и dy определяют малый и большой радиусы, откладываемые относительно центра (x,y). Соответственно, в методе draw мы рассчитываем 3 точки привязки и создаем объект OBJ_ELLIPSE.
class Ellipse : public Shape
|
Circle — это частный случай эллипса с равными радиусами.
Наконец, треугольники у нас поддерживаются на данном этапе только равносторонние: размер стороны содержится в дополнительном поле dx. С их методом draw предлагается ознакомиться в исходном коде самостоятельно.
Новый скрипт будет, как и прежде, генерировать заданное количество случайных фигур. Их созданием занимается функция addRandomShape.
Shape *addRandomShape()
|
Именно здесь мы видим использование фабричного метода create, вызываемого у случайно выбранного объекта-регистратора под номером n. Если мы решим позднее добавить другие классы фигур, нам не потребуется ничего менять в логике генерации.
Все фигуры размещаются в центральной части окна и имеют размеры не больше четверти окна.
Осталось рассмотреть непосредственно вызовы функции addRandomShape и особую настройку графика, которую мы уже упоминали.
Для обеспечения "квадратного" представления точек на экране установим режим CHART_SCALEFIX_11. Кроме того выберем самый плотный (сжатый) масштаб по оси времени CHART_SCALE (0), потому что в нем один бар занимает 1 пиксель по горизонтали (максимальная точность). Наконец, отключим отображение самого графика, поставив CHART_SHOW в false.
void OnStart()
|
Для хранения фигур зарезервируем массив умных указателей и заполним его случайными фигурами.
#define FIGURES 21
|
Затем запускаем бесконечный цикл, пока пользователь не остановит скрипт, в котором слегка двигаем фигуры с помощью метода move.
while(!IsStopped())
|
В конце восстанавливаем настройки графика.
// недостаточно отключить CHART_SCALEFIX_11, нужно CHART_SCALEFIX
|
На следующем скриншоте показано, как может выглядеть график с нарисованными фигурами.
Объекты-фигуры на графике
Особенностью отрисовки объектов является "умножение" цветов в тех местах, где они перекрываются.
Поскольку ось Y идет сверху вниз, все треугольники получаются перевернутыми, но это не критично, потому что мы в любом случае собираемся переделать программу рисования на базе ресурсов.