
Erstellen eines Handelsadministrator-Panels in MQL5 (Teil IX): Code Organisation (III): Kommunikationsmodul
Inhalt
- Einführung
- Entwicklung der Klasse CommunicationsDialog.
- Integration von CommunicationsDialog mit anderen Header-Dateien und dem Hauptprogramm
- Tests und Ergebnisse
- Schlussfolgerung
Einführung
Heute wollen wir unser New Admin Panel dort weiter ausbauen, wo wir im letzten Artikel aufgehört haben, in dem wir die Modularisierung als Schlüsselaspekt einer umfassenderen Codeorganisation vorgestellt haben. Wir haben die Klasse AdminHomeDialog eingeführt, die für die Erstellung der Admin-Home-Oberfläche zuständig ist. Dieses Home-Panel dient als zentraler Knotenpunkt für den Zugriff auf verschiedene Funktionen und besteht aus Zugriffskontrolltasten, die zu drei Hauptkomponenten-Panels führen:
- Trade Management Panel
- Communications Panel
- Analytics Panel
Dies sind nicht die endgültigen Grenzen des Systems, da neue Funktionen möglich sind, wenn wir die bestehende Grundlage weiter verfeinern und erweitern. In dieser Diskussion konzentrieren wir uns speziell auf das Kommunikations-Panel als Modul, das im Vergleich zu seiner vorherigen Version innerhalb des monolithischen Admin-Panels weiter verbessert wurde.
Die wichtigsten Erkenntnisse aus dieser Diskussion:
- Verstehen von Klassen in MQL5
- Entwicklung von Header-Dateien
- Vererbung der integrierten Klassen
- Verwendung der ListView-Header-Datei
- Anwendung von Farben für ein besseres UI-Design
Neuer modularer Ablauf des Admin-Panels
1. Hauptprogramm (New_Admin_Panel.mq5)
- Dies ist der Einstiegspunkt in die Anwendung.
- Es initialisiert das System und erstellt eine Instanz von AdminHomeDialog, die als primäre Nutzeroberfläche dient.
2. AdminHomeDialog
- Dient als zentraler Knotenpunkt für die Nutzerinteraktion.
- Reagiert auf Ereignisse, wie z. B. das Klicken auf eine Schaltfläche, um Instanzen anderer Dialoge wie CommunicationsDialog zu erstellen.
- Es ist so konzipiert, dass es erweiterbar ist und bei Bedarf zusätzliche Dialoge (z. B. FutureDialog) erzeugen kann.
3. CommunicationsDialog
- Ein spezialisiertes Dialogfeld, das für bestimmte Funktionen zuständig ist, z. B. das Senden von Nachrichten über die Telegram-API.
- Wird dynamisch von AdminHomeDialog erstellt, wenn es durch eine Nutzeraktion ausgelöst wird (z. B. Anklicken einer Schaltfläche „Nachricht senden“).
4. Ereignisgesteuerte Interaktionen
- Das System verwendet einen ereignisgesteuerten Ansatz, wie er in MQL5-Anwendungen üblich ist. So löst beispielsweise ein Schaltflächenklick im AdminHomeDialog die Erstellung und Anzeige des CommunicationsDialogs aus, der dann seine Aufgabe ausführt (z. B. die Interaktion mit Telegram).
5. Modularer Aufbau
- Jede Komponente hat eine bestimmte Aufgabe: Das Hauptprogramm initialisiert, AdminHomeDialog verwaltet die Schnittstelle, und CommunicationsDialog erledigt Kommunikationsaufgaben. Diese Modularität ermöglicht eine einfache Erweiterung oder Änderung.
Mit der obigen Einführung und dem Überblick über unser neues Programm können wir nun in die Details der Entwicklung unseres bahnbrechenden Moduls, CommunicationsDialog, eintauchen. In Teil (I) war dies nur eine einfache Kommunikationsschnittstelle, aber jetzt haben wir ihre Funktionalität durch die Einbeziehung neuer Merkmale erweitert. Mit diesem grundlegenden Wissen können wir uns besser ein Bild von unserer Richtung machen. In den nächsten Unterabschnitten werden wir uns mit den wesentlichen Bausteinen beschäftigen, die unsere nutzerdefinierten Klassen möglich machen.
Entwicklung der Klasse CommunicationsDialog
Zum besseren Verständnis habe ich unten ein Bild eingefügt, das die Hierarchie des Flusses von der Basisklasse zu unseren nutzerdefinierten Klassen veranschaulicht. Dieser Ansatz ist ein wirksames Mittel zur Förderung der Wiederverwendbarkeit von Code. Das gesamte Projekt besteht aus zahlreichen Schalttafelkomponenten, die jeweils einem bestimmten Zweck dienen. Diese Einzigartigkeit macht jede Codekomponente auch für die Integration in andere Projekte anpassbar.
In MQL5 verweist die Klasse Dialog normalerweise auf CAppDialog oder CDialog aus der Include-Datei (Dialog.mqh), da diese als grundlegende Dialogklassen in der Standardbibliothek dienen.
Sowohl CommunicationsDialog als auch CAdminHomeDialog erben von CAppDialog, wodurch eine strukturierte Hierarchie entsteht, in der mehrere Dialogklassen eine gemeinsame Basis für die Dialogfunktionalität nutzen. Diese Struktur wird in dem nachstehenden Hierarchie-Flussdiagramm dargestellt.
Beziehung zwischen der Basisklasse und den nutzerdefinierten Klassen
Um loszulegen, öffnen Sie MetaEditor 5 von Ihrem Desktop aus oder starten Sie es aus dem Terminal, indem Sie F4 drücken.
Suchen Sie im Navigator die Datei Dialog.mqh im Ordner Includes und öffnen Sie sie als Referenz.
Folgen Sie der nachstehenden Abbildung zur Orientierung.
Auffinden der Klasse Dialog in MetaEditor 5
Erstellen Sie anschließend eine neue Datei für die Entwicklung der neuen Klasse.
Im Allgemeinen besteht unser Programm aus Abschnitten wie Kopfzeile, Layout und Farbdefinitionen, Klassendeklaration und Methodenimplementierungen. Wir beginnen mit den Grundlagen unserer Datei. Die Header-Kommentare am Anfang sagen uns, um welche Datei es sich handelt (CommunicationsDialog.mqh), wem sie gehört (MetaQuotes Ltd., Sie können dies jedoch in Ihren Namen ändern) und woher sie stammt (MQL5-Community). Diese Kommentare sind wie ein Titelblatt für Ihren Code, das anderen hilft, seinen Zweck zu erkennen.
Als Nächstes verwenden wir #ifndef und #define, um eine „Wächter“ namens COMMUNICATIONS_DIALOG_MQH zu erstellen. Dadurch wird verhindert, dass die Datei mehrfach in ein Projekt aufgenommen wird, was zu Fehlern führen könnte. Stellen Sie es sich als ein Schloss vor, das sagt: „Wenn ich bereits geöffnet wurde, öffnen Sie mich nicht noch einmal“.
Die #include-Zeilen bringen die benötigten Werkzeuge aus der MQL5-Bibliothek ein. Mit (Dialog.mqh) erhalten wir die Basis-Dialogklasse (CAppDialog), während (Button.mqh) und (Edit.mqh) Schaltflächen- und Textfeldklassen bereitstellen. (Label.mqh) und (ListView.mqh) fügen ein Label und eine Liste für Schnellmeldungen hinzu. Schließlich ist (Telegram.mqh) eine nutzerdefinierte Datei (von der angenommen wird, dass sie existiert), die Telegram-Nachrichten verarbeitet. Sie sind wie Werkzeuge aus einem Werkzeugkasten, die wir uns für unseren Dialog ausleihen. Nachstehend finden Sie den Codeausschnitt für diesen Abschnitt.
Abschnitt 1: Dateikopf und Includes
//+------------------------------------------------------------------+ //| CommunicationsDialog.mqh | //| Copyright 2000-2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #ifndef COMMUNICATIONS_DIALOG_MQH #define COMMUNICATIONS_DIALOG_MQH #include <Controls\Dialog.mqh> #include <Controls\Button.mqh> #include <Controls\Edit.mqh> #include <Controls\Label.mqh> #include <Controls\ListView.mqh> #include "Telegram.mqh"
Abschnitt 2: Layout und Farbdefinitionen
Bevor wir den Dialog aufbauen, müssen wir seine Größe und sein Aussehen planen. Die #define-Anweisungen sind wie eine Blaupause mit Abmessungen und Farben. Für das Layout bestimmen COMMS_PANEL_WIDTH (300 Pixel) und COMMS_PANEL_HEIGHT (350 Pixel), wie groß unser Dialog auf dem Bildschirm sein wird. Ränder (COMMS_MARGIN_LEFT, COMMS_MARGIN_TOP, COMMS_MARGIN_RIGHT) sorgen für eine Auffüllung der Ränder, während COMMS_GAP_VERTICAL den vertikalen Abstand zwischen den Elementen erhöht. Jedes Steuerelement hat seine eigene Größe: das Eingabefeld ist hoch (COMMS_INPUT_HEIGHT), die Schaltflächen sind kleine Rechtecke (COMMS_BUTTON_WIDTH und HEIGHT), und die Listenansicht und die Beschriftung haben ihre eigenen Abmessungen.
Farben machen den Dialog schön und lesbar. Wir verwenden hexadezimale Zahlen (z. B. 0x808080 für Dunkelgrau), weil Computer in MQL5 Farben so verstehen. CLR_PANEL_BG legt den Haupthintergrund des Dialogs fest, CLR_CLIENT_BG färbt den Bereich, in dem die Steuerelemente sitzen, und CLR_CAPTION_BG und CLR_CAPTION_TEXT gestalten die Titelleiste. Umrandungen erhalten CLR_BORDER_BG und CLR_BORDER, während Steuerelemente wie das Eingabefeld (CLR_INPUT_BG, CLR_INPUT_TEXT) und Schaltflächen (CLR_SEND_BG, CLR_CLEAR_BG) ihre eigenen Farben erhalten. Die Kommentare neben den einzelnen Definitionen erklären, was sie bewirken, sodass sie später leicht angepasst werden können.
// **Layout Defines** #define COMMS_PANEL_WIDTH 300 #define COMMS_PANEL_HEIGHT 350 #define COMMS_MARGIN_LEFT 10 #define COMMS_MARGIN_TOP 10 #define COMMS_MARGIN_RIGHT 10 #define COMMS_GAP_VERTICAL 10 #define COMMS_INPUT_HEIGHT 30 #define COMMS_BUTTON_WIDTH 80 #define COMMS_BUTTON_HEIGHT 30 #define COMMS_LISTVIEW_WIDTH 280 #define COMMS_LISTVIEW_HEIGHT 80 #define COMMS_LABEL_HEIGHT 20 // **Color Defines (Hexadecimal Values)** #define CLR_PANEL_BG 0x808080 // Dark Gray (Dialog background) #define CLR_CLIENT_BG 0xD3D3D3 // Light Gray (Client area background) #define CLR_CAPTION_BG 0x404040 // Darker Gray (Caption background) #define CLR_CAPTION_TEXT 0xFFFFFF // White (Caption text) #define CLR_BORDER_BG 0xFFFFFF // White (Border background) #define CLR_BORDER 0xA9A9A9 // Gray (Border color) #define CLR_INPUT_BG 0xFFFFFF // White (Input box background) #define CLR_INPUT_TEXT 0x000000 // Black (Input box text) #define CLR_SEND_BG 0x00FF00 // Lime Green (Send button background) #define CLR_CLEAR_BG 0xF08080 // Light Coral (Clear button background) #define CLR_BUTTON_TEXT 0x000000 // Black (Button text) #define CLR_LABEL_TEXT 0xFFFFFF // White (Label text) #define CLR_LIST_BG 0xFFFFFF // White (List view background) #define CLR_LIST_TEXT 0x000000 // Black (List view text)
Abschnitt 3: Erklärung zur Klasse
Jetzt definieren wir das Herzstück unseres Dialogs: die Klasse CCommunicationDialog. Betrachten Sie eine Klasse als ein Rezept für die Erstellung eines Dialogobjekts. Wir sagen: public CAppDialog, weil unser Dialog auf CAppDialog aufbaut, einer vorgefertigten Dialogklasse aus MQL5, die uns grundlegende Dialogfunktionen wie eine Titelleiste und Rahmen bietet.
Der private Bereich listet die Zutaten auf, die wir im Dialog verwenden werden. m_inputBox ist ein Textfeld, in das der Nutzer Nachrichten einträgt, m_sendButton und m_clearButton sind Schaltflächen zum Senden bzw. Löschen der Nachricht, m_quickMsgLabel ist eine Text und m_quickMessageList ist eine Liste mit voreingestellten Nachrichten. Wir speichern auch m_chatId und m_botToken als Zeichenkette für Telegram und m_quickMessages als Array, um acht Optionen für Schnellnachrichten zu speichern.
Im öffentlichen Bereich führen wir Funktionen auf, die jeder verwenden kann, um mit unserem Dialog zu interagieren. Der Konstruktor (CCommunicationDialog) richtet den Dialog mit einer Chat-ID und einem Bot-Token ein, und der Destruktor (~CCommunicationDialog) räumt auf, wenn wir fertig sind. Create erstellt das Dialogfeld auf dem Chart, OnEvent behandelt Klicks und Aktionen, und Toggle zeigt oder verbirgt es.
Zurück im privaten Bereich haben wir Hilfsfunktionen, um jedes Steuerelement zu erstellen (CreateInputBox, etc.) und Event-Handler (OnClickSend, OnClickClear), um zu entscheiden, was passiert, wenn Schaltflächen angeklickt werden. Diese sind privat, weil es sich um interne Details der Funktionsweise des Dialogs handelt.
//+------------------------------------------------------------------+ //| Class CCommunicationDialog | //| Purpose: A dialog for sending Telegram messages with controls | //+------------------------------------------------------------------+ class CCommunicationDialog : public CAppDialog { private: CEdit m_inputBox; // Field to edit/send message CButton m_sendButton; // Send message button CButton m_clearButton; // Clear edit box button CLabel m_quickMsgLabel; // Label for "QuickMessages" CListView m_quickMessageList; // ListView for quick messages string m_chatId; // Telegram chat ID string m_botToken; // Telegram bot token string m_quickMessages[8]; // Array of quick messages public: CCommunicationDialog(const string chatId, const string botToken); ~CCommunicationDialog(); virtual bool Create(const long chart, const string name, const int subwin, const int x1, const int y1, const int x2, const int y2); virtual bool OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam); void Toggle(); // Toggle dialog visibility private: //--- Create dependent controls bool CreateInputBox(void); bool CreateClearButton(void); bool CreateSendButton(void); bool CreateQuickMsgLabel(void); bool CreateQuickMessageList(void); //--- Handlers of dependent controls events void OnClickSend(void); // Handler for Send button void OnClickClear(void); // Handler for Clear button };
Abschnitt 4: Konstruktor und Destruktor
Der Konstruktor ist so, als ob man ein neues Spielzeug aufbaut, bevor man damit spielt. Wenn jemand einen CCommunicationDialog erstellt, muss er ihm eine chatId und einen botToken (Zeichenkette für Telegram) geben. Die Teile m_chatId(chatId) und m_botToken(botToken) kopieren diese in unsere privaten Variablen, damit der Dialog weiß, wohin er Nachrichten senden soll. Innerhalb der geschweiften Klammern füllen wir das Array m_quickMessages mit acht praktischen Phrasen, aus denen die Nutzer auswählen können. Dies geschieht bei der Geburt des Dialogs, sodass er sofort einsatzbereit ist.
Der Destruktor ist die Putzkolonne. Er wird ausgeführt, wenn das Dialogfeld gelöscht wird (z. B. wenn Sie das Programm schließen). Zurzeit ist sie leer, weil CAppDialog die meisten Aufräumarbeiten für uns erledigt und wir nichts zusätzlich putzen müssen. Es ist hier als Platzhalter für den Fall, dass wir später spezielle Aufräumarbeiten, wie das Freigeben von Speicher, hinzufügen.
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CCommunicationDialog::CCommunicationDialog(const string chatId, const string botToken) : m_chatId(chatId), m_botToken(botToken) { // Initialize quick messages m_quickMessages[0] = "Updates"; m_quickMessages[1] = "Close all"; m_quickMessages[2] = "In deep profits"; m_quickMessages[3] = "Hold position"; m_quickMessages[4] = "Swing Entry"; m_quickMessages[5] = "Scalp Entry"; m_quickMessages[6] = "Book profit"; m_quickMessages[7] = "Invalid Signal"; } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CCommunicationDialog::~CCommunicationDialog() { }
Abschnitt 5: Methode erstellen
Mit der Methode „Erstellen“ bauen wir den Dialog auf dem Bildschirm auf, so wie man ein Spielzeug aus Teilen zusammensetzt. Sie benötigt Eingaben wie Chart (wo es erscheint), Name (eine eindeutige ID), Subwin (welches Chartfenster) und Koordinaten (x1, y1, x2, y2) für Position und Größe. Wir rufen zunächst CAppDialog::Create auf, um die grundlegende Dialogstruktur einzurichten. Wenn das fehlschlägt, geben wir false zurück, um zu sagen, dass etwas schief gelaufen ist.
Mit Caption("Communications Panel") wird der Titel am oberen Rand des Dialogfelds festgelegt. Dann färben wir ihn ein! ObjectSetInteger ändert die Farben, indem es die Teile des Dialogs direkt anspricht (z. B. „Back“ für den Hintergrund, „Client“ für den Kontrollbereich). Wir verwenden unsere Farbdefinitionen (z. B. CLR_PANEL_BG), damit es schön aussieht. Die Prüfung if(!m_panel_flag) fügt einen Rahmen nur hinzu, wenn der Dialog kein spezieller Typ ist (gesteuert durch m_panel_flag, eine Variable von CAppDialog).
Danach rufen wir Hilfsfunktionen auf, um jedes Steuerelement (Eingabefeld, Schaltflächen usw.) zu erstellen. Wenn einer davon fehlschlägt, brechen wir ab und geben false zurück. Schließlich geben wir die erste Kurznachricht mit m_inputBox.Text in das Eingabefeld ein und rufen ChartRedraw() auf, um alles im Chart anzuzeigen. Die Rückgabe von true bedeutet Erfolg.
//+------------------------------------------------------------------+ //| Create Method | //| Initializes the dialog and its controls with full color styling | //+------------------------------------------------------------------+ bool CCommunicationDialog::Create(const long chart, const string name, const int subwin, const int x1, const int y1, const int x2, const int y2) { // Create the base dialog if(!CAppDialog::Create(chart, name, subwin, x1, y1, x2, y2)) return(false); Caption("Communications Panel"); // Set the title // Set dialog background color ObjectSetInteger(m_chart_id, m_name + "Back", OBJPROP_BGCOLOR, CLR_PANEL_BG); // Set client area background color ObjectSetInteger(m_chart_id, m_name + "Client", OBJPROP_BGCOLOR, CLR_CLIENT_BG); // Set caption colors ObjectSetInteger(m_chart_id, m_name + "Caption", OBJPROP_BGCOLOR, CLR_CAPTION_BG); ObjectSetInteger(m_chart_id, m_name + "Caption", OBJPROP_COLOR, CLR_CAPTION_TEXT); // Set border colors (if border exists, i.e., m_panel_flag is false) if(!m_panel_flag) { ObjectSetInteger(m_chart_id, m_name + "Border", OBJPROP_BGCOLOR, CLR_BORDER_BG); ObjectSetInteger(m_chart_id, m_name + "Border", OBJPROP_BORDER_COLOR, CLR_BORDER); } // Create all controls if(!CreateInputBox()) return(false); if(!CreateClearButton()) return(false); if(!CreateSendButton()) return(false); if(!CreateQuickMsgLabel()) return(false); if(!CreateQuickMessageList()) return(false); // Set initial text in input box m_inputBox.Text(m_quickMessages[0]); ChartRedraw(); return(true); }
Abschnitt 6: Methoden zur Erstellung von Kontrollen
Diese Methoden sind wie das Zusammensetzen der Teile unseres Dialogs. Jeder erstellt ein Steuerelement (Eingabefeld, Schaltflächen, Etikett, Liste) und platziert es auf dem Dialogfeld. Sie geben alle true zurück, wenn sie erfolgreich sind, oder false, wenn etwas fehlschlägt, sodass wir anhalten können, wenn es ein Problem gibt.
Für CreateInputBox berechnen wir die Positionen anhand unserer Layout-Definitionen (z. B. COMMS_MARGIN_LEFT). Das Eingabefeld ist breit (mit ClientAreaWidth()) und hoch (dreimal COMMS_INPUT_HEIGHT). Wir rufen m_inputBox.Create mit der Chart-ID, einem eindeutigen Namen (m_name + „_InputBox“) und den Koordinaten auf. Add fügt es in das Dialogfeld ein, und ObjectSetInteger legt seine Farben fest.
CreateClearButton und CreateSendButton erzeugen Schaltflächen unterhalb des Eingabefeldes. Wir stapeln sie vertikal mit COMMS_GAP_VERTICAL und positionieren Send neben Clear mit clear_button_x2. Jedes erhält einen Namen, einen Text („Clear“ oder „Send“) und Farben aus unseren Definitionen.
CreateQuickMsgLabel fügt eine Beschriftung unter den Schaltflächen hinzu, wobei mehr Abstände berechnet werden. Da es sich nur um Text handelt, braucht er nur eine Textfarbe. CreateQuickMessageList macht eine Listenansicht noch niedriger und füllt sie mit unseren Kurznachrichten aus dem Konstruktor. Jedes Steuerelement verwendet dasselbe Muster: Erstellen, Hinzufügen, Einfärben und Prüfen auf Fehler mit Druckmeldungen zur Unterstützung der Fehlersuche.
//+------------------------------------------------------------------+ //| CreateInputBox | //+------------------------------------------------------------------+ bool CCommunicationDialog::CreateInputBox(void) { int x1 = COMMS_MARGIN_LEFT; int y1 = COMMS_MARGIN_TOP; int x2 = ClientAreaWidth() - COMMS_MARGIN_RIGHT; int y2 = y1 + 3 * COMMS_INPUT_HEIGHT; if(!m_inputBox.Create(m_chart_id, m_name + "_InputBox", m_subwin, x1, y1, x2, y2)) { Print("Failed to create InputBox"); return(false); } if(!Add(m_inputBox)) return(false); ObjectSetInteger(m_chart_id, m_name + "_InputBox", OBJPROP_BGCOLOR, CLR_INPUT_BG); ObjectSetInteger(m_chart_id, m_name + "_InputBox", OBJPROP_COLOR, CLR_INPUT_TEXT); return(true); } //+------------------------------------------------------------------+ //| CreateClearButton | //+------------------------------------------------------------------+ bool CCommunicationDialog::CreateClearButton(void) { int button_y1 = COMMS_MARGIN_TOP + 3 * COMMS_INPUT_HEIGHT + COMMS_GAP_VERTICAL; int x1 = COMMS_MARGIN_LEFT; int x2 = x1 + COMMS_BUTTON_WIDTH; int y2 = button_y1 + COMMS_BUTTON_HEIGHT; if(!m_clearButton.Create(m_chart_id, m_name + "_ClearButton", m_subwin, x1, button_y1, x2, y2)) { Print("Failed to create ClearButton"); return(false); } m_clearButton.Text("Clear"); if(!Add(m_clearButton)) return(false); ObjectSetInteger(m_chart_id, m_name + "_ClearButton", OBJPROP_BGCOLOR, CLR_CLEAR_BG); ObjectSetInteger(m_chart_id, m_name + "_ClearButton", OBJPROP_COLOR, CLR_BUTTON_TEXT); return(true); } //+------------------------------------------------------------------+ //| CreateSendButton | //+------------------------------------------------------------------+ bool CCommunicationDialog::CreateSendButton(void) { int button_y1 = COMMS_MARGIN_TOP + 3 * COMMS_INPUT_HEIGHT + COMMS_GAP_VERTICAL; int clear_button_x2 = COMMS_MARGIN_LEFT + COMMS_BUTTON_WIDTH; int x1 = clear_button_x2 + COMMS_GAP_VERTICAL; int x2 = x1 + COMMS_BUTTON_WIDTH; int y2 = button_y1 + COMMS_BUTTON_HEIGHT; if(!m_sendButton.Create(m_chart_id, m_name + "_SendButton", m_subwin, x1, button_y1, x2, y2)) { Print("Failed to create SendButton"); return(false); } m_sendButton.Text("Send"); if(!Add(m_sendButton)) return(false); ObjectSetInteger(m_chart_id, m_name + "_SendButton", OBJPROP_BGCOLOR, CLR_SEND_BG); ObjectSetInteger(m_chart_id, m_name + "_SendButton", OBJPROP_COLOR, CLR_BUTTON_TEXT); return(true); } //+------------------------------------------------------------------+ //| CreateQuickMsgLabel | //+------------------------------------------------------------------+ bool CCommunicationDialog::CreateQuickMsgLabel(void) { int label_y1 = COMMS_MARGIN_TOP + 3 * COMMS_INPUT_HEIGHT + COMMS_GAP_VERTICAL + COMMS_BUTTON_HEIGHT + COMMS_GAP_VERTICAL; int x1 = COMMS_MARGIN_LEFT; int x2 = x1 + COMMS_LISTVIEW_WIDTH; int y2 = label_y1 + COMMS_LABEL_HEIGHT; if(!m_quickMsgLabel.Create(m_chart_id, m_name + "_QuickMsgLabel", m_subwin, x1, label_y1, x2, y2)) { Print("Failed to create QuickMessages Label"); return(false); } m_quickMsgLabel.Text("QuickMessages"); if(!Add(m_quickMsgLabel)) return(false); ObjectSetInteger(m_chart_id, m_name + "_QuickMsgLabel", OBJPROP_COLOR, CLR_LABEL_TEXT); return(true); } //+------------------------------------------------------------------+ //| CreateQuickMessageList | //+------------------------------------------------------------------+ bool CCommunicationDialog::CreateQuickMessageList(void) { int list_y1 = COMMS_MARGIN_TOP + 3 * COMMS_INPUT_HEIGHT + COMMS_GAP_VERTICAL + COMMS_BUTTON_HEIGHT + COMMS_GAP_VERTICAL + COMMS_LABEL_HEIGHT + COMMS_GAP_VERTICAL; int x1 = COMMS_MARGIN_LEFT; int x2 = x1 + COMMS_LISTVIEW_WIDTH; int y2 = list_y1 + COMMS_LISTVIEW_HEIGHT; if(!m_quickMessageList.Create(m_chart_id, m_name + "_QuickMsgList", m_subwin, x1, list_y1, x2, y2)) { Print("Failed to create ListView"); return(false); } if(!Add(m_quickMessageList)) return(false); ObjectSetInteger(m_chart_id, m_name + "_QuickMsgList", OBJPROP_BGCOLOR, CLR_LIST_BG); ObjectSetInteger(m_chart_id, m_name + "_QuickMsgList", OBJPROP_COLOR, CLR_LIST_TEXT); for(int i = 0; i < ArraySize(m_quickMessages); i++) { if(!m_quickMessageList.AddItem("Message: " + m_quickMessages[i])) return(false); } return(true); }
Abschnitt 7: Toggle und Ereignisbehandlung
Toggle ist ein einfacher Schalter zum Ein- und Ausblenden des Dialogs. IsVisible() prüft, ob es auf dem Bildschirm ist. Wenn ja, lässt Hide() sie verschwinden; wenn nicht, bringt Show() sie zurück. ChartRedraw() aktualisiert das Chart, damit Sie die Änderung sofort sehen. Es ist, als würde man einen Lichtschalter für den Dialog umlegen.
OnEvent ist das Gehirn, das auf Nutzeraktionen, wie Klicks, hört. Es erhält eine id (was passiert ist), lparam (Details), dparam (weitere Details) und sparam (welches Objekt). Wenn id CHARTEVENT_OBJECT_CLICK lautet, bedeutet dies, dass etwas angeklickt wurde. Wir prüfen, ob sparam (der Name des angeklickten Objekts) mit m_sendButton.Name() oder m_clearButton.Name() übereinstimmt, und rufen dann OnClickSend oder OnClickClear auf. Die Rückgabe von true sagt, dass wir es geschafft haben.
Wenn id ON_CHANGE und sparam der Name der Liste ist, hat der Nutzer eine Kurznachricht ausgewählt. lparam sagt uns, welche (als Zahl), und wir setzen diese Nachricht mit m_inputBox.Text in das Eingabefeld. Wenn nichts passt, überlassen wir es CAppDialog::OnEvent, das Ereignis in der Kette weiterzuleiten.
//+------------------------------------------------------------------+ //| Toggle | //+------------------------------------------------------------------+ void CCommunicationDialog::Toggle() { if(IsVisible()) Hide(); else Show(); ChartRedraw(); } //+------------------------------------------------------------------+ //| OnEvent | //+------------------------------------------------------------------+ bool CCommunicationDialog::OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if(id == CHARTEVENT_OBJECT_CLICK) { if(sparam == m_sendButton.Name()) { OnClickSend(); return true; } else if(sparam == m_clearButton.Name()) { OnClickClear(); return true; } } else if(id == ON_CHANGE && sparam == m_quickMessageList.Name()) { int selectedIndex = (int)lparam; if(selectedIndex >= 0 && selectedIndex < ArraySize(m_quickMessages)) { m_inputBox.Text(m_quickMessages[selectedIndex]); } return true; } return CAppDialog::OnEvent(id, lparam, dparam, sparam); }
Abschnitt 8: Ereignisbehandlung der Schaltflächen
OnClickSend wird ausgeführt, wenn die Schaltfläche „Send“ angeklickt wird. Es wird der Text aus m_inputBox.Text() entnommen und überprüft, ob er nicht leer ist (""). Wenn es eine Nachricht gibt, versucht es, diese mit SendMessageToTelegram (eine Funktion aus Telegram.mqh) mit unserer m_chatId und m_botToken zu senden. Wenn es funktioniert, drucken wir eine Erfolgsmeldung, wenn nicht, eine Fehlermeldung. Wenn das Feld leer ist, drucken wir einfach eine Notiz. Dies ist die Hauptaufgabe des Dialogs - das Versenden von Nachrichten!
OnClickClear ist einfacher. Wenn Sie auf „Clear“ klicken, wird der Text des Eingabefeldes mit m_inputBox.Text("") auf Null ("") gesetzt, das Chart neu gezeichnet, um die Änderung anzuzeigen, und eine Bestätigung ausgedruckt. Das ist so, als würde man ein Formular zurücksetzen.
//+------------------------------------------------------------------+ //| OnClickSend | //+------------------------------------------------------------------+ void CCommunicationDialog::OnClickSend() { string message = m_inputBox.Text(); if(message != "") { if(SendMessageToTelegram(message, m_chatId, m_botToken)) Print("Message sent to Telegram: ", message); else Print("Failed to send message to Telegram"); } else { Print("No message to send - input box is empty"); } } //+------------------------------------------------------------------+ //| OnClickClear | //+------------------------------------------------------------------+ void CCommunicationDialog::OnClickClear() { m_inputBox.Text(""); // Clear the input box ChartRedraw(); Print("Input box cleared."); }
Fügen Sie am Ende der Datei #endif ein, um den Header Guard zu schließen:
#endif // COMMUNICATIONS_DIALOG_MQH
Dies entspricht dem #ifndef am Anfang und schließt alles sauber ab.
Integration von CommunicationsDialog mit anderen Header-Dateien und dem Hauptprogramm
Der CommunicationsDialog wird innerhalb des AdminHomeDialogs behandelt. In diesem Abschnitt werde ich erklären, wie das funktioniert. Ich werde nicht den gesamten Code von AdminHomeDialog behandeln, da wir ihn im vorherigen Artikel ausführlich besprochen haben.
Schritt 1
Um den CommunicationsDialog mit dem AdminHomeDialog zu verbinden, müssen wir ihn in unsere Datei aufnehmen. Die Zeile #include (CommunicationsDialog.mqh) ist wie das Öffnen einer Tür zwischen den beiden. Sie weist MQL5 an, die Datei CommunicationsDialog.mqh zu laden und ihre Klasse CCommunicationDialog für uns verfügbar zu machen. Platzieren Sie dies am Anfang von (AdminHomeDialog.mqh), nach anderen Includes wie (Dialog.mqh) und (Button.mqh). Ohne dies wüsste AdminHomeDialog nichts über das Kommunikationspanel, es ist also der erste Schritt zur Verknüpfung der beiden.
#include <CommunicationsDialog.mqh> // Use the enhanced Communications dialog
Schritt 2
Innerhalb der Klasse CAdminHomeDialog benötigen wir eine Möglichkeit, unser Kommunikationspanel zu speichern. Wir fügen CCommunicationDialog *m_commPanel in den privaten Abschnitt ein, wobei wir einen Zeiger verwenden (das * bedeutet, dass es sich um einen Verweis auf ein Objekt handelt, das wir später erstellen werden). Das ist so, als würden wir einen Platz für ein Spielzeug reservieren, das wir bei Bedarf auspacken. Wir fügen auch m_chatId und m_botToken als String-Variablen hinzu, um Telegram-Details zu speichern, die wir an CCommunicationDialog übergeben werden. Diese sind privat, da nur diese Klasse sie verwalten muss, um die Voraussetzungen für die Integration zu schaffen.
class CAdminHomeDialog : public CAppDialog { private: CCommunicationDialog *m_commPanel; // Pointer to the Communications panel string m_chatId; // Telegram Chat ID string m_botToken; // Telegram Bot Token ///.................Space for other members e.g. buttons };
Schritt 3
Der Konstruktor richtet CAdminHomeDialog ein, wenn er erstellt wird. Wir aktualisieren es, um chatId und botToken als Eingaben zu nehmen, die wir in m_chatId und m_botToken kopieren, indem wir den : m_chatId(chatId), m_botToken(botToken) verwenden. Wir setzen auch m_commPanel auf NULL (noch nichts) mit : m_commPanel(NULL). Das bedeutet, dass wir das Kommunikationspanel nicht sofort einrichten, sondern warten, bis der Nutzer es anfordert. Es ist, als würde man eine Schachtel geschlossen halten, bis man bereit ist, mit dem zu spielen, was darin ist.
Der Destruktor räumt auf, wenn der Dialog beendet ist. Wir prüfen if(m_commPanel), um festzustellen, ob wir ein Kommunikationspanel erstellt haben. Wenn dies der Fall ist, wird durch das Löschen von m_commPanel der Speicher freigegeben (wie beim Wegwerfen eines gebrauchten Spielzeugs), und m_commPanel = NULL stellt sicher, dass wir nicht versehentlich versuchen, es erneut zu verwenden. Dadurch bleibt unser Programm übersichtlich und verhindert Abstürze beim Verbinden mit CommunicationsDialog.
CAdminHomeDialog::CAdminHomeDialog(string chatId, string botToken) : m_commPanel(NULL), m_chatId(chatId), m_botToken(botToken) { } CAdminHomeDialog::~CAdminHomeDialog(void) { if(m_commPanel) { delete m_commPanel; m_commPanel = NULL; } }
Schritt 4
Hier passiert der Clou: OnClickCommunications startet das Kommunikationspanel, wenn die Schaltfläche „Communications“ angeklickt wird. Zunächst wird geprüft, ob (m_commPanel == NULL), um festzustellen, ob es noch nicht erstellt wurde. Wenn er NULL ist, verwenden wir new CCommunicationDialog(m_chatId, m_botToken), um einen neuen Dialog zu erstellen und unsere gespeicherten Telegram-Details zu übergeben. Das ist so, als würde man die Spielzeugkiste öffnen und sie mit der richtigen Anleitung aufstellen.
Wenn new fehlschlägt (vielleicht ging dem Computer der Speicherplatz aus), bleibt m_commPanel NULL, und wir geben eine Fehlermeldung aus und halten an. Andernfalls rufen wir m_commPanel.Create auf, um es im Chart zu erstellen. Wir verwenden m_chart_id (das Chart, in dem wir uns befinden), „CommPanel“ als Namen, m_subwin (welches Fenster) und Koordinaten (20, 435 für die linke obere Ecke, 300 breit und 350 hoch für die Größe). Wenn Create fehlschlägt, drucken wir eine Fehlermeldung, löschen es und setzen m_commPanel auf NULL zurück.
Wenn es bereits erstellt ist oder gerade erfolgreich erstellt wurde, schaltet m_commPanel.Toggle() es ein oder aus - zeigt es an, wenn es versteckt ist, und versteckt es, wenn es angezeigt wird. Diese „träge Erstellung“ bedeutet, dass wir sie erst erstellen, wenn der Nutzer klickt, und so Ressourcen sparen, bis sie benötigt werden.
void CAdminHomeDialog::OnClickCommunications() { if(m_commPanel == NULL) { m_commPanel = new CCommunicationDialog(m_chatId, m_botToken); // Pass chatId and botToken if(m_commPanel == NULL) { Print("Error: Failed to allocate Communications panel"); return; } if(!m_commPanel.Create(m_chart_id, "CommPanel", m_subwin, 20, 435, 20 + 300, 435 + 350)) { Print("Error: Failed to create Communications panel"); delete m_commPanel; m_commPanel = NULL; return; } } m_commPanel.Toggle(); }
Schritt 5
OnEvent wartet auf Nutzeraktionen wie Klicks und verbindet CommunicationsDialog, indem es Ereignisse an es weitergibt. Wenn id == CHARTEVENT_OBJECT_CLICK ist, prüfen wir, ob sparam (der Name des angeklickten Objekts) mit m_commButton.Name() übereinstimmt. Wenn dies der Fall ist, drucken wir eine Meldung und rufen OnClickCommunications auf, um das Panel zu öffnen.
Der wichtigste Teil der Integration ist der else-Block if(m_commPanel != NULL && m_commPanel.IsVisible()). Wenn das Kommunikationspanel existiert (!= NULL) und auf dem Bildschirm zu sehen ist (IsVisible()), senden wir das Ereignis (id, lparam, etc.) an m_commPanel.OnEvent. Dadurch kann CommunicationsDialog seine eigenen Klicks, wie „Send“ oder „Clear“, verarbeiten. Wir geben zurück, was m_commPanel.OnEvent zurückgibt, und verknüpfen so die Ereignissysteme der beiden Dialoge. Wenn es keine Übereinstimmung gibt, übernimmt CAppDialog::OnEvent die Aufgabe. Durch dieses Zusammenspiel können beide Dialoge reibungslos auf den Nutzer reagieren.
bool CAdminHomeDialog::OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if(id == CHARTEVENT_OBJECT_CLICK) { Print("Clicked object: ", sparam); if(sparam == m_commButton.Name()) { Print("Communications button detected"); OnClickCommunications(); return true; } // ... other button checks ... } // Forward remaining events to CommPanel if visible else if(m_commPanel != NULL && m_commPanel.IsVisible()) { return m_commPanel.OnEvent(id, lparam, dparam, sparam); } return CAppDialog::OnEvent(id, lparam, dparam, sparam); }
Tests und Ergebnisse
Nachdem wir das Kommunikationsmodul erfolgreich kompiliert und in das Hauptprogramm integriert hatten, starteten wir das Programm auf der Terminalkarte ohne Probleme. Alle Panels reagieren auf Klicks, was durch die Einträge im Expertenprotokoll bestätigt wird. Wenn Sie in AdminHomeDialog auf die Schaltfläche „Kommunikationspanel“ klicken, wird der Kommunikationsdialog erstellt, aber die anfängliche Logik blendet ihn aus, sodass ein zweiter Klick erforderlich ist, um ihn sichtbar zu machen. Wenn der Kommunikationsdialog im Chart sichtbar ist, wird er durch erneutes Klicken auf die Schaltfläche ausgeblendet, d. h. er wird ein- und ausgeschaltet.
Bei der Listenansicht ist mir jedoch ein großes Problem aufgefallen: Die Klick- und Bildlaufereignisse für die Auswahl der zu versendenden Kurznachrichten funktionieren nicht wie erwartet, und wir müssen herausfinden, wo wir einen Fehler gemacht haben. Ich glaube, das ist ein kleines Problem, das wir beheben können. Im Moment ist das Kernkonzept funktional und sichtbar, und unsere nächsten Schritte bestehen darin, es zu verfeinern und weitere Funktionen hinzuzufügen.
Testen des Kommunikationsmoduls
Schlussfolgerung
Die Entwicklung des Moduls CommunicationsDialog, das in AdminHomeDialog.mqh integriert ist, hat zu einem funktionalen und modularen System für Telegram-Nachrichten innerhalb unserer neuen Admin Panel, MQL5 Handelsanwendung, geführt. Wir haben eine reaktionsschnelle Verwaltungsoberfläche geschaffen, die das Kommunikationspanel bei Bedarf erfolgreich startet und umschaltet, wie das Expertenprotokoll mit Klickereignissen wie „Clicked object: m_SendButton“ zeigt. Der Ansatz der verzögerten Erstellung und des Umschaltens optimiert die Ressourcennutzung, während das ereignisgesteuerte Design die Skalierbarkeit sicherstellt und beweist, dass das Kernkonzept auf dem Chart sichtbar und einsatzfähig ist. Dieser modulare Aufbau mit CAdminHomeDialog als Drehscheibe und CCommunicationDialog als spezialisiertem Werkzeug bildet eine solide Grundlage für zukünftige Erweiterungen.
Allerdings gibt es noch kleinere Probleme, z. B. funktionieren die Klick- und Bildlaufereignisse der Listenansicht nicht für die Schnellauswahl von Nachrichten, und die Schaltflächenereignisse des Kommunikationspanels müssen weiter verfeinert werden. Trotzdem überwiegen die Stärken die Mängel des Systems - Effizienz, Reaktionsschnelligkeit und eine klare Integration. Mit gezielten Korrekturen an der Ereignisausbreitung und Plänen zur Verfeinerung und Hinzufügung weiterer Funktionen sind wir gut positioniert, um diesen Prototyp in ein ausgefeiltes, funktionsreiches Tool für Händler zu verwandeln. Gute Nachrichten! Die Datei Telegram.mqh ist jetzt in der Codebase verfügbar.
Tabelle der Dateianhänge
Dateiname | Beschreibung |
---|---|
CommunicationsDialog.mqh | Definiert einen Dialog zum Versenden von Telegram-Nachrichten, der eine Texteingabe und eine Liste von Schnellnachrichtenoptionen enthält. |
AdminHomeDialog.mqh | Für die Erstellung des Admin-Home-Dialogs. Sie enthält alle Koordinatenangaben. |
New_Admin_Panel.mqh | Das neueste Admin Panel beinhaltet das Konzept der Modularität. |
Telegram.mqh | Zur Übermittlung von Nachrichten und Benachrichtigungen über Telegram. |
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/17044
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.





- 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.