- Типы объектов и особенности указания их координат
- Объекты с привязкой ко времени и цене
- Объекты с привязкой к экранным координатам
- Создание объектов
- Удаление объектов
- Поиск объектов
- Обзор функций доступа к свойствам объектов
- Основные свойства объектов
- Координаты времени и цены
- Угол окна привязки и экранные координаты
- Определение точки привязки на объекте
- Управление состоянием объекта
- Приоритет объектов (Z-порядок)
- Настройка отображения объекта: цвет, стиль и рамка
- Настройки шрифта
- Поворот текста на произвольный угол
- Определение ширины и высоты объектов
- Видимость объектов в разрезе таймфреймов
- Назначение кода символа для метки
- Свойства лучей для объектов c прямыми линиями
- Управление нажатым состоянием объекта
- Настройка изображений в объектах-картинках
- Кадрирование (вывод части) изображения
- Свойства поля ввода: выравнивание и "только чтение"
- Ширина канала стандартного отклонения
- Настройка уровней в объектах с их поддержкой
- Дополнительные свойства Ганна, Фибоначчи и Эллиота
- Объект-график
- Перемещение объектов
- Получение времени или цены в заданных точках линий
Определение ширины и высоты объектов
Некоторые типы объектов позволяют устанавливать свои размеры в пикселях. К их числу относятся OBJ_BUTTON, OBJ_CHART, OBJ_BITMAP, OBJ_BITMAP_LABEL, OBJ_EDIT, OBJ_RECTANGLE_LABEL. Кроме того, объекты OBJ_LABEL поддерживают чтение (но не установку) размеров, поскольку надписи автоматически расширяются или сужаются под содержащийся в них текст. Попытка обратиться к свойствам у других типов объектов приведет к ошибке OBJECT_WRONG_PROPERTY (4203).
Идентификатор |
Описание |
---|---|
OBJPROP_XSIZE |
Ширина объекта по оси X в пикселях |
OBJPROP_YSIZE |
Высота объекта по оси Y в пикселях |
Оба размера — это целые числа и потому обрабатываются функциями ObjectGetInteger/ObjectSetInteger.
Особая обработка размеров производится для объектов OBJ_BITMAP и OBJ_BITMAP_LABEL.
Без назначения картинки эти объекты позволяют задать произвольный размер. При этом они рисуются прозрачными (видна только рамка, если её не "спрятать" также, установив цвет clrNone), но получают все события, в частности, о перемещении мыши (с показом текстового описания, если оно есть, во всплывающей подсказке) и нажатиях её кнопок на объекте.
Когда картинка назначена, она по умолчанию определяет высоту и ширину объекта. Однако MQL-программа может установить меньшие размеры и выбрать фрагмент картинки для показа — подробнее об этом в разделе Кадрирование. Если высоту или ширину попытаться установить больше размера картинки, она перестает отображаться, а размеры объекта не меняются.
В качестве примера разработаем усовершенствованную версию скрипта ObjectAnchorLabel.mq5 из раздела Определение точки привязки на объекте. Там мы перемещали надпись по окну и "разворачивали" её по достижению любой из границ окна, однако делали это только с учетом точки привязки. В связи с этим, в зависимости от расположения точки привязки на объекте, могла складываться ситуация, когда надпись почти полностью "выезжала" за пределы окна. Например, если точка привязки находилась на правой стороне объекта, то при движении влево практически весь текст уходил за левую границу окна, прежде чем точка привязки упиралась в край.
В новом скрипте ObjectSizeLabel.mq5 мы будем учитывать размер объекта и менять направление перемещения, как только он касается края окна любой своей стороной.
Для правильной реализации такого режима следует учитывать, что каждый угол окна, используемый в качестве центра отсчета координат до точки привязки на объекте, обуславливает характерное направление обоих осей X и Y. Например, если пользователь выбрал во входной переменной ENUM_BASE_CORNER Corner левый верхний угол, то X увеличивается слева направо, а Y — сверху вниз. Если же центром считается правый нижний угол, то X увеличивается справа налево от него, а Y — снизу вверх.
Различное взаимное сочетание угла привязки в окне и точки привязки на объекте требует разной корректировки расстояний между краями объекта и границами окна. В частности, когда выбран один из правых углов и одна из точек привязки на правой стороне объекта, то поправка у правой границы окна не требуется, а у противоположной — левой — мы должны учитывать ширину объекта (чтобы его габариты не вышли налево за пределы окна).
Это правило о внесении поправки на размер объекта можно обобщить:
- на границе окна, прилегающей к углу привязки, поправка нужна при нахождении точки привязки на дальней стороне объекта по отношению к этому углу;
- на границе окна, противоположной углу привязки, поправка нужна при нахождении точки привязки на ближней стороне объекта по отношению к этому углу.
Иными словами, если в названии угла (в элементе ENUM_BASE_CORNER) и точки привязки (в элементе ENUM_ANCHOR_POINT) встречается общее слово (например, RIGHT), поправка нужна на дальней стороне окна (то есть удаленной от выбранного угла). Если же в сочетании сторон ENUM_BASE_CORNER и ENUM_ANCHOR_POINT обнаруживаются противоположные направления (например, LEFT и RIGHT), поправка нужна у ближайшей стороны окна. Данные правила работают одинаково для горизонтальной и вертикальной оси.
Дополнительно следует учитывать, что точка привязки может быть в середине какой-либо стороны объекта. Тогда в перпендикулярном направлении требуется отступ от границ окна в половину размера объекта.
Особый случай представляет точка привязки в центре объекта. Для неё следует всегда иметь запас расстояния в любом направлении, равный половине размера объекта.
Описанная логика реализована в специальной функции GetMargins. Она принимает на вход выбранный угол и точку привязки, а также размеры объекта (dx и dy). Возвращает функция структуру с 4-мя полями, содержащими размеры дополнительных отступов, которые следует отложить от точки привязки в направлении ближних и дальних границ окна, чтобы объект не вышел за пределы видимости. Отступы резервируют расстояние согласно габаритам и относительному расположению самого объекта.
struct Margins
|
Для унификации алгоритма введены следующие макроопределения направлений (сторон):
#define LEFT 0x1
|
С их помощью определены битовые маски (комбинации), описывающие элементы перечислений ENUM_BASE_CORNER и ENUM_ANCHOR_POINT.
const int corner_flags[] = // флаги для элементов ENUM_BASE_CORNER
|
Каждый из массивов corner_flags и anchor_flags содержит ровно столько элементов, сколько имеется в соответствующем перечислении.
Далее идет основной фрагмент функции. Прежде всего "разбираемся" с самым простым вариантом: центральной точкой привязки.
if(anchor == ANCHOR_CENTER)
|
Для анализа остальных ситуаций воспользуемся битовыми масками из вышеприведенных массивов путем прямой адресации по полученным значениями corner и anchor.
const int mask = corner_flags[corner] & anchor_flags[anchor];
|
Если угол и точка привязки на одной стороне по горизонтали, сработает следующее условие, и будет сделана поправка на ширину объекта у дальней границы окна.
if((mask & (LEFT | RIGHT)) != 0)
|
Если они не на одной стороне, то могут быть на противоположных, а может быть и случай, что точка привязки в середине горизонтальной стороны (сверху или снизу). Проверка на точку привязки в середине делается с помощью выражения (anchor_flags[anchor] & (LEFT | RIGHT)) == 0 — тогда поправка равна половине ширины объекта.
else
|
Иначе, при противоположной ориентации угла и точки привязки делаем поправку в ширину объекта у ближней границы окна.
Аналогичные проверки выполняются для оси Y.
if((mask & (UPPER | LOWER)) != 0)
|
Теперь функция GetMargins готова, и можно приступать к основному коду скрипта в функции OnStart. Как и ранее, мы определяем размер окна, рассчитываем начальные координаты в центре, создаем объект OBJ_LABEL и выделяем его для наглядности.
void OnStart()
|
Для анимации в бесконечном цикле предусмотрены переменные pass (счетчик итераций) и anchor (точка привязки, которая будет периодически выбираться случайным образом).
int pass = 0;
|
Но по сравнению с ObjectAnchorLabel.mq5 сделан и ряд изменений.
Мы не станем генерировать случайные перемещения объекта. Вместо этого зададим постоянную скорость 5 пикселей по диагонали.
int px = 5, py = 5; |
Для хранения размера надписи зарезервируем две новых переменных.
int dx = 0, dy = 0; |
Результат подсчета дополнительных отступов будем сохранять в переменной m типа Margins.
Margins m = {}; |
Далее следует непосредственно цикл перемещения и модификации объекта. В нем на каждой 75-ой итерации (одна итерация 100 мсек, см. далее) мы случайно выбираем новую точку привязки, формируем из неё новый текст (содержимое объекта) и ждем, когда изменения применятся к объекту (вызываем ChartRedraw). Последнее необходимо, потому что размер надписи автоматически подгоняется под содержимое, а нам важен новый размер, чтобы корректно посчитать отступы в вызове GetMargins.
Размеры мы получаем с помощью вызовов ObjectGetInteger со свойствами OBJPROP_XSIZE и OBJPROP_YSIZE.
for( ;!IsStopped(); ++pass)
|
После того как точка привязки и все расстояния известны, выполняем перемещение объекта. Если он "упрется" в стенку, меняем направление движения на противоположное (px на -px или py на -py, в зависимости от стороны).
// отскок от границ окна, объект полностью видимый
|
Остается обновить состояние самого объекта: вывести текущие координаты в надпись, и назначить их свойствам OBJPROP_XDISTANCE и OBJPROP_YDISTANCE.
ObjectSetString(0, name, OBJPROP_TEXT, " " + EnumToString(anchor)
|
После изменения объекта вызываем ChartRedraw и ждем 100 мсек, чтобы обеспечить достаточно плавную анимацию.
ChartRedraw();
|
В концовке цикла мы снова проверяем размер окна, так как пользователь может его изменить в процессе работы скрипта, а также повторяем запрос размеров.
h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, t) - 1;
|
Некоторые прочие нововведения скрипта ObjectSizeLabel.mq5 мы опустили ради краткости — желающие могут обратиться к коду. В частности, были использованы отличительные цвета для надписи: каждый конкретный цвет соответствует собственной точке привязки, что делает более заметными моменты её переключения. Кроме того, вы можете нажать Delete во время работы скрипта: это удалит выделенный объект с графика, и скрипт автоматически завершится.