Графика в библиотеке DoEasy (Часть 90): События стандартных графических объектов. Базовый функционал
Содержание
- Концепция
- Доработка классов библиотеки
- События стандартных графических объектов
- Тестирование
- Что дальше
Концепция
При работе с графическими объектами, предоставляемыми нам клиентским терминалом, иногда встают задачи определения некоторых значений свойств этих объектов программно. Программа может отслеживать значение какой-либо из линий графического объекта и при, например, пересечении его ценой, выполнять заложенный в неё алгоритм. Линия может быть смещена при перемещении графического объекта пользователем терминала. Соответственно, программа должна на это отреагировать. Но программа должна либо знать, что значение отслеживаемой линии изменилось, либо постоянно опрашивать значения свойств графического объекта, что является затратным действием, и об этом напрямую говорит и справка.
Гораздо удобнее иметь функционал, сигнализирующий нам о каком-либо изменении какого-либо графического объекта. И такой функционал есть в терминале — это обработчик событий OnChartEvent(). Добавим в его арсенал несколько событий, которые будут нам уточнять в нашей библиотеке, что именно произошло с графическим объектом, а программа, работающая под управлением библиотеки, сможет точно знать, что произошло с графическим объектом, и узнать из его свойств, какое именно свойство было изменено.
Разделим работу по созданию описанного функционала на два этапа — сначала создадим общие события, которые могут произойти с графическими объектами, а затем добавим функционал, уточняющий что именно произошло с объектом, и позволяющий быстро программно это узнать. В принципе, у нас уже всё готово для создания такого функционала, и нам остаётся лишь немного доработать классы библиотеки и создать место в обработчике событий, в котором будут обрабатываться события, происходящие с графическими объектами.
Базовые события графических объектов определим такими:
- Создание нового графического объекта,
- Изменение свойств графического объекта,
- Переименование графического объекта,
- Удаление графического объекта,
- Удаление графического объекта вместе с окном графика.
Вот именно эти события мы сегодня сделаем. И они будут отправляться в обработчик OnChartEvent(). В следующей статье создадим обработку каждого из этих событий так, чтобы мы могли точно знать какие свойства были изменены у графического объекта.
Стоит отметить, что переименование графического объекта является в свою очередь изменением свойств графического объекта — изменение свойства "Имя". Но я решил его вынести в отдельное событие для упрощения обработки, так как изменение имени вызывает несколько событий подряд — удаление графического объекта, создание нового и изменение его свойств. Все эти состояния у нас уже обработаны в библиотеке, и она верно вычисляет именно событие переименования. Поэтому мы его и будем отсылать для дальнейшей программной обработки этого события.
Если удаление графического объекта на данный момент позволяет точно узнать какой объект был удалён и сообщить его имя, то при удалении окна графика, на котором были графические объекты, на данном этапе мы можем знать лишь количество графических объектов, находящихся на удалённом графике. Эти два события пока будут лишь сообщать нам об удалении графических объектов. В последующем, может быть, обдумаем целесообразность запоминания свойств удалённых объектов. Пока я не вижу острую необходимость помнить свойства удалённых объектов.
Доработка классов библиотеки
Для отслеживания факта выделения графического объекта нам необходимо добавить обработку события щелчка по графическому объекту. Двойной щелчок по невыделенному графическому объекту выделяет его для редактирования, а двойной щелчок по выделенному объекту снимает с него выделение. Для обработки такого события объекта на текущем графике нам достаточно добавить в обработчик событий новый идентификатор события, а вот для обработки такого события на других графиках, нам нужно доработать индикатор контроля событий, который у нас автоматически устанавливается на каждое вновь открываемое окно графика.
Откроем файл этого индикатора \MQL5\Indicators\DoEasy\EventControl.mq5 и добавим новый идентификатор события, которое будет отсылаться на график управляющей программы:
//+------------------------------------------------------------------+ //| EventControl.mq5 | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/ru/users/artmedia70" #property version "1.00" #property indicator_chart_window #property indicator_plots 0 //--- input parameters input long InpChartSRC = 0; input long InpChartDST = 0; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator shortname IndicatorSetString(INDICATOR_SHORTNAME,"EventSend_From#"+(string)InpChartSRC+"_To#"+(string)InpChartDST); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { return rates_total; } //+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- if(id==CHARTEVENT_OBJECT_CHANGE || id==CHARTEVENT_OBJECT_DRAG || id==CHARTEVENT_OBJECT_CLICK) { EventChartCustom(InpChartDST,(ushort)id,InpChartSRC,dparam,sparam); } } //+------------------------------------------------------------------+
В файл \MQL5\Include\DoEasy\Data.mqh впишем индексы новых сообщений:
//--- CGraphElementsCollection MSG_GRAPH_OBJ_FAILED_GET_ADDED_OBJ_LIST, // Не удалось получить список вновь добавленных объектов MSG_GRAPH_OBJ_FAILED_DETACH_OBJ_FROM_LIST, // Не удалось изъять графический объект из списка MSG_GRAPH_OBJ_CREATE_EVN_CTRL_INDICATOR, // Создан индикатор контроля и отправки событий MSG_GRAPH_OBJ_FAILED_CREATE_EVN_CTRL_INDICATOR, // Не удалось создать индикатор контроля и отправки событий MSG_GRAPH_OBJ_ADDED_EVN_CTRL_INDICATOR, // Индикатор контроля и отправки событий успешно добавлен на график MSG_GRAPH_OBJ_FAILED_ADD_EVN_CTRL_INDICATOR, // Не удалось добавить индикатор контроля и отправки событий на график MSG_GRAPH_OBJ_CLOSED_CHARTS, // Закрыто окон графиков: MSG_GRAPH_OBJ_OBJECTS_ON_CLOSED_CHARTS, // С ними удалено объектов: MSG_GRAPH_OBJ_FAILED_CREATE_EVN_OBJ, // Не удалось создать объект-событие для графического объекта MSG_GRAPH_OBJ_FAILED_ADD_EVN_OBJ, // Не удалось добавить объект-событие в список MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CREATE, // Создан новый графический объект MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CHANGE, // Изменено свойство графического объекта MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_RENAME, // Графический объект переименован MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DELETE, // Графический объект удалён MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DEL_CHART, // Графический объект удалён вместе с графиком }; //+------------------------------------------------------------------+
и тексты сообщений, соответствующие вновь добавленным индексам:
//--- CGraphElementsCollection {"Не удалось получить список вновь добавленных объектов","Failed to get the list of newly added objects"}, {"Не удалось изъять графический объект из списка","Failed to detach graphic object from the list"}, {"Создан индикатор контроля и отправки событий","An indicator for monitoring and sending events has been created"}, {"Не удалось создать индикатор контроля и отправки событий","Failed to create indicator for monitoring and sending events"}, {"Индикатор контроля и отправки событий успешно добавлен на график ","The indicator for monitoring and sending events has been successfully added to the chart"}, {"Не удалось добавить индикатор контроля и отправки событий на график ","Failed to add the indicator of monitoring and sending events to the chart "}, {"Закрыто окон графиков: ","Closed chart windows: "}, {"С ними удалено объектов: ","Objects removed with them: "}, {"Не удалось создать объект-событие для графического объекта","Failed to create event object for graphic object"}, {"Не удалось добавить объект-событие в список","Failed to add event object to list"}, {"Создан новый графический объект","New graphic object created"}, {"Изменено свойство графического объекта","Changed graphic object property"}, {"Графический объект переименован","Graphic object renamed"}, {"Графический объект удалён","Graphic object deleted"}, {"Графический объект удалён вместе с графиком","The graphic object has been removed along with the chart"}, }; //+---------------------------------------------------------------------+
Внесём исправления в файле \MQL5\Include\DoEasy\Defines.mqh.
Из списка возможных событий графических объектов удалим событие перемещения объекта так как это изменение его свойств, которое и так уже есть в этом перечислении событий графического объекта:
//+------------------------------------------------------------------+ //| Список возможных событий графических объектов | //+------------------------------------------------------------------+ enum ENUM_GRAPH_OBJ_EVENT { GRAPH_OBJ_EVENT_NO_EVENT = CHART_OBJ_EVENTS_NEXT_CODE,// Нет события GRAPH_OBJ_EVENT_CREATE, // Событие "Создание нового графического объекта" GRAPH_OBJ_EVENT_CHANGE, // Событие "Изменение свойств графического объекта" GRAPH_OBJ_EVENT_MOVE, // Событие "Перемещение графического объекта" GRAPH_OBJ_EVENT_RENAME, // Событие "Переименование графического объекта" GRAPH_OBJ_EVENT_DELETE, // Событие "Удаление графического объекта" };
И добавим к этому списку новую константу для события удаления графического объекта вместе с графиком:
//+------------------------------------------------------------------+ //| Данные для работы с графическими элементами | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Список возможных событий графических объектов | //+------------------------------------------------------------------+ enum ENUM_GRAPH_OBJ_EVENT { GRAPH_OBJ_EVENT_NO_EVENT = CHART_OBJ_EVENTS_NEXT_CODE,// Нет события GRAPH_OBJ_EVENT_CREATE, // Событие "Создание нового графического объекта" GRAPH_OBJ_EVENT_CHANGE, // Событие "Изменение свойств графического объекта" GRAPH_OBJ_EVENT_RENAME, // Событие "Переименование графического объекта" GRAPH_OBJ_EVENT_DELETE, // Событие "Удаление графического объекта" GRAPH_OBJ_EVENT_DEL_CHART, // Событие "Удаление графического объекта вместе с окном графика" }; #define GRAPH_OBJ_EVENTS_NEXT_CODE (GRAPH_OBJ_EVENT_DEL_CHART+1) // Код следующего события после последнего кода события графических объектов //+------------------------------------------------------------------+
Код следующего события после кодов событий графических объектов теперь будет рассчитываться от последнего добавленного в перечисление значения.
При создании любого стандартного графического объекта, в его конструкторе мы считываем данные из физического объекта на графике и записываем их в свойства объекта класса. Некоторые из свойств графических объектов у нас прописаны как общие для всех графических объектов — они есть у каждого объекта, и прописываются в базовом классе всех графических объектов библиотеки.
Алгоритм такой: сначала считываются параметры от графического объекта при помощи стандартных ObjectGetXXX-функций (Integer, Double и String), затем, при успешном получении данных, эти параметры записываются сначала в свойство объекта класса базового графического объекта CGBaseObj, а затем — в свойства его наследника. Для стандартных графических объектов это класс CGStdGraphObj.
И тут у нас возникает некая коллизия. У нас есть методы для установки параметров графическому объекту. И эти методы должны установить переданное в них значение свойства как самому графическому объекту, так и в соответствующее свойство объекта класса — при успешной установке параметра графическому объекту при помощи функций ObjectSetXXX (Integer, Double и String). Но иногда нам нужно просто установить в свойство класса уже известное значение параметра графического объекта. И для этого нам нет необходимости ни считывать ещё раз это значение, ни присваивать его графическому объекту, а просто назначить его значение переменной класса. Методы же Set базового графического объекта библиотеки у нас сначала устанавливают значение в параметры графического объекта, а уже затем — прописывают их в переменные класса. Чтобы избежать лишней работы по установке уже известного свойства графического объекта опять в него же, и просто прописать его в переменную класса, мы в такие методы добавим bool-переменную only_prop, которая будет указывать на необходимость установки значения либо только в значения переменных, либо и в параметры графического объекта, и в свойства объекта класса. Если эта входная переменная будет иметь значение true, то параметры будут записываться только в переменные класса, в противном случае — сначала значение будет установлено в графический объект, а уже затем — в переменные класса.
В классе базового объекта всех графических объектов библиотеки \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh добавим в такие методы эту переменную и поменяем логику методов:
//--- Устанавливает флаг "Объект на заднем плане" bool SetFlagBack(const bool flag,const bool only_prop) { ::ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_BACK,flag)) || only_prop) { this.m_back=flag; return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); return false; }
Здесь: если переменная имеет значение false и графическому объекту установлено свойство, или если переменная имеет значение true,
то вписываем в переменную класса переданное в метод значение.
Все методы этого класса, доработанные таким образом:
//--- Устанавливает флаг "Объект на заднем плане" bool SetFlagBack(const bool flag,const bool only_prop) { ::ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_BACK,flag)) || only_prop) { this.m_back=flag; return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); return false; } //--- Устанавливает флаг "Выделенность объекта" bool SetFlagSelected(const bool flag,const bool only_prop) { ::ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_SELECTED,flag)) || only_prop) { this.m_selected=flag; return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); return false; } //--- Устанавливает флаг "Выделенность объекта" bool SetFlagSelectable(const bool flag,const bool only_prop) { ::ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_SELECTABLE,flag)) || only_prop) { this.m_selectable=flag; return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); return false; } //--- Устанавливает флаг "Запрет на показ имени графического объекта в списке объектов терминала" bool SetFlagHidden(const bool flag,const bool only_prop) { ::ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_SELECTABLE,flag)) || only_prop) { this.m_hidden=flag; return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); return false; } //--- Устанавливает Приоритет графического объекта на получение события нажатия мышки на графике bool SetZorder(const long value,const bool only_prop) { ::ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_ZORDER,value)) || only_prop) { this.m_zorder=value; return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); return false; } //--- Устанавливает видимость/невидимость объекта на всех таймфреймах bool SetVisible(const bool flag,const bool only_prop) { long value=(flag ? OBJ_ALL_PERIODS : OBJ_NO_PERIODS); ::ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_TIMEFRAMES,value)) || only_prop) { this.m_visible=flag; return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); return false; } //--- Устанавливает флаги видимости на таймфреймах, записанных как флаги bool SetVisibleOnTimeframes(const int flags,const bool only_prop) { ::ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_TIMEFRAMES,flags)) || only_prop) { this.m_timeframes_visible=flags; return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); return false; } //--- Добавляет флаг видимости на указанном таймфрейме bool SetVisibleOnTimeframe(const ENUM_TIMEFRAMES timeframe,const bool only_prop) { int flags=this.m_timeframes_visible; switch(timeframe) { case PERIOD_M1 : flags |= OBJ_PERIOD_M1; break; case PERIOD_M2 : flags |= OBJ_PERIOD_M2; break; case PERIOD_M3 : flags |= OBJ_PERIOD_M3; break; case PERIOD_M4 : flags |= OBJ_PERIOD_M4; break; case PERIOD_M5 : flags |= OBJ_PERIOD_M5; break; case PERIOD_M6 : flags |= OBJ_PERIOD_M6; break; case PERIOD_M10 : flags |= OBJ_PERIOD_M10; break; case PERIOD_M12 : flags |= OBJ_PERIOD_M12; break; case PERIOD_M15 : flags |= OBJ_PERIOD_M15; break; case PERIOD_M20 : flags |= OBJ_PERIOD_M20; break; case PERIOD_M30 : flags |= OBJ_PERIOD_M30; break; case PERIOD_H1 : flags |= OBJ_PERIOD_H1; break; case PERIOD_H2 : flags |= OBJ_PERIOD_H2; break; case PERIOD_H3 : flags |= OBJ_PERIOD_H3; break; case PERIOD_H4 : flags |= OBJ_PERIOD_H4; break; case PERIOD_H6 : flags |= OBJ_PERIOD_H6; break; case PERIOD_H8 : flags |= OBJ_PERIOD_H8; break; case PERIOD_H12 : flags |= OBJ_PERIOD_H12; break; case PERIOD_D1 : flags |= OBJ_PERIOD_D1; break; case PERIOD_W1 : flags |= OBJ_PERIOD_W1; break; case PERIOD_MN1 : flags |= OBJ_PERIOD_MN1; break; default : return true; } ::ResetLastError(); if((!only_prop && ::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_TIMEFRAMES,flags)) || only_prop) { this.m_timeframes_visible=flags; return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); return false; }
Теперь мы сможем выбрать как записать свойство — либо установить новое свойство графическому объекту и переменной класса (чтобы оно соответствовало вновь установленному значению), либо установить переменной класса уже известное значение параметра графического объекта (как это делается в конструкторе класса при создании нового графического объекта). Это позволит нам избежать лишнего обращения к достаточно медленным функциям работы с графическими объектами в виду их синхронности.
В файле \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh подправим вызов доработанных методов:
//--- Возвращает (1) идентификатор элемента, (2) номер элемента в списке, (3) флаг наличия тени у формы, (4) цвет фона чарта int ID(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ID); } int Number(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_NUM); } bool IsShadow(void) const { return this.m_shadow; } color ChartColorBackground(void) const { return this.m_chart_color_bg; } //--- Устанавливает объект выше всех void BringToTop(void) { CGBaseObj::SetVisible(false,false); CGBaseObj::SetVisible(true,false);} //--- (1) Показывает, (2) скрывает элемент virtual void Show(void) { CGBaseObj::SetVisible(true,false); } virtual void Hide(void) { CGBaseObj::SetVisible(false,false); } //+------------------------------------------------------------------+ //| Методы получения растровых данных | //+------------------------------------------------------------------+
В файле \MQL5\Include\DoEasy\Objects\Graph\Form.mqh так же подправим два метода:
//+------------------------------------------------------------------+ //| Создаёт объект тени | //+------------------------------------------------------------------+ void CForm::CreateShadowObj(const color colour,const uchar opacity) { //--- Если флаг тени выключен, или объект тени уже существует - уходим if(!this.m_shadow || this.m_shadow_obj!=NULL) return; //--- Рассчитываем координаты объекта тени в соответствии с отступом сверху и слева int x=this.CoordX()-OUTER_AREA_SIZE; int y=this.CoordY()-OUTER_AREA_SIZE; //--- Рассчитываем ширину и высоту в соответствии с отступом сверху, снизу, слева и справа int w=this.Width()+OUTER_AREA_SIZE*2; int h=this.Height()+OUTER_AREA_SIZE*2; //--- Создаём новый объект тени и записываем указатель на него в переменную this.m_shadow_obj=new CShadowObj(this.ChartID(),this.SubWindow(),this.CreateNameDependentObject("Shadow"),x,y,w,h); if(this.m_shadow_obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_FORM_OBJECT_ERR_FAILED_CREATE_SHADOW_OBJ)); return; } //--- Устанавливаем свойства созданному объекту-тени this.m_shadow_obj.SetID(this.ID()); this.m_shadow_obj.SetNumber(-1); this.m_shadow_obj.SetOpacityShadow(opacity); this.m_shadow_obj.SetColorShadow(colour); this.m_shadow_obj.SetMovable(true); this.m_shadow_obj.SetActive(false); this.m_shadow_obj.SetVisible(false,false); //--- Объект-форму перемещаем на передний план this.BringToTop(); } //+------------------------------------------------------------------+ //| Рисует тень | //+------------------------------------------------------------------+ void CForm::DrawShadow(const int shift_x,const int shift_y,const color colour,const uchar opacity=127,const uchar blur=4) { //--- Если флаг тени выключен - уходим if(!this.m_shadow) return; //--- Если объект тени не создан - создадим его if(this.m_shadow_obj==NULL) this.CreateShadowObj(colour,opacity); //--- Если объект тени существует, рисуем на нём тень, //--- устанавливаем флаг видимости объекта-тени и //--- перемещаем объект-форму на передний план if(this.m_shadow_obj!=NULL) { this.m_shadow_obj.DrawShadow(shift_x,shift_y,blur); this.m_shadow_obj.SetVisible(true,false); this.BringToTop(); } } //+------------------------------------------------------------------+
В классе стандартного графического объекта сделаем все методы для установки свойств графических объектов с булевым типом возвращаемого значения (сейчас методы не возвращают никакого значения и, соответственно, имеют тип void). Это нужно для того, чтобы соответствующие им методы родительского класса, имеющие тоже возвращаемый тип bool, были переназначены в дочернем классе, и при написании кода не было в подсказке двух методов — с типом bool от базового объекта, и с типом void — от его наследника, что могло приводить к путанице.
Рассмотрим на примере метода для установки номера подокна:
//--- Номер подокна графика int SubWindow(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_WND_NUM,0); } bool SetSubWindow(void) { if(!CGBaseObj::SetSubwindow(CGBaseObj::ChartID(),CGBaseObj::Name())) return false; this.SetProperty(GRAPH_OBJ_PROP_WND_NUM,0,CGBaseObj::SubWindow()); return true; }
Так как номер подокна ищется по идентификатору графика и имени объекта, то в метод базового класса для установки номера подокна просто передаются идентификатор графика, уже записанный в базовом объекте, и имя графического объекта. Если в базовом объекте установить значение не удалось — возвращаем false, иначе — устанавливаем в свойство объекта значение, только что записанное в базовый объект, и возвращаем true.
И на примере метода для установки видимости объекта на всех таймфреймах:
//--- Видимость объекта на всех таймфреймах bool Visible(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0); } bool SetFlagVisible(const bool flag,const bool only_prop) { if(!CGBaseObj::SetVisible(flag,only_prop)) return false; this.SetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0,flag); return true; }
Здесь в метод передаётся новый параметр, ранее добавленный нами для указания в какие именно значения будем записывать — только в свойство объекта, или в параметры графического объекта и в свойства класса. Далее устанавливаем эти свойства в базовый объект и, если не получилось — возвращаем false.
Иначе — записываем значение в свойство объекта класса и возвращаем true.
Все остальные методы доработаны идентично вышерассмотренным:
//--- Объект на заднем плане bool Back(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_BACK,0); } bool SetFlagBack(const bool flag,const bool only_prop) { if(!CGBaseObj::SetFlagBack(flag,only_prop)) return false; this.SetProperty(GRAPH_OBJ_PROP_BACK,0,flag); return true; } //--- Приоритет графического объекта на получение события нажатия мышки на графике long Zorder(void) const { return this.GetProperty(GRAPH_OBJ_PROP_ZORDER,0); } bool SetZorder(const long value,const bool only_prop) { if(!CGBaseObj::SetZorder(value,only_prop)) return false; this.SetProperty(GRAPH_OBJ_PROP_ZORDER,0,value); return true; } //--- Запрет на показ имени графического объекта в списке объектов терминала bool Hidden(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_HIDDEN,0); } bool SetFlagHidden(const bool flag,const bool only_prop) { if(!CGBaseObj::SetFlagHidden(flag,only_prop)) return false; this.SetProperty(GRAPH_OBJ_PROP_HIDDEN,0,flag); return true; } //--- Выделенность объекта bool Selected(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTED,0); } bool SetFlagSelected(const bool flag,const bool only_prop) { if(!CGBaseObj::SetFlagSelected(flag,only_prop)) return false; this.SetProperty(GRAPH_OBJ_PROP_SELECTED,0,flag); return true; } //--- Доступность объекта bool Selectable(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTABLE,0); } bool SetFlagSelectable(const bool flag,const bool only_prop) { if(!CGBaseObj::SetFlagSelectable(flag,only_prop)) return false; this.SetProperty(GRAPH_OBJ_PROP_SELECTABLE,0,flag); return true; } //--- Координата времени datetime Time(const int modifier) const { return (datetime)this.GetProperty(GRAPH_OBJ_PROP_TIME,modifier); } bool SetTime(const datetime time,const int modifier) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_TIME,modifier,time)) return false; this.SetProperty(GRAPH_OBJ_PROP_TIME,modifier,time); return true; } //--- Цвет color Color(void) const { return (color)this.GetProperty(GRAPH_OBJ_PROP_COLOR,0); } bool SetColor(const color colour) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_COLOR,colour)) return false; this.SetProperty(GRAPH_OBJ_PROP_COLOR,0,colour); return true; } //--- Стиль ENUM_LINE_STYLE Style(void) const { return (ENUM_LINE_STYLE)this.GetProperty(GRAPH_OBJ_PROP_STYLE,0); } bool SetStyle(const ENUM_LINE_STYLE style) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_STYLE,style)) return false; this.SetProperty(GRAPH_OBJ_PROP_STYLE,0,style); return true; } //--- Толщина линии int Width(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_WIDTH,0); } bool SetWidth(const int width) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_WIDTH,width)) return false; this.SetProperty(GRAPH_OBJ_PROP_WIDTH,0,width); return true; } //--- Заливка объекта цветом bool Fill(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_FILL,0); } bool SetFlagFill(const bool flag) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_FILL,flag)) return false; this.SetProperty(GRAPH_OBJ_PROP_FILL,0,flag); return true; } //--- Возможность редактирования текста в объекте Edit bool ReadOnly(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_READONLY,0); } bool SetFlagReadOnly(const bool flag) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_READONLY,flag)) return false; this.SetProperty(GRAPH_OBJ_PROP_READONLY,0,flag); return true; } //--- Количество уровней int Levels(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_LEVELS,0); } bool SetLevels(const int levels) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_LEVELS,levels)) return false; this.SetProperty(GRAPH_OBJ_PROP_LEVELS,0,levels); return true; } //--- Цвет линии-уровня color LevelColor(const int modifier) const { return (color)this.GetProperty(GRAPH_OBJ_PROP_LEVELCOLOR,modifier); } bool SetLevelColor(const color colour,const int modifier) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_LEVELCOLOR,modifier,colour)) return false; this.SetProperty(GRAPH_OBJ_PROP_LEVELCOLOR,modifier,colour); return true; } //--- Стиль линии-уровня ENUM_LINE_STYLE LevelStyle(const int modifier)const { return (ENUM_LINE_STYLE)this.GetProperty(GRAPH_OBJ_PROP_LEVELSTYLE,modifier); } bool SetLevelStyle(const ENUM_LINE_STYLE style,const int modifier) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_LEVELSTYLE,modifier,style)) return false; this.SetProperty(GRAPH_OBJ_PROP_LEVELSTYLE,modifier,style); return true; } ///--- Толщина линии-уровня int LevelWidth(const int modifier)const { return (int)this.GetProperty(GRAPH_OBJ_PROP_LEVELWIDTH,modifier); } bool SetLevelWidth(const int width,const int modifier) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_LEVELWIDTH,modifier,width)) return false; this.SetProperty(GRAPH_OBJ_PROP_LEVELWIDTH,modifier,width); return true; } //--- Горизонтальное выравнивание текста в объекте "Поле ввода" (OBJ_EDIT) ENUM_ALIGN_MODE Align(void) const { return (ENUM_ALIGN_MODE)this.GetProperty(GRAPH_OBJ_PROP_ALIGN,0); } bool SetAlign(const ENUM_ALIGN_MODE align) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_ALIGN,align)) return false; this.SetProperty(GRAPH_OBJ_PROP_ALIGN,0,align); return true; } //--- Размер шрифта int FontSize(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_FONTSIZE,0); } bool SetFontSize(const int size) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_FONTSIZE,size)) return false; this.SetProperty(GRAPH_OBJ_PROP_FONTSIZE,0,size); return true; } //--- Луч продолжается влево bool RayLeft(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_RAY_LEFT,0); } bool SetFlagRayLeft(const bool flag) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_RAY_LEFT,flag)) return false; this.SetProperty(GRAPH_OBJ_PROP_RAY_LEFT,0,flag); return true; } //--- Луч продолжается вправо bool RayRight(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_RAY_RIGHT,0); } bool SetFlagRayRight(const bool flag) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_RAY_RIGHT,flag)) return false; this.SetProperty(GRAPH_OBJ_PROP_RAY_RIGHT,0,flag); return true; } //--- Вертикальная линия продолжается на все окна графика bool Ray(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_RAY,0); } bool SetFlagRay(const bool flag) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_RAY,flag)) return false; this.SetProperty(GRAPH_OBJ_PROP_RAY,0,flag); return true; } //--- Отображение полного эллипса для объекта "Дуги Фибоначчи" bool Ellipse(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_ELLIPSE,0); } bool SetFlagEllipse(const bool flag) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_ELLIPSE,flag)) return false; this.SetProperty(GRAPH_OBJ_PROP_ELLIPSE,0,flag); return true; } //--- Код стрелки для объекта "Стрелка" uchar ArrowCode(void) const { return (uchar)this.GetProperty(GRAPH_OBJ_PROP_ARROWCODE,0); } bool SetArrowCode(const uchar code) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_ARROWCODE,code)) return false; this.SetProperty(GRAPH_OBJ_PROP_ARROWCODE,0,code); return true; } //--- Положение точки привязки графического объекта int Anchor(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_ANCHOR,0); } bool SetAnchor(const int anchor) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_ANCHOR,anchor)) return false; this.SetProperty(GRAPH_OBJ_PROP_ANCHOR,0,anchor); return true; } //--- Дистанция в пикселях по оси X от угла привязки int XDistance(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_XDISTANCE,0); } bool SetXDistance(const int distance) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_XDISTANCE,distance)) return false; this.SetProperty(GRAPH_OBJ_PROP_XDISTANCE,0,distance); return true; } //--- Дистанция в пикселях по оси Y от угла привязки int YDistance(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_YDISTANCE,0); } bool SetYDistance(const int distance) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_YDISTANCE,distance)) return false; this.SetProperty(GRAPH_OBJ_PROP_YDISTANCE,0,distance); return true; } //--- Тренд объекта Ганна ENUM_GANN_DIRECTION Direction(void) const { return (ENUM_GANN_DIRECTION)this.GetProperty(GRAPH_OBJ_PROP_DIRECTION,0); } bool SetDirection(const ENUM_GANN_DIRECTION direction) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_DIRECTION,direction)) return false; this.SetProperty(GRAPH_OBJ_PROP_DIRECTION,0,direction); return true; } //--- Уровень волновой разметки Эллиотта ENUM_ELLIOT_WAVE_DEGREE Degree(void) const { return (ENUM_ELLIOT_WAVE_DEGREE)this.GetProperty(GRAPH_OBJ_PROP_DEGREE,0); } bool SetDegree(const ENUM_ELLIOT_WAVE_DEGREE degree) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_DEGREE,degree)) return false; this.SetProperty(GRAPH_OBJ_PROP_DEGREE,0,degree); return true; } //--- Отображение линий для волновой разметки Эллиотта bool DrawLines(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_DRAWLINES,0); } bool SetFlagDrawLines(const bool flag) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_DRAWLINES,flag)) return false; this.SetProperty(GRAPH_OBJ_PROP_DRAWLINES,0,flag); return true; } //--- Состояние кнопки (Нажата/Отжата) bool State(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_STATE,0); } bool SetFlagState(const bool flag) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_STATE,flag)) return false; this.SetProperty(GRAPH_OBJ_PROP_STATE,0,flag); return true; } //--- Идентификатор объекта "График" (OBJ_CHART) long ChartObjChartID(void) const { return this.GetProperty(GRAPH_OBJ_PROP_CHART_OBJ_CHART_ID,0); } bool SetChartObjChartID(const long chart_id) { this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_CHART_ID,0,chart_id); return true; } //--- Период для объекта "График" ENUM_TIMEFRAMES ChartObjPeriod(void) const { return (ENUM_TIMEFRAMES)this.GetProperty(GRAPH_OBJ_PROP_CHART_OBJ_PERIOD,0); } bool SetChartObjPeriod(const ENUM_TIMEFRAMES timeframe) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_PERIOD,timeframe)) return false; this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_PERIOD,0,timeframe); return true; } //--- Признак отображения шкалы времени для объекта "График" bool ChartObjDateScale(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE,0); } bool SetFlagChartObjDateScale(const bool flag) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_DATE_SCALE,flag)) return false; this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE,0,flag); return true; } //--- Признак отображения ценовой шкалы для объекта "График" bool ChartObjPriceScale(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE,0); } bool SetFlagPriceScale(const bool flag) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_PRICE_SCALE,flag)) return false; this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE,0,flag); return true; } //--- Масштаб для объекта "График" int ChartObjChartScale(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE,0); } bool SetChartObjChartScale(const int scale) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_CHART_SCALE,scale)) return false; this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE,0,scale); return true; } //--- Ширина объекта по оси X в пикселях int XSize(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_XSIZE,0); } bool SetXSize(const int size) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_XSIZE,size)) return false; this.SetProperty(GRAPH_OBJ_PROP_XSIZE,0,size); return true; } //--- Высота объекта по оси Y в пикселях int YSize(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_YSIZE,0); } bool SetYSize(const int size) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_YSIZE,size)) return false; this.SetProperty(GRAPH_OBJ_PROP_YSIZE,0,size); return true; } //--- X-координата левого верхнего угла прямоугольной области видимости int XOffset(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_XOFFSET,0); } bool SetXOffset(const int offset) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_XOFFSET,offset)) return false; this.SetProperty(GRAPH_OBJ_PROP_XOFFSET,0,offset); return true; } //--- Y-координата левого верхнего угла прямоугольной области видимости int YOffset(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_YOFFSET,0); } bool SetYOffset(const int offset) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_YOFFSET,offset)) return false; this.SetProperty(GRAPH_OBJ_PROP_YOFFSET,0,offset); return true; } //--- Цвет фона для OBJ_EDIT, OBJ_BUTTON, OBJ_RECTANGLE_LABEL color BGColor(void) const { return (color)this.GetProperty(GRAPH_OBJ_PROP_BGCOLOR,0); } bool SetBGColor(const color colour) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_BGCOLOR,colour)) return false; this.SetProperty(GRAPH_OBJ_PROP_BGCOLOR,0,colour); return true; } //--- Угол графика для привязки графического объекта ENUM_BASE_CORNER Corner(void) const { return (ENUM_BASE_CORNER)this.GetProperty(GRAPH_OBJ_PROP_CORNER,0); } bool SetCorner(const ENUM_BASE_CORNER corner) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_CORNER,corner)) return false; this.SetProperty(GRAPH_OBJ_PROP_CORNER,0,corner); return true; } //--- Тип рамки для объекта "Прямоугольная рамка" ENUM_BORDER_TYPE BorderType(void) const { return (ENUM_BORDER_TYPE)this.GetProperty(GRAPH_OBJ_PROP_BORDER_TYPE,0); } bool SetBorderType(const ENUM_BORDER_TYPE type) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_BORDER_TYPE,type)) return false; this.SetProperty(GRAPH_OBJ_PROP_BORDER_TYPE,0,type); return true; } //--- Цвет рамки для объекта OBJ_EDIT и OBJ_BUTTON color BorderColor(void) const { return (color)this.GetProperty(GRAPH_OBJ_PROP_BORDER_COLOR,0); } bool SetBorderColor(const color colour) { if(!::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_BORDER_COLOR,colour)) return false; this.SetProperty(GRAPH_OBJ_PROP_BORDER_COLOR,0,colour); return true; } //--- Координата цены double Price(const int modifier) const { return this.GetProperty(GRAPH_OBJ_PROP_PRICE,modifier); } bool SetPrice(const double price,const int modifier) { if(!::ObjectSetDouble(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_PRICE,modifier,price)) return false; this.SetProperty(GRAPH_OBJ_PROP_PRICE,modifier,price); return true; } //--- Значение уровня double LevelValue(const int modifier)const { return this.GetProperty(GRAPH_OBJ_PROP_LEVELVALUE,modifier); } bool SetLevelValue(const double value,const int modifier) { if(!::ObjectSetDouble(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_LEVELVALUE,modifier,value)) return false; this.SetProperty(GRAPH_OBJ_PROP_LEVELVALUE,modifier,value); return true; } //--- Масштаб double Scale(void) const { return this.GetProperty(GRAPH_OBJ_PROP_SCALE,0); } bool SetScale(const double scale) { if(!::ObjectSetDouble(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_SCALE,scale)) return false; this.SetProperty(GRAPH_OBJ_PROP_SCALE,0,scale); return true; } //--- Угол double Angle(void) const { return this.GetProperty(GRAPH_OBJ_PROP_ANGLE,0); } bool SetAngle(const double angle) { if(!::ObjectSetDouble(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_ANGLE,angle)) return false; this.SetProperty(GRAPH_OBJ_PROP_ANGLE,0,angle); return true; } //--- Отклонение для канала стандартного отклонения double Deviation(void) const { return this.GetProperty(GRAPH_OBJ_PROP_DEVIATION,0); } bool SetDeviation(const double deviation) { if(!::ObjectSetDouble(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_DEVIATION,deviation)) return false; this.SetProperty(GRAPH_OBJ_PROP_DEVIATION,0,deviation); return true; } //--- Имя объекта string Name(void) const { return this.GetProperty(GRAPH_OBJ_PROP_NAME,0); } bool SetName(const string name) { if(CGBaseObj::Name()==name) return true; if(CGBaseObj::Name()=="") { CGBaseObj::SetName(name); this.SetProperty(GRAPH_OBJ_PROP_NAME,0,name); return true; } else { if(!::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_NAME,name)) return false; CGBaseObj::SetName(name); this.SetProperty(GRAPH_OBJ_PROP_NAME,0,name); return true; } } //--- Описание объекта (текст, содержащийся в объекте) string Text(void) const { return this.GetProperty(GRAPH_OBJ_PROP_TEXT,0); } bool SetText(const string text) { if(!::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_TEXT,text)) return false; this.SetProperty(GRAPH_OBJ_PROP_TEXT,0,text); return true; } //--- Текст всплывающей подсказки string Tooltip(void) const { return this.GetProperty(GRAPH_OBJ_PROP_TOOLTIP,0); } bool SetTooltip(const string tooltip) { if(!::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_TOOLTIP,tooltip)) return false; this.SetProperty(GRAPH_OBJ_PROP_TOOLTIP,0,tooltip); return true; } //--- Описание уровня string LevelText(const int modifier) const { return this.GetProperty(GRAPH_OBJ_PROP_LEVELTEXT,modifier); } bool SetLevelText(const string text,const int modifier) { if(!::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_LEVELTEXT,modifier,text)) return false; this.SetProperty(GRAPH_OBJ_PROP_LEVELTEXT,modifier,text); return true; } //--- Шрифт string Font(void) const { return this.GetProperty(GRAPH_OBJ_PROP_FONT,0); } bool SetFont(const string font) { if(!::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_FONT,font)) return false; this.SetProperty(GRAPH_OBJ_PROP_FONT,0,font); return true; } //--- Имя BMP-файла для объекта "Графическая метка" string BMPFile(const int modifier) const { return this.GetProperty(GRAPH_OBJ_PROP_BMPFILE,modifier); } bool SetBMPFile(const string bmp_file,const int modifier) { if(!::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_BMPFILE,bmp_file)) return false; this.SetProperty(GRAPH_OBJ_PROP_BMPFILE,modifier,bmp_file); return true; } //--- Символ для объекта "График" string ChartObjSymbol(void) const { return this.GetProperty(GRAPH_OBJ_PROP_CHART_OBJ_SYMBOL,0); } bool SetChartObjSymbol(const string symbol) { if(!::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_SYMBOL,symbol)) return false; this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_SYMBOL,0,symbol); return true; }
В методе, получающем и сохраняющем целочисленные свойства, сначала запишем данные, которые есть в базовом объекте, в свойства базового объекта, а затем уже из него запишем значения в свойства класса графического объекта:
//+------------------------------------------------------------------+ //| Получает и сохраняет целочисленные свойства | //+------------------------------------------------------------------+ void CGStdGraphObj::GetAndSaveINT(void) { //--- Свойства, присущие всем графическим объектам, имеющиеся у графического объекта CGBaseObj::SetVisibleOnTimeframes((int)::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_TIMEFRAMES),true); // Записываем в базовый объект Видимость объекта на таймфреймах CGBaseObj::SetFlagBack(::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_BACK),true); // Записываем в базовый объект флаг Объект на заднем плане CGBaseObj::SetZorder(::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ZORDER),true); // Записываем в базовый объект Приоритет графического объекта на получение события нажатия мышки на графике CGBaseObj::SetFlagHidden(::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_HIDDEN),true); // Записываем в базовый объект Запрет на показ имени графического объекта в списке объектов терминала CGBaseObj::SetFlagSelectable(::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_SELECTABLE),true); // Записываем в базовый объект Доступность объекта CGBaseObj::SetFlagSelected(::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_SELECTED),true); // Записываем в базовый объект Выделенность объекта this.SetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0,CGBaseObj::VisibleOnTimeframes()); // Видимость объекта на таймфреймах this.SetProperty(GRAPH_OBJ_PROP_BACK,0,CGBaseObj::IsBack()); // Объект на заднем плане this.SetProperty(GRAPH_OBJ_PROP_ZORDER,0,CGBaseObj::Zorder()); // Приоритет графического объекта на получение события нажатия мышки на графике this.SetProperty(GRAPH_OBJ_PROP_HIDDEN,0,CGBaseObj::IsHidden()); // Запрет на показ имени графического объекта в списке объектов терминала this.SetProperty(GRAPH_OBJ_PROP_SELECTABLE,0,CGBaseObj::IsSelectable()); // Доступность объекта this.SetProperty(GRAPH_OBJ_PROP_SELECTED,0,CGBaseObj::IsSelected()); // Выделенность объекта this.SetProperty(GRAPH_OBJ_PROP_CREATETIME,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_CREATETIME)); // Время создания объекта for(int i=0;i<this.m_pivots;i++) // Координаты времени точек this.SetTimePivot(i); this.SetProperty(GRAPH_OBJ_PROP_COLOR,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_COLOR)); // Цвет this.SetProperty(GRAPH_OBJ_PROP_STYLE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_STYLE)); // Стиль this.SetProperty(GRAPH_OBJ_PROP_WIDTH,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_WIDTH)); // Толщина линии //--- Свойства, принадлежащие разным графическим объектам this.SetProperty(GRAPH_OBJ_PROP_FILL,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_FILL)); // Заливка объекта цветом this.SetProperty(GRAPH_OBJ_PROP_READONLY,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_READONLY)); // Возможность редактирования текста в объекте Edit this.SetProperty(GRAPH_OBJ_PROP_LEVELS,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_LEVELS)); // Количество уровней if(this.GetProperty(GRAPH_OBJ_PROP_LEVELS,0)!=this.GetPropertyPrev(GRAPH_OBJ_PROP_LEVELS,0)) // Проверка изменения количества уровней { this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELCOLOR,this.Levels()); this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELSTYLE,this.Levels()); this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELWIDTH,this.Levels()); this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELVALUE,this.Levels()); this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELTEXT,this.Levels()); } for(int i=0;i<this.Levels();i++) // Данные уровней { this.SetLevelColor(i); this.SetLevelStyle(i); this.SetLevelWidth(i); } this.SetProperty(GRAPH_OBJ_PROP_ALIGN,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ALIGN)); // Горизонтальное выравнивание текста в объекте "Поле ввода" (OBJ_EDIT) this.SetProperty(GRAPH_OBJ_PROP_FONTSIZE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_FONTSIZE)); // Размер шрифта this.SetProperty(GRAPH_OBJ_PROP_RAY_LEFT,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_RAY_LEFT)); // Луч продолжается влево this.SetProperty(GRAPH_OBJ_PROP_RAY_RIGHT,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_RAY_RIGHT)); // Луч продолжается вправо this.SetProperty(GRAPH_OBJ_PROP_RAY,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_RAY)); // Вертикальная линия продолжается на все окна графика this.SetProperty(GRAPH_OBJ_PROP_ELLIPSE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ELLIPSE)); // Отображение полного эллипса для объекта "Дуги Фибоначчи" this.SetProperty(GRAPH_OBJ_PROP_ARROWCODE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ARROWCODE)); // Код стрелки для объекта "Стрелка" this.SetProperty(GRAPH_OBJ_PROP_ANCHOR,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ANCHOR)); // Положение точки привязки графического объекта this.SetProperty(GRAPH_OBJ_PROP_XDISTANCE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_XDISTANCE)); // Дистанция в пикселях по оси X от угла привязки this.SetProperty(GRAPH_OBJ_PROP_YDISTANCE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_YDISTANCE)); // Дистанция в пикселях по оси Y от угла привязки this.SetProperty(GRAPH_OBJ_PROP_DIRECTION,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_DIRECTION)); // Тренд объекта Ганна this.SetProperty(GRAPH_OBJ_PROP_DEGREE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_DEGREE)); // Уровень волновой разметки Эллиотта this.SetProperty(GRAPH_OBJ_PROP_DRAWLINES,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_DRAWLINES)); // Отображение линий для волновой разметки Эллиотта this.SetProperty(GRAPH_OBJ_PROP_STATE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_STATE)); // Состояние кнопки (Нажата/Отжата) this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_CHART_ID,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_CHART_ID));// Идентификатор объекта "График" (OBJ_CHART). this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_PERIOD,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_PERIOD)); // Период для объекта "График" this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_DATE_SCALE)); // Признак отображения шкалы времени для объекта "График" this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_PRICE_SCALE));// Признак отображения ценовой шкалы для объекта "График" this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_CHART_SCALE));// Масштаб для объекта "График" this.SetProperty(GRAPH_OBJ_PROP_XSIZE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_XSIZE)); // Ширина объекта по оси X в пикселях. this.SetProperty(GRAPH_OBJ_PROP_YSIZE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_YSIZE)); // Высота объекта по оси Y в пикселях. this.SetProperty(GRAPH_OBJ_PROP_XOFFSET,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_XOFFSET)); // X-координата левого верхнего угла прямоугольной области видимости. this.SetProperty(GRAPH_OBJ_PROP_YOFFSET,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_YOFFSET)); // Y-координата левого верхнего угла прямоугольной области видимости. this.SetProperty(GRAPH_OBJ_PROP_BGCOLOR,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_BGCOLOR)); // Цвет фона для OBJ_EDIT, OBJ_BUTTON, OBJ_RECTANGLE_LABEL this.SetProperty(GRAPH_OBJ_PROP_CORNER,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_CORNER)); // Угол графика для привязки графического объекта this.SetProperty(GRAPH_OBJ_PROP_BORDER_TYPE,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_BORDER_TYPE));// Тип рамки для объекта "Прямоугольная рамка" this.SetProperty(GRAPH_OBJ_PROP_BORDER_COLOR,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_BORDER_COLOR));// Цвет рамки для объекта OBJ_EDIT и OBJ_BUTTON } //+------------------------------------------------------------------+
Таким образом мы заполняем все свойства, которые дублируются в базовом классе и в его наследнике. Иначе, при получении свойств объекта, мы используем методы базового объекта, а в них не было записано данных графического объекта.
Некоторые доработки оставим на рассмотрение при создании функционала для отслеживания событий стандартных графических объектов.
События стандартных графических объектов
Логика работы с событиями графических объектов у нас будет построена на такой концепции: для каждого объекта мы уже можем определять любые события, происходящие с его свойствами. Создадим небольшой класс базового события графического объекта, и при определении любого события с графическим объектом будем создавать новый объект-событие, в который будут записываться:
- Идентификатор события,
- Идентификатор графика, на котором расположен графический объект, в котором произошло событие,
- Имя объекта, в котором произошло событие.
Вообще, у пользовательского события, отправляемого на указанный график функцией EventChartCustom(), есть пять параметров:
//+------------------------------------------------------------------+ bool EventChartCustom( long chart_id, // идентификатор графика-получателя события ushort custom_event_id, // идентификатор события long lparam, // параметр типа long double dparam, // параметр типа double string sparam // строковый параметр события ); //+------------------------------------------------------------------+
Из них нам нужны для заполнения custom_event_id — сюда будем указывать идентификатор события графического объекта, lparam — сюда будем указывать идентификатор графика, на котором расположен графический объект, в котором произошло событие, и sparam — сюда будем указывать имя графического объекта. И у нас остаётся ещё один свободный параметр — dparam, в который будем указывать значение константы изменённого свойства при его изменении, или количество удалённых графических объектов, которые были удалены совместно с закрытым окном графика.
На каждое событие объекта будем создавать объект-событие и размещать их в список событий. После проверки свойств объекта на изменение у нас окажется заполненным список всех событий, которые произошли с графическим объектом. По окончании циклов проверки всех свойств объекта мы пройдёмся по созданному списку событий и каждое отправим на график управляющей программы, где в свою очередь будет вызван метод-обработчик событий OnChartEvent() класса-коллекции графических элементов. В обработчике мы сможем обработать каждое событие, отправленное из созданного списка событий графического объекта. Соответственно, в каждом таком событии будет указан идентификатор события, идентификатор графика, на котором расположен изменённый графический объект, и его имя. Это позволит точно указать на этот графический объект, а значение константы изменённого свойства точно укажет на то, какое свойство было изменено, и мы сможем получить указатель на этот объект в коллекции и прочитать новое значение изменённого свойства. Всё это позволит нам точно указать как на сам графический объект, так и на изменённое свойство — чтобы далее обработать событие в программе как это заложено в её логике.
Сегодня мы сделаем лишь создание событий и отправку их в обработчик событий коллекции графических объектов. Разложение этих событий на их составляющие мы сделаем в следующей статье.
В файле базового графического объекта \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh, в самом его начале, пропишем новый класс базового события графических объектов библиотеки:
//+------------------------------------------------------------------+ //| GBaseObj.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/ru/users/artmedia70" #property version "1.00" #property strict // Нужно для mql4 //+------------------------------------------------------------------+ //| Включаемые файлы | //+------------------------------------------------------------------+ #include "..\..\Services\DELib.mqh" #include <Graphics\Graphic.mqh> //+------------------------------------------------------------------+ //| Класс события графического объекта | //+------------------------------------------------------------------+ class CGBaseEvent : public CObject { private: ushort m_id; long m_lparam; double m_dparam; string m_sparam; public: void ID(ushort id) { this.m_id=id; } ushort ID(void) const { return this.m_id; } void LParam(const long value) { this.m_lparam=value; } long Lparam(void) const { return this.m_lparam; } void DParam(const double value) { this.m_dparam=value; } double Dparam(void) const { return this.m_dparam; } void SParam(const string value) { this.m_sparam=value; } string Sparam(void) const { return this.m_sparam; } bool Send(const long chart_id) { ::ResetLastError(); return ::EventChartCustom(chart_id,m_id,m_lparam,m_dparam,m_sparam); } CGBaseEvent (const ushort event_id,const long lparam,const double dparam,const string sparam) : m_id(event_id),m_lparam(lparam),m_dparam(dparam),m_sparam(sparam){} ~CGBaseEvent (void){} }; //+------------------------------------------------------------------+ //| Класс базового объекта графических объектов библиотеки | //+------------------------------------------------------------------+ class CGBaseObj : public CObject
В приватной секции класса объявлены переменные-члены класса для хранения всех вышеописанных свойств объекта-события, а в публичной — методы для установки и возврата значений этих переменных и метод для отправки пользовательского события на указанный график.
В конструктор класса передаются все значения, которые необходимо присвоить переменным класса, и они в списке инициализации конструктора сразу же и присваиваются переменным.
В защищённой секции класса базового графического объекта объявим список для хранения событий объекта и напишем методы для работы со списком и событиями графического объекта:
//+------------------------------------------------------------------+ //| Класс базового объекта графических объектов библиотеки | //+------------------------------------------------------------------+ class CGBaseObj : public CObject { protected: CArrayObj m_list_events; // Список событий объекта ENUM_OBJECT m_type_graph_obj; // Тип графического объекта ENUM_GRAPH_ELEMENT_TYPE m_type_element; // Тип графического элемента ENUM_GRAPH_OBJ_BELONG m_belong; // Принадлежность программе ENUM_GRAPH_OBJ_GROUP m_group; // Группа графического объекта string m_name_prefix; // Префикс имени объекта string m_name; // Имя объекта long m_chart_id; // Идентификатор графика объекта long m_object_id; // Идентификатор объекта long m_zorder; // Приоритет графического объекта на получение события нажатия мышки int m_subwindow; // Номер подокна int m_shift_y; // Смещение координаты Y для подокна int m_type; // Тип объекта int m_timeframes_visible; // Видимость объекта на таймфреймах (набор флагов) int m_digits; // Количество знаков после запятой в котировке символа чарта bool m_visible; // Видимость объекта bool m_back; // Флаг "Объект на заднем плане" bool m_selected; // Флаг "Выделенность объекта" bool m_selectable; // Флаг "Доступность объекта" bool m_hidden; // Флаг "Запрет на показ имени графического объекта в списке объектов терминала" datetime m_create_time; // Время создания объекта //--- Создаёт (1) структуру объекта, (2) объект из структуры virtual bool ObjectToStruct(void) { return true; } virtual void StructToObject(void){;} //--- Возвращает список событий объекта CArrayObj *GetListEvents(void) { return &this.m_list_events; } //--- Создаёт новое событие объекта CGBaseEvent *CreateNewEvent(const ushort event_id,const long lparam,const double dparam,const string sparam) { CGBaseEvent *event=new CGBaseEvent(event_id,lparam,dparam,sparam); return event; } //--- Создаёт новое событие объекта и добавляет его в список событий bool CreateAndAddNewEvent(const ushort event_id,const long lparam,const double dparam,const string sparam) { return this.AddEvent(new CGBaseEvent(event_id,lparam,dparam,sparam)); } //--- Добавляет объект события в список событий bool AddEvent(CGBaseEvent *event) { return this.m_list_events.Add(event);} //--- Очищает список событий void ClearEventsList(void) { this.m_list_events.Clear(); } //--- Возвращает количество событий в списке int EventsTotal(void) { return this.m_list_events.Total(); } public:
Все написанные методы достаточно просты и, надеюсь, не вызывают никаких вопросов. В любом случае, все вопросы можно задать в обсуждении статьи.
В конструкторе класса очистим список событий объекта и установим ему флаг сортированного списка:
//+------------------------------------------------------------------+ //| Конструктор | //+------------------------------------------------------------------+ CGBaseObj::CGBaseObj() : m_shift_y(0),m_visible(false), m_name_prefix(::MQLInfoString(MQL_PROGRAM_NAME)+"_"),m_belong(GRAPH_OBJ_BELONG_PROGRAM) { this.m_list_events.Clear(); // Очистка списка событий this.m_list_events.Sort(); // Флаг сортированного списка this.m_type=OBJECT_DE_TYPE_GBASE; // Тип объекта this.m_type_graph_obj=WRONG_VALUE; // Тип графического объекта this.m_type_element=WRONG_VALUE; // Тип графического элемента this.m_belong=WRONG_VALUE; // Принадлежность программе/терминалу this.m_name_prefix=""; // Префикс имени объекта this.m_name=""; // Имя объекта this.m_chart_id=0; // Идентификатор графика объекта this.m_object_id=0; // Идентификатор объекта this.m_zorder=0; // Приоритет графического объекта на получение события нажатия мышки this.m_subwindow=0; // Номер подокна this.m_shift_y=0; // Смещение координаты Y для подокна this.m_timeframes_visible=OBJ_ALL_PERIODS; // Видимость объекта на таймфреймах (набор флагов) this.m_visible=true; // Видимость объекта this.m_back=false; // Флаг "Объект на заднем плане" this.m_selected=false; // Флаг "Выделенность объекта" this.m_selectable=false; // Флаг "Доступность объекта" this.m_hidden=true; // Флаг "Запрет на показ имени графического объекта в списке объектов терминала" this.m_create_time=0; // Время создания объекта } //+------------------------------------------------------------------+
В классе стандартного графического объекта \MQL5\Include\DoEasy\Objects\Graph\Standard\GStdGraphObj.mqh в методе, проверяющем изменение свойств объекта, очистим список событий, пропишем создание и добавление в список объектов-событий и их отправку в обработчик событий коллекции графических объектов из созданного списка:
//+------------------------------------------------------------------+ //| Проверяет изменения свойств объекта | //+------------------------------------------------------------------+ void CGStdGraphObj::PropertiesCheckChanged(void) { CGBaseObj::ClearEventsList(); bool changed=false; int begin=0, end=GRAPH_OBJ_PROP_INTEGER_TOTAL; for(int i=begin; i<end; i++) { ENUM_GRAPH_OBJ_PROP_INTEGER prop=(ENUM_GRAPH_OBJ_PROP_INTEGER)i; if(!this.SupportProperty(prop)) continue; for(int j=0;j<Prop.CurrSize(prop);j++) { if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j)) { changed=true; this.CreateAndAddNewEvent(GRAPH_OBJ_EVENT_CHANGE,this.ChartID(),prop,this.Name()); } } } begin=end; end+=GRAPH_OBJ_PROP_DOUBLE_TOTAL; for(int i=begin; i<end; i++) { ENUM_GRAPH_OBJ_PROP_DOUBLE prop=(ENUM_GRAPH_OBJ_PROP_DOUBLE)i; if(!this.SupportProperty(prop)) continue; for(int j=0;j<Prop.CurrSize(prop);j++) { if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j)) { changed=true; this.CreateAndAddNewEvent(GRAPH_OBJ_EVENT_CHANGE,this.ChartID(),prop,this.Name()); } } } begin=end; end+=GRAPH_OBJ_PROP_STRING_TOTAL; for(int i=begin; i<end; i++) { ENUM_GRAPH_OBJ_PROP_STRING prop=(ENUM_GRAPH_OBJ_PROP_STRING)i; if(!this.SupportProperty(prop)) continue; for(int j=0;j<Prop.CurrSize(prop);j++) { if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j) && prop!=GRAPH_OBJ_PROP_NAME) { changed=true; this.CreateAndAddNewEvent(GRAPH_OBJ_EVENT_CHANGE,this.ChartID(),prop,this.Name()); } } } if(changed) { for(int i=0;i<this.m_list_events.Total();i++) { CGBaseEvent *event=this.m_list_events.At(i); if(event==NULL) continue; ::EventChartCustom(::ChartID(),event.ID(),event.Lparam(),event.Dparam(),event.Sparam()); } PropertiesCopyToPrevData(); } } //+------------------------------------------------------------------+
Здесь мы в цикле по всем свойствам объекта — целочисленным, вещественным и строковым, проверяем наличие изменений каждого очередного свойства объекта и, если есть изменение, то создаём объект-событие и добавляем его в список событий объекта. Затем, если есть хоть одно изменение, то в цикле по созданному списку получаем каждый очередной объект-событие и отправляем его на график управляющей программы. Далее эти события попадут в обработчик OnChartEvent() класса-коллекции графических элементов, и в нём будут обработаны.
Откроем файл класса-коллекции графических элементов \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh. Немного доработаем метод класса управления графическими объектами, проверяющий объекты на чарте:
//+------------------------------------------------------------------+ //| CChartObjectsControl: Проверяет объекты на чарте | //+------------------------------------------------------------------+ void CChartObjectsControl::Refresh(void) { //--- Графические объекты на чарте this.m_total_objects=::ObjectsTotal(this.ChartID()); this.m_delta_graph_obj=this.m_total_objects-this.m_last_objects; //--- Если добавлен объект на график if(this.m_delta_graph_obj>0) { //--- находим последний добавленный графический объект, выбираем его и записываем его имя string name=this.LastAddedGraphObjName(); //--- Обрабатываем только объекты, созданные не программно if(name!="" && ::StringFind(name,m_name_program)==WRONG_VALUE) { //--- Создаём объект класса графического объекта, соответствующий типу добавленного графического объекта ENUM_OBJECT type=(ENUM_OBJECT)::ObjectGetInteger(this.ChartID(),name,OBJPROP_TYPE); ENUM_OBJECT_DE_TYPE obj_type=ENUM_OBJECT_DE_TYPE(type+OBJECT_DE_TYPE_GSTD_OBJ+1); CGStdGraphObj *obj=this.CreateNewGraphObj(type,name); if(obj==NULL) { CMessage::ToLog(DFUN,MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ); return; } //--- Устанавливаем объекту его принадлежность и добавляем созданный объект в список новых объектов obj.SetBelong(GRAPH_OBJ_BELONG_NO_PROGRAM); if(this.m_list_new_graph_obj.Search(obj)==WRONG_VALUE) { //--- Если объект добавить в список не удалось - сообщаем об этом, удаляем объект и уходим if(!this.m_list_new_graph_obj.Add(obj)) { CMessage::ToLog(DFUN_ERR_LINE,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); delete obj; return; } //--- Отправляем событие на график управляющей программы ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_CREATE,this.ChartID(),0,obj.Name()); } } } //--- сохранение индекса последнего добавленного графического объекта и разницы по сравнению с прошлой проверкой this.m_last_objects=this.m_total_objects; this.m_is_graph_obj_event=(bool)this.m_delta_graph_obj; } //+------------------------------------------------------------------+
Здесь мы добавили вывод сообщений об ошибках в журнал и отправку события создания объекта на график управляющей программы.
В публичной секции класса-коллекции графических элементов добавим метод, возвращающий список объектов управления графиками:
//--- Возвращает количество новых графических объектов, (3) флаг произошедшего изменения в списке графических объектов int NewObjects(void) const { return this.m_delta_graph_obj; } bool IsEvent(void) const { return this.m_is_graph_obj_event; } //--- Возвращает графический объект по имени и идентификатору графика CGStdGraphObj *GetStdGraphObject(const string name,const long chart_id); //--- Возвращает список объектов управления графиками CArrayObj *GetListChartsControl(void) { return &this.m_list_charts_control; } //--- Конструктор CGraphElementsCollection();
Так как в методах создания стандартных графических объектов, созданных нами в прошлой статье, у нас есть достаточно крупный повторяющийся блок кода, вынесем его в отдельный приватный метод:
//--- Обработчик событий void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam); private: //--- Создаёт новый графический объект, возвращает указатель на объект управления чартами CChartObjectsControl *CreateNewStdGraphObjectAndGetCtrlObj(const long chart_id, const string name, int subwindow, const ENUM_OBJECT type_object, const datetime time1, const double price1, const datetime time2=0, const double price2=0, const datetime time3=0, const double price3=0, const datetime time4=0, const double price4=0, const datetime time5=0, const double price5=0) { //--- Если объект с идентификатором графика и именем уже есть в коллекции - сообщаем об этом и возвращаем NULL if(this.IsPresentGraphObjInList(chart_id,name)) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_ELM_COLLECTION_ERR_GR_OBJ_ALREADY_EXISTS)," ChartID ",(string)chart_id,", ",name); return NULL; } //--- Если не удалось создать новый стандартный графический объект - сообщаем об этом и возвращаем NULL if(!this.CreateNewStdGraphObject(chart_id,name,type_object,subwindow,time1,0)) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_STD_GRAPH_OBJ),StdGraphObjectTypeDescription(type_object)); CMessage::ToLog(::GetLastError(),true); return NULL; } //--- Если не удалось получить объект управления графиком - сообщаем об этом CChartObjectsControl *ctrl=this.GetChartObjectCtrlObj(chart_id); if(ctrl==NULL) ::Print(DFUN,CMessage::Text(MSG_GRAPH_ELM_COLLECTION_ERR_FAILED_GET_CTRL_OBJ),(string)chart_id); //--- Возвращаем указатель на объект управления графиком, либо NULL при ошибке его получения return ctrl; } //--- Добавляет созданный объект в список bool AddCreatedObjToList(const string source,const long chart_id,const string name,CGStdGraphObj *obj) { bool res=true; if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(source,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,name); delete obj; res=false; } ::ChartRedraw(chart_id); return res; } public: //--- Создаёт графический объект "Вертикальная линия"
И далее, во всех методах для создания стандартных графических объектов будем возвращать результат работы этого метода:
public: //--- Создаёт графический объект "Вертикальная линия" bool CreateLineVertical(const long chart_id,const string name,const int subwindow,const datetime time) { //--- Задаём имя и тип создаваемого объекта string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_VLINE; //--- Создаём новый графический объект и получаем указатель на объект управления графиком CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,0); if(ctrl==NULL) return false; //--- Создаём новый объект класса, соответствующий созданному графическому объекту CGStdVLineObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Устанавливаем объекту необходимые минимальные параметры obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true,false); obj.SetFlagSelected(true,false); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); //--- Возвращаем результат добавления объекта в список return this.AddCreatedObjToList(DFUN,chart_id,nm,obj); }
Здесь же пропишем флаги, необходимые для правильной работы изменённого сегодня метода для установки значений свойств графического объекта.
Все остальные методы для создания стандартных графических объектов изменены точно таким же образом, и здесь мы их рассматривать не будем — с ними можно ознакомиться в прикреплённых к статье файлах.
В методе, создающем новый объект управления графическими объектами указанного чарта и добавляющем его в список, добавим вывод сообщений в журнал при добавлении индикатора контроля событий на график:
//+------------------------------------------------------------------+ //| Создаёт новый объект управления графическими объектами | //| указанного чарта и добавляет его в список | //+------------------------------------------------------------------+ CChartObjectsControl *CGraphElementsCollection::CreateChartObjectCtrlObj(const long chart_id) { //--- Создаём новый объект управления объектами графика по идентификатору CChartObjectsControl *obj=new CChartObjectsControl(chart_id); //--- Если объект не создан - сообщаем об ошибке и возвращаем NULL if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_ELM_COLLECTION_ERR_FAILED_CREATE_CTRL_OBJ),(string)chart_id); return NULL; } //--- Если объект не удалось добавить в список - сообщаем об ошибке, удаляем объект и возвращаем NULL if(!this.m_list_charts_control.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); delete obj; return NULL; } if(obj.ChartID()!=CBaseObj::GetMainChartID() && obj.CreateEventControlInd(CBaseObj::GetMainChartID())) if(!obj.AddEventControlInd()) { CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_FAILED_ADD_EVN_CTRL_INDICATOR); CMessage::ToLog(DFUN,::GetLastError(),true); } else ::Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_ADDED_EVN_CTRL_INDICATOR),obj.Symbol()," #",obj.ChartID()); //--- Возвращаем указатель на созданный и добавленный в список объект return obj; } //+------------------------------------------------------------------+
В методе, обновляющем список всех графических объектов, вместо простого вывода в журнал найденного удалённого графического объекта в списке-коллекции, теперь будем отправлять событие об удалении графического объекта:
//+------------------------------------------------------------------+ //| Обновляет список всех графических объектов | //+------------------------------------------------------------------+ void CGraphElementsCollection::Refresh(void) { this.RefreshForExtraObjects(); //--- Объявим переменные для поиска графиков long chart_id=0; int i=0; //--- В цикле по всем открытым графикам терминала (не более 100) while(i<CHARTS_MAX) { //--- Получим идентификатор графика chart_id=::ChartNext(chart_id); if(chart_id<0) break; //--- Получаем указатель на объект управления графическими объектами //--- и обновляем список графических объектов по идентификатору чарта CChartObjectsControl *obj_ctrl=this.RefreshByChartID(chart_id); //--- Если указатель получить не удалось - идём к следующему графику if(obj_ctrl==NULL) continue; //--- Если есть событие изменения количества объектов на графике if(obj_ctrl.IsEvent()) { //--- Если добавлен графический объект на график if(obj_ctrl.Delta()>0) { //--- Получаем список добавленных графических объектов и перемещаем их в список-коллекцию //--- (если объект поместить в коллекцию не удалось - идём к следующему объекту) if(!this.AddGraphObjToCollection(DFUN_ERR_LINE,obj_ctrl)) continue; } //--- Если удалён графический объект else if(obj_ctrl.Delta()<0) { // Найдём лишний объект в списке CGStdGraphObj *obj=this.FindMissingObj(chart_id); if(obj!=NULL) { //--- Отправляем событие на график управляющей программы ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_DELETE,obj.ChartID(),0,obj.Name()); //--- Удалим объект класса удалённого графического объекта из списка-коллекции if(!this.DeleteGraphObjFromList(obj)) CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_FAILED_DETACH_OBJ_FROM_LIST); } } } //--- Увеличиваем индекс цикла i++; } } //+------------------------------------------------------------------+
В событии, в его строковом параметре, передаём имя удалённого объекта. Вполне возможно, что далее сделаем сохранение удалённых объектов в отдельный список — чтобы при получении события об удалении можно было посмотреть какой объект был удалён, и какие свойства он имел, ну и восстановить его при необходимости программно.
В методе, обрабатывающем удаление окна графика, тоже вместо простого сообщения в журнал теперь будем отправлять событие на график управляющей программы:
//+------------------------------------------------------------------+ //| Обрабатывает удаление окна графика | //+------------------------------------------------------------------+ void CGraphElementsCollection::RefreshForExtraObjects(void) { for(int i=this.m_list_charts_control.Total()-1;i>WRONG_VALUE;i--) { CChartObjectsControl *obj_ctrl=this.m_list_charts_control.At(i); if(obj_ctrl==NULL) continue; if(!this.IsPresentChartWindow(obj_ctrl.ChartID())) { long chart_id=obj_ctrl.ChartID(); string chart_symb=obj_ctrl.Symbol(); int total_ctrl=this.m_list_charts_control.Total(); this.DeleteGraphObjCtrlObjFromList(obj_ctrl); int total_obj=this.m_list_all_graph_obj.Total(); this.DeleteGraphObjectsFromList(chart_id); int del_ctrl=total_ctrl-this.m_list_charts_control.Total(); int del_obj=total_obj-this.m_list_all_graph_obj.Total(); ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_DEL_CHART,chart_id,del_obj,chart_symb); } } } //+------------------------------------------------------------------+
Здесь мы передаём в параметрах события идентификатор закрытого графика, количество графических объектов, удалённых вместе с ним, и символ закрытого графика.
В обработчике событий класса коллекции графических элементов добавим обработку базовых событий графических объектов:
//+------------------------------------------------------------------+ //| Обработчик событий | //+------------------------------------------------------------------+ void CGraphElementsCollection::OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { CGStdGraphObj *obj=NULL; ushort idx=ushort(id-CHARTEVENT_CUSTOM); if(id==CHARTEVENT_OBJECT_CHANGE || id==CHARTEVENT_OBJECT_DRAG || id==CHARTEVENT_OBJECT_CLICK || idx==CHARTEVENT_OBJECT_CHANGE || idx==CHARTEVENT_OBJECT_DRAG || idx==CHARTEVENT_OBJECT_CLICK) { //--- Рассчитаем идентификатор графика //--- Если id события соответствует событию с текущего графика, то идентификатор графика получаем от ChartID //--- Если id события соответствует пользовательскому событию, то идентификатор графика получаем из lparam //--- Иначе - идентификатору графика присваиваем -1 long param=(id==CHARTEVENT_OBJECT_CLICK ? ::ChartID() : idx==CHARTEVENT_OBJECT_CLICK ? lparam : WRONG_VALUE); long chart_id=(param==WRONG_VALUE ? (lparam==0 ? ::ChartID() : lparam) : param); //--- Получим объект из списка-коллекции по его имени, записанном в sparam, //--- свойства которого изменены, или который был перемещён obj=this.GetStdGraphObject(sparam,chart_id); //--- Если объект не удалось получить по имени - он отсутствует в списке, //--- а значит его имя было изменено if(obj==NULL) { //--- Найдём в списке объект, которого нет на графике obj=this.FindMissingObj(chart_id); //--- Если и тут не удалось найти объект - уходим if(obj==NULL) return; //--- Получим имя переименованного графического объекта на графике, которого нет в списке-коллекции string name_new=this.FindExtraObj(chart_id); //--- Отправим событие со старым именем объекта на график управляющей программы и //--- установим новое имя объекту в списке-коллекции, которому не соответствует ни один графический объект на графике ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_RENAME,obj.ChartID(),0,obj.Name()); obj.SetName(name_new); } //--- Обновим свойства полученного объекта //--- и проверим их изменение obj.PropertiesRefresh(); obj.PropertiesCheckChanged(); } //--- Обработка событий стандартных графических объектов if(idx>GRAPH_OBJ_EVENT_NO_EVENT && idx<GRAPH_OBJ_EVENTS_NEXT_CODE) { //--- В зависимости от типа события выводим сообщение о нём в журнал switch(idx) { case GRAPH_OBJ_EVENT_CREATE : ::Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CREATE)); obj=this.GetStdGraphObject(sparam,lparam); if(obj!=NULL) obj.PrintShort(); break; case GRAPH_OBJ_EVENT_CHANGE : ::Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CHANGE)); obj=this.GetStdGraphObject(sparam,lparam); if(obj!=NULL) obj.PrintShort(); break; case GRAPH_OBJ_EVENT_RENAME : ::Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_RENAME)); obj=this.GetStdGraphObject(sparam,lparam); if(obj!=NULL) obj.PrintShort(); break; case GRAPH_OBJ_EVENT_DELETE : ::Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DELETE)); break; case GRAPH_OBJ_EVENT_DEL_CHART: ::Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DEL_CHART),": ChartID: ",lparam,", ChartSymbol: ",sparam); break; default: break; } } } //+------------------------------------------------------------------+
Логика метода расписана в комментариях к коду. Здесь мы добавили реакцию на событие щелчка по графику, и если был двойной щелчок, то у объекта изменится его состояние выделенности, что приведёт к появлению события об изменении параметров объекта. Таким образом, мы сможем обрабатывать события выделения и снятия выделения с объекта.
В конечном итоге в этот же метод приходят уже обработанные события графического объекта, для которых были созданы базовые события. Эти базовые события обрабатываются в новом блоке кода. Пока здесь у нас лишь вывод сообщений в журнал. В последующих статьях создадим полноценную обработку каждого из этих событий так, чтобы программа могла знать и быстро получить доступ как к самому изменённому объекту, так и к его свойствам.
Для упрощения работы с графическими объектами, создадим в главном классе библиотеки CEngine в \MQL5\Include\DoEasy\Engine.mqh методы для работы с графическими объектами.
Чтобы при использовании классов стандартных графических объектов определить какие графики у нас открыты, мы можем посмотреть список объектов управления графиками. В этом списке можно взять из объектов идентификаторы всех открытых графиков, тем самым получив возможность к ним обращаться. Чтобы в программе упростить получение идентификаторов, создадим метод, который будет заполнять переданный в него long-массив значениями идентификаторов открытых графиков, для которых созданы объекты управления:
//--- Запускает новый отсчёт паузы void Pause(const ulong pause_msc,const datetime time_start=0) { this.PauseSetWaitingMSC(pause_msc); this.PauseSetTimeBegin(time_start*1000); while(!this.PauseIsCompleted() && !::IsStopped()){} } //--- Возвращает коллекцию графических объектов CGraphElementsCollection *GetGraphicObjCollection(void) { return &this.m_graph_objects; } //--- Заполняет массив идентификаторами графиков, открытых в терминале void GraphGetArrayChartsID(long &array_charts_id[]) { CArrayObj *list=this.m_graph_objects.GetListChartsControl(); if(list==NULL) return; ::ArrayResize(array_charts_id,list.Total()); ::ArrayInitialize(array_charts_id,WRONG_VALUE); for(int i=0;i<list.Total();i++) { CChartObjectsControl *obj=list.At(i); if(obj==NULL) continue; array_charts_id[i]=obj.ChartID(); } } //--- Создаёт графический объект "Вертикальная линия"
Здесь мы сразу устанавливаем размер переданного в метод массива равным размеру списка объектов управления графиками и инициализируем массив значениями -1. Это нужно в случае ошибки получения объекта управления графиками в цикле, чтобы в ячейку массива, соответствующую индексу объекта управления графиками, который получить не удалось, было вписано значение -1, что будет служить сигналом ошибки получения этого объекта.
Можно конечно увеличивать размер массива в цикле только в случае успешного получения объекта, но вызов ArrayResize() внутри цикла не способствует быстродействию. Поэтому массив сразу увеличиваем под размер списка, и в случае ошибки соответствующая ячейка массива будет содержать -1.
Далее в цикле по полученному списку получаем очередной объект управления графиками и записываем в соответствующую индексу цикла ячейку массива идентификатор графика, содержащийся в объекте управления графиками.
Для упрощённого доступа к методам создания стандартных графических объектов напишем методы для их создания — для каждого объекта по два метода. Первый метод будет вызывать одноимённый метод класса коллекции графических элементов для создания стандартного графического объекта,
а второй — будет вызывать первый метод с указанием идентификатора текущего чарта:
//--- Создаёт графический объект "Вертикальная линия" bool CreateLineVertical(const long chart_id,const string name,const int subwindow,const datetime time) { return this.m_graph_objects.CreateLineVertical(chart_id,name,subwindow,time); } bool CreateLineVertical(const string name,const int subwindow,const datetime time) { return this.CreateLineVertical(::ChartID(),name,subwindow,time); } //--- Создаёт графический объект "Горизонтальная линия" bool CreateLineHorizontal(const long chart_id,const string name,const int subwindow,const double price) { return this.m_graph_objects.CreateLineHorizontal(chart_id,name,subwindow,price); } bool CreateLineHorizontal(const string name,const int subwindow,const double price) { return this.CreateLineHorizontal(::ChartID(),name,subwindow,price); } //--- Создаёт графический объект "Трендовая линия" bool CreateLineTrend(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.m_graph_objects.CreateLineTrend(chart_id,name,subwindow,time1,price1,time2,price2); } bool CreateLineTrend(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.CreateLineTrend(::ChartID(),name,subwindow,time1,price1,time2,price2); } //--- Создаёт графический объект "Трендовая линия по углу" bool CreateLineTrendByAngle(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const double angle) { return this.m_graph_objects.CreateLineTrendByAngle(chart_id,name,subwindow,time1,price1,time2,price2,angle); } bool CreateLineTrendByAngle(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const double angle) { return this.CreateLineTrendByAngle(::ChartID(),name,subwindow,time1,price1,time2,price2,angle); } //--- Создаёт графический объект "Циклические линии" bool CreateLineCycle(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.m_graph_objects.CreateLineCycle(chart_id,name,subwindow,time1,price1,time2,price2); } bool CreateLineCycle(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.CreateLineCycle(::ChartID(),name,subwindow,time1,price1,time2,price2); } //--- Создаёт графический объект "Линия со стрелкой" bool CreateLineArrowed(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.m_graph_objects.CreateLineArrowed(chart_id,name,subwindow,time1,price1,time2,price2); } bool CreateLineArrowed(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.CreateLineArrowed(::ChartID(),name,subwindow,time1,price1,time2,price2); } //--- Создаёт графический объект "Равноудаленный канал" bool CreateChannel(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const datetime time3,const double price3) { return this.m_graph_objects.CreateChannel(chart_id,name,subwindow,time1,price1,time2,price2,time3,price3); } bool CreateChannel(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const datetime time3,const double price3) { return this.CreateChannel(::ChartID(),name,subwindow,time1,price1,time2,price2,time3,price3); } //--- Создаёт графический объект "Канал стандартного отклонения" bool CreateChannelStdDeviation(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const double deviation=1.5) { return this.m_graph_objects.CreateChannelStdDeviation(chart_id,name,subwindow,time1,price1,time2,price2,deviation); } bool CreateChannelStdDeviation(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const double deviation=1.5) { return this.CreateChannelStdDeviation(::ChartID(),name,subwindow,time1,price1,time2,price2,deviation); } //--- Создаёт графический объект "Канал на линейной регрессии" bool CreateChannelRegression(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.m_graph_objects.CreateChannelRegression(chart_id,name,subwindow,time1,price1,time2,price2); } bool CreateChannelRegression(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.CreateChannelRegression(::ChartID(),name,subwindow,time1,price1,time2,price2); } //--- Создаёт графический объект "Вилы Эндрюса" bool CreatePitchforkAndrews(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const datetime time3,const double price3) { return this.m_graph_objects.CreatePitchforkAndrews(chart_id,name,subwindow,time1,price1,time2,price2,time3,price3); } bool CreatePitchforkAndrews(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const datetime time3,const double price3) { return this.CreatePitchforkAndrews(::ChartID(),name,subwindow,time1,price1,time2,price2,time3,price3); } //--- Создаёт графический объект "Линия Ганна" bool CreateGannLine(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2,const double angle) { return this.m_graph_objects.CreateGannLine(chart_id,name,subwindow,time1,price1,time2,price2,angle); } bool CreateGannLine(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2,const double angle) { return this.CreateGannLine(::ChartID(),name,subwindow,time1,price1,time2,price2,angle); } //--- Создаёт графический объект "Веер Ганна" bool CreateGannFan(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const ENUM_GANN_DIRECTION direction,const double scale) { return this.m_graph_objects.CreateGannFan(chart_id,name,subwindow,time1,price1,time2,price2,direction,scale); } bool CreateGannFan(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const ENUM_GANN_DIRECTION direction,const double scale) { return this.CreateGannFan(::ChartID(),name,subwindow,time1,price1,time2,price2,direction,scale); } //--- Создаёт графический объект "Сетка Ганна" bool CreateGannGrid(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2, const ENUM_GANN_DIRECTION direction,const double scale) { return this.m_graph_objects.CreateGannGrid(chart_id,name,subwindow,time1,price1,time2,direction,scale); } bool CreateGannGrid(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2, const ENUM_GANN_DIRECTION direction,const double scale) { return this.CreateGannGrid(::ChartID(),name,subwindow,time1,price1,time2,direction,scale); } //--- Создаёт графический объект "Уровни Фибоначчи" bool CreateFiboLevels(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.m_graph_objects.CreateFiboLevels(chart_id,name,subwindow,time1,price1,time2,price2); } bool CreateFiboLevels(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.CreateFiboLevels(::ChartID(),name,subwindow,time1,price1,time2,price2); } //--- Создаёт графический объект "Временные зоны Фибоначчи" bool CreateFiboTimeZones(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.m_graph_objects.CreateFiboTimeZones(chart_id,name,subwindow,time1,price1,time2,price2); } bool CreateFiboTimeZones(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.CreateFiboTimeZones(::ChartID(),name,subwindow,time1,price1,time2,price2); } //--- Создаёт графический объект "Веер Фибоначчи" bool CreateFiboFan(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.m_graph_objects.CreateFiboFan(chart_id,name,subwindow,time1,price1,time2,price2); } bool CreateFiboFan(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.CreateFiboFan(::ChartID(),name,subwindow,time1,price1,time2,price2); } //--- Создаёт графический объект "Дуги Фибоначчи" bool CreateFiboArc(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const double scale,const bool ellipse) { return this.m_graph_objects.CreateFiboArc(chart_id,name,subwindow,time1,price1,time2,price2,scale,ellipse); } bool CreateFiboArc(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const double scale,const bool ellipse) { return this.CreateFiboArc(::ChartID(),name,subwindow,time1,price1,time2,price2,scale,ellipse); } //--- Создаёт графический объект "Канал Фибоначчи" bool CreateFiboChannel(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2,const datetime time3,const double price3) { return this.m_graph_objects.CreateFiboChannel(chart_id,name,subwindow,time1,price1,time2,price2,time3,price3); } bool CreateFiboChannel(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2,const datetime time3,const double price3) { return this.CreateFiboChannel(::ChartID(),name,subwindow,time1,price1,time2,price2,time3,price3); } //--- Создаёт графический объект "Расширение Фибоначчи" bool CreateFiboExpansion(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2,const datetime time3,const double price3) { return this.m_graph_objects.CreateFiboExpansion(chart_id,name,subwindow,time1,price1,time2,price2,time3,price3); } bool CreateFiboExpansion(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2,const datetime time3,const double price3) { return this.CreateFiboExpansion(::ChartID(),name,subwindow,time1,price1,time2,price2,time3,price3); } //--- Создаёт графический объект "5-волновка Эллиота" bool CreateElliothWave5(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const datetime time3,const double price3,const datetime time4,const double price4, const datetime time5,const double price5,const ENUM_ELLIOT_WAVE_DEGREE degree, const bool draw_lines) { return this.m_graph_objects.CreateElliothWave5(chart_id,name,subwindow,time1,price1,time2,price2,time3,price3,time4,price4,time5,price5,degree,draw_lines); } bool CreateElliothWave5(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2, const datetime time3,const double price3,const datetime time4,const double price4, const datetime time5,const double price5,const ENUM_ELLIOT_WAVE_DEGREE degree, const bool draw_lines) { return this.CreateElliothWave5(::ChartID(),name,subwindow,time1,price1,time2,price2,time3,price3,time4,price4,time5,price5,degree,draw_lines); } //--- Создаёт графический объект "3-волновка Эллиота" bool CreateElliothWave3(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2, const double price2,const datetime time3,const double price3, const ENUM_ELLIOT_WAVE_DEGREE degree,const bool draw_lines) { return this.m_graph_objects.CreateElliothWave3(chart_id,name,subwindow,time1,price1,time2,price2,time3,price3,degree,draw_lines); } bool CreateElliothWave3(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2, const double price2,const datetime time3,const double price3, const ENUM_ELLIOT_WAVE_DEGREE degree,const bool draw_lines) { return this.CreateElliothWave3(::ChartID(),name,subwindow,time1,price1,time2,price2,time3,price3,degree,draw_lines); } //--- Создаёт графический объект "Прямоугольник" bool CreateRectangle(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.m_graph_objects.CreateRectangle(chart_id,name,subwindow,time1,price1,time2,price2); } bool CreateRectangle(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { return this.CreateRectangle(::ChartID(),name,subwindow,time1,price1,time2,price2); } //--- Создаёт графический объект "Треугольник" bool CreateTriangle(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2,const datetime time3,const double price3) { return this.m_graph_objects.CreateTriangle(chart_id,name,subwindow,time1,price1,time2,price2,time3,price3); } bool CreateTriangle(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2,const datetime time3,const double price3) { return this.CreateTriangle(::ChartID(),name,subwindow,time1,price1,time2,price2,time3,price3); } //--- Создаёт графический объект "Эллипс" bool CreateEllipse(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2,const datetime time3,const double price3) { return this.m_graph_objects.CreateEllipse(chart_id,name,subwindow,time1,price1,time2,price2,time3,price3); } bool CreateEllipse(const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2,const datetime time3,const double price3) { return this.CreateEllipse(::ChartID(),name,subwindow,time1,price1,time2,price2,time3,price3); } //--- Создаёт графический объект "Знак "Хорошо" bool CreateThumbUp(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { return this.m_graph_objects.CreateThumbUp(chart_id,name,subwindow,time,price); } bool CreateThumbUp(const string name,const int subwindow,const datetime time,const double price) { return this.CreateThumbUp(::ChartID(),name,subwindow,time,price); } //--- Создаёт графический объект "Знак "Плохо" bool CreateThumbDown(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { return this.m_graph_objects.CreateThumbDown(chart_id,name,subwindow,time,price); } bool CreateThumbDown(const string name,const int subwindow,const datetime time,const double price) { return this.CreateThumbDown(::ChartID(),name,subwindow,time,price); } //--- Создаёт графический объект "Знак "Стрелка вверх" bool CreateArrowUp(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { return this.m_graph_objects.CreateArrowUp(chart_id,name,subwindow,time,price); } bool CreateArrowUp(const string name,const int subwindow,const datetime time,const double price) { return this.CreateArrowUp(::ChartID(),name,subwindow,time,price); } //--- Создаёт графический объект "Знак "Стрелка вниз" bool CreateArrowDown(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { return this.m_graph_objects.CreateArrowDown(chart_id,name,subwindow,time,price); } bool CreateArrowDown(const string name,const int subwindow,const datetime time,const double price) { return this.CreateArrowDown(::ChartID(),name,subwindow,time,price); } //--- Создаёт графический объект "Знак "Стоп" bool CreateSignalStop(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { return this.m_graph_objects.CreateSignalStop(chart_id,name,subwindow,time,price); } bool CreateSignalStop(const string name,const int subwindow,const datetime time,const double price) { return this.CreateSignalStop(::ChartID(),name,subwindow,time,price); } //--- Создаёт графический объект "Знак "Птичка" bool CreateSignalCheck(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { return this.m_graph_objects.CreateSignalCheck(chart_id,name,subwindow,time,price); } bool CreateSignalCheck(const string name,const int subwindow,const datetime time,const double price) { return this.CreateSignalCheck(::ChartID(),name,subwindow,time,price); } //--- Создаёт графический объект "Левая ценовая метка" bool CreatePriceLabelLeft(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { return this.m_graph_objects.CreatePriceLabelLeft(chart_id,name,subwindow,time,price); } bool CreatePriceLabelLeft(const string name,const int subwindow,const datetime time,const double price) { return this.CreatePriceLabelLeft(::ChartID(),name,subwindow,time,price); } //--- Создаёт графический объект "Правая ценовая метка" bool CreatePriceLabelRight(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { return this.m_graph_objects.CreatePriceLabelRight(chart_id,name,subwindow,time,price); } bool CreatePriceLabelRight(const string name,const int subwindow,const datetime time,const double price) { return this.CreatePriceLabelRight(::ChartID(),name,subwindow,time,price); } //--- Создаёт графический объект "Знак "Buy" bool CreateSignalBuy(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { return this.m_graph_objects.CreateSignalBuy(chart_id,name,subwindow,time,price); } bool CreateSignalBuy(const string name,const int subwindow,const datetime time,const double price) { return this.CreateSignalBuy(::ChartID(),name,subwindow,time,price); } //--- Создаёт графический объект "Знак "Sell" bool CreateSignalSell(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { return this.m_graph_objects.CreateSignalSell(chart_id,name,subwindow,time,price); } bool CreateSignalSell(const string name,const int subwindow,const datetime time,const double price) { return this.CreateSignalSell(::ChartID(),name,subwindow,time,price); } //--- Создаёт графический объект "Стрелка" bool CreateArrow(const long chart_id,const string name,const int subwindow,const datetime time,const double price, const uchar arrow_code,const ENUM_ARROW_ANCHOR anchor) { return this.m_graph_objects.CreateArrow(chart_id,name,subwindow,time,price,arrow_code,anchor); } bool CreateArrow(const string name,const int subwindow,const datetime time,const double price, const uchar arrow_code,const ENUM_ARROW_ANCHOR anchor) { return this.CreateArrow(::ChartID(),name,subwindow,time,price,arrow_code,anchor); } //--- Создаёт графический объект "Текст" bool CreateText(const long chart_id,const string name,const int subwindow,const datetime time,const double price, const string text,const int size,const ENUM_ANCHOR_POINT anchor_point,const double angle) { return this.m_graph_objects.CreateText(chart_id,name,subwindow,time,price,text,size,anchor_point,angle); } bool CreateText(const string name,const int subwindow,const datetime time,const double price, const string text,const int size,const ENUM_ANCHOR_POINT anchor_point,const double angle) { return this.CreateText(::ChartID(),name,subwindow,time,price,text,size,anchor_point,angle); } //--- Создаёт графический объект "Текстовая метка" bool CreateTextLabel(const long chart_id,const string name,const int subwindow,const int x,const int y, const string text,const int size,const ENUM_BASE_CORNER corner, const ENUM_ANCHOR_POINT anchor_point,const double angle) { return this.m_graph_objects.CreateTextLabel(chart_id,name,subwindow,x,y,text,size,corner,anchor_point,angle); } bool CreateTextLabel(const string name,const int subwindow,const int x,const int y, const string text,const int size,const ENUM_BASE_CORNER corner, const ENUM_ANCHOR_POINT anchor_point,const double angle) { return this.CreateTextLabel(::ChartID(),name,subwindow,x,y,text,size,corner,anchor_point,angle); } //--- Создаёт графический объект "Кнопка" bool CreateButton(const long chart_id,const string name,const int subwindow,const int x,const int y,const int w,const int h, const ENUM_BASE_CORNER corner,const int font_size,const bool button_state) { return this.m_graph_objects.CreateButton(chart_id,name,subwindow,x,y,w,h,corner,font_size,button_state); } bool CreateButton(const string name,const int subwindow,const int x,const int y,const int w,const int h, const ENUM_BASE_CORNER corner,const int font_size,const bool button_state) { return this.CreateButton(::ChartID(),name,subwindow,x,y,w,h,corner,font_size,button_state); } //--- Создаёт графический объект "График" bool CreateChart(const long chart_id,const string name,const int subwindow,const int x,const int y,const int w,const int h, const ENUM_BASE_CORNER corner,const int scale,const string symbol,const ENUM_TIMEFRAMES timeframe) { return this.m_graph_objects.CreateChart(chart_id,name,subwindow,x,y,w,h,corner,scale,symbol,timeframe); } bool CreateChart(const string name,const int subwindow,const int x,const int y,const int w,const int h, const ENUM_BASE_CORNER corner,const int scale,const string symbol,const ENUM_TIMEFRAMES timeframe) { return this.CreateChart(::ChartID(),name,subwindow,x,y,w,h,corner,scale,symbol,timeframe); } //--- Создаёт графический объект "Рисунок" bool CreateBitmap(const long chart_id,const string name,const int subwindow,const datetime time,const double price, const string image1,const string image2,const ENUM_ANCHOR_POINT anchor) { return this.m_graph_objects.CreateBitmap(chart_id,name,subwindow,time,price,image1,image2,anchor); } bool CreateBitmap(const string name,const int subwindow,const datetime time,const double price, const string image1,const string image2,const ENUM_ANCHOR_POINT anchor) { return this.CreateBitmap(::ChartID(),name,subwindow,time,price,image1,image2,anchor); } //--- Создаёт графический объект "Графическая метка" bool CreateBitmapLabel(const long chart_id,const string name,const int subwindow,const int x,const int y,const int w,const int h, const string image1,const string image2,const ENUM_BASE_CORNER corner,const ENUM_ANCHOR_POINT anchor, const bool state) { return this.m_graph_objects.CreateBitmapLabel(chart_id,name,subwindow,x,y,w,h,image1,image2,corner,anchor,state); } bool CreateBitmapLabel(const string name,const int subwindow,const int x,const int y,const int w,const int h, const string image1,const string image2,const ENUM_BASE_CORNER corner,const ENUM_ANCHOR_POINT anchor, const bool state) { return this.CreateBitmapLabel(::ChartID(),name,subwindow,x,y,w,h,image1,image2,corner,anchor,state); } //--- Создаёт графический объект "Поле ввода" bool CreateEditField(const long chart_id,const string name,const int subwindow,const int x,const int y,const int w,const int h, const int font_size,const ENUM_BASE_CORNER corner,const ENUM_ALIGN_MODE align,const bool readonly) { return this.m_graph_objects.CreateEditField(chart_id,name,subwindow,x,y,w,h,font_size,corner,align,readonly); } bool CreateEditField(const string name,const int subwindow,const int x,const int y,const int w,const int h, const int font_size,const ENUM_BASE_CORNER corner,const ENUM_ALIGN_MODE align,const bool readonly) { return this.CreateEditField(::ChartID(),name,subwindow,x,y,w,h,font_size,corner,align,readonly); } //--- Создаёт графический объект "Событие экономического календаря" bool CreateCalendarEvent(const long chart_id,const string name,const int subwindow,const datetime time) { return this.m_graph_objects.CreateCalendarEvent(chart_id,name,subwindow,time); } bool CreateCalendarEvent(const string name,const int subwindow,const datetime time) { return this.CreateCalendarEvent(::ChartID(),name,subwindow,time); } //--- Создаёт графический объект "Прямоугольная метка" bool CreateRectangleLabel(const long chart_id,const string name,const int subwindow,const int x,const int y,const int w,const int h, const ENUM_BASE_CORNER corner,const ENUM_BORDER_TYPE border) { return this.m_graph_objects.CreateRectangleLabel(chart_id,name,subwindow,x,y,w,h,corner,border); } bool CreateRectangleLabel(const string name,const int subwindow,const int x,const int y,const int w,const int h, const ENUM_BASE_CORNER corner,const ENUM_BORDER_TYPE border) { return this.CreateRectangleLabel(::ChartID(),name,subwindow,x,y,w,h,corner,border); } //--- Конструктор/Деструктор CEngine(); ~CEngine();
На сегодня это все доработки, необходимые для создания базового функционала для отслеживания событий графических объектов.
Тестирование
Для тестирования возьмём советник из прошлой статьи и
сохраним его в новой папке \MQL5\Experts\TestDoEasy\Part90\ под новым именем TestDoEasyPart90.mq5.
Как будем тестировать? В прошлой статье мы при щелчке по графику с зажатой клавишей Ctrl создавали вертикальную линию. Теперь же мы будем тоже создавать её, но на всех открытых графиках в терминале.
В обработчике OnChartEvent() в блоке кода обработки события щелчка по графику добавим код для заполнения массива идентификаторами всех открытых графиков, и в цикле по полученному массиву на каждом из графиков создадим вертикальную линию:
if(id==CHARTEVENT_CLICK) { if(!IsCtrlKeyPressed()) return; datetime time=0; double price=0; int sw=0; if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,sw,time,price)) { long array[]; engine.GraphGetArrayChartsID(array); for(int i=0;i<ArraySize(array);i++) engine.CreateLineVertical(array[i],"LineVertical",0,time); } } engine.GetGraphicObjCollection().OnChartEvent(id,lparam,dparam,sparam); } //+------------------------------------------------------------------+
Скомпилируем советник и запустим его на графике, предварительно открыв ещё один график, и расположив их горизонтально. При щелчке по графику с советником будут созданы вертикальные линии — по одной на каждом из графиков. Теперь поизменяем их свойства и посмотрим, как в журнале выводятся сообщения о полученных событиях:
Как видим, сообщения о событиях объектов выводятся в журнал. При программном создании объектов событие создания объекта не создаётся по причине, что программист и так знает в какой момент времени у него создаётся графический объект программно. Поэтому дублировать этот факт ещё и отсылкой события считаю излишним.
Конечно же, простой вывод в журнал обобщённых сообщений о произошедшем событии недостаточен для обработки событий. Но это лишь сообщения о базовых событиях, в параметрах которых содержится вся информация о событии, которые будем определять далее.
Что дальше
В следующей статье продолжим работу над событиями графических объектов и создадим обработку каждого полученного события.
*Статьи этой серии:
Графика в библиотеке DoEasy (Часть 86): Коллекция графических объектов - контролируем модификацию свойств
Графика в библиотеке DoEasy (Часть 87): Коллекция графических объектов - контроль модификации свойств объектов на всех открытых графиках
Графика в библиотеке DoEasy (Часть 88): Коллекция графических объектов - двумерный динамический массив для хранения динамически изменяемых свойств объектов
Графика в библиотеке DoEasy (Часть 89): Программное создание стандартных графических объектов. Базовый функционал
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования