Von der Grundstufe bis zur Mittelstufe: Objektereignisse (IV)
Einführung
Im vorigen Artikel „Von der Grundstufe zur Mittelstufe: Objektereignisse (III)“ haben wir den ersten Teil von etwas Größerem gezeigt, das wir hier fortsetzen und in diesem Artikel zum Abschluss bringen werden.
Da der Zweck dieser Artikel darin besteht, Ihnen, lieber Leser, zu zeigen, wie Sie MetaTrader 5 steuern können, um jede Anwendung zu implementieren, hoffe ich, dass Sie das hier Gezeigte studieren und üben. Wichtig ist, dass der in diesen Artikeln besprochene Code niemals als fertige Anwendung betrachtet oder ohne angemessene Vorsicht und gesunden Menschenverstand verwendet werden sollte.
Viele Leute könnten das, was wir hier besprechen, für eine Art Unsinn meinerseits halten, da viele nicht glauben, dass eine Anwendung überhaupt dazu gedacht sein könnte, das zu tun, was ich hier zeige. Doch die Schönheit des Programmierens liegt nicht darin, das Offensichtliche zu tun, sondern neue Möglichkeiten dort zu entdecken, wo nur wenige sie sehen. Deshalb habe ich mich entschlossen, diese Reihe zu erstellen, deren Zweck genau darin besteht, zu zeigen, dass fast alles erreicht werden kann, wenn man bestimmte Konzepte versteht. Es ist also an der Zeit, dass alles, was im vorigen Artikel gezeigt wurde, wirklich Sinn ergibt.
Verbesserung der Interaktivität
Was wir im vorigen Artikel gesehen haben, ist noch weit von einer in sich schlüssigen, vollständigen Lösung entfernt, zumindest wenn wir nur den aktuellen Code im Anhang betrachten. Er blieb jedoch bewusst als Übungsmaterial erhalten, um zu zeigen, wie eine solche Interaktion mit einem Minimum an Code erstellt werden kann.
Die angehängte Datei aus dem vorigen Artikel ist noch nicht vollständig ausgereift. Obwohl wir bereits einige Dinge gezeigt haben, die wir für ein ziemlich ungewöhnliches Ziel implementieren können, und zwar auf sehr einfache Weise, erlaubt es uns immer noch nicht, mit Objekten so zu interagieren, wie wir es möchten. Kurz gesagt, dieser Code erstellt nur das, was in Animation 01 unten zu sehen ist.

Animation 01
Obwohl dies wie etwas Albernes und nicht besonders Nützliches erscheinen mag, zeigt uns diese Animation das Potenzial, das bereits in diesem Code steckt, da sie es uns ermöglicht, einen Rahmen um das ausgewählte Objekt zu erstellen. Unser Ziel ist es jedoch nicht nur, einen solchen Rahmen zu erstellen, sondern auch die Größe des ausgewählten Objekts zu ändern. Hier wird es etwas verwirrend. Genau hier treten einige Schwierigkeiten auf, insbesondere für diejenigen, die das in den vorherigen Artikeln Gezeigte nicht geübt und studiert haben.
In meinen bisherigen Artikeln haben wir gezeigt, dass wir mit MetaTrader 5 interagieren können, um bestimmte Ergebnisse zu erzielen. Die meiste Arbeit wurde jedoch an MetaTrader 5 delegiert, während wir bei der Implementierung des Codes nur einen Teil davon selbst übernommen haben. Hier haben wir jedoch ein kleines Problem. Es lässt sich wie folgt zusammenfassen: MetaTrader 5 kann den Großteil der Arbeit erledigen, die unsere Anwendung benötigt, um die Größenänderung des ausgewählten Objekts zu handhaben, aber – und das ist der entscheidende Punkt – wir können dies nicht ohne zusätzliche Arbeit tun. Ich sage das, weil wir bei Verwendung des unten vollständig gezeigten Quellcodes KEINE ZU 100 % KORREKTE INTERAKTION ERHALTEN WERDEN, wie in Animation 01 zu sehen ist.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_Prefix "Demo" 05. //+------------------------------------------------------------------+ 06. #define macro_NameObject def_Prefix + (string)(ObjectsTotal(0) + 1) 07. //+------------------------------------------------------------------+ 08. struct st_BoxResize 09. { 10. private : 11. ushort x, y, w, h; 12. bool isBack; 13. string szObjects[4]; 14. //+----------------+ 15. void CreateEdge(const char what) 16. { 17. ushort p1 = x, p2 = y, p3 = w, p4 = h; 18. 19. switch (what) 20. { 21. case 0: p4 = 1; break; 22. case 1: p3 = 1; break; 23. case 2: p2 = y + h; p4 = 1; break; 24. case 3: p1 = x + w; p3 = 1; break; 25. } 26. szObjects[what] = macro_NameObject; 27. ObjectCreate(0, szObjects[what], OBJ_RECTANGLE_LABEL, 0, 0, 0); 28. ObjectSetInteger(0, szObjects[what], OBJPROP_BGCOLOR, clrLime); 29. ObjectSetInteger(0, szObjects[what], OBJPROP_XDISTANCE, p1); 30. ObjectSetInteger(0, szObjects[what], OBJPROP_YDISTANCE, p2); 31. ObjectSetInteger(0, szObjects[what], OBJPROP_XSIZE, p3); 32. ObjectSetInteger(0, szObjects[what], OBJPROP_YSIZE, p4); 33. } 34. //+----------------+ 35. public : 36. void CreateBoxResize(const string szArg, const uchar adjust = 2) 37. { 38. isBack = (bool)ObjectGetInteger(0, szArg, OBJPROP_BACK); 39. ObjectSetInteger(0, szArg, OBJPROP_BACK, true); 40. ObjectSetInteger(0, szArg, OBJPROP_SELECTED, false); 41. x = (ushort)ObjectGetInteger(0, szArg, OBJPROP_XDISTANCE) - adjust; 42. y = (ushort)ObjectGetInteger(0, szArg, OBJPROP_YDISTANCE) - adjust; 43. w = (ushort)ObjectGetInteger(0, szArg, OBJPROP_XSIZE) + adjust; 44. h = (ushort)ObjectGetInteger(0, szArg, OBJPROP_YSIZE) + adjust; 45. for (uchar c = 0; c < 4; c++) 46. CreateEdge(c); 47. } 48. //+----------------+ 49. void RemoveBoxResize(const string szArg) 50. { 51. ObjectSetInteger(0, szArg, OBJPROP_BACK, isBack); 52. ObjectSetInteger(0, szArg, OBJPROP_SELECTED, false); 53. ObjectsDeleteAll(0, def_Prefix); 54. } 55. //+----------------+ 56. }gl_RZ; 57. //+------------------------------------------------------------------+ 58. int OnInit() 59. { 60. IndicatorSetString(INDICATOR_SHORTNAME, def_Prefix); 61. 62. return INIT_SUCCEEDED; 63. }; 64. //+------------------------------------------------------------------+ 65. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 66. { 67. return rates_total; 68. }; 69. //+------------------------------------------------------------------+ 70. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 71. { 72. static string szNameObj = NULL; 73. 74. switch (id) 75. { 76. case CHARTEVENT_KEYDOWN : 77. if ((szNameObj != NULL) && (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE))) 78. { 79. gl_RZ.RemoveBoxResize(szNameObj); 80. szNameObj = NULL; 81. } 82. break; 83. case CHARTEVENT_OBJECT_CLICK : 84. if (StringFind(sparam, def_Prefix) != INVALID_HANDLE) break; 85. if (szNameObj != NULL) gl_RZ.RemoveBoxResize(szNameObj); 86. if (szNameObj != sparam) gl_RZ.CreateBoxResize(szNameObj = sparam); else szNameObj = NULL; 87. break; 88. case CHARTEVENT_OBJECT_DRAG : 89. break; 90. } 91. ChartRedraw(); 92. }; 93. //+------------------------------------------------------------------+ 94. void OnDeinit(const int reason) 95. { 96. ObjectsDeleteAll(0, def_Prefix); 97. ChartRedraw(); 98. }; 99. //+------------------------------------------------------------------+
Code 01
„Aber warum sagen Sie, dass die Interaktion nicht zu 100 % korrekt sein wird? Das ergibt für mich keinen Sinn, da der Mechanismus anscheinend funktioniert.“ In der Tat, lieber Leser, der Mechanismus funktioniert. Es gibt jedoch ein kleines Problem, das in der folgenden Abbildung dargestellt ist.

Abbildung 01
Abbildung 01 sagt Ihnen für sich genommen vielleicht nichts. Aber ich hebe darin genau einen Punkt hervor, um Ihre Aufmerksamkeit auf ein Detail zu lenken. Der Rahmen hat keine interaktiven Elemente, daher haben weder MetaTrader 5 noch der Benutzer klare Interaktionspunkte, um sie zu verschieben. Passen Sie jetzt auf: In Code 01 ist Zeile 84 genau dazu da, den Benutzer daran zu hindern, ein Objekt auszuwählen, dessen Name das in Zeile 04 desselben Codes angegebene Präfix enthält. Und was bedeutet das? Nun, dies erklärt, warum wir das Objekt OBJ_BUTTON nicht auswählen können, da dieses Objekt dieses Präfix in seinem Namen hat. Sie können dies in Abbildung 02 unten sehen.

Abbildung 02
Bitte beachten Sie, dass wir genau das Präfix grün markiert haben, das uns daran hindert, dieses Objekt auszuwählen. Obwohl dies auf den ersten Blick nicht so bedeutend erscheinen mag, reicht dieses einfache Detail bereits aus, damit die Prüfung in Zeile 84 uns daran hindert, es auszuwählen. Gut, aber wir müssen die grünen Linien, die als Rahmen verwendet werden, nicht auswählen. Wir können es besser machen, wenn wir das Objekt OBJ_RECTANGLE_LABEL selbst auswählen und MetaTrader 5 den Rest für uns erledigen lassen. Ja, das stimmt teilweise. Dies erfordert eine einfache Änderung an Code 01. Sie können es im folgenden Code-Fragment sehen:
. . . 034. //+----------------+ 035. public : 036. void CreateBoxResize(const string szArg, const uchar adjust = 2) 037. { 038. isBack = (bool)ObjectGetInteger(0, szArg, OBJPROP_BACK); 039. ObjectSetInteger(0, szArg, OBJPROP_BACK, true); 040. ObjectSetInteger(0, szArg, OBJPROP_SELECTED, false); 041. x = (ushort)ObjectGetInteger(0, szArg, OBJPROP_XDISTANCE) - adjust; 042. y = (ushort)ObjectGetInteger(0, szArg, OBJPROP_YDISTANCE) - adjust; 043. w = (ushort)ObjectGetInteger(0, szArg, OBJPROP_XSIZE) + adjust; 044. h = (ushort)ObjectGetInteger(0, szArg, OBJPROP_YSIZE) + adjust; 045. for (uchar c = 0; c < 4; c++) 046. { 047. CreateEdge(c); 048. ObjectSetInteger(0, szObjects[c], OBJPROP_WIDTH, 3); 049. ObjectSetInteger(0, szObjects[c], OBJPROP_SELECTABLE, true); 050. ObjectSetInteger(0, szObjects[c], OBJPROP_SELECTED, true); 051. } 052. } 053. //+----------------+ . . .
Code 02
Was dieses Fragment von Code 02 in Code 01 erzeugen wird, ist in Animation 02 unten dargestellt:

Animation 02
In Ordnung, in Animation 02 können wir etwas sehen. Lassen Sie uns das in Ruhe untersuchen. Siehe Abbildung 03:

Abbildung 03
Der in Abbildung 03 hervorgehobene Punkt ist genau der Ankerpunkt, den wir verwenden können, um das Objekt auf dem Chart zu verschieben. Dafür verwenden wir MetaTrader 5, ohne uns um weitere Details kümmern zu müssen. Wenn Sie jedoch genau hinsehen, werden Sie feststellen, dass nur drei Punkte sichtbar sind. Wo ist der vierte? Nun, das ist unser erstes Problem. Der vierte Punkt befindet sich neben einem anderen Punkt, genau an der in Abbildung 03 hervorgehobenen Stelle. Wäre dies das einzige wirkliche Problem, wäre es keine große Sache. Dagegen könnten wir wenig tun. Dennoch gibt es ein Problem, das etwas ärgerlicher ist als dieses. Dies wird in der folgenden Animation gezeigt:

