Grafische Interfaces IX: Die Fortschrittsanzeige und das Linienchart-Control (Kapitel 2)
Inhalt
- Einleitung
- Fortschrittsanzeige
- Das Linienchart Control
- Verbesserung von einigen Klassen der Standardbibliothek
- Entwicklung der CLineGraph Klasse
- Schreiben eine Anwendung für den Test des Linien-Charts
- Schlussfolgerung
Einleitung
Bitte lesen Sie dazu auch den ersten Artikel Grafische Interfaces I: Vorbereiten der Bibliotheks-Struktur (Kapitel 1), damit Sie die Anwendung dieser Bibliothek besser verstehen können. Eine vollständige Liste der Links zu den Artikeln finden Sie am Ende von jedem Kapitel. Zudem finden Sie dort eine Möglichkeit das Projekt, entsprechend dem aktuellen Entwicklungsstand, herunterzuladen. Die Dateien müssen in den gleichen Verzeichnissen untergebracht werden, so, wie Sie auch in dem Archiv abgelegt sind.
In dem vorherigen Artikel haben wir zwei miteinander verbundene Elemente besprochen: die Farbauswahl/Palette und den Farb-Button Das zweite Kapitel befasst sich mit der Fortschrittsanzeige und dem Control für Linien-Charts Wie immer, gibt es auch hier detaillierte Beispiele, um deutlich zu machen, wie die Controls in den benutzerdefinierten MQL Anwendungen verwendet werden können.
Fortschrittsanzeige
Wenn Prozesse durchgeführt werden, die ein wenig länger dauern, dann braucht man einen Control, welches dem User zeigt, wie weit der Prozess bereits fortgeschritten ist und wieviel Zeit er noch in Anspruch nehmen wird. Dieses macht man normalerweise mit einer Fortschrittsanzeige innerhalb eines grafischen Interfaces. In der einfachsten Art der Implementation dieses Controls, benötigt man zwei Rechtecke: einer von ihnen zeigt die Gesamtlänge des Prozesses an und das Zweite den bisher abgeschlossenen Teil des Prozesses. Zudem kann diesem Control noch eine Prozentangabe hinzugefügt werden, was eine zusätzliche Aussagekraft herstellt.
Lassen Sie uns alle dafür notwendigen Komponenten auflisten:
- Hintergrund
- Beschreibung:
- Indikator Scrollbar
- Indikator Hintergrund
- Prozentangabe
Abbildung 1. Komponenten der Fortschrittsanzeige.
Lassen Sie uns nun die Klasse für die Erzeugung des Controls näher betrachten.
Entwicklung der CProgressBar Klasse
Wir erzeugen die ProgressBar.mqh Datei mit der CProgressBar Klasse mit allen Standardmethoden die wir auch für alle anderen Controls benötigen und beziehen Sie in der WndContainer.mqh Datei mit ein. Nachfolgend sehen Sie eine Liste der verfügbaren konfigurierbaren Controls:
- Die Farbe des gemeinsamen Hintergrundes des Controls
- Der Text für die Beschreibung
- Die Farbe des Textes
- Der Offset für die Beschreibung (x, y)
- Die Farbe des Bereiches und des Rahmens des Indikators
- Die Größe des Hintergrundes des Indikators
- Die Breite des Rahmens
- Die Farbe der Scrollbar des Indikators
- Der Offset für das Label für die Prozentangabe des Prozesses
- Die Anzahl der Nachkommastellen für die Prozentangabe
class CProgressBar : public CElement { private: //--- Die Farbe des Controls (Bereich) color m_area_color; //--- Der Text für den angezeigten Prozess string m_label_text; //--- Die Textfarbe color m_label_color; //--- Der Offset für das Text-Label int m_label_x_offset; int m_label_y_offset; //--- Die Farben für die Fortschrittsanzeige und den Rahmen color m_bar_area_color; color m_bar_border_color; //--- Die Größe der Fortschrittsanzeige int m_bar_x_size; int m_bar_y_size; //--- Der Offset für die Fortschrittsanzeige (2 Achsen) int m_bar_x_offset; int m_bar_y_offset; //--- Die Breite des Rahmens für die Fortschrittsanzeige int m_bar_border_width; //--- Die Farbe des Indikators color m_indicator_color; //--- Der Offset für das Label der Prozentangabe int m_percent_x_offset; int m_percent_y_offset; //--- Anzahl der Nachkommastellen int m_digits; //--- public: //--- Anzahl der Nachkommastellen void SetDigits(const int digits) { m_digits=::fabs(digits); } //--- (1) Farbe des Bereichs, (2) Label-Text und (3) Label-Farbe void AreaColor(const color clr) { m_area_color=clr; } void LabelText(const string text) { m_label_text=text; } void LabelColor(const color clr) { m_label_color=clr; } //--- Der Ostsee für das Text Label void LabelXOffset(const int x_offset) { m_label_x_offset=x_offset; } void LabelYOffset(const int y_offset) { m_label_y_offset=y_offset; } //--- (1) Farbe (Bereich) (2) Rahmen der Fortschrittsanzeige, (3) Indikatorfarbe void BarAreaColor(const color clr) { m_bar_area_color=clr; } void BarBorderColor(const color clr) { m_bar_border_color=clr; } void IndicatorColor(const color clr) { m_indicator_color=clr; } //--- (1) Breite des Rahmens, (2) Die Größe des Bereichs des Indikators void BarBorderWidth(const int width) { m_bar_border_width=width; } void BarXSize(const int x_size) { m_bar_x_size=x_size; } void BarYSize(const int y_size) { m_bar_y_size=y_size; } //--- (1) Der Offset für die Fortschrittsanzeige (zwei Achsen), (2) Der Offset für die Prozentangabe void BarXOffset(const int x_offset) { m_bar_x_offset=x_offset; } void BarYOffset(const int y_offset) { m_bar_y_offset=y_offset; } //--- Offset für das Text-Label (Prozentangabe) void PercentXOffset(const int x_offset) { m_percent_x_offset=x_offset; } void PercentYOffset(const int y_offset) { m_percent_y_offset=y_offset; } };
Um die Fortschrittsanzeige zu erzeugen, benötigen wir fünfprivate Methoden und eine öffentliche (public) methode:
class CProgressBar : public CElement { private: //--- Objekte für die Erzeugung des Controls CRectLabel m_area; CLabel m_label; CRectLabel m_bar_bg; CRectLabel m_indicator; CLabel m_percent; //--- public: //--- Methoden für die Erzeugung des Controls bool CreateProgressBar(const long chart_id,const int subwin,const int x,const int y); //--- private: bool CreateArea(void); bool CreateLabel(void); bool CreateBarArea(void); bool CreateIndicator(void); bool CreatePercent(void); };
Damit die Fortschrittsanzeige einwandfrei funktioniert, brauchen wir eine gesamtanzahl von Schritten für den Prozess und einen Index für die aktuelle Position. Hierfür brauchen wir zwei Hilfsmethoden CProgressBar::StepsTotal() und CProgressBar::CurrentIndex(). Sie werden nur für private Zwecke erstellt. Diesen Methoden wird nur ein Wert übergeben und dieser wird eventuell noch korrigiert, damit die Spanne der Fortschrittsanzeige nicht überschritten wird.
class CProgressBar : public CElement { private: //--- Gesamt Anzahl an Schritten double m_steps_total; //--- Aktuelle Indikator-Position double m_current_index; //--- private: //--- Festlegen neuer Werte für den Indikator void CurrentIndex(const int index); void StepsTotal(const int total); }; //+---------------------------------------------------------------- //| Die Gesamtanzahl der Schritte in der Fortschrittsanzeige | //+---------------------------------------------------------------- void CProgressBar::StepsTotal(const int total) { //--- Korrigieren falls kleiner 0 m_steps_total=(total<1)? 1 : total; //--- Korrigieren falls außerhalb der Spanne if(m_current_index>m_steps_total) m_current_index=m_steps_total; } //+---------------------------------------------------------------- //| Aktueller Status des Indikators | //+---------------------------------------------------------------- void CProgressBar::CurrentIndex(const int index) { //--- Korrigieren falls kleiner 0 if(index<0) m_current_index=1; //--- Korrigieren falls außerhalb der Spanne else m_current_index=(index>m_steps_total)? m_steps_total : index; }
Die Methoden CProgressBar::StepsTotal() und CProgressBar::CurrentIndex() werden in der Hauptmethode für die Interaktion mit der äußeren Umgebung aufgerufen — CProgressBar::Update(). Alle Parameter, die für die einwandfreie Funktion der Fortschrittsanzeige und des Neuzeichnens notwendig sind, werden dieser Methode übergeben. Das erste Argument ist der Fortschritts-Index(index), und der Zweite — Die Gesamtanzahl der Schritte(total). Nachdem alle diese Werte überprüft worden sind, wird die neue Breite der Indikator-Scrollbar berechnet. Dieser Wert kann später, falls erforderlich, geändert werden. Anschließend (1) wird die neue Breite für die Indikator-Scrollbar gesetzt (2) die Prozentangabe wird berechnet und der entsprechende String gebildet, und am Ende der Methode(3) wird der neue Wert für das Text-Label der Prozentangabe gesetzt.
class CProgressBar : public CElement { public: //--- Aktualisieren des Indikators mit den angegebenen Werten void Update(const int index,const int total); }; //+---------------------------------------------------------------- //| Aktualisierung der Fortschrittsanzeige | //+---------------------------------------------------------------- void CProgressBar::Update(const int index,const int total) { //--- Neuen Index setzen CurrentIndex(index); //--- Neue Spanne setzen StepsTotal(total); //--- Berechnung der Breite des Indikators double new_width=(m_current_index/m_steps_total)*m_bar_bg.XSize(); //--- Korrigieren falls kleiner 1 if((int)new_width<1) new_width=1; else { //--- Korrigieren unter Berücksichtigung der Breite des Rahmens int x_size=m_bar_bg.XSize()-(m_bar_border_width*2); //--- Korrigieren falls der Rahmen überschritten wird if((int)new_width>=x_size) new_width=x_size; } //--- Die neue Breite des Indikators setzen m_indicator.X_Size((int)new_width); //--- Die Berechnung des Prozentwertes und das Bilden des Strings double percent =m_current_index/m_steps_total*100; string desc =::DoubleToString((percent>100)? 100 : percent,m_digits)+"%"; //--- Den neuen Wert setzen m_percent.Description(desc); }
Alle Methoden für die Erzeugung und das Verwalten der Fortschrittsanzeige sind nun fertig. Jetzt werden wir ihn Testen und sehen, wie er in dem grafischen Interface einer MQL Anwendung aussieht.
Test der Fortschrittsanzeige
Einer der Expert Advisors aus den vorherigen Artikeln kann als Vorlage für den Test verwendet werden. Wie löschen aus diesem Expert Advisor alles, bis auf das Hauptmenü und die Statusbar. Dann fügen wir den Expert Advisor acht Fortschritt anzeigen hinzu. Um das ganze etwas interessanter zu gestalten, verwenden wir einen Schieberegler für die Verwaltung der Anzahl der Schritte.
In dem Körper der CProgram Benutzerdefinierten Klasse deklarieren wir Instanzen der notwendigen Controls und Methoden für deren Erzeugung mit Abständen von dem Eckpunkt der Form:
class CProgram : public CWndEvents { private: //--- Schieberegler CSlider m_slider1; //--- Fortschrittsanzeigen CProgressBar m_progress_bar1; CProgressBar m_progress_bar2; CProgressBar m_progress_bar3; CProgressBar m_progress_bar4; CProgressBar m_progress_bar5; CProgressBar m_progress_bar6; CProgressBar m_progress_bar7; CProgressBar m_progress_bar8; //--- private: //--- Schieberegler #define SLIDER1_GAP_X (7) #define SLIDER1_GAP_Y (50) bool CreateSlider1(const string text); //--- #define PROGRESSBAR1_GAP_X (7) #define PROGRESSBAR1_GAP_Y (100) bool CreateProgressBar1(void); //--- #define PROGRESSBAR2_GAP_X (7) #define PROGRESSBAR2_GAP_Y (125) bool CreateProgressBar2(void); //--- #define PROGRESSBAR3_GAP_X (7) #define PROGRESSBAR3_GAP_Y (150) bool CreateProgressBar3(void); //--- #define PROGRESSBAR4_GAP_X (7) #define PROGRESSBAR4_GAP_Y (175) bool CreateProgressBar4(void); //--- #define PROGRESSBAR5_GAP_X (7) #define PROGRESSBAR5_GAP_Y (200) bool CreateProgressBar5(void); //--- #define PROGRESSBAR6_GAP_X (7) #define PROGRESSBAR6_GAP_Y (225) bool CreateProgressBar6(void); //--- #define PROGRESSBAR7_GAP_X (7) #define PROGRESSBAR7_GAP_Y (250) bool CreateProgressBar7(void); //--- #define PROGRESSBAR8_GAP_X (7) #define PROGRESSBAR8_GAP_Y (275) bool CreateProgressBar8(void); };
Die Erzeugung dieser Controls haben wir schon in den vorangegangenen Artikel besprochen und daher besprechen wir hier nur eine Methode für die Erzeugung einer Fortschrittsanzeige als Beispiel. Sehen Sie sich dazu das nachfolgende listing an. Wie Sie sehen können, sollte es hier keine Dinge geben, die unverständlich sind. Alles ist transparent und einfach.
//+---------------------------------------------------------------- //| Die Fortschrittsanzeige Nr. 1 wird erzeugt | //+---------------------------------------------------------------- bool CProgram::CreateProgressBar1(void) { //--- Abspeichern des Pointers des Formulars m_progress_bar1.WindowPointer(m_window1); //--- Koordinaten int x=m_window1.X()+PROGRESSBAR1_GAP_X; int y=m_window1.Y()+PROGRESSBAR1_GAP_Y; //--- Festlegen der Eigenschaften vor der Erzeugung m_progress_bar1.XSize(220); m_progress_bar1.YSize(15); m_progress_bar1.BarXSize(123); m_progress_bar1.BarYSize(11); m_progress_bar1.BarXOffset(65); m_progress_bar1.BarYOffset(2); m_progress_bar1.LabelText("Progress 01:"); //--- Erzeugung des Controls if(!m_progress_bar1.CreateProgressBar(m_chart_id,m_subwin,x,y)) return(false); //--- Den Pointer zum Control in der Basis hinzufügen CWndContainer::AddToElementsArray(0,m_progress_bar1); return(true); }
Der Aufruf der Methoden wird in der Hauptmethode für die Erzeugung des grafischen Interfaces durchgeführt. Der nachfolgend Programmcode zeigt eine abgekürzte Version dieser Methode, der nur zeigt was hinzugefügt werden muss:
//+---------------------------------------------------------------- //| Erzeugung eines Expert-Bedienfeldes (Panel) | //+---------------------------------------------------------------- bool CProgram::CreateExpertPanel(void) { //--- Erzeugung des Formulars für die Controls //---Erzeugung der Controls: // Hauptmenü //--- Kontextmenü //--- Schieberegler if(!CreateSlider1("Iterations total:")) return(false); //--- Fortschrittsanzeigen if(!CreateProgressBar1()) return(false); if(!CreateProgressBar2()) return(false); if(!CreateProgressBar3()) return(false); if(!CreateProgressBar4()) return(false); if(!CreateProgressBar5()) return(false); if(!CreateProgressBar6()) return(false); if(!CreateProgressBar7()) return(false); if(!CreateProgressBar8()) return(false); //--- Neuzeichnen des Charts m_chart.Redraw(); return(true); }
Lassen Sie uns und darüber sprechen wie dieses funktioniert. Wir steuern den Prozess für alle Forschers anzeigen über den Timer der benutzerdefinierten Klasse CProgram::OnTimerEvent(). Die Anzahl der Durchläufe kann über den Schieberegler manuell gesteuert werden und bei jedem Event des Timers, wird der aktuelle Wert in die Editbox geschrieben. Für jede Fortschrittsanzeige gibt es einen statischen Zähler, der seine Wertein unterschiedlichen Schritten vergrößert. Somit können wir simulieren, dass die Fortschrittsanzeigen asynchron arbeiten.
//+---------------------------------------------------------------- //| Timer | //+---------------------------------------------------------------- void CProgram::OnTimerEvent(void) { CWndEvents::OnTimerEvent(); //--- Anzahl der Iterationen int total=(int)m_slider1.GetValue(); //--- 8 Fortschrittsanzeigen static int count1=0; count1=(count1>=total) ? 0 : count1+=8; m_progress_bar1.Update(count1,total); //--- static int count2=0; count2=(count2>=total) ? 0 : count2+=3; m_progress_bar2.Update(count2,total); //--- static int count3=0; count3=(count3>=total) ? 0 : count3+=12; m_progress_bar3.Update(count3,total); //--- static int count4=0; count4=(count4>=total) ? 0 : count4+=6; m_progress_bar4.Update(count4,total); //--- static int count5=0; count5=(count5>=total) ? 0 : count5+=18; m_progress_bar5.Update(count5,total); //--- static int count6=0; count6=(count6>=total) ? 0 : count6+=10; m_progress_bar6.Update(count6,total); //--- static int count7=0; count7=(count7>=total) ? 0 : count7+=1; m_progress_bar7.Update(count7,total); //--- static int count8=0; count8=(count8>=total) ? 0 : count8+=15; m_progress_bar8.Update(count8,total); //--- Timer für die Statusbar static int count9=0; if(count9<TIMER_STEP_MSC*10) { count9+=TIMER_STEP_MSC; return; } count9=0; m_status_bar.ValueToItem(1,::TimeToString(::TimeLocal(),TIME_DATE|TIME_SECONDS)); }
Kompilieren Sie dieses Programm und laden Sie es auf einen Chart. Das Ergebnis sieht wie folgt aus:
Abbildung 2. Test der Fortschrittsanzeige
Gar nicht so schlecht. In der nachfolgenden Testanwendung für den Linienchart, werden wir die Fortschrittsanzeige noch einmal an einem speziellen Beispiel besprechen.
Das Linienchart Control
Ein Linien-Chart erlaubt es uns in einem vorgegebenen rechteckigen Bereich Datenreihen zu visualisieren, in welchem Punkte über Linien miteinander verbunden werden. Es ist schwierig den Nutzen eines solchen Werkzeuges zu unterschätzen. Zum Beispiel könnte man einen Chart erzeugen wo der Kontostand und der Equity-Wert eines Traders dargestellt wird. Meiner Meinung nach zum Beispiel ist ein Standard Indikator des MetaTrader Terminal nicht sehr praktisch, weil er es nicht erlaubt, die gesamte Datenreihe in dem sichtbaren Bereich des Fensters darzustellen.
Die Charts in einem Terminal können nur die Skala von 0 (Die stärkste Kompression) bis 5 einstellen. Bei der stärksten Kompression entspricht ein Pixel einem Element der Datenreihe und wenn dann die gesamte Datenreihe nicht in den sichtbaren Bereich passt, gibt es die Option die Scrollbars zu verwenden. Der nächste Schritt, um mehr Daten sichtbar zu machen, ist, dass man in einen höheren Timeframe schaltet. Wenn man dann auch Kerzen auswählt, kann man die gesamte Spanne der angegebenen Periode sehen.
Es wäre hilfreicher, wenn es keine Einschränkungen für die Skala des Charts gäbe und die Datenreihen immer innerhalb des sichtbaren Bereiches passen, sodass ein Pixel auch mehr als ein Datensatz darstellt. Somit ist es auch notwendig, dass die Datenreihe immer präzise vom Anfang des sichtbaren Bereichs startet und auch am Ende des sichtbaren Bereiches endet. Also sollte der erste Punkt wie ein Magnet an der Linken Seite des Bereichs heften und der letzte Punkt an der rechten Seite. Die Charts in Excel arbeiten auf diese Weise. Auf diese Weise können Sie jede Datenreihe in einem Linien-Chart darstellen ohne Maximal- oder Minimal-Werte zu verlieren.
Nachfolgend sehen Sie einen Linienchart von Excel, wo eine Datenreihe von 50000 Kontrollpunkten innerhalb des sichtbaren Bereichs ohne Verluste dargestellt wird.
Abbildung 3. Linienchart in Excel. Größe des Datensatzes - 50000 Werte.
Die Entwickler des Terminals bieten Klassen für die Erzeugung von unterschiedlichen Charts innerhalb der Standardbibliothek. Sie finden diese in dem Verzeichnis: <data folder>\MQLX\Include\Canvas\Charts. Nachfolgend listen wir diese Klassen auf:
- CChartCanvas – Basisklasse für die Erzeugung von 3 angebotenen Typen von Charts.
- CLineChart – Abgeleitete Klasse von CChartCanvas Für die Erzeugung eines Liniencharts.
- CHistogramChart – Abgeleitete Klasse von CChartCanvas für die Erzeugung eines Histogramms.
- CPieChart – Abgeleitete Klasse von CChartCanvas Für die Erzeugung eines Kuchendiagramms.
Vorübergehend übernehmen wir das, was bereits durch die Standardbibliothek angeboten wird, damit die Charts in graphischen Interfaces einer MQL Anwendung verwendet werden können. Einige Methoden dieser Klassen werden geändert und andere werden komplett ersetzt. Wir werden noch ein paar weitere Optionen hinzufügen. Eventuell werden wir später noch eine zusätzliche Bibliothek schreiben, damit wir Charts in einer höheren Qualität darstellen können.
Verbesserung von einigen Klassen der Standardbibliothek
Wie schon zuvor erwähnt, finden wir die notwendigen Klassen in den folgenden Verzeichnis: <data folder>\MQLX\Include\Canvas\Charts. Lassen Sie uns den Charts Ordner in dem Verzeichnis der zu entwickelnden Bibliothek erzeugen (<data folder>\MQLX\Include\EasyAndFastGUI\Canvas) und kopieren Sie hierhin die ChartCanvas.mqh und LineChart.mqh Dateien der Standardbibliothek.
Zunächst führen wir Veränderungen an der Basisklasse durch CChartCanvas. Im Moment ist die Basisklasse die CCanvas. Wir haben zuvor schon einmal die CCanvas Klasse aus der Standardbibliothek in unsere zu entwickelnde Bibliothek übernommen und umbenannt in CCustomCanvas. Nun wird diese zu einer Basisklasse für CChartCanvas. In dem nachfolgenden listing sehen Sie die Veränderungen an der ChartCanvas.mqh Datei.
//+---------------------------------------------------------------- //| ChartCanvas.mqh | //| Copyright 2009-2016, MetaQuotes Software Corp. | //| http://www.mql5.com | //+---------------------------------------------------------------- #include "..\CustomCanvas.mqh" ... //+---------------------------------------------------------------- //| Class CChartCanvas | //| Verwendung: Basisklasse für graphische Charts | //+---------------------------------------------------------------- class CChartCanvas : public CCustomCanvas { ...
Wir ziehen einen Hintergrund mit einem Farbverlauf vor, daher werden wir diese Option jetzt hinzufügen. Um dieses zu implementieren, verbinden wir die Datei mit der Klasse, die mit Farben(CColors) arbeitet mit der CustomCanvas.mqh Datei:
//+---------------------------------------------------------------- //| CustomCanvas.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+---------------------------------------------------------------- #include "..\Colors.mqh" ...
Somit können wir in der CChartCanvas Klasse eine Instanz dieser Klasse bilden und sie dann in unserem Projekt verwenden.
Da diese Standardbibliothek von Grund auf neu geschrieben werden muss, gibt es keinen Grund hier auf die bisherigen Methoden genauer einzugehen. Wir werden kurz darauf eingehen, bei welchen Methoden wir Veränderungen vorgenommen haben und welche Methoden wir hinzugefügt haben, damit sie bei Bedarf auf eigene Faust die Versionen vergleichen können.
Lassen Sie uns mit einer Aufzählung weitermachen. Für einen Farbverlauf brauchen wir mindestens zwei Farben. In der CChartCanvas Klasse befindet sich bereits eine Methode für die Hintergrundfarbe — CChartCanvas::ColorBackground(). Wir fügen eine ähnliche Methode CChartCanvas::ColorBackground2() für die zweite Farbe hinzu. Und eine spezielle Methode für die Initialisierung des Arrays mit den Farben für die Farbverläufe, die Gleiche wie in der CElements Klasse – die InitColorArray() Methode. Um den Hintergrund zu zeichnen, muss die CChartCanvas::DrawBackground() Methode geändert werden. Wo es früher ausreichend war, den Hintergrund mit einer Farbe und dem Aufruf CCustomCanvas::Erase() durchzuführen, müssen wir nun (1) eine Datenreihe mit der Größe der Höhe des Canvas angeben, (2) diese Datenreihe mit den Farben des Farbverlaufs initialisieren, (3) und in einer Schleife jede Linie, unter Verwendung der Farben aus der Datenreihe, zeichnen. Für das Zurücksetzen der Datenreihen und das Löschen der Objekte benötigen wir noch eine weitere Methode CChartCanvas::DeleteAll() die der Klasse hinzugefügt werden muss.
Alle anderen Veränderungen in der CChartCanvas Klasse haben einen Effekt auf die Methoden, wo der Offset, die Schrift, die Größe der Marker in der Beschreibung der Datenreihen und der Stil des Gitters berechnet und gesetzt werden. Den veränderten Programmcode finden Sie in den angehängten Dateien. Sie können diese mit der Originalversion der Standardbibliothek vergleichen.
Später werden wir noch die Veränderungen in der CLineChart Klasse besprechen. Die CChartCanvas ist hierfür eine Basisklasse. Eine Methode für das Zerstören der Objekte — CLineChart::DeleteAll() muss hier ebenfalls erzeugt werden. Die wesentlichen Veränderungen in Zusammenhang mit dem Zeichnen der Daten auf dem Charts sind in der CLineChart::DrawData() Methode. Zunächst brauchen wir drei Hilfsmethoden CLineChart::CheckLimitWhile(), CLineChart::CalculateVariables() und CLineChart::CalculateArray() in welchen die Berechnung durchgeführt werden. Und, am wichtigsten, der Kompressionsalgorithmus für die dargestellten Daten auf dem Chart, wird korrigiert. In der vorgeschlagenen Version, wird die Kompression so ausgeführt, dass wenn die Breite der Datenreihe die sichtbaren Pixel überschreitet, die Datenreihe in kleinere Teile aufgeteilt die der Anzahl der Pixel des Charts entspricht. In diesem Fall (ausgenommen des ersten Teils) überlappt jeder Teil mit dem vorherigen. Auf diese Weise wird der Versatz über dem rechten Rand immer reduziert und die rechte Seite der Datenreihe entspricht immer der rechten Seite des Charts und es gibt keine sichtbaren Verluste bezüglich der Minima und Maxima.
Nun Erzeugen wir die Klasse des Linienchart-Controls, was ähnlich abläuft, wie es auch bei den Klassen aller anderen bisherigen Controls dieser Bibliothek geschehen ist, die wir bereits in vorherigen Artikel besprochen haben.
Entwicklung der CLineGraph Klasse
Zunächst müssen wir einen neuen Objekttyp in der Objects.mqh Datei erzeugen. Dieses geschieht in der CLineChartObject Klasse. CLineChart wird die Basisklasse darstellen und die Datei muss mit derObjects.mqh Datei verbunden werden:
//+---------------------------------------------------------------- //| Objects.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+---------------------------------------------------------------- #include "..\Canvas\Charts\LineChart.mqh" ...
Wir erzeugen die LineGraph.mqh Dateien mit der CLineGraph Klasse und verbinden diese mit der Bibliotheks-Engine(WndContainer.mqh). Die CLineGraph Klasse benötigt ebenfalls die Standard virtuellen Methoden:
//+---------------------------------------------------------------- //| Klasse für die Erzeugung eines Liniencharts | //+---------------------------------------------------------------- class CLineGraph : public CElement { private: //--- Pointer zu dem Formular, zu welchem das Control hinzugefügt wurde CWindow *m_wnd; public: //--- Abspeichern des Pointers der Form void WindowPointer(CWindow &object) { m_wnd=::GetPointer(object); } //--- public: //--- Der Eventhandler für Chart-Events virtual void OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) {} //--- Timer virtual void OnEventTimer(void) {} //--- Verschieben eines Controls virtual void Moving(const int x,const int y); //--- (1) Anzeigen, (2) verstecken, (3) zurücksetzen, (4) löschen virtual void Show(void); virtual void Hide(void); virtual void Reset(void); virtual void Delete(void); };
Wir Liste nun die Eigenschaften auf, die für das Setzen der Eigenschaften für das Erscheinungsbild des Linien-Charts verfügbar sind.
- Farben für den Farbverlauf des Hintergrundes.
- Rahmenfarbe
- Farbe des Tabellen-Gitters
- Die Textfarbe
- Anzahl der Nachkommastellen (Für die Werte auf der vertikalen Skala)
class CLineGraph : public CElement { private: //--- Farbe des Farbverlaufs color m_bg_color; color m_bg_color2; //--- Rahmenfarbe color m_border_color; //--- Farbe des Gitters color m_grid_color; //--- Die Textfarbe color m_text_color; //--- Anzahl der Nachkommastellen int m_digits; //--- public: //--- Anzahl der Nachkommastellen void SetDigits(const int digits) { m_digits=::fabs(digits); } //--- Zwei Farben für den Farbverlauf void BackgroundColor(const color clr) { m_bg_color=clr; } void BackgroundColor2(const color clr) { m_bg_color2=clr; } //--- Farben für (1) den Rahmen, (2) das Gitter und(3) den Text void BorderColor(const color clr) { m_border_color=clr; } void GridColor(const color clr) { m_grid_color=clr; } void TextColor(const color clr) { m_text_color=clr; } };
Für die Erzeugung des Liniencharts, benötigen wir eine private und eine öffentliche (public) Methode:
class CLineGraph : public CElement { public: //--- Methoden für die Erzeugung des Controls bool CreateLineGraph(const long chart_id,const int subwin,const int x,const int y); //--- private: bool CreateGraph(void); };
Anschließend, um mit dem Linienchart auch arbeiten zu können, benötigen wir Methoden, die uns folgendes erlauben:
- Festlegen der maximalen Anzahl der Serien auf dem Chart
- Festlegen der maximalen und minimalen Werte für die vertikale Skala und die Anzahl der Linien des Gitters.
- Hinzufügen der Datenreihen
- Aktualisieren der Datenreihen
- Löschen der Datenreihen
class CLineGraph : public CElement { public: //--- Maximale Anzahl der Datenreihen void MaxData(const int total) { m_line_chart.MaxData(total); } //--- Festlegen der Parameter für die vertikale Skala void VScaleParams(const double max,const double min,const int num_grid); //--- Hinzufügen von Datenreihen zu dem Chart void SeriesAdd(double &data[],const string descr,const color clr); //--- Aktualisierung der Datenreihen auf dem Chart void SeriesUpdate(const uint pos,const double &data[],const string descr,const color clr); //--- Entfernen der Datenreihen vom Chart void SeriesDelete(const uint pos); };
Dieser minimale Satz von Methoden ist ausreichend, um mit Liniencharts in einer MQL Anwendung arbeiten zu können. Anschließend werden wir eine Anwendung erzeugen, die uns einen Test der Funktionen erlaubt.
Schreiben eine Anwendung für den Test des Linien-Charts
Für den Test können wir einfach eine Kopie des EOS machen, den wir zuvor schon verwendet haben. Wir entfernen jetzt alle Controls von diesem Expert Advisor, mit Ausnahme der Statusbar. Wir bestimmen nun, welche Controls für die Verwaltung des Liniencharts gebraucht werden.
Wie implementieren einen Modus, wo das Hinzufügen und das Entfernen von Daten in den Datenreihen der Serien automatisch durchgeführt wird. Dieser Prozess wird über den Timer der CProgram Klasse gesteuert. Die Geschwindigkeit der Aktualisierung steuern wir über die Eingabe eines Wertes in Millisekunden in einee Editbox (Delay Parameter). Wir führen hierfür zwei zusätzliche Controls ein und in diese Editboxen können Sie dann die Spanne eingeben (Min. Mindestgröße Parameter) und maximale Größe(Max. Mindestgröße Parameter) Anzahl von Controls in Datenreihen. Durch die Aktivierung dieses Modus, werden die Datenreihen bei jedem Event des Timers um ein Control erhöht, bis sie die angegebene maximale Größe erreicht haben und anschließend werden sie wieder reduziert, bis Sie die minimale Größe erreicht haben.Dann wird der Vorgang wieder von Vorne begonnen. Die aktuelle Größe der Datenreihen (Größe der Datenreihen Parameter), die ebenfalls manuell verwaltet werden können, werden in einer separaten Editbox dargestellt.
Die Anzahl der Datenreihen (Datenreihen) Können über eine Dropdown-Liste konfiguriert werden 1 bis 24 (Anzahl der Datenreihen Parameter). Die Daten werden über eine trigonometrische Formel, basierend auf den zurückgegebenen Werten der mathematischen Funktionen Sinus und Cosinus, berechnet. Wir werden noch Controls hinzufügen, die die Verwaltung der Parameter für die Berechnung beeinflussen können. Dieses sind (1) Inkrement-Ratio (Increment ratio Parameter) und (2) der Offset zwischen den Serien unter Berücksichtigung der vorangegangenen Datenreihe (der Offset Series Parameter). Über die Veränderung des Inkrement-Ratio, erhalten wir verschiedene Visualisierungen der Serien. Der Increment ratio Parameter enthält noch eine Checkbox, die dazu verwendet wird, eine automatischen Weiterschaltung zu dem nächsten Wert vorzunehmen, sobald die Schleife der Erhöhung/Verringerung der Größe der Serie durchlaufen wurde. Der Anstieg wird solange erhöht, bis wir die maximalen Grenzen des Controls erreicht haben. Sobald wir die Begrenzung erreicht haben, wird der Zähler für Increment ratio umgedreht. Mit anderen Worten wird dieser Zähler jedes mal umgedreht, sobald ein Minimum oder Maximum erreicht worden ist.
Um dieses noch interessanter zu gestalten, implementieren wir noch einen Modus der es uns erlaubt, eine Animation einer Serie hinzuzufügen. Und mit einem zusätzlichen Parameter kontrollieren wir auch die Geschwindigkeit (Run speed). Zudem Fügen wir hier noch eine Fortschrittsanzeige hinzu, die uns die verbleibende Zeit eines jeden Prozesses anzeigt, wenn gerade eine automatische Erhöhung oder Verringerung der Datenserien durchgeführt wird.
In der Klasse deklarieren wir jetzt die Klassen und Methoden der Controls für die Erzeugung und Verwendung des Grafischen Interfaces:
class CProgram : public CWndEvents { private: //--- Controls CSpinEdit m_delay_ms; CComboBox m_series_total; CCheckBoxEdit m_increment_ratio; CSpinEdit m_offset_series; CSpinEdit m_min_limit_size; CCheckBoxEdit m_max_limit_size; CCheckBoxEdit m_run_speed; CSpinEdit m_series_size; CLineGraph m_line_chart; CProgressBar m_progress_bar; //--- private: //--- Controls for managing the line chart #define SPINEDIT1_GAP_X (7) #define SPINEDIT1_GAP_Y (25) bool CreateSpinEditDelay(const string text); #define COMBOBOX1_GAP_X (7) #define COMBOBOX1_GAP_Y (50) bool CreateComboBoxSeriesTotal(const string text); #define CHECKBOX_EDIT1_GAP_X (161) #define CHECKBOX_EDIT1_GAP_Y (25) bool CreateCheckBoxEditIncrementRatio(const string text); #define SPINEDIT2_GAP_X (161) #define SPINEDIT2_GAP_Y (50) bool CreateSpinEditOffsetSeries(const string text); #define SPINEDIT3_GAP_X (330) #define SPINEDIT3_GAP_Y (25) bool CreateSpinEditMinLimitSize(const string text); #define CHECKBOX_EDIT2_GAP_X (330) #define CHECKBOX_EDIT2_GAP_Y (50) bool CreateCheckBoxEditMaxLimitSize(const string text); #define CHECKBOX_EDIT3_GAP_X (501) #define CHECKBOX_EDIT3_GAP_Y (25) bool CreateCheckBoxEditRunSpeed(const string text); #define SPINEDIT4_GAP_X (501) #define SPINEDIT4_GAP_Y (50) bool CreateSpinEditSeriesSize(const string text); //--- Linien-Chart #define LINECHART1_GAP_X (5) #define LINECHART1_GAP_Y (75) bool CreateLineChart(void); //--- Indikator für die Performance #define PROGRESSBAR1_GAP_X (5) #define PROGRESSBAR1_GAP_Y (364) bool CreateProgressBar(void); };
Alle hierfür notwendigen Methoden haben wir bereits in den vorherigen Artikeln besprochen. Nachfolgend wird der Programmcode der Methode für die Erzeugung eines Liniencharts besprochen. Aber zuvor müssen wir noch Methoden für das Arbeiten mit Datenreihen und Methoden für die Berechnung der zugehörigen Daten erzeugen. Dazu deklarieren wir die Series Struktur mit den Datenreihen für die darzustellenden data[] und eine zusätzliche Datenreihe data_temp[] für vorläufige Berechnungen. Datenreihen für Farben und Beschreibungen der Serien werden ebenso benötigt.
class CProgram : public CWndEvents { private: //--- Struktur für die Serien auf dem Chart struct Series { double data[]; // array of displayed data double data_temp[]; // Zusätzliche Datenreihe für die Berechnungen }; Series m_series[]; //--- (1) Namen und (2) Farben der Serien string m_series_name[]; color m_series_color[]; };
Die CProgram::ResizeDataArrays() Methode wird dazu verwendet, den Datenreihen eine neue Größe zuzuweisen. Current number of series and their size is obtained from controls:
class CProgram : public CWndEvents { private: //--- Festlegen einer neuen Größe der Serie void ResizeDataArrays(void); }; //+---------------------------------------------------------------- //| Festlegen einer neuen Größe der Datenreihe | //+---------------------------------------------------------------- void CProgram::ResizeDataArrays(void) { int total =(int)m_series_total.ButtonText(); int size_of_series =(int)m_series_size.GetValue(); //--- for(int s=0; s<total; s++) { //--- Festlegen der neuen Größe der Datenreihe ::ArrayResize(m_series[s].data,size_of_series); ::ArrayResize(m_series[s].data_temp,size_of_series); } }
Nach dem Festlegen der Größe, sollte die Datenreihe für die Berechnungen mit neuen Daten initialisiert werden. Für diesen Vorgang verwenden wir die CProgram::InitArrays() Methode. Für die vorläufigen Berechnung werden die Parametern Offset series und Increment ratio verwendet. Für den "running" Serien-Modus, wird der m_run_speed_counter Zähler benötigt. Wenn durch den Timer die CProgram::ShiftLineChartSeries() Methode aufgerufen wird, wird der Wert um den Wert der Edit box der Run Speed erhöht, falls die Checkbox dieses Controls aktiviert ist.
class CProgram : public CWndEvents { private: //--- Geschwindigkeitszähler der "running" Serie double m_run_speed_counter; //--- Initialisierung der Hilfs-Datenreihen für die Berechnung void InitArrays(void); //--- Verschieben der Serien auf dem Linienchart ("running" chart) void ShiftLineChartSeries(void); }; //+---------------------------------------------------------------- //| Konstruktor | //+---------------------------------------------------------------- CProgram::CProgram(void) : m_run_speed_counter(0.0) { //--- ... } //+---------------------------------------------------------------- //| Verschieben der Serie auf dem Linienchart | //+---------------------------------------------------------------- void CProgram::ShiftLineChartSeries(void) { if(m_run_speed.CheckButtonState()) m_run_speed_counter+=m_run_speed.GetValue(); } //+---------------------------------------------------------------- //| Initialisierung der Hilfs-Datenreihen für die Berechnungen | //+---------------------------------------------------------------- void CProgram::InitArrays(void) { int total=(int)m_series_total.ButtonText(); //--- for(int s=0; s<total; s++) { int size_of_series=::ArraySize(m_series[s].data_temp); //--- for(int i=0; i<size_of_series; i++) { if(i==0) { if(s>0) m_series[s].data_temp[i]=m_series[s-1].data_temp[i]+m_offset_series.GetValue(); else m_series[s].data_temp[i]=m_run_speed_counter; } else m_series[s].data_temp[i]=m_series[s].data_temp[i-1]+(int)m_increment_ratio.GetValue(); } } }
Als Beispiel für die Berechnungen der Serien, verwenden wir drei Formeln, die über die externen Parameter des Expert Advisors ausgewählt werden können. Hierfür deklarieren wir die ENUM_FORMULA Enumeration. Das Festlegen der Parameter für die Farben der Serien wird über externe Parameter durchgeführt (Sehen Sie sich dazu. Das nachfolgende Listing an).
//--- Enumeration of functions enum ENUM_FORMULA { FORMULA_1=0, // Formula 1 FORMULA_2=1, // Formula 2 FORMULA_3=2 // Formula 3 }; //--- Externe Parameter input ENUM_FORMULA Formula =FORMULA_1; // Formula input color ColorSeries_01 =clrRed; // Color series 01 input color ColorSeries_02 =clrDodgerBlue; // Color series 02 input color ColorSeries_03 =clrWhite; // Color series 03 input color ColorSeries_04 =clrYellow; // Color series 04 input color ColorSeries_05 =clrMediumPurple; // Color series 05 input color ColorSeries_06 =clrMagenta; // Color series 06
Das Festlegen der anfänglichen Größe der Datenreihen der Serien, und auch die Initialisierung der Datenreihen der Beschreibungen und Farben der Serien werden in den Konstruktor der CProgram Klasse durchgeführt:
//+---------------------------------------------------------------- //| Konstruktor | //+---------------------------------------------------------------- CProgram::CProgram(void) : m_run_speed_counter(0.0) { //--- Festlegen der Größe der Datenreihen der Serien int number_of_series=24; ::ArrayResize(m_series,number_of_series); ::ArrayResize(m_series_name,number_of_series); ::ArrayResize(m_series_color,number_of_series); //--- Initialisierung der Datenreihe der Seriennamen for(int i=0; i<number_of_series; i++) m_series_name[i]="Series "+string(i+1); //--- Initialisierung der Datenreihen der Serien-Farben m_series_color[0] =m_series_color[6] =m_series_color[12] =m_series_color[18] =ColorSeries_01; m_series_color[1] =m_series_color[7] =m_series_color[13] =m_series_color[19] =ColorSeries_02; m_series_color[2] =m_series_color[8] =m_series_color[14] =m_series_color[20] =ColorSeries_03; m_series_color[3] =m_series_color[9] =m_series_color[15] =m_series_color[21] =ColorSeries_04; m_series_color[4] =m_series_color[10] =m_series_color[16] =m_series_color[22] =ColorSeries_05; m_series_color[5] =m_series_color[11] =m_series_color[17] =m_series_color[23] =ColorSeries_06; }
Für die Berechnung der Serien, basierend auf den Formeln, die in den externen Parametern ausgewählt werden, verwenden wir die CProgram::CalculateSeries() Methode:
class CProgram : public CWndEvents { private: //--- Berechnung der Serien void CalculateSeries(void); }; //+---------------------------------------------------------------- //| Berechnung der Serien | //+---------------------------------------------------------------- void CProgram::CalculateSeries(void) { int total=(int)m_series_total.ButtonText(); //--- for(int s=0; s<total; s++) { int size_of_series=::ArraySize(m_series[s].data_temp); //--- for(int i=0; i<size_of_series; i++) { m_series[s].data_temp[i]+=m_offset_series.GetValue(); //--- switch(Formula) { case FORMULA_1 : m_series[s].data[i]=::sin(m_series[s].data_temp[i])-::cos(m_series[s].data_temp[i]); break; case FORMULA_2 : m_series[s].data[i]=::sin(m_series[s].data_temp[i]-::cos(m_series[s].data_temp[i])); break; case FORMULA_3 : m_series[s].data[i]=::sin(m_series[s].data_temp[i]*10)-::cos(m_series[s].data_temp[i]); break; } } } }
Nachdem alle Werte für die Daten-Serien berechnet worden sind, können wir diese in die Datenreihen verschieben und (1) dem Chart unter Verwendung der CProgram::AddSeries() Methode hinzufügen, oder wenn sie zuvor schon hinzugefügt worden sind, (2) eine Aktualisierung mit der CProgram::UpdateSeries() Methode vornehmen.
class CProgram : public CWndEvents { private: //--- Hinzufügen der Serien zu dem Chart void AddSeries(void); //--- Aktualisierung der Datenreihen auf dem Chart void UpdateSeries(void); }; //+---------------------------------------------------------------- //| Berechnen und setzen der Serien auf dem Chart | //+---------------------------------------------------------------- void CProgram::AddSeries(void) { int total=(int)m_series_total.ButtonText(); for(int s=0; s<total; s++) m_line_chart.SeriesAdd(m_series[s].data,m_series_name[s],m_series_color[s]); } //+---------------------------------------------------------------- //| Berechnen und Aktualisieren der Serien auf dem Chart | //+---------------------------------------------------------------- void CProgram::UpdateSeries(void) { int total=(int)m_series_total.ButtonText(); for(int s=0; s<total; s++) m_line_chart.SeriesUpdate(s,m_series[s].data,m_series_name[s],m_series_color[s]); }
Nach der Erzeugung des Linien-Charts, (1) wird die Größe der Datenreihen gesetzt und(2) die Initialisierung der Hilfsdatenreihen wird durchgeführt. Anschließend werden die Serien (3) berechnet und(4) dem Chart hinzugefügt.
//+---------------------------------------------------------------- //| Erzeugung des Linien-Charts | //+---------------------------------------------------------------- bool CProgram::CreateLineChart(void) { //--- Abspeichern des Fenster-Pointers m_line_chart.WindowPointer(m_window1); //--- Koordinaten int x=m_window1.X()+LINECHART1_GAP_X; int y=m_window1.Y()+LINECHART1_GAP_Y; //--- Festlegen der Eigenschaften vor der Erzeugung m_line_chart.XSize(630); m_line_chart.YSize(280); m_line_chart.BorderColor(clrSilver); m_line_chart.VScaleParams(2,-2,4); m_line_chart.MaxData(int(m_series_total.ButtonText())); //--- Erzeugung des Controls if(!m_line_chart.CreateLineGraph(m_chart_id,m_subwin,x,y)) return(false); //--- (1) Festlegen der Größe der Datenreihen und (2) deren Initialisierung ResizeDataArrays(); InitArrays(); //--- (1) Berechnung und (2) hinzufügen zum Chart CalculateSeries(); AddSeries(); //--- Den Pointer zum Control in der Basis hinzufügen CWndContainer::AddToElementsArray(0,m_line_chart); return(true); }
Die gleiche Abfolge von Aktionen muss häufig durchgeführt werden, aber nur bei der Aktualisierung der Serie. Hierfür schreiben wir die Hilfsmethode CProgram::RecalculatingSeries(), die den Programmcode vereinfacht, indem nur eine Methode anstelle von vieren aufgerufen werden muss:
class CProgram : public CWndEvents { private: //--- Neuberechnung der Serien auf dem Chart void RecalculatingSeries(void); }; //+---------------------------------------------------------------- //| Neuberechnung der Serien auf den Chart | //+---------------------------------------------------------------- void CProgram::RecalculatingSeries(void) { //--- (1) Festlegen der Größe der Datenreihen und (2) deren Initialisierung ResizeDataArrays(); InitArrays(); //--- (1) Berechnen und(2) aktualisieren der Serien CalculateSeries(); UpdateSeries(); }
Zu dem augenblicklichen Entwicklungszeitpunkt erhalten wir das folgende Ergebnis, sobald wir die Anwendung auf einen Chart laden:
Abbildung 4. Test des Linienchart-Controls
Falls der Checkbox-ParameterMax. limit size aktiviert ist, dann wird die automatische Neuberechnung der Größe der Datenreihen der Serien in der CProgram::AutoResizeLineChartSeries() Methode gestartet. Dieser Algorithmus wurde schon zuvor in diesem Artikel beschrieben. Daher reichen hier detaillierte Kommentare für das Studieren des Programmcodes aus. Sehen Sie sich jetzt das nachfolgende Listing an:
class CProgram : public CWndEvents { private: //--- Automatisch Veränderung der größe der Serien der Linien-Charts void AutoResizeLineChartSeries(void); }; //+---------------------------------------------------------------- //| Automatische Veränderung der Größe der Serien der Linien-Charts | //+---------------------------------------------------------------- void CProgram::AutoResizeLineChartSeries(void) { //--- Abbrechen, falls die automatische Vergrößerung über den Timer deaktiviert ist if(!m_max_limit_size.CheckButtonState()) return; //--- Für den Hinweis auf die Richtung der Veränderung der Größe der Datenreihe static bool resize_direction=false; //--- Falls die minimale Größe der Datenreihe erreicht wird if((int)m_series_size.GetValue()<=m_min_limit_size.GetValue()) { //--- Ändere die Richtung zu einer Erhöhung der Datenreihen resize_direction=false; //--- Falls der X-Wert geändert werden muss if(m_increment_ratio.CheckButtonState()) { //--- Für die Anzeige der Erhöhung des Verhältnisses static bool increment_ratio_direction=true; //--- Falls der Zähler erhöht werden soll if(increment_ratio_direction) { //--- Falls eine maximale Begrenzung erreicht wurde, dann verändern wir die Richtung des Zählers if(m_increment_ratio.GetValue()>=m_increment_ratio.MaxValue()-1) increment_ratio_direction=false; } //--- Falls eine Verringerung des Zählers erwünscht ist else { //--- Wenn wir die minimale Begrenzung erreicht haben, dann wechseln wir die Richtung des Zählers if(m_increment_ratio.GetValue()<=m_increment_ratio.MinValue()+1) increment_ratio_direction=true; } //--- Abfrage des aktuellen Wertes des "Increment ratio" Parameters und verändern seines Wertes in die angegebene Richtung int increase_value=(int)m_increment_ratio.GetValue(); m_increment_ratio.ChangeValue((increment_ratio_direction)? ++increase_value : --increase_value); } } //--- Wechseln der Richtung in Richtung der Verringerung der Datenreihe, wenn das Maximum erreicht wurde if((int)m_series_size.GetValue()>=m_max_limit_size.GetValue()) resize_direction=true; //--- Zeige den Prozess an, falls die Fortschrittsanzeige aktiviert ist if(m_progress_bar.IsVisible()) { if(!resize_direction) m_progress_bar.Update((int)m_series_size.GetValue(),(int)m_max_limit_size.GetValue()); else m_progress_bar.Update(int(m_max_limit_size.GetValue()-m_series_size.GetValue()),(int)m_max_limit_size.GetValue()); } //--- Verändere die Größe der Datenreihe entsprechend der Richtung int size_of_series=(int)m_series_size.GetValue(); m_series_size.ChangeValue((!resize_direction)? ++size_of_series : --size_of_series); //--- Festlegen der neuen Größe der Datenreihe ResizeDataArrays(); }
Die Animation des Charts, wie zuvor schon erwähnt, wird durch den Timer durchgeführt. Alle dazu notwendigen Aktionen werden in der CProgram::UpdateLineChartByTimer() Methode durchgeführt. Das Programm bricht die Methode ab, wenn (1) das Formular minimiert wird, oder (2) der Modus für die Aktualisierungen für über den timer deaktiviert wird. Ein kleines Hindernis wird noch durch das delay in der Editbox verursacht. Wenn alle Überprüfungen vollständig sind, dann werden die notwendige Berechnungen für die aktivierten Modi durchgeführt und die Serien auf dem Linienchart werden aktualisiert.
class CProgram : public CWndEvents { private: //--- Aktualisieren des Liniencharts über den Timer void UpdateLineChartByTimer(void); }; //+---------------------------------------------------------------- //| Aktualisierung durch den Timer | //+---------------------------------------------------------------- void CProgram::UpdateLineChartByTimer(void) { //--- Abbrechen, falls das Formular minimiert ist oder sich in den Prozess einer Verschiebung befindet if(m_window1.IsMinimized()) return; //--- Abbrechen, falls Sie Animation deaktiviert ist if(!m_max_limit_size.CheckButtonState() && !m_run_speed.CheckButtonState()) return; //--- Delay static int count=0; if(count<m_delay_ms.GetValue()) { count+=TIMER_STEP_MSC; return; } count=0; //--- Falls die "Running series" Option aktiviert ist, dann verschieben wir den ersten Wert der Serie ShiftLineChartSeries(); //--- Falls das Verwalten der Größe der Datenreihen der Serien über den Timer aktiviert ist AutoResizeLineChartSeries(); //--- Initialisiere die Datenreihen InitArrays(); //--- (1) Berechnen und(2) aktualisieren der Serien CalculateSeries(); UpdateSeries(); }
Jetzt schauen wir uns noch schnell den CProgram::OnEvent() Eventhandler der entwickelten Anwendung an. Um schnell visuelle Veränderungen des Linien-Charts durchführen zu können, werden die nachfolgend aufgeführten Controls verwendet.
- Der Anzahl der Serien Parameter (Combobox). Jedes Mal, wenn hier ein neuer Wert in das Control eingegeben wird, wird die Anzahl der Serien auf dem Linienchart geändert. Der Programmcode für die Verwaltung dieses Events wird in dem nachfolgendem Listing gezeigt
... //--- Event für das Selektieren eines Elementes in der Combobox if(id==CHARTEVENT_CUSTOM+ON_CLICK_COMBOBOX_ITEM) { //--- Abfrage der neuen Gesamtanzahl der Serien m_line_chart.MaxData((int)m_series_total.ButtonText()); //--- (1) Festlegen der Größe der Datenreihen und (2) deren Initialisierung ResizeDataArrays(); InitArrays(); //--- (1) Berechnen, (2) dem Chart hinzufügen (3) aktualisierung der Serien CalculateSeries(); AddSeries(); UpdateSeries(); return; } ...
Standardmäßig werden sechs Serien gezeigt. ComboBox-Wert:(6). Ändern Sie ihn auf 3. Das Ergebnis zeigt der nachfolgende Screenshot:
Abbildung 5. Test einer Veränderung der Gesamtanzahl der Serien auf dem Linienchart.
- Max parameters limit size (Checkbox mit Editbox) und Size of series (Editbox). Der Event mit dem ON_CLICK_LABEL Bezeichner wird generiert, wenn auf einer dieser Controls geklickt wird. Wenn auf das Size of series Control geklickt wird, dann wird der Wert in der Editbox auf den minimalen Wert zurückgesetzt. Wenn auf den Max control limit size geklickt wird, dann wird der Status der Checkbox invertiert. Von dem Status der Checkbox hängt ab, ob die Fortschrittsanzeige, die wir bei der automatischen Größenänderung der Serien verwenden, angezeigt wird oder nicht.
... //--- Click Event auf das Text-Label des Controls if(id==CHARTEVENT_CUSTOM+ON_CLICK_LABEL) { //--- Falls die Nachricht von dem 'Size of series' Control stammt if(sparam==m_series_size.LabelText()) { //--- Neuberechnung der Serien auf dem Chart RecalculatingSeries(); return; } //--- Falls die Nachricht von dem 'Max. Limit Size' Control stammt if(sparam==m_max_limit_size.LabelText()) { //--- Anzeigen oder verstecken der Fortschrittsanzeige in Abhängigkeit des Status der Checkbox 'Max. limit size' if(m_max_limit_size.CheckButtonState()) m_progress_bar.Show(); else m_progress_bar.Hide(); //--- return; } } ...
Der nachfolgende Screenshot zeigt ein Beispiel, wie das Ganze aussieht:
Abbildung 6. Test des Linien-Charts in dem Modus der automatischen Größenänderung der Serien.
- Wenn Werte in der EditBoxen Increment ratio, Offset series und Size of series eingegeben werden, dann wird die CProgram::RecalculatingSeries() Methode für die Neuberechnung der Serien aufgerufen:
... //--- Event für die Eingabe eines neuen Wertes in eine EditBox if(id==CHARTEVENT_CUSTOM+ON_END_EDIT) { //--- Falls es sich um eine Nachricht von dem 'Increment ratio' oder dem 'Offset series' oder dem 'Size of series' Control handelt if(sparam==m_increment_ratio.LabelText() || sparam==m_offset_series.LabelText() || sparam==m_series_size.LabelText()) { //--- Neuberechnung der Serien auf dem Chart RecalculatingSeries(); return; } return; } //--- Event eines Klicks auf einen der Schalter-Buttons der EditBox if(id==CHARTEVENT_CUSTOM+ON_CLICK_INC || id==CHARTEVENT_CUSTOM+ON_CLICK_DEC) { //--- Falls es sich um eine Nachricht von dem 'Increment ratio' oder dem 'Offset series' oder dem 'Size of series' Control handelt if(sparam==m_increment_ratio.LabelText() || sparam==m_offset_series.LabelText() || sparam==m_series_size.LabelText()) { //--- Neuberechnung der Serien auf dem Chart RecalculatingSeries(); return; } return; } ...
Der nachfolgende Screenshot zeigt ein weiteres Beispiel. Versuchen Sie die selben Parameter in ihrer Kopie anzuwenden und schauen Sie sich an, wie es in dem animierten Modus aussieht.
Abbildung 7. Test des "running series" Modus.
Nachdem wir nun einige Veränderungen in den Kopien der Klassen der Standardbibliothek durchgeführt haben, können wir mehrere Datenreihen innerhalb des Bereiches eines Charts korrekt darstellen. Der nachfolgende Screenshot zeigt ein Beispiel, bei dem eine Serie 1000 Elemente besitzt (Dieses können Sie an dem Size of series Parameter erkennen).
Fig. 8. Test einer Serie mit einer großen Datenmenge.
Die Anwendung ist für die Tests nun fertig Sie können die Datei dieses Expert Advisors am Ende des Artikels herunterladen und testen.
Schlussfolgerung
Dieser Artikel präsentierte die Klassen für die Erzeugung einer Fortschrittsanzeige und des Linienchart-Controls.
Zur Zeit sieht das schematische Diagramm unserer Bibliothek für das Erzeugen von grafischen Interfaces wie folgt aus: Einige Fragmente dieses Schemas stellen nur eine vorläufige Lösung dar, denn es werden noch einige Änderungen während der weiteren Entwicklung dieser Bibliothek vorgenommen.
Abbildung 9 Die Struktur unserer Bibliothek zum aktuellen Stand der Entwicklung
Die mit schließen wir den Hauptteil der Serie der Artikel über die Entwicklung der Easy And Fast Bibliothek für das Erzeugen von grafischen interfaces in dem MetaTrader. Die folgenden Artikel beschäftigen sich mit der Anwendung dieser Bibliothek. Es wird eine Anzahl von Beispielen geben, sowie Ergänzungen und Aktualisierungen. Wenn Sie möchten, können Sie dem Prozess der Entwicklung dieses Projektes beitreten. Wenn Sie mit diesem Ergebnis schon glücklich sind, dann testen Sie die Bibliothek in Ihren eigenen Projekten, berichten sie über eventuelle Fehler und stellen Sie Fragen.
Nachfolgend finden Sie das gesamte Material des neuen Teils. Sie können es herunterladen und testen. Wenn Sie fragen zur Verwendung dieses Materials haben, dann können Sie zunächst auf die detaillierte Beschreibung in dem Artikel zu dieser Bibliothek zurückgreifen oder Sie stellen Ihre Frage(n) in den Kommentaren zu diesem Artikel.
Liste der Artikel (Kapitel) des neunten Teils:
- Grafische Interfaces IX: Das Farbauswahl Control (Kapitel 1)
- Grafische Interfaces IX: Die Fortschrittsanzeige und das Linienchart-Control(Kapitel 2)
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/2580
- 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.