Grafiken in der DoEasy-Bibliothek (Teil 91): Standard-Ereignisse für grafische Objekte. Geschichte der Objektnamensänderung
Inhalt
Konzept
Die Bibliothek ist bereits in der Lage, Ereignisse zu definieren, die bei grafischen Standardobjekten auftreten. In diesem Artikel möchte ich die Funktionsweisen implementieren, die es Nutzern, die in einem bibliotheksbasierten Programm arbeiten, ermöglicht, beim Empfang eines Ereignisses genau zu definieren, welche Eigenschaft um wie viel geändert wurde. Die Idee ist einfach und basiert auf der Übergabe einer Ereignis-ID und des Namens eines Objekts, in dem das Ereignis aufgetreten ist, der ID des Charts, in dem sich das geänderte grafische Objekt befindet, und eines Namens einer Eigenschaft, deren Wert geändert wurde. Wenn grafische Objekte aus dem Chart entfernt werden, werden Objekte der Klassen, die diese entfernten grafischen Objekte beschreiben, in die Liste der entfernten Objekte aufgenommen. Auf diese Weise können wir programmatisch festlegen, welches Objekt entfernt wurde, und alle seine Eigenschaften erhalten oder ein versehentlich gelöschtes Objekt wiederherstellen.
Wenn wir das Konzept der Speicherung entfernter grafischer Objekte auf die Speicherung geänderter Objekteigenschaften ausweiten, können wir eine attraktive Funktionalität schaffen, die es uns ermöglicht, die gesamte Änderungshistorie von Objekten zu wiederholen. Mit anderen Worten, wenn wir die Liste aller nacheinander geänderten Objekteigenschaften haben, können wir jederzeit jeden Zustand reproduzieren, den das Objekt vor dem aktuellen Zustand hatte. Auf diese Weise ist es möglich, verschiedene Objektpositionen relativ zu einem Chart "festzulegen" und sie anschließend nacheinander zu reproduzieren (oder auch nicht). Dadurch wird es möglich, technische Analysewerkzeuge mit einem eigenen "Gedächtnis" zu entwickeln, einschließlich zusammengesetzter grafischer Objekte, die ich in den nächsten Artikeln behandeln werde.
Ich werde dieses Konzept hier nicht in vollem Umfang vorstellen, sondern seine Funktionalität am Beispiel der Änderung der Eigenschaft "Objektname" testen. In den folgenden Artikeln werde ich nach und nach die Möglichkeit schaffen, alle Objekteigenschaften zu erfassen und zu reproduzieren.
Dieses Konzept entstand aus dem Bedürfnis heraus, dem Programm mitzuteilen, welcher Name eines grafischen Objekts geändert wurde, um dessen vorherigen und aktuellen Namen im Journal anzuzeigen. Ich beschloss, dass es gut wäre, die Funktionalität der Bibliothek und der bibliotheksbasierten grafischen Objekte zu erweitern. So habe ich mir das aktuelle Konzept ausgedacht.
Verbesserung der Klassenbibliothek
Wie üblich beginne ich mit der Implementierung neuer Bibliotheksmeldungen in \MQL5\Include\DoEasy\Data.mqh.
Hinzufügen der neuen Nachrichtenindizes:
//--- 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 }; //+------------------------------------------------------------------+
und die Textnachrichten, die den neu hinzugefügten Indizes entsprechen:
//--- 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"}, }; //+---------------------------------------------------------------------+
Im öffentlichen Abschnitt der abstrakten grafischen Standardobjektklasse fügen wir in \MQL5\Include\DoEasy\Objects\Graph\Standard\GStdGraphObj.mqh die neue Methode zur Rückgabe des Zeigers auf das Eigenschaftsobjekt hinzu und deklarieren die Methode zur Anzeige der Historie der Umbenennung des grafischen Objekts im Journal:
//--- 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); }
Bei der Umbenennung eines Objekts können wir seinen vorherigen Namen nicht kennen, wenn er nirgendwo im Voraus festgelegt wurde. Wir verfügen bereits über einen Mechanismus zur Definition einer Objektumbenennung. Die Kollektion der grafische Objekte speichert das Objekt der grafischen Standardobjektklasse zusammen mit der Beschreibung seiner Parameter für jedes physische Objekt in einem Chart. Sobald das Ereignis der Objektumbenennung definiert ist, ändern wir den Namen des Objekts in dem entsprechenden Klassenobjekt. Bevor wir den Namen ändern, können wir den vorherigen Namen des Objekts erfahren und ihn für die Historie speichern. Das Gleiche kann mit den übrigen Eigenschaften gemacht werden.
Ich habe mich entschlossen, den vorherigen Objektnamen direkt in den Objekteigenschaften zu speichern, da die Objekte der grafischen Objektklasse jetzt auf mehrdimensionalen dynamischen Arrays basieren, was es uns ermöglicht, die erforderliche Array-Größe in jeder ihrer Dimensionen jederzeit festzulegen. Lassen Sie uns das nutzen. Ich werde das Array der Objektnamen-Eigenschaft bei jeder nachfolgenden Umbenennung des grafischen Objekts vergrößern und seinen vorherigen Namen an das Ende des Arrays setzen. So wird die Zelle 0 des Objektnamen-Eigenschaftsarrays seinen aktuellen Namen speichern, während die anderen Array-Zellen seine vorherigen Namen speichern. In diesem Fall sind wir immer in der Lage, die gesamte Geschichte der Objektumbenennung zu kennen. Wenn wir das Gleiche für die anderen Eigenschaften tun, erhalten wir ein interessantes grafisches Objekt, das in der Lage ist, seine Zustände zu schreiben und zu speichern. Durch die Angabe der notwendigen Zelle, aus der die Parameter des Objekts gelesen werden sollen, können wir schnell alle seine Zustände reproduzieren, was für die Erstellung von Analysetools mit Speicher nützlich sein kann.
Andere Eigenschaften (nicht der Objektname) werden später verbessert. Hier werde ich nur das Konzept der Speicherung der Geschichte der Ersetzung von Objekteigenschaften prüfen. Dann werde ich die Struktur des mehrdimensionalen Arrays, in dem die Objekteigenschaften gespeichert werden, leicht verändern, so dass sie zum Speichern der Mehrfacheigenschaften des Objekts (z. B. seiner Ebenen) und der Historie der Ebenenänderungen verwendet werden können. Derzeit werden die Ebenen in denselben Array-Zellen gespeichert wie die Historie der Änderungen der Eigenschaftswerte des Objekts Name. Um Eigenschaftskollisionen zu vermeiden, muss ich die Struktur des multidimensionalen Eigenschaftsarrays leicht ändern. Dies werde ich im nächsten Artikel tun.
Im öffentlichen Abschnitt der Klasse legen wir die Methode zum Setzen des vorherigen Namens des Objekts fest:
//--- 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:
Die Methode erhält den vorherigen Namen des Objekts, die Größe des Eigenschafts-Arrays, das den Namen enthält, wird um 1 erhöht, während die neue Array-Zelle den vorherigen Namen des an die Methode übergebenen grafischen Objekts erhält und true zurückgegeben wird. Wenn das Ändern der Arraygröße oder das Setzen des Namens fehlschlägt, wird die entsprechende Journalmeldung angezeigt und false zurückgegeben.
Außerhalb des Klassenkörpers implementieren wir die Methode, die den Verlauf der Objektumbenennung im Journal anzeigt:
//+------------------------------------------------------------------+ //| 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)); } //+------------------------------------------------------------------+
Hier wird zuerst die Kopfzeile mit dem neuen Objektnamen angezeigt, gefolgt von seinem aktuellen Namen mit Index 0.
Als Nächstes wird in der Rückwärtsschleife bis zur Array-Zelle mit dem Index 1 einschließlich die Beschreibung der Objekteigenschaft Name angezeigt, die zu den Array-Zellen des Objektnamens hinzugefügt wurde, in denen seine Umbenennungsgeschichte gespeichert ist nach dem Schleifenindex. Da der aktuelle Objektname immer in der Nullzelle gespeichert wird, während der vorherige in der letzten Zelle gespeichert wird, werden wir diesen Wert berechnen, anstatt den Schleifenindex für die korrekte Anzeige des Objektumbenennungsreihenindexes zu verwenden.
Für ein dreimal umbenanntes Objekt erscheint zum Beispiel folgender Eintrag:
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
Hier sehen wir deutlich die Reihenfolge der Umbenennung eines grafischen Objekts. (Wahrscheinlich werde ich die Nummerierung der Reihenfolge so ändern, dass 0 immer dem ursprünglichen Namen des Objekts entspricht, während der letzte Index dem aktuellen Namen entspricht).
Die Hauptarbeit bei der Fertigstellung der Verfolgung von Ereignissen für grafische Objekte wird in der Kollektionsklasse der grafischen Objekte geleistet.
Wir öffnen \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh und nehmen die erforderlichen Änderungen vor.
In der aktuellen Implementierung der Behandlung des Ereignisses des Hinzufügens von Objekten, holen wir das letzte hinzugefügte Objekt aus der Terminal-Liste. Dies funktioniert nur, wenn dem Chart ein einzelnes neues Objekt hinzugefügt wurde. Wenn mehrere Objekte gleichzeitig hinzugefügt werden, erhalten wir immer noch ein einzelnes Ereignis in der Bibliothek — das Hinzufügen eines einzelnen Objekts zu einem Chart aus mehreren. Um dies zu vermeiden, müssen wir prüfen, ob ein Objekt zum Chart hinzugefügt wurde, und wenn ja, das zuletzt hinzugefügte Objekt abrufen. Wenn mehrere Objekte gleichzeitig hinzugefügt wurden, müssen wir jedes einzelne in der Schleife durch alle Chartobjekte auswählen und ein Ereignis über seine Hinzufügung senden.
Ich werde diese Struktur jedoch leicht abändern. Zunächst werde ich die Liste aller hinzugefügten Objekte erstellen. Von dieser Liste aus werden dann Ereignisse an die Kontrollprogrammkarte gesendet. Neu hinzugefügte Objekte werden in den Methoden Refresh() der CChartObjectsControl-Chartverwaltungsklassenobjekte überprüft, die wir für jede der geöffneten Karten haben. In den Methoden erstellen wir Klassenobjekte für jedes hinzugefügte Objekt, fügen sie dann der Liste hinzu und senden in dieser Schleife Nachrichten über die Erstellung von Objekten an das Kontrollprogramm in der Liste. Die Ereignisbehandlung der Bibliothek wird vom Programm aus aufgerufen. Sie empfängt das notwendige Chartverwaltungsobjekt, von dem die Nachrichten gekommen sind, holt die Klassenobjekte der grafischen Standardobjekte ab und fügt sie in die Sammelliste ein.
Nach den Verbesserungen soll die Methode Refresh() der Chartmanagement-Objektklasse wie folgt aussehen:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Die Logik der Methode ist in den Code-Kommentaren vollständig beschrieben. Der Parameter dparam der Funktion EventChartCustom() übergibt die Erstellungszeit des grafischen Objekts. Dies ist notwendig, um Objekte zu identifizieren, die sich auf einem wiederhergestellten Chart befinden, das zuvor gelöscht wurde. Mit anderen Worten, wenn wir grafische Objekte auf einem geöffneten Chart setzen, dieses Chart dann entfernen und wiederherstellen, werden die grafischen Objekte, die zum Zeitpunkt des Löschens auf dem Chart gesetzt waren, ebenfalls wiederhergestellt. Solche Objekte haben eine Erstellungszeit von Null. Diese Zeit können wir später für die Identifizierung von grafischen Objekten verwenden, die zusammen mit dem Symbol Chart wiederhergestellt wurden.
Vergewissern wir uns in der Methode zum Hinzufügen des Ereignissteuerungsindikators der Chart-Verwaltungsklasse auf dem Chart, dass ein solcher Indikator noch nicht auf dem Chart vorhanden ist, bevor wir ihn hinzufügen:
//+------------------------------------------------------------------+ //|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); } //+------------------------------------------------------------------+
Hier erstellen wir zunächst den kurzen Indikatornamen, der für die Suche nach Indikatoren im Chart verwendet wird. Als Nächstes erhalten wir in der Schleife die nächste Chart-ID. Wenn der Kurzname des erhaltenen Indikators mit dem erforderlichen Indikator übereinstimmt, wird mitgeteilt, dass ein solcher Indikator bereits im Chart vorhanden ist und true zurückgegeben. Nach Beendigung der Schleife wird das Ergebnis des Hinzufügens des Indikators zum Chart zurückgegeben.
Im privaten Teile der Kollektionsklasse der grafischen Objekte deklarieren wir die Liste der entfernten grafischen Objekte und wir die überladene Methode, die das Objekt findet (das in der Kollektion vorhanden ist, aber im Chart fehlt), die neben dem Zeiger auf das gefundene Objekt auch den Objektindex in der Liste zurückgibt. Deklaration der Methoden, die entfernte Objekte in die Liste der entfernten grafischen Objekte verschieben.
//+------------------------------------------------------------------+ //| 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:
In den öffentlichen Abschnitt der Klasse schreiben wir die Methoden, die die Listen der entfernten grafischen Objekte nach ausgewählten Eigenschaften zurückgeben, deklarieren die Methode, die den Zeiger auf das entfernte grafische Objekt zurückgibt und schreiben die Methoden zurück, die die Liste der entfernten grafischen Objekte zurückgeben, den Zeiger auf das letzte entfernte grafische Objekt und die Methode zurück, die die Größe des Arrays der Eigenschaften des grafischen Objekts zurückgibt.
Schließlich deklarieren wir die Methode, die den Verlauf der Objektumbenennung im Journal anzeigt:
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:
Im Klassenkonstruktor löschen wir die Liste der entfernten grafischen Objekte und setzen das Flag für die sortierte Liste:
//+------------------------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
In der Methode Refresh() der Kollektionsklasse der grafischen Objekte haben wir nur ein entferntes Objekt gefunden, als wir das Ereignis des Entfernens grafischer Objekte aus dem Chart behandelt haben. Jetzt müssen wir jedes entfernte Objekt finden und das entsprechende Ereignis an das Steuerprogramm Chart in der Schleife nach der Anzahl der entfernten Objekte senden. Dies geschieht im verbesserten Block zur Behandlung der Entfernung von grafischen Objekten:
//+------------------------------------------------------------------+ //| 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++; } } //+------------------------------------------------------------------+
Zuvor habe ich die Objekte der Klassen, die grafische Objekte beschreiben, die zusammen mit dem Chart-Fenster entfernt wurden, in der Methode zum Entfernen eines Chart-Fensters entfernt. Nun werde ich sie in die Liste der entfernten grafischen Objekte verschieben. Diese Liste wird es uns später ermöglichen, alle Eigenschaften jedes mit dem Chart entfernten Objekts abzurufen:
//+------------------------------------------------------------------+ //| 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); } } } //+------------------------------------------------------------------+
Die Methode befindet sich in der Kollektion, aber nicht auf dem Chart und gibt den Zeiger auf das Objekt und seinen Index in der Liste zurück:
//+------------------------------------------------------------------+ //|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; } //+------------------------------------------------------------------+
Diese Methode ist eine Überladung der zuvor implementierten gleichnamigen Methode. Im Gegensatz zu ihrem "Partner" gibt die Methode zusätzlich zu dem Zeiger auf ein erkanntes Objekt auch den Index zurück, was beim Zugriff auf das erkannte Objekt über seinen Index in der Liste notwendig sein kann.
Die Methode verschiebt alle Objekte nach Chart-ID in die Liste der entfernten grafischen Objekte:
//+------------------------------------------------------------------+ //| 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); } } //+------------------------------------------------------------------+
Die Methode verschiebt alle Objekte mit der angegebenen Chart ID in die Liste der entfernten Objekte.
Hier erhalten wir die Liste der Objekte mit der angegebenen Chart ID. Außerdem holen wir in der Schleife durch die angegebene Liste das nächste Objekt und verschieben es in die Liste der entfernten grafischen Objekte mit der unten betrachteten Methode.
Die Methode verschiebt das Objekt der grafischen Objektklasse nach Index in die Liste der entfernten Objekte:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Die Methode verschiebt das Objekt, dessen Index an die Methode übergeben wurde, aus der Kollektionsliste in die Liste der entfernten Objekte.
Hier holen wir uns das Objekt aus der Liste mit dem angegebenen Index. Wenn das Abrufen des Objekts fehlgeschlagen ist, informieren wir darüber und geben falsezurück.
Wenn es nicht gelungen ist, das Objekt in die Liste der entfernten Objekte aufzunehmen, informiere darüber, entfernen wir das Objekt und geben false zurück.
Als Ergebnis wird true zurückgegeben.
Die Methode verschiebt das angegebene Objekt der grafischen Objektklasse in die Liste der entfernten grafischen Objekte:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Die Methode verschiebt das durch den Zeiger angegebene Objekt in die Liste der entfernten grafischen Objekte.
Hier setzen wir das Flag für die sortierte Liste der Kollektion und erhalten den Objektindex in der Liste mit Hilfe der Methode Search().
Wenn man die Methode verwendet, die das Objekt nach seinem Index in der oben betrachteten Liste verschiebt, verschiebt man das Objekt in die Liste der entfernten grafischen Objekte.
Die Methode gibt ein entferntes grafisches Objekt nach Chart-Name und ID zurück:
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
Der Objektname und die Chart-ID werden der Methode übergeben. Als Nächstes erhalten wir die Liste der Objekte nach Chart ID. Aus der erhaltenen Liste die Liste der Objekte nach Namen erhalten (es sollte nur ein solches Objekt geben). Wenn die Liste empfangen wurde und ihre Größe größer als Null ist, gib den Zeiger auf das einzelne Objekt in der Liste zurück, das sich bei Index 0 befindet. Andernfalls wird NULL zurückgegeben — das Objekt konnte nicht erhalten werden.
Aus der Ereignisbehandlung entfernen wir den Codeblock, der für die Behandlung von grafischen Objektereignissen verantwortlich ist. Verschieben wir ihn in die Ereignisbehandlung des 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; } } } //+------------------------------------------------------------------+
In der Ereignisbehandlung selbst ist die Logik für das Senden der Nachricht zur Umbenennung des grafischen Objekts festzulegen. Wenn ein neuer Name für das Objekt festgelegt wurde, sende das Ereignis an das Kontrollprogramm Chart:
//+------------------------------------------------------------------+ //| 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(); } } //+------------------------------------------------------------------+
Die Methode, die den Verlauf der Objektumbenennung im Journal anzeigt:
//+------------------------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
Die Methode erhält den Namen des grafischen Objekts und die ID des Charts, auf dem es sich befindet.
Wir erhalten den Zeiger auf das Objekt anhand des Namens und der ID des Charts und rufen seine Methode, die den Verlauf der Objektumbenennung im Journal anzeigt, unter Verwendung der zuvor betrachteten Methode PrintRenameHistory() der abstrakten grafischen Standardobjektklasse.
Wir fügen der Hauptklasse der CEngine Bibliothek einige Methoden hinzu, um die Arbeit mit dem Programm zu erleichtern.
Wir öffnen \MQL5\Include\DoEasy\Engine.mqh und fügen die Methode hinzu, die die Liste der entfernten grafischen Objekte zurückgibt, die Methode, die die Anzahl der entfernten grafischen Objekte zurückgibt und die Methode, die die Größe des angegebenen Eigenschaftsarrays zurückgibt:
//--- 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
Diese Methoden geben das Ergebnis zurück, das von den gleichnamigen Methoden der grafischen Objektkollektion geliefert wird. Ich glaube, mehr Details sind hier nicht nötig.
Test
Um den Test durchzuführen, verwenden wir den EA aus dem vorherigen Artikel und speichern ihn in \MQL5\Experts\TestDoEasy\Part91\ als TestDoEasyPart91.mq5.
Alles, was wir tun müssen, ist den Codeblock für die Behandlung von Ereignissen von grafischen Objekten, der aus dem Handler von Ereignissen der Klasse der grafischen Objekte der Kollektion entfernt wurde, zum Handler OnChartEvent() des EA hinzuzufügen:
//+------------------------------------------------------------------+ //| 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; } } } //+------------------------------------------------------------------+
Die Logik des hinzugefügten Codeblocks ist sehr detailliert beschrieben. Ich glaube, es ist nicht nötig, hier darauf einzugehen.
Wenn Sie Fragen haben, können Sie diese gerne in den Kommentaren unten stellen.
Kompilieren Sie den EA und starten Sie ihn auf dem Chart:
Wie wir sehen können, wird die Umbenennung des Objekts in seinem "Speicher" gespeichert.
Auch das Löschen von grafischen Objekten im Paket wird korrekt gehandhabt:
Beachten Sie, dass nicht alle grafischen Objekte, die zusammen mit dem Chart entfernt wurden, in der Liste der grafischen Kollektion korrekt wiederhergestellt werden, nachdem das zuvor entfernte Chart-Fenster wiederhergestellt wurde. Ich habe den Grund für das Überspringen von Objekten noch nicht gefunden. Ich werde dies aber auf jeden Fall beheben.
Was kommt als Nächstes?
Im nächsten Artikel werde ich meine Arbeit an grafischen Objektereignissen fortsetzen und mit der Implementierung der Funktionalität zum Speichern der Änderungshistorie von Objekteigenschaften in den Objekteigenschaften beginnen.
*Frühere Artikel dieser Serie:
Grafiken in der Bibliothek DoEasy (Teil 86): Grafische Objektkollektion - Verwaltung der Eigenschaftsänderungen
Grafiken in der Bibliothek DoEasy (Teil 87): Grafische Kollektion - Verwaltung der Änderungen von Eigenschaften von Objekten auf allen offenen Charts
Grafiken in der Bibliothek DoEasy (Teil 88): Grafische Objektkollektion — zweidimensionales dynamisches Array zur Speicherung der sich dynamisch ändernden Objekteigenschaften
Grafiken in der Bibliothek DoEasy (Teil 89): Programmieren von grafischen Standardobjekten, grundlegende Funktionsweise
Grafiken in der DoEasy-Bibliothek (Teil 90): Standard-Ereignisse für grafische Objekte. grundlegende Funktionsweise
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/10184
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.