Animation 03
„Jetzt verstehe ich gar nichts mehr. Warum sollte das, was wir in Animation 03 sehen, ein Problem sein, wenn es Teil des normalen Verhaltens von MetaTrader 5 ist?“ Nun, lieber Leser, ich glaube, Sie haben nicht genau verstanden, was wir machen wollen. Lassen Sie mich die Situation ein wenig verdeutlichen. Was wir versuchen, wäre ähnlich dem, was in Animation 03 gezeigt wird, aber ohne dass die grünen Linien ihre Verbindung zueinander verlieren. Mit anderen Worten: Wenn ein Ankerpunkt verschoben wird, muss sich jede damit verbundene Linie entsprechend mitbewegen, um den neuen Bereich zu begrenzen und so zu zeigen, wie die neuen Abmessungen des ausgewählten Objekts sein werden. Das ist unser Ziel, nicht das, was in Animation 03 zu sehen ist. Verstehen Sie jetzt das Problem?
In Ordnung, an diesem Punkt würden viele Leute aufgeben und anfangen zu sagen: „Was Sie machen möchten, ist unmöglich.“ Ist das wirklich wahr, lieber Leser? Denken Sie einen Moment darüber nach: Wir wissen, wie MetaTrader 5 Informationen über Ereignisse, die bei Objekten auftreten, an unsere Anwendung weitergibt. Wir haben auch gesehen, wie bestimmte Entscheidungen während der Implementierung die Art des Ergebnisses beeinflussen können, das wir erreichen. Also frage ich:
Ist das, was ich tun möchte, wirklich unmöglich?
Die Antwort lautet: Nein. Aufgrund von begrenztem Wissen und begrenzter Erfahrung glauben die meisten Menschen jedoch irgendwann, dass es unmöglich ist, da die Dinge, wie in Animation 03 gezeigt, nicht wie erwartet funktionieren. Aber wir müssen noch einmal Folgendes betonen:
Hängen Sie sich nicht zu sehr an den Code. Versuchen Sie immer, die zugrunde liegenden Konzepte und deren Anwendung zu verstehen.
Genau das tun Programmierer. Sie nehmen ein Problem und finden und entwickeln mithilfe ihrer Programmierkenntnisse eine Lösung. Die Art und Weise, wie jeder Entwickler diese Lösung erstellt, wird sich sicherlich von Programmierer zu Programmierer unterscheiden. Was jedoch wirklich zählt, ist das Endergebnis. Daher können verschiedene Anwendungen mehr oder weniger interaktiv sein, und das Erlernen der Arbeit mit ihnen kann mehr oder weniger einfach sein. Aber in allen Fällen ist das, was wirklich zählt und uns interessiert, das Endergebnis.
In Ordnung, lassen Sie uns einen Moment innehalten und darüber nachdenken. Wir wissen, dass wir KEINES DER OBJEKTE AUSWÄHLEN KÖNNEN, deren Namen das im Code definierte Präfix haben. Dies geschieht genau wegen der Zeile 84 in Code 01. Aber jetzt wissen wir, dass wir diese Objekte nicht auswählen müssen. Während der Objekterstellung können wir MetaTrader 5 mitteilen, dass das Objekt bereits ausgewählt sein wird. Dies bezieht sich auf die Zeilen 49 und 50, die im Fragment von Code 02 sichtbar sind und genau die Modifikation darstellen, die wir in Code 01 implementiert haben.
Dadurch konnten wir MetaTrader 5 veranlassen, dem Benutzer das Verschieben solcher Objekte zu ermöglichen, die zuvor nicht verschoben werden konnten, weil sie sich nicht auswählen ließen. Ich weiß nicht, ob Sie dem folgen können, was ich Ihnen zu zeigen versuche, lieber Leser. Falls nicht, versuchen Sie, das bereits in den vorherigen Artikeln erklärte Material noch einmal zu lesen. Was ich Ihnen zu zeigen versuche, ist genau das Konzept, das wir verwenden werden, um unser Ziel zu erreichen. Ich spreche von einer vollständig interaktiven Möglichkeit, die Größe eines auf dem Chart vorhandenen Objekts zu ändern.
In Ordnung, lassen Sie uns Folgendes tun: Da viele Leute möglicherweise nicht wissen, wie sie das Fragment aus Code 02 in Code 01 einbauen können, um das in Animation 03 Gezeigte zu erhalten, werden wir einen neuen Code erstellen, damit der Anhang uns Zugriff auf den vollständigen Code 02 gibt. Auf diese Weise können Sie ihn in Ruhe studieren und lernen, wie man einen Code-Teil in einen bestehenden integriert. Darauf werden wir noch oft zurückkommen. Zu wissen, wie man dies tut, wird Ihnen helfen, Dinge zu studieren und zu üben, die sonst unmöglich wären.
Alles klar, dann erstellen wir neuen Code auf Basis von Code 01 mit der Änderung, die im Fragment von Code 02 gezeigt wird. Sie können es unten sehen:
. . . 013. string szObjects[6]; 014. //+----------------+ 015. void CreateEdge(const char what) 016. { 017. ushort p1 = x, p2 = y, p3 = w, p4 = h; 018. 019. switch (what) 020. { 021. case 0: p4 = 1; break; 022. case 1: p3 = 1; break; 023. case 2: p2 = y + h; p4 = 1; break; 024. case 3: p1 = x + w; p3 = 1; break; 025. case 5: p1 += p3; p2 += p4; 026. case 4: p3 = p4 = 1; break; 027. } . . . 035. } 036. //+----------------+ 037. public : 038. void CreateBoxResize(const string szArg, const uchar adjust = 2) 039. { . . . 047. for (uchar c = 0; c < szObjects.Size(); c++) 048. { 049. CreateEdge(c); 050. if (c > 3) 051. { 052. ObjectSetInteger(0, szObjects[c], OBJPROP_WIDTH, 3); 053. ObjectSetInteger(0, szObjects[c], OBJPROP_SELECTABLE, true); 054. ObjectSetInteger(0, szObjects[c], OBJPROP_SELECTED, true); 055. } 056. } 057. } 058. //+----------------+ . . .
Code 03
Schauen Sie sich nun Folgendes an, lieber Leser: In diesem Fragment von Code 03 gibt es eine einfache Änderung. Diese ist jedoch sehr bedeutsam. Bitte beachten Sie, dass wir in Zeile 13 die Anzahl der Elemente im Array geändert haben. In der switch-Anweisung in Zeile 19 gibt es zudem zwei weitere interessante Punkte, die in den Zeilen 25 und 26 zu sehen sind. Lassen Sie uns nun einen Moment innehalten, damit wir etwas anderes erklären können, das sehr wichtig ist.
Im Artikel „Von der Grundstufe zur Mittelstufe: Der SWITCH-Operator“ gab es ein Bild, das einige rote Linien erwähnte. Sie können dieses Bild unten noch einmal sehen:

