Erstellen Ihrer eigenen grafischen Panels in MQL5
Einleitung
In der Standardbibliothek steht Ihnen eine Reihe neuer Klassen zur Verfügung. Diese Klassen dienen der unabhängigen Entwicklung von Kontrolldialogen und Anzeigefeldern in MQL5-Programmen.
Mit den neuen Klassen kann jeder benutzerdefinierte Oberflächenkomponenten mithilfe des zugrundeliegenden ereignisbasierten Modells erstellen. Alles basiert auf den eingebetteten Diagrammobjekten und Ereignissen des Terminals.
- Anzeigefeld in einem separaten Unterfenster eines Diagramms;
- Bedienfeld für einen Expert Advisor;
- Bedienfeld für einen benutzerdefinierten Indikator.
Dieser Beitrag führt vor, wie einfach es ist, mithilfe der Klassen aus der Standardbibliothek Ihre eigenen Anzeigefelder in einem separaten Unterfenster eines Diagramms zu erstellen.
Was hat die Standardbibliothek zu bieten?
Die Standardbibliothek stellt Entwicklern die folgenden benutzerfreundlichen Bedienelemente zur Verfügung:
1. Einfache Bedienelemente:
Bedienelement Anwendung Umsetzung auf Basis des eingebetteten Objekts Datei in der Standardbibliothek Button mit Text
Sicherstellen der Interaktion zwischen Maus und MQL-Programm
"Button" <Controls\Button.mqh> Button mit Bild
Sicherstellen der Interaktion zwischen Maus und MQL-Programm
"Grafisches Label" <Controls\BmpButton.mqh> Bearbeitungsfeld
Eingabe oder Anzeige von Textinformationen (im "Nur lesen"-Modus)
"Bearbeitungsfeld" <Controls\Edit.mqh> Beschriftung
Anzeige von Hilfstexten
"Textbeschriftung" <Controls\Label.mqh> Panel
Zusätzliches Bedienelement (optische Gruppierung von Bedienelementen)
"Rechteckige Beschriftung" <Controls\Panel.mqh> Bild
Dekoratives Bedienelement
"Grafisches Label" <Controls\Picture.mqh>
2. Komplexe Bedienelemente:
Bedienelement Anwendung Umsetzung auf Basis der Bedienelemente Datei in der Standardbibliothek Liste
Ansicht einer Liste
"Rechteck", "Button mit Bild" und "Bearbeitungsfeld" <Controls\List.mqh> Feld mit Drop-Down-Liste
Auswahl aus einer Drop-Down-Liste
"Bearbeitungsfeld", "Button mit Bild" und "Liste" <Controls\ComboBox.mqh> Zunahme-/Abnahmefeld
Aufzählung von Werten
"Bearbeitungsfeld" und "Button mit Bild" <Controls\SpinEdit.mqh> Optionsschaltfläche
Umschalter "Button mit Bild" und "Beschriftung" <Controls\RadioButton.mqh> Gruppe von Optionsschaltflächen Bearbeiten von Feldern des Typen enum "Rechteck" und "Optionsschaltfläche" <Controls\RadioGroup.mqh> Kontrollkästchen
Auswahloption
"Button mit Bild" und "Beschriftung" <Controls\CheckBox.mqh> Gruppe von Kontrollkästchen
Bearbeitung eines Satzes von Flags
"Rechteck" und "Kontrollkästchen" <Controls\CheckGroup.mqh> Dialog Dialogform "Rechteck", "Button mit Bild" und "Bearbeitungsfeld" <Controls\Dialog.mqh>
Erstellen eines Anzeigefelds
Legen wir zuerst die Terminologie fest. Anzeigefeld ist ein Begriff, den wir verwenden werden, um einen benutzerdefinierten Indikator in einem separaten Fenster zu beschreiben, der über keinen Zeichenpuffer verfügt. Ein solches Feld zeigt einfach die erforderlichen Informationen mithilfe der im Terminal eingebetteten Diagrammobjekte an. Die Informationen können
- numerisch,
- als Text,
- als Farbe
- usw. dargestellt werden.
Wir sehen uns jeden erforderlichen Schritt im Detail an und erstellen ein grafisches Panel, das wie folgt aussieht:
Um ein Anzeigefeld zu erstellen, benötigen wir zwei Dateien:
- Die Include-Datei, die die Beschreibung der Klasse des Anzeigefelds enthält.
- Die Datei mit dem Quellcode des Indikators.
Templates dieser Dateien können mithilfe des MQL5 Wizard abgerufen werden. Erstellen Sie im Verzeichnis der Indikatoren (MQL5\Indicators) einen separaten Ordner mit dem Namen MyIndicators und einen Unterordner mit dem Namen MyPanel. Der Prozess zum Erstellen von Ordnern wird hier nicht behandelt, da er in der Hilfe ausführlich beschrieben wird.
Klassenbeschreibung
Den Arbeitsordner haben wir also schon erstellt. Suchen wir ihn im "Navigator"-Fenster und rechtsklicken auf ihn. Wählen Sie im aufgerufenen Menü "New File" (Neue Datei). Wählen Sie in den Optionen des MQL5 Wizard "New Class" (Neue Klasse) und klicken Sie auf "Weiter >". Füllen Sie den Dialog für die Klassenbeschreibung aus, wie unten abgebildet:
Klicken Sie auf "Fertigstellen". Als Ergebnis erhalten wir den folgenden Code:
//+------------------------------------------------------------------+ //| PanelDialog.mqh | //| Copyright 2011, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2011, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CPanelDialog : public CAppDialog { private: public: CPanelDialog(); ~CPanelDialog(); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CPanelDialog::CPanelDialog() { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CPanelDialog::~CPanelDialog() { } //+------------------------------------------------------------------+
Fügen Sie die Include-Datei <Controls\Dialog.mqh> aus der Standardbibliothek mit der Beschreibung der Basisklasse CAppDialog und Kommentaren hinzu.
//+------------------------------------------------------------------+ //| PanelDialog.mqh | //| Copyright 2011, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include <Controls\Dialog.mqh> //+------------------------------------------------------------------+ //| CPanelDialog class | //| Function: main application dialog | //+------------------------------------------------------------------+ class CPanelDialog : public CAppDialog { private: public: CPanelDialog(void); ~CPanelDialog(void); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CPanelDialog::CPanelDialog(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CPanelDialog::~CPanelDialog(void) { } //+------------------------------------------------------------------+
Nun haben wir die Beschreibung der Klasse, die es uns ermöglichen wird, ein Dialogfenster in ihrem Indikator zu verwenden. Unser Dialog ist derzeit noch leer, aber wir werden ihm etwas später Bedienelemente hinzufügen. Fahren wir aber vorerst mit dem Indikator fort.
Der Quellcode des Indikators
Der Indikator wird ebenfalls mithilfe des MQL5 Wizard erstellt. Die dafür erforderlichen Aktionen sind denen beim Schreiben der Klassenbeschreibung ähnlich. Es gibt nur einen Unterschied: In den Optionen des MQL5 Wizard wählen wir "Custom Indicator" (Benutzerdefinierter Indikator). Um einen Indikator zu erstellen, müssen drei Dialoge ausgefüllt werden.
Im ersten muss der Name des Indikators angegeben werden:
Im zweiten Dialog setzen Sie das Häkchen bei "OnChartEvent" (erforderlich) und "OnTimer":
Setzen Sie im dritten Dialog das Häkchen bei "Indicator in separate window" (Indikator in separatem Fenster) (erforderlich):
Und klicken Sie auf "Fertigstellen". Der Code sieht so aus:
//+------------------------------------------------------------------+ //| PanelIndicator.mq5 | //| Copyright 2011, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2011, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property indicator_separate_window //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping //--- return(0); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { //--- //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer() { //--- } //+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- } //+------------------------------------------------------------------+
Wir fügen unserem Template Folgendes hinzu:
- fehlende Beschreibungen von Indikatoreigenschaften;
- Include-Datei mit der Klassenbeschreibung unseres Dialogs;
- eine globale Variable – das Klassenobjekt unseres Dialogs;
- Code für die Erstellung des Dialogs, den Start der Anwendung und die Erstellung des Timers im Hauptteil der OnInit()-Funktion;
- OnDeinit()-Funktion mit einem Code, der den Dialog und den Timer zerstört;
- OnChartEvent(...)-Funktion für den Aufrufcode des Ereignis-Handlers;
- Kommentare.
Nun haben wir einen gebrauchsfertigen Indikator:
//+------------------------------------------------------------------+ //| PanelIndicator.mq5 | //| Copyright 2011, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2011, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property indicator_separate_window #property indicator_plots 0 #property indicator_buffers 0 #property indicator_minimum 0.0 #property indicator_maximum 0.0 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "PanelDialog.mqh" //+------------------------------------------------------------------+ //| Global variables | //+------------------------------------------------------------------+ CPanelDialog ExtDialog; //+------------------------------------------------------------------+ //| Initialization | //+------------------------------------------------------------------+ int OnInit() { //--- creating the application dialog if(!ExtDialog.Create(0,"Panel Indicator",0,0,0,0,130)) return(-1); //--- starting the application if(!ExtDialog.Run()) return(-2); //--- creating the timer EventSetTimer(1); //--- success return(0); } //+------------------------------------------------------------------+ //| Deinitialization | //+------------------------------------------------------------------+ int OnDeinit() { //--- destroying the dialog ExtDialog.Destroy(); //--- killing the timer EventKillTimer(); } //+------------------------------------------------------------------+ //| Iteration | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { //--- returning the prev_calculated value for the next call return(rates_total); } //+------------------------------------------------------------------+ //| Timer event handler | //+------------------------------------------------------------------+ void OnTimer() { //--- } //+------------------------------------------------------------------+ //| Chart event handler | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- handling the event ExtDialog.ChartEvent(id,lparam,dparam,sparam); } //+------------------------------------------------------------------+
In dieser Form zeigt der Indikator noch nichts an. Wenn er kompiliert und aus dem Navigator in das Diagramm übertragen wird, wird er als leerer Dialog in einem separaten Fenster angezeigt.
Obwohl der Dialog leer ist, hat unser Indikator bereits bestimmte Merkmale erhalten:
- Die Höhe des Unterfensters wurde während der Erstellung durch den Indikator an die Höhe des Dialogs angepasst;
- Die Breite des Dialogs entspricht immer der Breite des Diagramms;
- Der Indikator kann sein eigenes Unterfenster minimieren und maximieren.
Ermöglichen der Anzeige
Damit unser Panel anfangen kann, Informationen anzuzeigen, müssen wir drei Fragen beantworten:
- Welche Art von Informationen wollen wir anzeigen lassen?
- Welche zusätzlichen Anzeige- und/oder Bedienelemente müssen in unserem Dialog platziert werden?
- Wie interagieren diese zusätzlichen Anzeige-/Bedienelemente miteinander?
Ein weiterer wichtiger Faktor ist, dass unser Dialog optisch ansprechend und benutzerfreundlich sein soll. Dies betrifft nicht die Funktionalität des Dialogs, doch es zeigt, dass wir uns um die Anwender unseres zukünftigen MQL5-Programms kümmern.
Schritt 1. Welche Art von Informationen wollen wir anzeigen lassen?
Da dieser Beitrag Lernzwecken dient, halten wir uns nicht mit der Benutzerfreundlichkeit des Indikators auf. Die Farbe wird als Funktion von drei Parametern angezeigt. Wir wollen die Parameter nicht zu kompliziert machen, deshalb werden es "rote", "grüne" und "blaue" Anteile sein.
Die Parameterwerte werden wie folgt festgelegt:
- Der Wert des "roten" Anteils liegt im Bereich zwischen 0 und 255 und ändert sich zufällig bei jedem Calculate-Ereignis;
- Der Wert des "grünen" Anteils liegt im Bereich zwischen 0 und 255 und ändert sich zufällig bei jedem Timer-Ereignis;
- Der Wert des "blauen" Anteils liegt im Bereich zwischen 0 und 255 und wird durch ein spezielles Bedienelement manuell geändert.
Übrigens werden die Werte dieser Anteile auch in unserem Indikator angezeigt.
Schritt 2. Welche zusätzlichen Bedienelemente werden benötigt?
Schritt 3. Wie interagieren diese zusätzlichen Dialog-Bedienelemente miteinander?
- Die Farbe wird mithilfe des Bedienelements "Panel" angezeigt.
- Der "rote" und "grüne" Anteil wird mithilfe des Bedienelements "Eingabefeld" im "Nur lesen"-Modus angezeigt.
- Der "blaue" Anteil wird mithilfe des Bedienelements "Spin-Button" verwaltet. Dasselbe Bedienelement hilft auch bei der Anzeige des Anteilswerts.
- Die Bedienelemente "Bearbeitungsfeld" und "Spin-Button" werden beide durch das Bedienelement "Beschriftung" um erläuternde Beschriftungen erweitert.
Fügen Sie die Include-Dateien aus der Standardbibliothek sowie die erforderlichen Bedienelemente und Variablen zur Speicherung der Parameterwerte zur Klassenbeschreibung hinzu, nachdem Sie sie mit Kommentaren versehen haben.
Wir erhalten:
//+------------------------------------------------------------------+ //| PanelDialog.mqh | //| Copyright 2011, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include <Controls\Dialog.mqh> #include <Controls\Panel.mqh> #include <Controls\Edit.mqh> #include <Controls\Label.mqh> #include <Controls\SpinEdit.mqh> //+------------------------------------------------------------------+ //| CPanelDialog class | //| Function: main application dialog | //+------------------------------------------------------------------+ class CPanelDialog : public CAppDialog { private: //--- additional controls CPanel m_color; // object for displaying color CLabel m_label_red; // "red" level caption object CEdit m_field_red; // "red" value display object CLabel m_label_green; // "green" level caption object CEdit m_field_green; // "green" value display object CLabel m_label_blue; // "blue" level caption object CSpinEdit m_edit_blue; // "blue" value control object //--- parameter values int m_red; // "red" value int m_green; // "green" value int m_blue; // "blue" value public: CPanelDialog(void); ~CPanelDialog(void); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CPanelDialog::CPanelDialog(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CPanelDialog::~CPanelDialog(void) { } //+------------------------------------------------------------------+
Das Prinzip der Interaktion zwischen den Dialog-Bedienelementen ist sehr einfach: "Der Dialog muss Änderungen in jedem Parameter ("roten", "grünen" und "blauen" Anteil) anzeigen". Auf die Umsetzung der Interaktionsalgorithmen gehen wir später ein, da es jetzt an der Zeit ist, mit der Erstellung des Dialogs zu beginnen.
Ein paar Wörter zum ansprechenden Äußeren
Bevor wir mit der Erstellung des Dialogs fortfahren, gehen wir kurz auf das ansprechende Äußere ein. Oder, besser gesagt, eine benutzerfreundliche Anordnung und (mögliche) zukünftige Neuanordnung der Dialog-Bedienelemente. Benannte Konstanten (#define) dienen diesem Zweck am besten.
Die vordefinierten benannten Konstanten haben einige Vorteile:
- Bestimmte numerische Werte, die in bestimmten Fällen verwendet werden, müssen nicht verinnerlicht werden. Die richtig ausgewählten Namen der Konstanten ermöglichen den schnellen Zugriff über "Auto-Listennamen";
- Wenn die Werte der Konstanten weiter angepasst werden, müssen zahlreiche Einbindungen numerischer Werte nicht gesucht und ersetzt werden. Es reicht aus, nur die Beschreibung der Konstante zu ändern.
Die Verwendung der folgenden Konstanten wird empfohlen:
//+------------------------------------------------------------------+ //| defines | //+------------------------------------------------------------------+ //--- indents and spacing #define INDENT_LEFT (11) // left indent (including the border width) #define INDENT_TOP (11) // top indent (including the border width) #define INDENT_RIGHT (11) // right indent (including the border width) #define INDENT_BOTTOM (11) // bottom indent (including the border width) #define CONTROLS_GAP_X (10) // spacing along the X-axis #define CONTROLS_GAP_Y (10) // spacing along the Y-axis //--- for labels #define LABEL_WIDTH (50) // size along the X-axis //--- for edits #define EDIT_WIDTH (50) // size along the Y-axis #define EDIT_HEIGHT (20) // size along the Y-axis //--- for base colors (RGB) #define BASE_COLOR_MIN (0) // minimum value of the color component #define BASE_COLOR_MAX (255) // maximum value of the color component
Ausfüllen des Anzeigefelds
Vorher haben wir die Klasse des Anzeigefelds erstellt. Um nun die nötige Funktionalität zu erhalten, müssen wir Folgendes tun:
1. Die Create(...)-Methode der übergeordneten Klasse neu definieren. Zunächst sieht unsere Methode so aus:
//+------------------------------------------------------------------+ //| Creation | //+------------------------------------------------------------------+ bool CPanelDialog::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2) { //--- calling the method of the parent class if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2)) return(false); //--- additional controls shall be created here //--- success return(true); }
2. Erstellen zusätzlicher Bedienelemente.
Hier machen wir eine kleine "lyrische" Abweichung. Die Codes für die Erstellung aller zusätzlichen Bedienelemente können natürlich direkt in den Hauptteil der Create(...)-Methode eingefügt werden, doch damit riskieren wir, dass wir eine große, unlesbare "Masse" erhalten.
Deshalb teilen wir den Erstellungsprozess in eigenständige Teile auf, die durch Methoden repräsentiert werden:
- bool CreateColor(void) – Erstellen des Farb-Panels,
- bool CreateRed(void) – Erstellen des Anzeigeelements "Red" mit erläuternder Beschriftung,
- bool CreateGreen(void) – Erstellen des Anzeigeelements "Green" mit erläuternder Beschriftung,
- bool CreateGreen(void) – Erstellen des Bedienelements "Blue" mit erläuternder Beschriftung.
Diese Methoden werden durch die Create(...)-Methode sequentiell aufgerufen:
//+------------------------------------------------------------------+ //| Creation | //+------------------------------------------------------------------+ bool CPanelDialog::Create(const long chart,const string name,const int subwin, const int x1,const int y1,const int x2,const int y2) { //--- calling the parent class method if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2)) return(false); //--- creating additional elements if(!CreateColor()) return(false); if(!CreateRed()) return(false); if(!CreateGreen()) return(false); if(!CreateBlue()) return(false); //--- success return(true); }
Erstellen von Bedienelementen
Wir gehen nicht auf die Erstellung jedes einzelnen zusätzlichen Bedienelements ein, sehen uns aber die Methode bool CreateBlue(void) im Detail an.
Sie sieht wie folgt aus:
//+------------------------------------------------------------------+ //| Creating the "Blue" control with explanatory caption | //+------------------------------------------------------------------+ bool CPanelDialog::CreateBlue(void) { //--- coordinates int x1=INDENT_LEFT; int y1=INDENT_TOP+2*(EDIT_HEIGHT+CONTROLS_GAP_Y); int x2=x1+EDIT_WIDTH; int y2=y1+EDIT_HEIGHT; //--- creating the caption if(!m_label_blue.Create(m_chart_id,m_name+"LabelBlue",m_subwin,x1,y1+1,x2,y2)) return(false); if(!m_label_blue.Text("Blue")) return(false); if(!Add(m_label_blue)) return(false); //--- adjusting coordinates x1+=LABEL_WIDTH+CONTROLS_GAP_X; x2=x1+EDIT_WIDTH; //--- creating the control if(!m_edit_blue.Create(m_chart_id,m_name+"Blue",m_subwin,x1,y1,x2,y2)) return(false); if(!Add(m_edit_blue)) return(false); m_edit_blue.MinValue(BASE_COLOR_MIN); m_edit_blue.MaxValue(BASE_COLOR_MAX); m_edit_blue.Value(m_blue); //--- success return(true); }
Es gibt zwei Feinheiten zu beachten:
- Das Bedienelement wird mit relativen Koordinaten erstellt. Das heißt, die Verschiebung bezieht sich auf die linke obere Ecke des Containers (des komplexen Elements), dem das Bedienelement nach seiner Erstellung hinzugefügt wird.
- Nach der Erstellung muss das Bedienelement mithilfe der Add(...)-Methode einem Container hinzugefügt werden. In unserem Fall dient der Dialog als Container.
Ändern der Parameter
Fügen Sie die Methode void SetColor(void) hinzu, um die Farbe des Farb-Panels zu ändern;
Um die Parameter (Anteile der Basisfarben) extern ändern zu können, fügen wir drei öffentliche Methoden hinzu:
- void SetRed(const in value) – ändert den "roten" Anteil und zeigt die Änderung im Indikator an,
- void SetGreen(const in value) – ändert den "grünen" Anteil und zeigt die Änderung im Indikator an,
- void SetBlue(const in value) – ändert den "blauen" Anteil und zeigt die Änderung im Indikator an.
Der Teilsatz "zeigt die Änderung im Indikator an" bedeutet, dass der neue Wert des Basisfarbenanteils in numerischer Form im entsprechenden Bedienelement angezeigt wird und das Farb-Panel seine Farbe ändert.
Hier sehen Sie den Code einer der Methoden als Beispiel:
//+------------------------------------------------------------------+ //| Setting the "Red" value | //+------------------------------------------------------------------+ void CPanelDialog::SetRed(const int value) { //--- checking if(value<0 || value>255) return; //--- saving m_red=value; //--- setting m_field_red.Text(IntegerToString(value)); //--- setting the panel color SetColor(); }
Wie oben beschrieben:
- Der Wert des "roten" Anteils ändert sich zufällig bei jedem Calculate-Ereignis;
- Der Wert des "grünen" Anteils ändert sich zufällig bei jedem Timer-Ereignis;
Fügen wir den entsprechenden Code im ursprünglichen Indikator ein:
//+------------------------------------------------------------------+ //| PanelIndicator.mq5 | //| Copyright 2011, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2011, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property indicator_separate_window #property indicator_plots 0 #property indicator_buffers 0 #property indicator_minimum 0.0 #property indicator_maximum 0.0 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "PanelDialog.mqh" //+------------------------------------------------------------------+ //| Global variables | //+------------------------------------------------------------------+ CPanelDialog ExtDialog; //+------------------------------------------------------------------+ //| Initialization | //+------------------------------------------------------------------+ int OnInit() { //--- creating the application dialog if(!ExtDialog.Create(0,"Panel Indicator",0,0,0,0,130)) return(-1); //--- starting the application if(!ExtDialog.Run()) return(-2); //--- creating the timer EventSetTimer(1); //--- success return(0); } //+------------------------------------------------------------------+ //| Deinitialization | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- destroying the dialog ExtDialog.Destroy(); //--- killing the timer EventKillTimer(); } //+------------------------------------------------------------------+ //| Iteration | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { //--- changing the dialog property ExtDialog.SetRed(MathRand()%256); //--- returning the prev_calculated value for the next call return(rates_total); } //+------------------------------------------------------------------+ //| Timer event handler | //+------------------------------------------------------------------+ void OnTimer() { //--- changing the dialog property ExtDialog.SetGreen(MathRand()%256); } //+------------------------------------------------------------------+ //| Chart event handler | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- handling the event ExtDialog.ChartEvent(id,lparam,dparam,sparam); } //+------------------------------------------------------------------+
Verarbeiten der Ereignisse
Die gesamte Interaktion zwischen dem Dialog und dem Terminal sowie die Interaktion zwischen den Dialog-Bedienelementen basieren auf dem Ereignis-Mechanismus. Wir sehen uns seine Funktionen nicht an, sondern nutzen ihn einfach.
Ereignisse können für gewöhnlich in zwei Gruppen unterteilt werden:
- Interne Ereignisse, die außerhalb der Ereigniswarteschlange des Terminals verarbeitet werden;
- Externe Ereignisse, die über die Ereigniswarteschlange des Terminals verarbeitet werden.
Wir verarbeiten beide Typen von Ereignissen.
Von den internen Ereignissen müssen nur die Ereignisse der Änderung der Größe des Dialogs verarbeitet werden. Laden Sie zu diesem Zweck die OnResize()-Methode der übergeordneten Klasse erneut. Unsere Methode wird einfach ausfallen, da die Höhe des Dialogs nicht verändert wird. Wenn sich die Breite des Dialogs ändert, müssen wir nur die Breite des Farb-Panels anpassen:
//+------------------------------------------------------------------+ //| Resize handler | //+------------------------------------------------------------------+ bool CPanelDialog::OnResize(void) { //--- calling the parent class method if(!CAppDialog::OnResize()) return(false); //--- changing the color panel width m_color.Width(ClientAreaWidth()-(INDENT_RIGHT+LABEL_WIDTH+CONTROLS_GAP_X+EDIT_WIDTH+CONTROLS_GAP_X+INDENT_LEFT)); //--- success return(true); }
Die Liste externer Ereignisse beschränkt sich ebenfalls auf einen Eintrag: Das Ereignis der Änderung des "blauen" Anteils. Die Anforderungen an den externen Ereignis-Handler sind minimal: Der Handler muss die parameterlose Methode der Klasse des Typen void sein.
Beschreiben wir den Handler dieses Ereignisses:
//+------------------------------------------------------------------+ //| Handler of the event of changing the "blue" level | //+------------------------------------------------------------------+ void CPanelDialog::OnChangeBlue(void) { //--- saving m_blue=m_edit_blue.Value(); //--- setting the panel color SetColor(); }
Wie Sie sehen, ist es absolut nicht schwierig.
Damit unser Dialog externe Ereignisse verarbeitet, muss die Methode der übergeordneten Klasse erneut geladen werden:
virtual bool OnEvent(const int id,const long &lparam, const double &dparam,const string &sparam);
Und jetzt noch etwas "Mysteriöses". Falls Sie die Datei PanelDialog.mqh bereits im Editor geöffnet haben, werden Sie feststellen, dass die OnEvent(...)-Methode keinen Hauptteil hat.
Lassen Sie sich dadurch nicht verwirren. Das liegt daran, dass für die Beschreibung der Verarbeitung externer Ereignisse eine Reihe von Makros erstellt wurde (siehe Datei <Controls\Defines.mqh> in der Standardbibliothek).
Unser Ereignis-Handler sieht wie folgt aus:
//+------------------------------------------------------------------+ //| Handling events | //+------------------------------------------------------------------+ EVENT_MAP_BEGIN(CPanelDialog) ON_EVENT(ON_CHANGE,m_edit_blue,OnChangeBlue) EVENT_MAP_END(CAppDialog)
Der Pseudocode, der auf den ersten Blick unklar aussieht, bewirkt Folgendes:
- Wenn das Ereignis ON_CHANGE vom Bedienelement m_edit_blue eingeht, wird die Methode OnChangeBlue aufgerufen und die Verarbeitung des Ereignisses abgeschlossen (gibt true aus);
- Nach dem Eingang jedes beliebigen anderen Ereignisses wird die Steuerung an die Methode der übergeordneten Klasse übertragen.
Fazit
Mit diesem Beitrag haben Sie einen Überblick über den Prozess der Erstellung eines Anzeigefelds mithilfe der Klassen der Standardbibliothek erhalten.
Es ist unwahrscheinlich, dass Sie den Indikator in dieser Form nutzen werden, allerdings ist er nicht mit unnötigen Informationen überladen und wir sind auf fast alle Besonderheiten des Prozesses zu seiner Erstellung eingegangen.
Komplexere Beispiele für die Standardbereitstellung finden Sie in den folgenden Verzeichnissen Ihres Terminals:
- Experts\Examples\Controls\
- Indicators\Examples\Panels\ChartPanel\
- Indicators\Examples\Panels\SimplePanel\
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/345
- 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.