English Русский 中文 Español 日本語 Português
Grafische Interfaces IX: Die Fortschrittsanzeige und das Linienchart-Control (Kapitel 2)

Grafische Interfaces IX: Die Fortschrittsanzeige und das Linienchart-Control (Kapitel 2)

MetaTrader 5Beispiele | 15 September 2016, 12:31
785 0
Anatoli Kazharski
Anatoli Kazharski


Inhalt

 

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:

  1. Hintergrund
  2. Beschreibung:
  3. Indikator Scrollbar
  4. Indikator Hintergrund
  5. 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

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.

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

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.

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.

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.

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.

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

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:


Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/2580

Beigefügte Dateien |
Grafische Interfaces X: Updates für die Easy And Fast Bibliothek (Build 2) Grafische Interfaces X: Updates für die Easy And Fast Bibliothek (Build 2)
Seit der Veröffentlichung des vorangegangenen Artikels dieser Serie, hat die Easy And Fast Bibliothek einige neue Features bekommen. Die Bibliotheksstruktur und der Programmcode wurden teilweise optimiert, was die CPU-Auslastung leicht reduziert hat. Einige wiederkehrende Methoden in vielen Control-Klassen wurden in die CElement Basisklasse bewegt.
Grafische Interfaces IX: Das Farbauswahl Control (Kapitel 1) Grafische Interfaces IX: Das Farbauswahl Control (Kapitel 1)
Mit diesem Artikel starten wir das Kapitel 9 der Serie der Artikel über die entwicklung von grafischen Interfaces in den Metatrader Trading-Terminals. Diese Serie besteht aus zwei Kapiteln, in welcher neue Elemente und Interfaces vorgestellt werden, wie zum Beispiel das Farbauswahl-Control, farbige Buttons, die Fortschrittsanzeige und Linien Charts.
Rezepte MQL5 - Handelssignale der gleitenden Kanäle Rezepte MQL5 - Handelssignale der gleitenden Kanäle
Der Artikel beschreibt den Prozess der Entwicklung und Implementierung einer Klasse, die Signale auf der Basis gleitender Kanäle entwickelt. Auf der Basis dieser Signale, werden wir eine Handelsstrategie erstellen. Es werden die Klassen der Standardbibliothek zur Erstellung der abgeleiteten Unterklassen verwendet.
Grafische Interfaces VIII: Das Datei-Navigator Control (Kapitel 3) Grafische Interfaces VIII: Das Datei-Navigator Control (Kapitel 3)
In den vorherigen Kapiteln des 8 Teils dieser Serie, haben wir unsere Bibliothek um mehrere Klassen für die Entwicklung von Mauszeigern, Kalendern und Baum-Ansichten erweitert. In dem aktuellen Artikel beschäftigen wir uns mit dem Datei-Navigator-Control, welcher auch als Teil eines grafischen Interfaces einer MQL Anwendung verwendet werden kann.