Abbildung 04
Der Punkt hier sind genau diese roten Linien, die in Abbildung 04 zu sehen sind. Diese switch-Anweisung in Zeile 19 verwendet genau diese roten Linien. Das ist wichtig, weil dadurch die Ausführung in den nächsten Case-Zweig übergehen kann. „Wow, und ich dachte, die Dinge könnten einfach nicht noch verwirrender werden! Und jetzt erzählen Sie mir, dass die Ausführung in einen anderen Codeblock springen kann? Und dass dies absichtlich geschieht? Es scheint mir, dass Programmieren viel schwieriger ist, als ich dachte. Jetzt denke ich darüber nach, alles aufzugeben und etwas anderes zu machen.“
Beruhigen Sie sich, lieber Leser, es gibt keinen Grund aufzugeben. Möchten Sie wirklich aussteigen, obwohl es gerade anfängt, noch interessanter zu werden? Nein, lassen Sie uns gemeinsam verstehen, was dieser Schritt, von dem ich spreche, tatsächlich beinhaltet.
Betrachten Sie die Abfolge der Case-Anweisungen. Sie werden feststellen, dass sie fast in einer Reihe angeordnet sind, bis sich die Reihenfolge plötzlich ändert: vom dritten zum fünften und dann zum vierten. „Wie seltsam. Das ist mir nicht aufgefallen. Jetzt, wo Sie es erwähnen, erscheint es wirklich seltsam. Aber warum tun Sie das?“ Der Grund ist, dass ich es absichtlich getan habe. Warum? Genau um die rote Linie zu erklären, die in Abbildung 04 zu sehen ist.
Passen Sie also auf: Obwohl dies einfach erscheinen mag, verwirrt es normalerweise viele gute Programmierer ernsthaft, nicht nur Anfänger. Beachten Sie, dass jede Zeile zwischen 21 und 26 im Fragment aus Code 03 am Ende eine break-Anweisung hat. Jede Zeile außer Zeile 25. Warum? Der Grund dafür ist, dass diese Zeile ausgeführt wird, wenn der Case in Zeile 25 zutrifft. Aber genau hier stellt sich die Frage, wie man zum nächsten Case übergeht, ICH MÖCHTE NICHT, DASS DIE AUSFÜHRUNG HIER ENDET. Ich möchte, dass die im nächsten Case enthaltene Prozedur ausgeführt wird, ohne diesen Case vorher auszuwerten.
„Du meine Güte! Ich habe nichts von dem verstanden, was Sie gerade gesagt haben. Aber warten Sie einen Moment, lassen Sie mich in Ruhe über Ihre Worte nachdenken. Soweit ich das verstehe, möchten Sie, dass bei einer Übereinstimmung in Zeile 25 und der Ausführung des Case-Zweigs auch der Case-Zweig aus Zeile 26 ausgeführt wird. Ist es das, was Sie meinen?“ Ja, und das ist möglich, lieber Leser. „Aber da ist etwas, das ich nicht verstanden habe. Wie wird der Code im Case in Zeile 26 ausgeführt, ohne diesen Case zu prüfen? Das ergibt keinen Sinn.
Genau dann erfolgt der Übergang zum nächsten Case.“ Passen Sie jetzt gut auf, lieber Leser. Da eine Case-Anweisung immer mit einer break-Anweisung endet, wie in Abbildung 04 gezeigt, wird die Ausführung im Zweig des nächsten Case fortgesetzt, WENN WIR KEINE BREAK-ANWEISUNG HABEN, OHNE DEN NÄCHSTEN Case ZU PRÜFEN. Und hier kommt die rote Linie ins Spiel.
Bitte beachten Sie, dass wir beim Erstellen von Objekt Nummer 04 dessen Abmessungen auf eins setzen. Beim Erstellen von Objekt Nummer 05 setzen wir dessen Abmessungen ebenfalls auf eins. Im Fall von Objekt Nummer 05 ändern wir jedoch seine Anfangsposition, sodass es sich in der unteren rechten Ecke befindet, im Gegensatz zu Objekt Nummer 04, das sich in der oberen linken Ecke befindet. All dies wird durch die Möglichkeit erreicht, zum nächsten Case durchzufallen, was in der switch-Anweisung in Zeile 19 zu sehen ist.
Beachten Sie jedoch Folgendes: In Zeile 50 prüfen wir, welches Objekt wir gerade erstellen. Nachdem wir das Quadrat gezeichnet haben, das das ausgewählte Objekt begrenzt, fahren wir mit dem Zeichnen der letzten beiden Objekte auf die gleiche Weise fort wie in dem in Code 02 gezeigten Fragment. Dennoch geschieht dies diesmal mit einem anderen Zweck und einem anderen Ergebnis, wie Sie in der folgenden Abbildung sehen können.

