
DoEasy 函数库中的图形(第九十一部分):标准图形对象事件。 对象名称更改历史记录
内容
概述
该函数库已经能够定义标准图形对象发生的事件。 在本文中,我想实现一种功能,允许基于函数库程序进行操作的用户在接收事件时能准确地判断哪些属性已经更改,以及更改了多少。 这个想法也很简单,它基于传递事件 ID、发生事件的对象名称、更改的图形对象所在的图表的 ID、以及数值更改的属性名称。 当从图表删除图形对象时,这些将删除图形对象的类对象会被放置到已删除对象列表当中。 这令我们能够通过编程来定义已删除对象,并接收其所有属性,或恢复被意外删除的对象。
如果我们把存储已删除图形对象的概念扩展到存储已更改对象属性,我们就能够创建一个有吸引力的功能,令我们能够重现整个对象更改历史。 换言之,如果我们存有全部连续变化的对象属性列表,我们就始终能够重现对象在当前状态之前的任何状态。 因此,可以为对象“设置”相对于图表的不同位置,然后按顺序(或不按顺序)复制它们。 这就有可能令技术分析工具拥有它们自己的“记忆”,包括我将在下一篇文章中要研究的复合图形对象。
我未打算在此全面引入这个概念,但我将以“对象名称”属性更改为例测试它的功能。 在后续的文章中,我将逐步创建记录和复制所有对象属性的能力。
这个概念的产生,来自于需要让程序知道哪些图形对象名称已更改,从而在日志中显示其以前和当前的名称。 我已决定扩展函数库和基于函数库的图形对象的功能。 如此我提出了当前的概念。
改进库类
与往常一样,我会从在 \MQL5\Include\DoEasy\Data.mqh 中实现新的函数库消息开始。
加入新的消息索引:
//--- CGraphElementsCollection MSG_GRAPH_OBJ_FAILED_GET_ADDED_OBJ_LIST, // Failed to get the list of newly added objects MSG_GRAPH_OBJ_FAILED_DETACH_OBJ_FROM_LIST, // Failed to remove a graphical object from the list MSG_GRAPH_OBJ_FAILED_ADD_OBJ_TO_DEL_LIST, // Failed to set a graphical object to the list of removed objects MSG_GRAPH_OBJ_FAILED_ADD_OBJ_TO_RNM_LIST, // Failed to set a graphical object to the list of renamed objects MSG_GRAPH_OBJ_CREATE_EVN_CTRL_INDICATOR, // Indicator for controlling and sending events created MSG_GRAPH_OBJ_FAILED_CREATE_EVN_CTRL_INDICATOR, // Failed to create the indicator for controlling and sending events MSG_GRAPH_OBJ_ADDED_EVN_CTRL_INDICATOR, // Indicator for controlling and sending events successfully added to the chart MSG_GRAPH_OBJ_FAILED_ADD_EVN_CTRL_INDICATOR, // Failed to add the indicator for controlling and sending events MSG_GRAPH_OBJ_ALREADY_EXIST_EVN_CTRL_INDICATOR, // Indicator for controlling and sending events is already present on the chart MSG_GRAPH_OBJ_CLOSED_CHARTS, // Chart windows closed: MSG_GRAPH_OBJ_OBJECTS_ON_CLOSED_CHARTS, // Objects removed together with charts: MSG_GRAPH_OBJ_FAILED_CREATE_EVN_OBJ, // Failed to create the event object for a graphical object MSG_GRAPH_OBJ_FAILED_ADD_EVN_OBJ, // Failed to add the event object to the list MSG_GRAPH_OBJ_GRAPH_OBJ_PROP_CHANGE_HISTORY, // Property change history: MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CREATE, // New graphical object created MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CHANGE, // Changed the graphical object property MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_RENAME, // Graphical object renamed MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DELETE, // Graphical object removed MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DEL_CHART, // Graphical object removed together with the chart }; //+------------------------------------------------------------------+
以及与新添加的索引相对应的文本消息:
//--- CGraphElementsCollection {"Не удалось получить список вновь добавленных объектов","Failed to get the list of newly added objects"}, {"Не удалось изъять графический объект из списка","Failed to detach graphic object from the list"}, {"Не удалось поместить графический объект в список удалённых объектов","Failed to place graphic object in the list of deleted objects"}, {"Не удалось поместить графический объект в список переименованных объектов","Failed to place graphic object in the list of renamed objects"}, {"Создан индикатор контроля и отправки событий","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 "}, {"Индикатор контроля и отправки событий уже есть на графике","The indicator for monitoring and sending events is already on the chart"}, {"Закрыто окон графиков: ","Closed chart windows: "}, {"С ними удалено объектов: ","Objects removed with them: "}, {"Не удалось создать объект-событие для графического объекта","Failed to create event object for graphic object"}, {"Не удалось добавить объект-событие в список","Failed to add event object to list"}, {"История изменения свойства: ","Property change history: "}, {"Создан новый графический объект","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\Objects\Graph\Standard\GStdGraphObj.mqh 的公开部分,添加新的返回指向属性对象指针的方法,并声明在日志中显示图形对象重命名历史记录的方法:
//--- Return (1) itself and (2) the properties CGStdGraphObj *GetObject(void) { return &this; } CProperties *Properties(void) { return this.Prop; } //--- Return the flag of the object supporting this property virtual bool SupportProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_GRAPH_OBJ_PROP_STRING property) { return true; } //--- Get description of (1) integer, (2) real and (3) string properties string GetPropertyDescription(ENUM_GRAPH_OBJ_PROP_INTEGER property); string GetPropertyDescription(ENUM_GRAPH_OBJ_PROP_DOUBLE property); string GetPropertyDescription(ENUM_GRAPH_OBJ_PROP_STRING property); //--- Return the description of the graphical object anchor point position virtual string AnchorDescription(void) const { return (string)this.GetProperty(GRAPH_OBJ_PROP_ANCHOR,0); } //--- Display the description of the object properties in the journal (full_prop=true - all properties, false - supported ones only - implemented in descendant classes) virtual void Print(const bool full_prop=false,const bool dash=false); //--- Display a short description of the object in the journal virtual void PrintShort(const bool dash=false,const bool symbol=false); //--- Return the object short name virtual string Header(const bool symbol=false); //--- Display the history of renaming the object to the journal void PrintRenameHistory(void); //--- Return the description of the (1) (ENUM_OBJECT) graphical object type virtual string TypeDescription(void) const { return CMessage::Text(MSG_GRAPH_STD_OBJ_ANY); }
当重命名一个对象时,如果事先没有设置它,我们就无法知道它以前的名称。 我们已经有了定义对象重命名的机制。 图形对象集合存储位于图表上的标准图形对象类对象,及其每个物理对象的参数描述。 一旦定义了对象重命名事件,我们就在相应的类对象中更改其名称。 在更改名称之前,我们可以知晓对象以前的名称,并将其保存为历史记录。 其它属性亦可如此做。
我已决定将以前的对象名称直接存储在对象属性中,因为图形对象类的对象现在基于多维动态数组,这允许我们随时在其任何维度中设置必要的数组大小。 我们l来这样做。 我打算在以后每次重命名图形对象时增加对象名属性数组,并在数组末尾输入其以前的名称。 因此,对象名称属性数组的单元格 0 将存储其实际名称,而其它数组单元格将存储其以前的名称。 在这种情况下,我们就能始终了解对象重命名的整个历史。 如果我们针对它的其余属性进行同样的处理,我们就能够得到一个有趣的图形对象,能够写入和存储它的状态。 通过指定欲读取对象参数所需的单元,我们就能够快速重现其所有状态,这对于创建具有记忆的分析工具非常有用。
其它属性(非对象名称)将在稍后进行改进。 此处我只验证存储替换对象属性的历史记录的概念。 然后,我将稍微修改存储对象属性的多维数组的结构,以便它们可以用来存储对象的更多属性(例如,其级别),以及级别更改的历史。 当前,级别存储在与对象名称属性值更改历史相同的数组单元格当中。 为了避免属性冲突,我必须稍微修改多维属性数组的结构。 我在下一篇文章中将会这样做。
在类的公开部分中,编写设置对象前一个名称的方法:
//--- Compare CGStdGraphObj objects by a specified property (to sort the list by an object property) virtual int Compare(const CObject *node,const int mode=0) const; //--- Compare CGStdGraphObj objects with each other by all properties (to search for equal objects) bool IsEqual(CGStdGraphObj* compared_req) const; //--- Set the object previous name bool SetNamePrev(const string name) { if(!this.Prop.SetSizeRange(GRAPH_OBJ_PROP_NAME,this.Prop.CurrSize(GRAPH_OBJ_PROP_NAME)+1)) return false; this.SetProperty(GRAPH_OBJ_PROP_NAME,this.Prop.CurrSize(GRAPH_OBJ_PROP_NAME)-1,name); return true; } //--- Default constructor CGStdGraphObj(){ this.m_type=OBJECT_DE_TYPE_GSTD_OBJ; m_group=WRONG_VALUE; } //--- Destructor ~CGStdGraphObj() { if(this.Prop!=NULL) delete this.Prop; } protected:
该方法接收对象的前一个名称,包含该名称属性的数组大小加 1,而新增数组单元格接收传递给该方法的图形对象的前一个名称,然后返回 true。 如果更改数组大小或设置名称失败,则显示相应的日志消息,并返回 false。
在类主体之外,实现在日志中显示对象重命名历史记录的方法:
//+------------------------------------------------------------------+ //| Display the history of renaming the object to the journal | //+------------------------------------------------------------------+ void CGStdGraphObj::PrintRenameHistory(void) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_GRAPH_OBJ_PROP_CHANGE_HISTORY),this.GetPropertyDescription(GRAPH_OBJ_PROP_NAME),":"); int size=this.Properties().CurrSize(GRAPH_OBJ_PROP_NAME); ::Print(DFUN,"0: ",this.GetProperty(GRAPH_OBJ_PROP_NAME,0)); for(int i=size-1;i>0;i--) ::Print(DFUN,size-i,": ",this.GetProperty(GRAPH_OBJ_PROP_NAME,i)); } //+------------------------------------------------------------------+
此处,首先显示带有新对象名称的标题,随后是索引 0 处的当前名称。
接下来,自索引为 1(含)开始反向循环遍历数组单元格,按照循环索引显示添加到数组单元格的对象名称属性的重命名历史描述。 由于当前对象名始终存储在零单元格中,而前一个对象名存储在最后一个单元格中,因此我们只需要计算该值 ,而不必按顺序循环遍历索引来正确显示对象重命名。
例如,重命名三次的对象将显示以下修改项:
CGStdGraphObj::PrintRenameHistory: Property change history: Name: "M30 Vertical Line 24264_3": CGStdGraphObj::PrintRenameHistory: 0: M30 Vertical Line 24264_3 CGStdGraphObj::PrintRenameHistory: 1: M30 Vertical Line 24264_2 CGStdGraphObj::PrintRenameHistory: 2: M30 Vertical Line 24264_1 CGStdGraphObj::PrintRenameHistory: 3: M30 Vertical Line 24264
在此,我们可以清晰地看到重命名图形对象的顺序。 (我可能会更改序列号,令索引 0 始终对应于对象的初始名称,而最后一个索引则对应于当前名称)。
在图形对象集合类中执行图形对象事件跟踪的主要工作。
打开 \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh,并进行必要的修改。
在当前处理添加对象事件的实现中,我们是从终端列表中获取最后添加的对象。 仅当图表中添加了一个新对象时,才会有此操作。 当同时添加若干个对象时,我们从中只会得到一个出现的事件 — 多个对象之一添加到图表上。 为了避免这种情况,我们需要检查是否有一个对象已添加到图表中;如果是,则获取最后添加的对象。 如果同时添加了若干个对象,那么我们需要循环遍历所有图表对象,选择每个对象,并据其发送一个添加事件。
不过,我会稍微改变这个结构。 首先,我将为所有添加对象创建列表。 之后,事件将从该列表发送到控制程序图表。 新添加的对象在 CChartObjectsControl 图表管理类对象的 Refresh() 方法中进行检查,每个打开的图表我们都准备了这些对象。 在这些方法中,我们为每个添加的对象创建类对象,然后将它们添加到列表当中,并循环遍历有关创建对象的消息,将之发送到列表的控制程序。 从程序中调用函数库事件处理程序。 它接收来自所需图表管理对象的消息,提取标准图形对象的类对象,并将它们放置到集合列表之中。
经过改进后,图表管理对象类的 Refresh() 方法如下所示:
//+------------------------------------------------------------------+ //| CChartObjectsControl: Check objects on a chart | //+------------------------------------------------------------------+ void CChartObjectsControl::Refresh(void) { //--- Clear the list of newly added objects this.m_list_new_graph_obj.Clear(); //--- Calculate the number of new objects on the chart this.m_total_objects=::ObjectsTotal(this.ChartID()); this.m_delta_graph_obj=this.m_total_objects-this.m_last_objects; //--- If an object is added to the chart if(this.m_delta_graph_obj>0) { //--- Create the list of added graphical objects for(int i=0;i<this.m_delta_graph_obj;i++) { //--- Get the name of the last added object (if a single new object is added), //--- or a name from the terminal object list by index (if several objects have been added) string name=(this.m_delta_graph_obj==1 ? this.LastAddedGraphObjName() : ::ObjectName(this.m_chart_id,i)); //--- Handle only non-programmatically created objects if(name==NULL || ::StringFind(name,this.m_name_program)>WRONG_VALUE) continue; //--- Create the object of the graphical object class corresponding to the added graphical object type 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 failed to create an object, inform of that and move on to the new iteration if(obj==NULL) { CMessage::ToLog(DFUN,MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ); continue; } //--- Set the object affiliation and add the created object to the list of new objects obj.SetBelong(GRAPH_OBJ_BELONG_NO_PROGRAM); //--- If failed to add the object to the list, inform of that, remove the object and move on to the next iteration if(!this.m_list_new_graph_obj.Add(obj)) { CMessage::ToLog(DFUN_ERR_LINE,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); delete obj; continue; } } //--- Send events to the control program chart from the created list for(int i=0;i<this.m_list_new_graph_obj.Total();i++) { CGStdGraphObj *obj=this.m_list_new_graph_obj.At(i); if(obj==NULL) continue; //--- Send an event to the control program chart ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_CREATE,this.ChartID(),obj.TimeCreate(),obj.Name()); } } //--- save the index of the last added graphical object and the difference with the last check this.m_last_objects=this.m_total_objects; this.m_is_graph_obj_event=(bool)this.m_delta_graph_obj; } //+------------------------------------------------------------------+
代码注释中完整描述了方法逻辑。 EventChartCustom() 函数的 dparam 参数传递图形对象的创建时间。 这对于在先前已删除,但随后又还原的图表上识别对象是必要的。 换言之,如果我们在一个打开的图表上设置图形对象,然后删除该图表,并将其还原,那么在删除时在该图表上设置的图形对象也会随之被还原。 这样的对象创建时间为零值。 稍后,我们可以利用该时间来识别与品种图表一起还原的图形对象。
在将图表管理类的事件控制指标添加到图表上的方法中,于加载之前,确保该指标不曾在图表上出现:
//+------------------------------------------------------------------+ //|CChartObjectsControl: Add the event control indicator to the chart| //+------------------------------------------------------------------+ bool CChartObjectsControl::AddEventControlInd(void) { if(this.m_handle_ind==INVALID_HANDLE) return false; ::ResetLastError(); string shortname="EventSend_From#"+(string)this.ChartID()+"_To#"+(string)this.m_chart_id_main; int total=::ChartIndicatorsTotal(this.ChartID(),0); for(int i=0;i<total;i++) if(::ChartIndicatorName(this.ChartID(),0,i)==shortname) { CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_ALREADY_EXIST_EVN_CTRL_INDICATOR); return true; } return ::ChartIndicatorAdd(this.ChartID(),0,this.m_handle_ind); } //+------------------------------------------------------------------+
在此,我们首先创建一个简短的指标名称,用于在图表上搜索该指标。 接着,循环遍历,获取下一个图表 ID。 如果获得的指标简称与所需名称匹配,则通知该指标已出现在图表上,并返回 true。 循环完成后,返回指标已加载到图表的结果。
在图形对象集合类的私密部分,声明保存已删除图形对象的列表,声明重载的查找对象的方法(存在于集合中,但在图表上不存在),它除了指向检测到的对象指针外,还返回列表中的对象索引。 声明将移除对象重新定位到已移除图形对象列表的方法.
//+------------------------------------------------------------------+ //| Collection of graphical objects | //+------------------------------------------------------------------+ #resource "\\"+PATH_TO_EVENT_CTRL_IND; // Indicator for controlling graphical object events packed into the program resources class CGraphElementsCollection : public CBaseObj { private: CArrayObj m_list_charts_control; // List of chart management objects CListObj m_list_all_canv_elm_obj; // List of all graphical elements on canvas CListObj m_list_all_graph_obj; // List of all graphical objects CArrayObj m_list_deleted_obj; // List of removed graphical objects bool m_is_graph_obj_event; // Event flag in the list of graphical objects int m_total_objects; // Number of graphical objects int m_delta_graph_obj; // Difference in the number of graphical objects compared to the previous check //--- Return the flag indicating the graphical element class object presence in the collection list of graphical elements bool IsPresentGraphElmInList(const int id,const ENUM_GRAPH_ELEMENT_TYPE type_obj); //--- Return the flag indicating the presence of the graphical object class in the graphical object collection list bool IsPresentGraphObjInList(const long chart_id,const string name); //--- Return the flag indicating the presence of a graphical object on a chart by name bool IsPresentGraphObjOnChart(const long chart_id,const string name); //--- Return the pointer to the object of managing objects of the specified chart CChartObjectsControl *GetChartObjectCtrlObj(const long chart_id); //--- Create a new object of managing graphical objects of a specified chart and add it to the list CChartObjectsControl *CreateChartObjectCtrlObj(const long chart_id); //--- Update the list of graphical objects by chart ID CChartObjectsControl *RefreshByChartID(const long chart_id); //--- Check if the chart window is present bool IsPresentChartWindow(const long chart_id); //--- Handle removing the chart window void RefreshForExtraObjects(void); //--- Return the first free ID of the graphical (1) object and (2) element on canvas long GetFreeGraphObjID(bool program_object); long GetFreeCanvElmID(void); //--- Add a graphical object to the collection bool AddGraphObjToCollection(const string source,CChartObjectsControl *obj_control); //--- Find an object present in the collection but not on a chart CGStdGraphObj *FindMissingObj(const long chart_id); CGStdGraphObj *FindMissingObj(const long chart_id,int &index); //--- Find the graphical object present on a chart but not in the collection string FindExtraObj(const long chart_id); //--- Remove the graphical object class object from the graphical object collection list: (1) specified object, (2) by chart ID bool DeleteGraphObjFromList(CGStdGraphObj *obj); void DeleteGraphObjectsFromList(const long chart_id); //--- Move the graphical object class object to the list of removed graphical objects: (1) specified object, (2) by index bool MoveGraphObjToDeletedObjList(CGStdGraphObj *obj); bool MoveGraphObjToDeletedObjList(const int index); //--- Move all objects by chart ID to the list of removed graphical objects void MoveGraphObjectsToDeletedObjList(const long chart_id); //--- Remove the object of managing charts from the list bool DeleteGraphObjCtrlObjFromList(CChartObjectsControl *obj); //--- Create a new standard graphical object, return an object name bool CreateNewStdGraphObject(const long chart_id, const string name, const ENUM_OBJECT type, const int subwindow, 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); public:
在类的公开部分,编写按选定属性返回已删除图形对象列表的方法,声明返回指向已删除图形对象指针的方法,并编写返回已删除图形对象的方法,指向最后删除的图形对象指针的方法,以及返回图形对象属性数组大小的方法。
最后,声明在日志中显示对象重命名历史记录的方法:
public: //--- Return itself CGraphElementsCollection *GetObject(void) { return &this; } //--- Return the full collection list of standard graphical objects "as is" CArrayObj *GetListGraphObj(void) { return &this.m_list_all_graph_obj; } //--- Return the full collection list of graphical elements on canvas "as is" CArrayObj *GetListCanvElm(void) { return &this.m_list_all_canv_elm_obj;} //--- Return the list of graphical elements by a selected (1) integer, (2) real and (3) string properties meeting the compared criterion CArrayObj *GetList(ENUM_CANV_ELEMENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByGraphCanvElementProperty(this.GetListCanvElm(),property,value,mode); } CArrayObj *GetList(ENUM_CANV_ELEMENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByGraphCanvElementProperty(this.GetListCanvElm(),property,value,mode); } CArrayObj *GetList(ENUM_CANV_ELEMENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByGraphCanvElementProperty(this.GetListCanvElm(),property,value,mode); } //--- Return the list of existing graphical objects by a selected (1) integer, (2) real and (3) string properties meeting the compared criterion CArrayObj *GetList(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),property,index,value,mode); } CArrayObj *GetList(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),property,index,value,mode); } CArrayObj *GetList(ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),property,index,value,mode); } //--- Return the list of removed graphical objects by a selected (1) integer, (2) real and (3) string properties meeting the compared criterion CArrayObj *GetListDel(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByGraphicStdObjectProperty(this.GetListDeletedObj(),property,index,value,mode); } CArrayObj *GetListDel(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByGraphicStdObjectProperty(this.GetListDeletedObj(),property,index,value,mode); } CArrayObj *GetListDel(ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByGraphicStdObjectProperty(this.GetListDeletedObj(),property,index,value,mode); } //--- Return the number of new graphical objects, (3) the flag of the occurred change in the list of graphical objects int NewObjects(void) const { return this.m_delta_graph_obj; } bool IsEvent(void) const { return this.m_is_graph_obj_event; } //--- Return an (1) existing and (2) removed graphical object by chart name and ID CGStdGraphObj *GetStdGraphObject(const string name,const long chart_id); CGStdGraphObj *GetStdDelGraphObject(const string name,const long chart_id); //--- Return the list of (1) chart management objects and (2) removed graphical objects CArrayObj *GetListChartsControl(void) { return &this.m_list_charts_control; } CArrayObj *GetListDeletedObj(void) { return &this.m_list_deleted_obj; } //--- Return (1) the last removed graphical object and (2) the array size of graphical object properties CGStdGraphObj *GetLastDeletedGraphObj(void) const { return this.m_list_deleted_obj.At(this.m_list_deleted_obj.Total()-1); } int GetSizeProperty(const string name,const long chart_id,const int prop) { CGStdGraphObj *obj=this.GetStdGraphObject(name,chart_id); return(obj!=NULL ? obj.Properties().CurrSize(prop) : 0); } //--- Constructor CGraphElementsCollection(); //--- Display the description of the object properties in the journal (full_prop=true - all properties, false - supported ones only - implemented in descendant classes) virtual void Print(const bool full_prop=false,const bool dash=false); //--- Display a short description of the object in the journal virtual void PrintShort(const bool dash=false,const bool symbol=false); //--- Display the history of renaming the object to the journal void PrintRenameHistory(const string name,const long chart_id); //--- Create the list of chart management objects and return the number of charts int CreateChartControlList(void); //--- Update the list of (1) all graphical objects, (2) on the specified chart, fill in the data on the number of new ones and set the event flag void Refresh(void); void Refresh(const long chart_id); //--- Event handler void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam); private:
在类构造函数中,清除已删除图形对象列表,并设置排序列表标志:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CGraphElementsCollection::CGraphElementsCollection() { this.m_type=COLLECTION_GRAPH_OBJ_ID; ::ChartSetInteger(::ChartID(),CHART_EVENT_MOUSE_MOVE,true); ::ChartSetInteger(::ChartID(),CHART_EVENT_MOUSE_WHEEL,true); this.m_list_all_graph_obj.Type(COLLECTION_GRAPH_OBJ_ID); this.m_list_all_graph_obj.Sort(SORT_BY_CANV_ELEMENT_ID); this.m_list_all_graph_obj.Clear(); this.m_list_charts_control.Sort(); this.m_list_charts_control.Clear(); this.m_total_objects=0; this.m_is_graph_obj_event=false; this.m_list_deleted_obj.Clear(); this.m_list_deleted_obj.Sort(); } //+------------------------------------------------------------------+
在图形对象集合类的 Refresh() 方法中,当处理从图表中删除图形对象的事件时,我们只找到一个已删除的对象。 现在我们需要找到每个删除的对象,并根据删除对象的数量在循环中将相应的事件发送到控制程序图表。 这是在改进的处理图形对象删除的模块中完成的:
//+------------------------------------------------------------------+ //| Update the list of all graphical objects | //+------------------------------------------------------------------+ void CGraphElementsCollection::Refresh(void) { this.RefreshForExtraObjects(); //--- Declare variables to search for charts long chart_id=0; int i=0; //--- In the loop by all open charts in the terminal (no more than 100) while(i<CHARTS_MAX) { //--- Get the chart ID chart_id=::ChartNext(chart_id); if(chart_id<0) break; //--- Get the pointer to the object for managing graphical objects //--- and update the list of graphical objects by chart ID CChartObjectsControl *obj_ctrl=this.RefreshByChartID(chart_id); //--- If failed to get the pointer, move on to the next chart if(obj_ctrl==NULL) continue; //--- If the number of objects on the chart changes if(obj_ctrl.IsEvent()) { //--- If a graphical object is added to the chart if(obj_ctrl.Delta()>0) { //--- Get the list of added graphical objects and move them to the collection list //--- (if failed to move the object to the collection, move on to the next object) if(!this.AddGraphObjToCollection(DFUN_ERR_LINE,obj_ctrl)) continue; } //--- If the graphical object has been removed else if(obj_ctrl.Delta()<0) { int index=WRONG_VALUE; //--- In the loop by the number of removed graphical objects for(int j=0;j<-obj_ctrl.Delta();j++) { // Find an extra object in the list CGStdGraphObj *obj=this.FindMissingObj(chart_id,index); if(obj!=NULL) { //--- Get the removed object parameters long lparam=obj.ChartID(); string sparam=obj.Name(); double dparam=(double)obj.TimeCreate(); //--- Move the graphical object class object to the list of removed objects //--- and send the event to the control program chart if(this.MoveGraphObjToDeletedObjList(index)) ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_DELETE,lparam,dparam,sparam); } } } } //--- Increase the loop index i++; } } //+------------------------------------------------------------------+
之前,在处理图表窗口移除的方法中,我把描述图形对象的类对象与图表窗口一并移除了。 现在我打算把它们移到已删除图形对象的列表中。 稍后,该列表将令我们能够提取随图表删除的每个对象的所有属性:
//+------------------------------------------------------------------+ //| Handle removing the chart window | //+------------------------------------------------------------------+ 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.MoveGraphObjectsToDeletedObjList(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); } } } //+------------------------------------------------------------------+
该方法定位位于集合中但于图表上不存在的对象,并返回列表中指向该对象及其索引的指针:
//+------------------------------------------------------------------+ //|Find an object present in the collection but not on a chart | //| Return the pointer to the object and its index in the list. | //+------------------------------------------------------------------+ CGStdGraphObj *CGraphElementsCollection::FindMissingObj(const long chart_id,int &index) { CArrayObj *list=CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),GRAPH_OBJ_PROP_CHART_ID,0,chart_id,EQUAL); if(list==NULL) return NULL; index=WRONG_VALUE; for(int i=0;i<list.Total();i++) { CGStdGraphObj *obj=list.At(i); if(obj==NULL) continue; if(!this.IsPresentGraphObjOnChart(obj.ChartID(),obj.Name())) { index=i; return obj; } } return NULL; } //+------------------------------------------------------------------+
该方法是之前实现的同名方法的重载。 与它的“伙伴”不同,除了指向检测到的对象指针,该方法还返回其索引,这在根据列表索引访问检测到的对象时可能是必要的。
依据图表 ID 将所有对象移动到已删除图形对象列表的方法:
//+------------------------------------------------------------------+ //| Move all objects (by chart ID) | //| to the list of removed graphical objects | //+------------------------------------------------------------------+ void CGraphElementsCollection::MoveGraphObjectsToDeletedObjList(const long chart_id) { CArrayObj *list=GetList(GRAPH_OBJ_PROP_CHART_ID,0,chart_id,EQUAL); if(list==NULL) return; for(int i=list.Total()-1;i>WRONG_VALUE;i--) { CGStdGraphObj *obj=list.At(i); if(obj==NULL) continue; this.MoveGraphObjToDeletedObjList(obj); } } //+------------------------------------------------------------------+
该方法将具有指定图表 ID 的所有对象移到已删除对象的列表当中。
于此,我们得到具有指定图表 ID 的对象列表。 另外,循环遍历指定的列表,获取下一个对象,并调用下面研究的方法将其移到已删除图形对象列表。
该方法将图形对象类对象按索引移到已删除对象列表:
//+------------------------------------------------------------------+ //| Move the class object of the graphical object by index | //| to the list of removed graphical objects | //+------------------------------------------------------------------+ bool CGraphElementsCollection::MoveGraphObjToDeletedObjList(const int index) { CGStdGraphObj *obj=this.m_list_all_graph_obj.Detach(index); if(obj==NULL) { CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_FAILED_DETACH_OBJ_FROM_LIST); return false; } if(!this.m_list_deleted_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_FAILED_ADD_OBJ_TO_DEL_LIST); delete obj; return false; } return true; } //+------------------------------------------------------------------+
该方法依据传递给该方法的索引,把对象从集合列表移到已删除对象列表当中。
此处,我们依据指定的索引从列表中提取对象。 如果提取对象失败,则发通知,并返回 false。
如果将对象放入已删除对象列表的操作失败,则发通知,移除对象并返回 false。
结果就是,返回 true。
该方法将指定的图形对象类对象移到已删除图形对象列表:
//+------------------------------------------------------------------+ //| Move the specified graphical object class object | //| to the list of removed graphical objects | //+------------------------------------------------------------------+ bool CGraphElementsCollection::MoveGraphObjToDeletedObjList(CGStdGraphObj *obj) { this.m_list_all_graph_obj.Sort(); int index=this.m_list_all_graph_obj.Search(obj); return this.MoveGraphObjToDeletedObjList(index); } //+------------------------------------------------------------------+
该方法将指针指向的对象移到已移除图形对象列表。
在此,我们为集合列表设置已排序列表标志 ,并调用 Search() 方法获取列表中的对象索引。
当调用方法依据上述列表中的索引移动对象时,将对象移到已移除图形对象列表之中。
该方法依据图表名称和 ID 返回已删除图形对象:
//+------------------------------------------------------------------+ //| Return a removed graphical object | //| by chart name and ID | //+------------------------------------------------------------------+ CGStdGraphObj *CGraphElementsCollection::GetStdDelGraphObject(const string name,const long chart_id) { CArrayObj *list=this.GetListDel(GRAPH_OBJ_PROP_CHART_ID,0,chart_id); list=CSelect::ByGraphicStdObjectProperty(list,GRAPH_OBJ_PROP_NAME,0,name,EQUAL); return(list!=NULL && list.Total()>0 ? list.At(0) : NULL); } //+------------------------------------------------------------------+
对象名和图表 ID 需传递给该方法。 接下来,按图表 ID 接收对象列表。 从所获列表中,按名称接收对象列表(应该只有一个这样的对象)。 如果收列表,且其大小超过零,则返回指向列表中位于索引 0 处的单个对象的指针。 否则,返回 NULL — 获取对象失败。
从事件响应程序中,删除负责处理图形对象事件的代码模块。 将其移到 EA 的事件响应程序里:
//+------------------------------------------------------------------+ //| Event handler | //+------------------------------------------------------------------+ 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) { //--- Calculate the chart ID //--- If the event ID corresponds to an event from the current chart, the chart ID is received from ChartID //--- If the event ID corresponds to a user event, the chart ID is received from lparam //--- Otherwise, the chart ID is assigned to -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); //--- Get the object, whose properties were changed or which was relocated, //--- from the collection list by its name set in sparam obj=this.GetStdGraphObject(sparam,chart_id); //--- If failed to get the object by its name, it is not on the list, //--- which means its name has been changed if(obj==NULL) { //--- Let's search the list for the object that is not on the chart obj=this.FindMissingObj(chart_id); //--- If failed to find the object here as well, exit if(obj==NULL) return; //--- Get the name of the renamed graphical object on the chart, which is not in the collection list string name_new=this.FindExtraObj(chart_id); //--- Send an event with the old name of an object to the control program chart and //--- set a new name for the collection list object, which does not correspond to any graphical object on the chart ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_RENAME,obj.ChartID(),0,obj.Name()); obj.SetName(name_new); } //--- Update the properties of the obtained object //--- and check their change obj.PropertiesRefresh(); obj.PropertiesCheckChanged(); } //--- Handle standard graphical object events if(idx>GRAPH_OBJ_EVENT_NO_EVENT && idx<GRAPH_OBJ_EVENTS_NEXT_CODE) { //--- Depending on the event type, display an appropriate message in the journal 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; } } } //+------------------------------------------------------------------+
在响应程序本身当中,修复发送图形对象重命名消息的逻辑。 如果为对象设置了新名称,就将事件发送到控制程序图表:
//+------------------------------------------------------------------+ //| Event handler | //+------------------------------------------------------------------+ 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) { //--- Calculate the chart ID //--- If the event ID corresponds to an event from the current chart, the chart ID is received from ChartID //--- If the event ID corresponds to a user event, the chart ID is received from lparam //--- Otherwise, the chart ID is assigned to -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); //--- Get the object, whose properties were changed or which was relocated, //--- from the collection list by its name set in sparam obj=this.GetStdGraphObject(sparam,chart_id); //--- If failed to get the object by its name, it is not on the list, //--- which means its name has been changed if(obj==NULL) { //--- Let's search the list for the object that is not on the chart obj=this.FindMissingObj(chart_id); //--- If failed to find the object here as well, exit if(obj==NULL) return; //--- Get the name of the renamed graphical object on the chart, which is not in the collection list string name_new=this.FindExtraObj(chart_id); //--- set a new name for the collection list object, which does not correspond to any graphical object on the chart, //--- and send an event with the new name of the object to the control program chart if(obj.SetNamePrev(obj.Name()) && obj.SetName(name_new)) ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_RENAME,obj.ChartID(),obj.TimeCreate(),obj.Name()); } //--- Update the properties of the obtained object //--- and check their change obj.PropertiesRefresh(); obj.PropertiesCheckChanged(); } } //+------------------------------------------------------------------+
该方法在日志中显示对象重命名历史记录:
//+------------------------------------------------------------------+ //| Display the history of renaming the object to the journal | //+------------------------------------------------------------------+ void CGraphElementsCollection::PrintRenameHistory(const string name,const long chart_id) { CGStdGraphObj *obj=this.GetStdGraphObject(name,chart_id); if(obj==NULL) return; obj.PrintRenameHistory(); } //+------------------------------------------------------------------+
该方法接收图形对象名称及其所在图表的 ID。
按名称和图表 ID 获取指向对象的指针,并调用其方法在日志中显示对象重命名历史记录,方法来自之前研究的抽象标准图形对象类的 PrintRenameHistory() 方法。
我们往 CEngine 函数库主类里添加一些方法,从而能更方便地使用该程序。
打开 \MQL5\Include\DoEasy\Engine.mqh,并添加返回已删除图形对象列表的方法, 返回已删除图形对象数的方法,和返回指定属性数组大小的方法:
//--- Launch the new pause countdown void Pause(const ulong pause_msc,const datetime time_start=0) { this.PauseSetWaitingMSC(pause_msc); this.PauseSetTimeBegin(time_start*1000); while(!this.PauseIsCompleted() && !::IsStopped()){} } //--- Return the (1) collection of graphical objects and (2) the list of removed objects CGraphElementsCollection *GetGraphicObjCollection(void) { return &this.m_graph_objects; } CArrayObj *GetListDeletedObj(void) { return this.m_graph_objects.GetListDeletedObj(); } //--- Return (1) the number of removed graphical objects and (2) the size of the property array int TotalDeletedGraphObjects(void) { return this.GetListDeletedObj().Total(); } int GraphGetSizeProperty(const string name,const long chart_id,const int prop) { return this.m_graph_objects.GetSizeProperty(name,chart_id,prop); } //--- Fill in the array with IDs of the charts opened in the terminal
这些方法返回图形对象集合类中同名方法提供的结果。 我相信,在此无需更多细节。
测试
为了执行测试,我们借用来自前一篇文章中的 EA,并将其保存到 \MQL5\Experts\TestDoEasy\Part91\,作为 TestDoEasyPart91.mq5。
我们需要做的就是把处理图形对象事件的代码模块,从图形对象集合类事件的处理程序中删除,然后添加到 EA 的 OnChartEvent() 响应程序当中:
//+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- If working in the tester, exit if(MQLInfoInteger(MQL_TESTER)) return; //--- If the mouse is moved /* if(id==CHARTEVENT_MOUSE_MOVE) { CForm *form=NULL; datetime time=0; double price=0; int wnd=0; //--- If Ctrl is not pressed, if(!IsCtrlKeyPressed()) { //--- clear the list of created form objects, allow scrolling a chart with the mouse and show the context menu list_forms.Clear(); ChartSetInteger(ChartID(),CHART_MOUSE_SCROLL,true); ChartSetInteger(ChartID(),CHART_CONTEXT_MENU,true); return; } //--- If X and Y chart coordinates are successfully converted into time and price, if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price)) { //--- get the bar index the cursor is hovered over int index=iBarShift(Symbol(),PERIOD_CURRENT,time); if(index==WRONG_VALUE) return; //--- Get the bar index by index CBar *bar=engine.SeriesGetBar(Symbol(),Period(),index); if(bar==NULL) return; //--- Convert the coordinates of a chart from the time/price representation of the bar object to the X and Y coordinates int x=(int)lparam,y=(int)dparam; if(!ChartTimePriceToXY(ChartID(),0,bar.Time(),(bar.Open()+bar.Close())/2.0,x,y)) return; //--- Disable moving a chart with the mouse and showing the context menu ChartSetInteger(ChartID(),CHART_MOUSE_SCROLL,false); ChartSetInteger(ChartID(),CHART_CONTEXT_MENU,false); //--- Create the form object name and hide all objects except one having such a name string name="FormBar_"+(string)index; HideFormAllExceptOne(name); //--- If the form object with such a name does not exist yet, if(!IsPresentForm(name)) { //--- create a new form object form=bar.CreateForm(index,name,x,y,114,16); if(form==NULL) return; //--- Set activity and unmoveability flags for the form form.SetActive(true); form.SetMovable(false); //--- Set the opacity of 200 form.SetOpacity(200); //--- The form background color is set as the first color from the color array form.SetColorBackground(array_clr[0]); //--- Form outlining frame color form.SetColorFrame(C'47,70,59'); //--- Draw the shadow drawing flag form.SetShadow(true); //--- Calculate the shadow color as the chart background color converted to the monochrome one color clrS=form.ChangeColorSaturation(form.ColorBackground(),-100); //--- If the settings specify the usage of the chart background color, replace the monochrome color with 20 units //--- Otherwise, use the color specified in the settings for drawing the shadow color clr=(InpUseColorBG ? form.ChangeColorLightness(clrS,-20) : InpColorForm3); //--- Draw the form shadow with the right-downwards offset from the form by three pixels along all axes //--- Set the shadow opacity to 200, while the blur radius is equal to 4 form.DrawShadow(2,2,clr,200,3); //--- Fill the form background with a vertical gradient form.Erase(array_clr,form.Opacity()); //--- Draw an outlining rectangle at the edges of the form form.DrawRectangle(0,0,form.Width()-1,form.Height()-1,form.ColorFrame(),form.Opacity()); //--- If failed to add the form object to the list, remove the form and exit the handler if(!list_forms.Add(form)) { delete form; return; } //--- Capture the form appearance form.Done(); } //--- If the form object exists, if(form!=NULL) { //--- draw a text with the bar type description on it and show the form. The description corresponds to the mouse cursor position form.TextOnBG(0,bar.BodyTypeDescription(),form.Width()/2,form.Height()/2-1,FRAME_ANCHOR_CENTER,C'7,28,21'); form.Show(); } //--- Re-draw the chart ChartRedraw(); } } */ 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); //--- Handle standard graphical object events ushort idx=ushort(id-CHARTEVENT_CUSTOM); CGStdGraphObj *obj=NULL; if(idx>GRAPH_OBJ_EVENT_NO_EVENT && idx<GRAPH_OBJ_EVENTS_NEXT_CODE) { CChartObjectsControl *chart_ctrl=NULL; int end=0; string evn=""; //--- Depending on the event type, display an appropriate message in the journal switch(idx) { //--- Graphical object creation event case GRAPH_OBJ_EVENT_CREATE : //--- Display the message about creating a new graphical object Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CREATE),":"); //--- Get the pointer to the object by chart name and ID passed to sparam and lparam, respectively //--- and display the short description of a newly created object to the journal obj=engine.GetGraphicObjCollection().GetStdGraphObject(sparam,lparam); if(obj!=NULL) { obj.PrintShort(); } break; //--- Event of changing the graphical object property case GRAPH_OBJ_EVENT_CHANGE : //--- Display the message about changing the graphical object property Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CHANGE),":"); //--- Get the pointer to the object by chart name and ID passed to sparam and lparam, respectively obj=engine.GetGraphicObjCollection().GetStdGraphObject(sparam,lparam); if(obj!=NULL) { //--- Display a short description of the changed object in the journal obj.PrintShort(); //--- calculate the code of the changed property passed to dparam and get the property description if(dparam<GRAPH_OBJ_PROP_INTEGER_TOTAL) evn=obj.GetPropertyDescription((ENUM_GRAPH_OBJ_PROP_INTEGER)dparam); else if(dparam<GRAPH_OBJ_PROP_DOUBLE_TOTAL) evn=obj.GetPropertyDescription((ENUM_GRAPH_OBJ_PROP_DOUBLE)dparam); else evn=obj.GetPropertyDescription((ENUM_GRAPH_OBJ_PROP_STRING)dparam); //--- Display the description of the graphical object's changed property in the journal Print(DFUN,evn); } break; //--- Graphical object renaming event case GRAPH_OBJ_EVENT_RENAME : //--- Display the message about renaming the graphical object Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_RENAME)); //--- Get the pointer to the object by chart name and ID passed to sparam and lparam, respectively obj=engine.GetGraphicObjCollection().GetStdGraphObject(sparam,lparam); if(obj!=NULL) { //--- Display the previous and new object name, as well as its entire renaming history, in the journal Print(DFUN,obj.GetProperty(GRAPH_OBJ_PROP_NAME,obj.Properties().CurrSize(GRAPH_OBJ_PROP_NAME)-1)," >>> ",obj.GetProperty(GRAPH_OBJ_PROP_NAME,0)); obj.PrintRenameHistory(); } break; //--- Graphical object deletion event case GRAPH_OBJ_EVENT_DELETE : //--- Display the message about removing the graphical object Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DELETE),":"); //--- Get the pointer to the removed object by chart name and ID passed to sparam and lparam, respectively //--- and display a short description of the removed object in the journal obj=engine.GetGraphicObjCollection().GetStdDelGraphObject(sparam,lparam); if(obj!=NULL) { obj.PrintShort(); } break; //--- Event of removing the graphical object together with the chart window case GRAPH_OBJ_EVENT_DEL_CHART: //--- Display the message about removing graphical objects together with the chart window, whose ID and symbol are passed to lparam and sparam Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DEL_CHART),": #",lparam,", ",sparam,":"); //--- Calculate the end value for the loop by the list of removed graphical objects end=engine.TotalDeletedGraphObjects()-(int)dparam; if(end<0) end=0; //--- In the loop from the end of the removed graphical objects list up to the value calculated in the 'end' variable, for(int i=engine.TotalDeletedGraphObjects()-1;i>=end;i--) { //--- get the next removed graphical object from the list obj=engine.GetListDeletedObj().At(i); if(obj==NULL) continue; //--- and display its brief description in the journal obj.PrintShort(); } break; //--- default: break; } } } //+------------------------------------------------------------------+
所添加的代码模块逻辑已有详尽描述。 我确信,在此无须赘述。
如果您有任何疑问,请随时在下面的评论中提问。
编译 EA,并在图表上启动它:
正如我们所见,对象重命名保存在其“记忆”之中。
图形对象的打包删除也得到了正确处理:
请记住,在还原之前删除的图表窗口后,并非所有与图表一起删除的图形对象都会在图形对象集合列表中正确恢复。 我还没有找到略过对象的原因。 但我肯定会修复这个问题。
下一步是什么?
在下一篇文章中,我将继续研究图形对象事件,并开始实现在对象属性中存储对象属性更改历史的功能。
*该系列的前几篇文章:
DoEasy 函数库中的图形(第八十六部分):图形对象集合 - 管理属性修改
DoEasy 函数库中的图形(第八十七部分):图形对象集合 - 在所有打开的图表上管理对象属性修改
DoEasy 函数库中的图形(第八十八部分):图形对象集合 — 存储对象动态变化属性的二维动态数组
DoEasy 函数库中的图形(第八十九部分):标准图形对象编程。 基本功能
DoEasy 函数库中的图形(第九十部分):标准图形对象事件。 基本功能
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/10184