Abbildung 05
Bitte beachten Sie, dass wir in diesem Fall zwei Punkte haben, und das ist alles, was wir brauchen. Wir sind jedoch noch nicht fertig. Entscheidend ist, dass es sich lohnt, zu sehen, was passiert, wenn wir mit den Punkten interagieren, obwohl sie bereits erstellt wurden. Dies zeigt die folgende Animation:

Animation 04
„Hmm, interessant. In gewisser Weise denke ich, dass wir jetzt relativ vielversprechende Aussichten haben. Was wird also unser nächster Schritt sein?“ Nun, lieber Leser, so einfach ist das nicht immer. Die Frage ist hier nicht, was der nächste Schritt sein wird. Die Hauptsache – und das ist es, was ich von Ihnen brauche – ist, dass Sie genau verstehen, was wir hier tun. Diese Art der Manipulation, die wir hier zeigen, erfordert ein gewisses Maß an Sorgfalt und die Erstellung von Tests, um sicherzustellen, dass nicht alles außer Kontrolle gerät. Ohne dies riskieren wir einen Fehler, der unseren Tag sehr unangenehm machen könnte.
Ich wiederhole also noch einmal: Hängen Sie nicht zu sehr am Code. Versuchen Sie, die anwendbaren Konzepte zu verstehen und eine Lösung zu erstellen, die für Sie klar ist. Nur so werden Sie in der Lage sein, die Probleme zu lösen, die während des Lebenszyklus Ihrer Anwendung auftreten.
Nach dieser Warnung, die ich nicht müde werde zu wiederholen, können wir zum nächsten Punkt übergehen. In diesem Fall müssen wir den Code für die Ereignisbehandlung sowie einiges andere, das wir später besprechen werden, ändern. Aber zuerst der Code für die Ereignisbehandlung. Sie können es unten sehen:
. . . 079. //+------------------------------------------------------------------+ 080. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 081. { 082. static string szNameObj = NULL; 083. 084. switch (id) 085. { 086. case CHARTEVENT_KEYDOWN : 087. if ((szNameObj != NULL) && (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE))) 088. { 089. gl_RZ.RemoveBoxResize(szNameObj); 090. szNameObj = NULL; 091. } 092. break; 093. case CHARTEVENT_OBJECT_CLICK : 094. Print((ushort)lparam, " x ", (ushort)dparam, " ->> ", sparam); 095. if (StringFind(sparam, def_Prefix) != INVALID_HANDLE) break; 096. if (szNameObj != NULL) gl_RZ.RemoveBoxResize(szNameObj); 097. if (szNameObj != sparam) gl_RZ.CreateBoxResize(szNameObj = sparam); else szNameObj = NULL; 098. break; 099. case CHARTEVENT_OBJECT_DRAG : 100. Print("CHARTEVENT_OBJECT_DRAG ->> ", sparam); 101. break; 102. } 103. ChartRedraw(); 104. }; 105. //+------------------------------------------------------------------+ . . .
Code 04
In dem in Code 04 gezeigten Fragment nehmen wir eine einfache Änderung vor, um Ihnen etwas zu zeigen, das meiner Meinung nach wichtig ist, auch wenn es bereits in früheren Artikeln gezeigt wurde. Dennoch ist es immer nützlich, bestimmte Konzepte zu festigen, bis Sie sie wirklich verstanden haben.
Beachten Sie, dass wir in den Zeilen 94 und 100 angeben, dass eine Nachricht im Terminal ausgegeben werden soll. Schauen wir uns also das Ergebnis der ersten, sozusagen normalen Ausführung des Codes an. Das ist in der folgenden Animation zu sehen:

Animation 05
Beobachten wir nun das Verhalten, das wir vermeiden wollen. Dies ist in der folgenden Animation 06 dargestellt:

Animation 06
In den Animationen 05 und 06 sehen wir das Standardverhalten von MetaTrader 5 in Aktion. Wir können jedoch mit dem in Animation 05 gezeigten Verhalten leben, nicht aber mit dem in Animation 06 gezeigten Verhalten. In Animation 06 schaltet der Benutzer die Objektauswahl für ein Objekt ein und aus, das er eigentlich nicht auswählen können sollte. Wir sollten das Objekt nur verschieben können. Wir sollten jedoch nicht in der Lage sein, den Auswahlstatus des Objekts zu aktivieren oder zu deaktivieren.
Lassen Sie uns diese Aufgabe also Schritt für Schritt lösen. Der erste Schritt besteht darin, zu verhindern, dass sich der Auswahlstatus des Objekts ändert, wenn der Benutzer einfach darauf klickt. Dies kann mit dem folgenden Code erreicht werden:
. . . 007. //+------------------------------------------------------------------+ 008. struct st_BoxResize 009. { . . . 036. //+----------------+ 037. public : . . . 065. //+----------------+ 066. void Block(const string szArg) 067. { 068. for (uchar c = 0; c < szObjects.Size(); c++) 069. if (szObjects[c] == szArg) 070. ObjectSetInteger(0, szObjects[c], OBJPROP_SELECTED, true); 071. } 072. //+----------------+ 073. }gl_RZ; 074. //+------------------------------------------------------------------+ . . . 086. //+------------------------------------------------------------------+ 087. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 088. { 089. static string szNameObj = NULL; 090. 091. switch (id) 092. { 093. case CHARTEVENT_KEYDOWN : 094. if ((szNameObj != NULL) && (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE))) 095. { 096. gl_RZ.RemoveBoxResize(szNameObj); 097. szNameObj = NULL; 098. } 099. break; 100. case CHARTEVENT_OBJECT_CLICK : 101. Print((ushort)lparam, " x ", (ushort)dparam, " ->> ", sparam); 102. if (StringFind(sparam, def_Prefix) != INVALID_HANDLE) 103. { 104. gl_RZ.Block(sparam); 105. break; 106. } 107. if (szNameObj != NULL) gl_RZ.RemoveBoxResize(szNameObj); 108. if (szNameObj != sparam) gl_RZ.CreateBoxResize(szNameObj = sparam); else szNameObj = NULL; 109. break; 110. case CHARTEVENT_OBJECT_DRAG : 111. Print("CHARTEVENT_OBJECT_DRAG ->> ", sparam); 112. break; 113. } 114. ChartRedraw(); 115. }; 116. //+------------------------------------------------------------------+ . . .
Code 05
Beachten Sie, wie einfach es ist, die Lösung für das erste Problem zu implementieren. Das Einzige, was wir machen müssen, ist, eine neue Methode zur Struktur hinzuzufügen, wie in Zeile 66 zu sehen ist. Dann müssen wir einen Aufruf dieser neuen Prozedur hinzufügen, was in Zeile 104 erfolgt. Und was ist das Ergebnis? Sie können es selbst sehen, wenn Sie sich die folgende Animation ansehen:

Animation 07
Kommen wir nun zum zweiten Teil des Problems, das wir lösen müssen. Zuerst möchte ich Sie jedoch daran erinnern, dass die hier gezeigte Lösung rein zu Lehrzwecken dient. Es ist nicht unbedingt die einzige Option, die wir implementieren können. Gerade wegen des Zwecks dieser Artikel werden wir jedoch die einfachste aller Lösungen zeigen.
Dennoch hindert Sie nichts daran, eine weitaus geeignetere Lösung zu implementieren. Es könnte sogar eine Lösung sein, bei der jede Mausbewegung zur Größenänderung sofort in den Abmessungen des Objekts sichtbar wird, dessen Größe wir interaktiv ändern möchten. Vielleicht zeige ich Ihnen später, wie das geht. Im Prinzip kann dies auch sehr lehrreich und sogar interessant für verschiedene Fälle sein, die Sie möglicherweise in Zukunft implementieren möchten.
Auf jeden Fall bleiben wir bei der einfachsten Lösung. Grundsätzlich besteht diese Lösung aus zwei Stufen, von denen die erste im folgenden Fragment zu sehen ist.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #define def_Prefix "Demo" 005. //+------------------------------------------------------------------+ 006. #define macro_NameObject def_Prefix + (string)(ObjectsTotal(0) + 1) 007. //+------------------------------------------------------------------+ 008. enum E_Elment { 009. TOP, 010. LEFT, 011. BOTTOM, 012. RIGHT, 013. TL, 014. BR 015. }; 016. //+------------------------------------------------------------------+ 017. struct st_BoxResize 018. { 019. private : 020. ushort x, y, w, h; 021. bool isBack; 022. string szObjects[6]; 023. //+----------------+ 024. void SetPosition(const E_Elment what) 025. { 026. ushort p1 = x, p2 = y, p3 = w, p4 = h; 027. 028. switch (what) 029. { 030. case TOP: p4 = 1; break; 031. case LEFT: p3 = 1; break; 032. case BOTTOM: p2 = y + h; p4 = 1; break; 033. case RIGHT: p1 = x + w; p3 = 1; break; 034. case BR: p1 += p3; p2 += p4; 035. case TL: p3 = p4 = 1; break; 036. } 037. ObjectSetInteger(0, szObjects[what], OBJPROP_XDISTANCE, p1); 038. ObjectSetInteger(0, szObjects[what], OBJPROP_YDISTANCE, p2); 039. ObjectSetInteger(0, szObjects[what], OBJPROP_XSIZE, p3); 040. ObjectSetInteger(0, szObjects[what], OBJPROP_YSIZE, p4); 041. } 042. //+----------------+ 043. void CreateEdge(const E_Elment what) 044. { 045. szObjects[what] = macro_NameObject; 046. ObjectCreate(0, szObjects[what], OBJ_RECTANGLE_LABEL, 0, 0, 0); 047. ObjectSetInteger(0, szObjects[what], OBJPROP_BGCOLOR, clrLime); 048. SetPosition(what); 049. } 050. //+----------------+ 051. public : . . . 072. //+----------------+ 073. void UpdateBoxSize(const string szArg) 074. { 075. for (E_Elment c = 0; c < (E_Elment)szObjects.Size(); c++) 076. if (szObjects[c] == szArg) 077. { 078. x = (ushort)ObjectGetInteger(0, szObjects[TL], OBJPROP_XDISTANCE); 079. y = (ushort)ObjectGetInteger(0, szObjects[TL], OBJPROP_YDISTANCE); 080. w = (ushort)ObjectGetInteger(0, szObjects[BR], OBJPROP_XDISTANCE) - x; 081. h = (ushort)ObjectGetInteger(0, szObjects[BR], OBJPROP_YDISTANCE) - y; 082. for (E_Elment i = 0; i < (E_Elment)4; i++) 083. SetPosition(i); 084. } 085. } 086. //+----------------+ . . . 100. //+----------------+ 101. }gl_RZ; 102. //+------------------------------------------------------------------+ . . . 114. //+------------------------------------------------------------------+ 115. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 116. { 117. static string szNameObj = NULL; 118. 119. switch (id) 120. { . . . 137. case CHARTEVENT_OBJECT_DRAG : 138. if (StringFind(sparam, def_Prefix) != INVALID_HANDLE) 139. gl_RZ.UpdateBoxSize(sparam); 140. break; 141. } 142. ChartRedraw(); 143. }; 144. //+------------------------------------------------------------------+ . . .
Code 06
Ich weiß, dass Code 06 ziemlich fragmentiert ist, aber wir haben dies absichtlich getan, weil wir nur die Teile hervorheben wollen, die tatsächlich implementiert werden müssen. Beachten Sie, dass wir zur Vereinfachung einiger Dinge in Zeile 08 eine Aufzählung erstellt haben. Dies wiederum wird die Implementierung der letzten beiden Schritte erleichtern. Wir haben auch den Teil, der sich auf die Erstellung der Objekte bezieht, die das umschließende Rechteck bilden, in zwei Blöcke aufgeteilt. Einer davon ist die Prozedur SetPosition, die in Zeile 24 zu sehen ist. Die andere Stufe ist die direkte Erstellung der Seiten des umschließenden Rechtecks. In diesem Fall befindet sich diese Prozedur in Zeile 43.
Wichtig ist hier die Einführung der Prozedur UpdateBoxSize, die sich in Zeile 73 befindet. Diese Prozedur baut im Wesentlichen das umschließende Rechteck an der neuen Position neu auf, wenn das Ereignis CHARTEVENT_OBJECT_DRAG die in Zeile 139 gezeigte Funktion aufruft.
Auf jeden Fall ist das Ziel, das wir erreichen wollen, letztendlich unten zu sehen.

Animation 08
Dieser Codeausschnitt, wie in Animation 08 gezeigt, ist der entscheidende Punkt. Hier kann die Art und Weise, wie das in Animation 08 gezeigte Ergebnis erzielt wird, geändert werden. Wichtig ist jedoch, dass es genau so funktioniert, wie in Animation 08 gezeigt. Doch neben diesem Ziel enthält Animation 08 etwas, das wir je nachdem ändern können, was der jeweilige Programmierer für notwendig oder wünschenswert hält. Wir könnten den Größenänderungsprozess in diesem Stadium als abgeschlossen betrachten, wie in Animation 08 gezeigt, oder wir könnten es anders machen – und das ist der Weg, den wir einschlagen werden. Der Grund ist einfach: den Lehransatz so nah wie möglich an dem zu halten, was ich für ideal halte.
Der nächste Schritt ist im folgenden Code zu sehen. In diesem Fall werde ich jedoch den vollständigen Code belassen, damit Sie die ganze Schönheit schätzen können, die mit einem sehr einfachen und grundlegenden Programmierniveau geschaffen werden kann, während Sie die verschiedenen bisher demonstrierten Konzepte und Kenntnisse korrekt anwenden. Bewundern und genießen Sie einfach das Ergebnis. Sie haben es sich verdient, lieber Leser, nach so langem Warten und so vielen Vermutungen darüber, wie die Lösung für das Problem der interaktiven Größenänderung eines Objekts im Chart aussehen würde.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #define def_Prefix "Demo" 005. //+------------------------------------------------------------------+ 006. #define macro_NameObject def_Prefix + (string)(ObjectsTotal(0) + 1) 007. //+------------------------------------------------------------------+ 008. enum E_Elment { 009. TOP, 010. LEFT, 011. BOTTOM, 012. RIGHT, 013. TL, 014. BR 015. }; 016. //+------------------------------------------------------------------+ 017. struct st_BoxResize 018. { 019. private : 020. ushort x, y, w, h; 021. bool isBack; 022. string szObjects[6]; 023. //+----------------+ 024. void SetPosition(const E_Elment what) 025. { 026. ushort p1 = x, p2 = y, p3 = w, p4 = h; 027. 028. switch (what) 029. { 030. case TOP: p4 = 1; break; 031. case LEFT: p3 = 1; break; 032. case BOTTOM: p2 = y + h; p4 = 1; break; 033. case RIGHT: p1 = x + w; p3 = 1; break; 034. case BR: p1 += p3; p2 += p4; 035. case TL: p3 = p4 = 1; break; 036. } 037. ObjectSetInteger(0, szObjects[what], OBJPROP_XDISTANCE, p1); 038. ObjectSetInteger(0, szObjects[what], OBJPROP_YDISTANCE, p2); 039. ObjectSetInteger(0, szObjects[what], OBJPROP_XSIZE, p3); 040. ObjectSetInteger(0, szObjects[what], OBJPROP_YSIZE, p4); 041. } 042. //+----------------+ 043. void CreateEdge(const E_Elment what) 044. { 045. szObjects[what] = macro_NameObject; 046. ObjectCreate(0, szObjects[what], OBJ_RECTANGLE_LABEL, 0, 0, 0); 047. ObjectSetInteger(0, szObjects[what], OBJPROP_BGCOLOR, clrLime); 048. SetPosition(what); 049. } 050. //+----------------+ 051. public : 052. void CreateBoxResize(const string szArg, const uchar adjust = 2) 053. { 054. isBack = (bool)ObjectGetInteger(0, szArg, OBJPROP_BACK); 055. ObjectSetInteger(0, szArg, OBJPROP_BACK, true); 056. ObjectSetInteger(0, szArg, OBJPROP_SELECTED, false); 057. x = (ushort)ObjectGetInteger(0, szArg, OBJPROP_XDISTANCE) - adjust; 058. y = (ushort)ObjectGetInteger(0, szArg, OBJPROP_YDISTANCE) - adjust; 059. w = (ushort)ObjectGetInteger(0, szArg, OBJPROP_XSIZE) + adjust; 060. h = (ushort)ObjectGetInteger(0, szArg, OBJPROP_YSIZE) + adjust; 061. for (E_Elment c = 0; c < (E_Elment)szObjects.Size(); c++) 062. { 063. CreateEdge(c); 064. if ((c == TL) || (c == BR)) 065. { 066. ObjectSetInteger(0, szObjects[c], OBJPROP_WIDTH, 3); 067. ObjectSetInteger(0, szObjects[c], OBJPROP_SELECTABLE, true); 068. ObjectSetInteger(0, szObjects[c], OBJPROP_SELECTED, true); 069. } 070. } 071. } 072. //+----------------+ 073. void UpdateBoxSize(const string szArg) 074. { 075. for (E_Elment c = 0; c < (E_Elment)szObjects.Size(); c++) 076. if (szObjects[c] == szArg) 077. { 078. x = (ushort)ObjectGetInteger(0, szObjects[TL], OBJPROP_XDISTANCE); 079. y = (ushort)ObjectGetInteger(0, szObjects[TL], OBJPROP_YDISTANCE); 080. w = (ushort)ObjectGetInteger(0, szObjects[BR], OBJPROP_XDISTANCE) - x; 081. h = (ushort)ObjectGetInteger(0, szObjects[BR], OBJPROP_YDISTANCE) - y; 082. for (E_Elment i = 0; i < (E_Elment)4; i++) 083. SetPosition(i); 084. } 085. } 086. //+----------------+ 087. void RemoveBoxResize(const string szArg, const bool update) 088. { 089. ObjectSetInteger(0, szArg, OBJPROP_BACK, isBack); 090. ObjectSetInteger(0, szArg, OBJPROP_SELECTED, false); 091. if (update) 092. { 093. ObjectSetInteger(0, szArg, OBJPROP_XDISTANCE, x); 094. ObjectSetInteger(0, szArg, OBJPROP_YDISTANCE, y); 095. ObjectSetInteger(0, szArg, OBJPROP_XSIZE, w); 096. ObjectSetInteger(0, szArg, OBJPROP_YSIZE, h); 097. } 098. ObjectsDeleteAll(0, def_Prefix); 099. } 100. //+----------------+ 101. void Block(const string szArg) 102. { 103. for (uchar c = 0; c < szObjects.Size(); c++) 104. if (szObjects[c] == szArg) 105. ObjectSetInteger(0, szObjects[c], OBJPROP_SELECTED, true); 106. } 107. //+----------------+ 108. }gl_RZ; 109. //+------------------------------------------------------------------+ 110. int OnInit() 111. { 112. IndicatorSetString(INDICATOR_SHORTNAME, def_Prefix); 113. 114. return INIT_SUCCEEDED; 115. }; 116. //+------------------------------------------------------------------+ 117. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 118. { 119. return rates_total; 120. }; 121. //+------------------------------------------------------------------+ 122. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 123. { 124. static string szNameObj = NULL; 125. 126. switch (id) 127. { 128. case CHARTEVENT_KEYDOWN : 129. if ((szNameObj != NULL) && (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE))) 130. { 131. gl_RZ.RemoveBoxResize(szNameObj, false); 132. szNameObj = NULL; 133. } 134. break; 135. case CHARTEVENT_OBJECT_CLICK : 136. if (StringFind(sparam, def_Prefix) != INVALID_HANDLE) 137. { 138. gl_RZ.Block(sparam); 139. break; 140. } 141. if (szNameObj != NULL) gl_RZ.RemoveBoxResize(szNameObj, true); 142. if (szNameObj != sparam) gl_RZ.CreateBoxResize(szNameObj = sparam); else szNameObj = NULL; 143. break; 144. case CHARTEVENT_OBJECT_DRAG : 145. if (StringFind(sparam, def_Prefix) != INVALID_HANDLE) 146. gl_RZ.UpdateBoxSize(sparam); 147. break; 148. } 149. ChartRedraw(); 150. }; 151. //+------------------------------------------------------------------+ 152. void OnDeinit(const int reason) 153. { 154. ObjectsDeleteAll(0, def_Prefix); 155. ChartRedraw(); 156. }; 157. //+------------------------------------------------------------------+
Code 07
In diesem Fall werde ich nicht erklären, was in Code 07 passiert. Ich möchte, dass Sie versuchen, ihn zu studieren und dadurch zu verstehen, wie dieser Code, der auf den ersten Blick bedeutungslos erscheint und von vielen als das Produkt der Fantasie eines brillanten Verrückten wahrgenommen wird, es schafft, das zu tun, was in den Animationen unten gezeigt wird. Zunächst die normale Größenänderung:

Animation 09
Nun die kombinierte Größenänderung mit einer anderen, die anschließend wieder rückgängig gemacht wird.

Animation 10
Schlussfolgerung
Programmieren ist nichts Langweiliges, Schwieriges oder Anstrengendes. Tatsächlich ist es angenehm, aufregend und macht Spaß. Der entscheidende Punkt ist folgender: Verstehen Sie, was Sie tun wollen? Sind Sie in der Lage, die grundlegenden Konzepte zu verstehen und sie in einer logischen Abfolge von Anweisungen anzuwenden, bei der Sie der Maschine sagen, was sie tun soll, wann sie es tun soll und warum? Oder sitzen Sie einfach nur da und tippen eine Menge völlig bedeutungsloser Dinge ein, in der Hoffnung, dass die Maschine herausfindet, was Sie von ihr wollen?
Viele Menschen glauben, dass Programmieren etwas ist, das nur großartige und talentierte Fachleute können. Ich bestreite nicht, dass dies sogar wahr sein mag, aber nichts hindert Sie, lieber Leser, daran, sich einen Weg zur Lösung eines Problems auszudenken, ihn zu Papier zu bringen, zu analysieren, welche Konzepte verwendet werden sollten, und ihn dann als Code zu implementieren.
Talent gehört den Wenigen, aber Anstrengung, Entschlossenheit, Studium und Disziplin gehören denen, die wirklich wissen, wohin sie wollen, und nach Wegen suchen, ihre Ziele zu erreichen.
Überlegen Sie es sich. Nutzen Sie die Ihnen zur Verfügung stehenden Materialien zum Lernen und Üben. Verschieben Sie nicht auf morgen, was gestern hätte getan werden können. Auf jeden Fall ist es nie zu spät, anzufangen und sich etwas zu widmen, das Ihnen Freude bereitet.
Vielleicht zeigen wir im nächsten Artikel weitere Möglichkeiten auf, den hier besprochenen Code weiter zu verbessern, der in der beigefügten Datei verfügbar sein wird. Alles wird davon abhängen, ob das vorgestellte Material etwas zu dem hinzufügt, was hier behandelt wurde, oder nicht. Erwarten Sie auf jeden Fall nicht von mir, dass ich Ihnen zeige, wie man die Dinge verbessert. Sie müssen die Lösung selbst finden, und wenn Sie nicht alles von Anfang an umsetzen können, versuchen Sie, das zu studieren, was bis zu diesem Punkt bereits behandelt wurde. Die Beherrschung dieses Materials erfordert Zeit und Übung.
| MQ5-Datei | Beschreibung |
|---|---|
| Code 01 | Demonstration von Ereignissen in Objekten |
| Code 02 | Demonstration von Ereignissen in Objekten |
Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/16094
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.
Neuronale Netze im Trading: Verallgemeinerung von Zeitreihen ohne datenspezifische Abhängigkeit (Mamba4Cast)
Neuronale Netze im Trading: Zeitreihenprognose mittels adaptiver Modenzerlegung (Abschließender Teil)
Eine alternative Log-datei mit der Verwendung der HTML und CSS
Von der Grundstufe bis zur Mittelstufe: Objektereignisse (III)
- 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.