English Русский 中文 Español 日本語 Português
preview
Verwendung der Klasse CCanvas in MQL-Anwendungen

Verwendung der Klasse CCanvas in MQL-Anwendungen

MetaTrader 5Beispiele | 19 April 2022, 17:05
232 1
Mihail Matkovskij
Mihail Matkovskij

Der Artikel befasst sich mit der CCanvas-Klasse und ihrer Verwendung in MQL-Anwendungen. Die Theorie wird von Beispielen begleitet.


Grafiken in Anwendungen

Der Umgang mit Grafiken in Anwendungen erfordert den Zugriff auf Pixeldaten auf einem Bildschirm. Wenn Sie einen solchen Zugriff haben, können Sie Pixelfarben ändern, verschiedene UI-Elemente, Eingabefelder, Schaltflächen, Panels, Fenster und andere Elemente erstellen oder Bilder aus Dateien anzeigen sowie Pixeldaten abrufen und verarbeiten. Im Allgemeinen ist dieser Mechanismus in der Klasse mit dem Wort "Canvas" (Leinwand, der Hintergrund) in ihrem Namen untergebracht. In C-basierten Programmiersprachen ist dies in der Regel CCanvas oder Canvas, je nach dem Stil, in dem der Code geschrieben wird. Jede Anwendung benötigt so eine Canvas, um auf Grafiken zugreifen zu können.


Canvas in MQL-Anwendungen

In MQL wird das Werkzeug in Form der Klasse CCanvas zur Verfügung gestellt, die das Array der Pixel, die Methoden zum Ändern des Pixelarrays und den Mechanismus zum Senden des Arrays an ein Terminal-Chart enthält. Das grafische Objekt OBJ_BITMAP oder OBJ_BITMAP_LABEL wird als Anzeigemittel verwendet. Das verwendete grafische Objekt erhält Daten von der grafischen Ressource, die wiederum Daten vom Pixelarray empfängt.

Die Klasse CCanvas ermöglicht die Implementierung aller Desktop-UI-Funktionen. Zusätzlich zu den Nutzeroberflächen ermöglicht CCanvas das Zeichnen von Indikatorpuffern, OHLC-Balken und vieles mehr. Die Möglichkeiten sind endlos... Ich werde dieses Thema in meinen anderen Artikeln behandeln. Nun ist es an der Zeit, sich wieder der Klasse CCanvas zuzuwenden.


Eintauchen in CCanvas

Jetzt ist es Zeit für den interessantesten Teil - das Eintauchen in die CCanvas-Klasse. Wenn Sie ihre Struktur verstehen, können Sie dieses Werkzeug beherrschen. Die Struktur von CCanvas ist im Folgenden dargestellt.

Eintauchen in CCanvas

Wie wir sehen können, ist die Klasse in zwei große Abschnitte unterteilt - Daten und Methoden.

Betrachten wir die Daten:

  • Pixeldaten. Das Pixel-Array m_pixels speichert Pixel, die von den Zeichen-Methoden behandelt werden, sowie die Werte von m_width- und m_height, die Bildbreite bzw. -höhe. Die Parameter m_width und m_height sind sehr wichtig für die Handhabung des Arrays m_pixels durch die Zeichenmethoden, sowie für die Übergabe des Array m_pixels an die grafische Ressource in der Update-Methode.
  • Schriftdaten für die Darstellung des Textes. Besteht aus den Feldern m_fontname, m_fontsize, m_fontflags und m_fontangle, Schriftartname, Größe, Attribute bzw. Textneigungswinkel. Die Daten werden benötigt, um die Schrifteigenschaften zu setzen und zu empfangen (mit den Methoden zum Lesen/Schreiben der Schrifteigenschaften), bevor die Methode TextOut aufgerufen wird.
  • Zeilenzeichnungsdaten. Sie bestehen aus zwei Feldern: m_style, Linienstil, und m_style_idx, der aktuelle Bitindex in der Linienstilvorlage. Sie werden für die Zeichenmethoden benötigt.
  • Standarddaten. Hier haben wir ein einzelnes Feld m_default_colors, ein Standard-Farbfeld. Es wird nicht in den Methoden der CCanvas-Klasse selbst verwendet, aber es kann für andere Funktionen oder Methoden von CCanvas-Nachfolgeklassen als Farbpalette nützlich sein.
  • Daten zur Chart-Interaktion. Sie enthalten das Folgende: m_chart_id, m_objname, m_objtype, m_rcname und m_format, Chartobjekt-ID, Chartobjektname, Chartobjekttyp, Name der grafischen Ressource bzw. Pixelformat. Die Daten sind für die folgenden Methoden bestimmt: Destroy, Update, Resize. Insbesondere das Feld m_format wird für die Operation der Methode TextOut benötigt.


Betrachten wir nun die Methoden:

  • Erstellungs-/Anbringungs-/Entfernungsmethoden. Diese Kategorie enthält verschiedene Methoden, die mit einem Chartobjekt arbeiten. Dies sind die Methoden zur Erstellung eines grafischen Objekts: CreateBitmap und CreateBitmapLabel. Sie sind für die Erstellung eines Chartobjekts und einer damit verbundenen grafischen Ressource gedacht, die wiederum ein Bild im Chart anzeigt. Attachment-Methoden: Attach. Wenn das Chart das grafische Objekt OBJ_BITMAP_LABEL mit einer angehängten grafischen Ressource oder ohne diese enthält, behandelt CCanvas es auf die gleiche Weise wie ein neu erstelltes grafisches Objekt. Fügen Sie es einfach mit der entsprechenden Attach-Methode hinzu. Die Methode zum Entfernen heißt Destroy. Sie entfernt ein grafisches Objekt, gibt den Pixel-Array-Puffer m_pixels frei und entfernt die grafische Ressource, die mit dem Chartobjekt verbunden ist. Daher sollten wir immer die Methode Destroy aufrufen, wenn CCanvas beendet wird, da die Klasse ihr Chartobjekt und die grafische Ressource nicht automatisch entfernt.
  • Methoden zum Hochladen von Bildern aus einer Datei. Die statische Methode LoadBitmap ist in der Lage, ein Bild aus einer *.bmp-Datei in ein beliebiges uint-Array hochzuladen, das ihr an einer Adresse als Parameter übergeben wird, wobei die Größe des erhaltenen Bildes in den Variablen 'width' und 'height' gespeichert wird, die der Methode ebenfalls an entsprechenden Adressen als ihre Parameter übergeben werden. Die Methode LoadFromFile lädt ein Bild aus einer *.bmp-Datei in das Array m_pixels und setzt die Bildparameter m_width und m_height. Das Pixelformat m_format sollte gleich COLOR_FORMAT_ARGB_RAW sein.
  • Methoden zum Lesen der Eigenschaften von Chartobjekten bestehen aus ChartObjectName, ResourceName, Width und Height und geben den Chartobjektnamen, den grafischen Ressourcennamen, die Bildbreite und -höhe entsprechend zurück. Diese Methoden erlauben es den Nutzern, nur einige Daten für die Interaktion mit dem Chart zu lesen, einschließlich m_objname, m_rcname, sowie m_width und m_height Bilddaten.
  • Methoden zum Lesen/Schreiben von Schrifteigenschaften eines angezeigten Textes. Betrachten wir zunächst die Schreibmethoden FontNameSet, FontSizeSet, FontFlagsSet und FontAngleSet. Diese Methoden setzen den Schriftnamen, die Größe, die Attribute und den Neigungswinkel des angezeigten Textes. Betrachten wir nun die Lesemethoden: FontSizeGet, FontFlagsGet und FontAngleGet. Die Methoden geben die Schriftgröße und -attribute bzw. den Neigungswinkel des angezeigten Textes zurück. Außerdem gibt es die Methoden zum Empfangen/Setzen der Schrifteigenschaften, die alle Schrifteigenschaften auf einmal zurückgeben/setzen. Die Methode zum Setzen der Eigenschaften FontSet setzt jeweils den Schriftnamen, die Größe, die Attribute und den Neigungswinkel des angezeigten Textes. Die Methode zum Empfangen der Eigenschaften FontGet gibt den Schriftnamen, die Größe, die Attribute bzw. den Neigungswinkel des angezeigten Textes zurück. 
  • Methoden zum Lesen/Schreiben des Linienzeichnungsstils. Die Methode LineStyleGet wird zum Lesen und LineStyleSet zum Schreiben verwendet. Der Linienstil ist notwendig für die Handhabung der Zeichenmethoden von LineAA, PolylineAA, PolygonAA, TriangleAA, CircleAA, EllipseAA, LineWu, PolylineWu, PolygonWu, TriangleWu, CircleWu, EllipseWu, LineThickVertical, LineThickHorizontal, LineThick, PolylineThick und PolygonThick grafischen Primitiven.
  • Methoden zum Zeichnen im Pixel-Array. Die Klasse CCanvas verfügt über zahlreiche Methoden zum Zeichnen grafischer Primitive unter Verwendung verschiedener Algorithmen, die es dem Nutzer ermöglichen, komplexe Grafiken unter Anwendung progressiver Glättungsmethoden zu erstellen, einschließlich Antialiasing, Wu-Algorithmus und Bézier-Kurven. Lassen Sie uns diese Methoden betrachten. Einfache Primitive ohne Glättung: LineVertical, LineHorizontal, Line, PolylinePolygon, Rectangle, Triangle, Circle, Ellipse, Arc und Pie. Die Methoden zeichnen die folgenden Primitive: vertikale Linie, horizontale Linie, Freihandlinie, Polylinie, Polygon, Rechteck, Dreieck, Kreis, Ellipse, Bogen bzw. gefüllter Ellipsensektor. Gefüllte Primitive: FillRectangle, FillTriangle, FillPolygon, FillCircle, FillEllipse und Fill. Diese Methoden zeichnen Rechteck, Dreieck, Polygon, Kreis, Ellipse bzw. füllen den Bereich aus. Die Methoden zum Zeichnen von Primitiven mit Glättung durch Antialiasing (AA): PixelSetAA, LineAA, PolylineAA, PolygonAA, TriangleAA, CircleAA und EllipseAA. Die Methoden füllen Pixel aus und stellen solche Primitive wie eine Freihandlinie, Polylinie, Polygon, Dreieck, Kreis und Ellipse dar. Methoden zum Zeichnen von Primitiven unter Verwendung des Wu's Algorithmus: LineWu, PolylineWu, PolygonWu, TriangleWu, CircleWu und EllipseWu. Die Methoden zeichnen eine Freihandlinie, eine Polylinie, ein Polygon, ein Dreieck, einen Kreis bzw. eine Ellipse. Die Methoden zum Zeichnen von Primitiven mit vorsortiertem Antialiasing und einstellbarer Linienbreite: LineThickVertical, LineThickHorizontal, LineThick, PolylineThick and PolygonThick. Sie sind für das Zeichnen der folgenden Primitive gedacht: vertikale Linie, horizontale Linie, Freihandlinie, Polylinie bzw. Polygon. Die Methoden zum Zeichnen von geglätteten Primitiven verwenden Bézier Methoden: PolylineSmooth und PolygonSmooth. Die Methoden zeichnen solche Primitive als geglättete Linie bzw. geglättetes Polygon. Neben den oben beschriebenen Methoden gehört auch die Methode zur Anzeige eines Textes TextOut in diese Kategorie, da sie auch die Farbwerte im Pixelarray ändert, obwohl sie im ursprünglichen Code der Klasse CCanvas in die Gruppe der Methoden zur Textbehandlung fällt.
  • Methoden zur Übergabe des Bildes für die Anzeige im Chart. Diese Kategorie umfasst zwei Methoden. Die Methode Update übergibt das Pixel-Array m_pixels an die grafische Ressource, die mit einem Chart-Objekt verbunden ist, das ein Bild anzeigt. Wie bereits erwähnt, ändert sich das Pixelarray m_pixels mit Hilfe der oben erwähnten Zeichenmethoden im Pixelarray. Die Methode Resize ändert die Arraygröße m_pixels (Bildgröße) und übergibt sie ebenfalls an die grafische Ressource.
  • Dienste. CCanvas verfügt über zwei Dienstmethoden: GetDefaultColor gibt neu definierte Farben zurück, während TransparentLevelSet die Bildtransparenz ändert, indem es die Alphakanalwerte im Array m_pixels ändert.
  • Andere Einstellungen. Hier gibt es eine einzige Methode, FilterFunction, zum Setzen des Antialiasing-Filters, die den Filter für alle Zeichnungsmethoden setzt, die AA-Symbole in ihrem Namen haben.

Die Klasse CCanvas hat Felder und Methoden im Bereich der "privaten" Sichtbarkeit. Ich werde sie in diesem Artikel nicht berücksichtigen, da sie intern sind und nicht in umdefinierten Methoden von CCanvas-Nachfolgeklassen verwendet werden können. Sie können sie im Quellcode des Moduls Canvas.mqh im MetaEditor finden.


Abfolge der Aktionen bei der Verwendung von CCanvas in MQL-Anwendungen

In Anbetracht der obigen Ausführungen ist es nun möglich, eine gemeinsame Abfolge von Aktionen für die Handhabung der CCanvas-Klasse bei ihrer Verwendung in MQL-Anwendungen aufzuzeigen. Betrachten wir die Aktionen, die durchgeführt werden müssen, um ein Bild in einem Chart erscheinen zu lassen.

  • Erstellen oder Anhängen eines Chart-Objekts (OBJ_BITMAP oder OBJ_BITMAP_LABEL) oder Anhängen an das bereits vorhandene OBJ_BITMAP_LABEL
  • Schriftparameter und primitive Zeichenstile einstellen
  • Zeichnen im Array m_pixels unter Verwendung der entsprechenden Methoden
  • Aktualisierung der Ressource des grafischen Objekts (OBJ_BITMAP oder OBJ_BITMAP_LABEL)

Als Ergebnis wird das Chart ein Objekt mit grafischen Konstruktionen oder einem Text haben. Betrachten wir nun detailliert die Aktion

Erstellen eines Chart-Objekts und Anhängen an ein bereits vorhandenes Chart-Objekt

Damit eine MQL-Anwendung Grafiken in einem Chart anzeigen kann, müssen wir ein Objekt erstellen, das auf der Klasse CCanvas oder deren abgeleiteten Klassen basiert. Nach dem Erstellen eines CCanvas-Objekts können wir mit der Entwicklung des OBJ_BITMAP- oder OBJ_BITMAP_LABEL-Chart-Objekts beginnen. Alternativ können wir auch das bereits vorhandene OBJ_BITMAP_LABEL an das erstellte CCanvas-Objekt anhängen.

Um ein grafisches Objekt zu erstellen, hat CCanvas die Methoden CreateBitmap und CreateBitmapLabel. Jede von ihnen verfügt über eine eigene Überladungsoption für eine bequemere Verwendung.

bool  CreateBitmap(const long chart_id, const int subwin, const string name, const datetime time, const double price, const int width, const int height, ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA);

bool  CreateBitmap(const string name, const datetime time, const double price, const int width, const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA);

bool  CreateBitmapLabel(const long chart_id, const int subwin, const string name, const int x, const int y, const int width, const int height, ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA);

bool  CreateBitmapLabel(const string name,const int x,const int y, const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA);


Die Methode CreateBitmap erstellt eine Bitmap (Objekttyp OBJ_BITMAP), deren Koordinaten als Zeit und Preis auf einem Chart mit Handelssymbolen festgelegt werden, und enthält die folgenden Parameter:

  • chart_id - Chart ID (0 = aktuell)
  • window - Index des Chart-Unterfensters (0 - Hauptfenster)
  • name - Name eines erstellten grafischen Objekts in einem Chart
  • time - Zeitkoordinate eines grafischen Objekts in einem Chart
  • price - Preiskoordinate eines grafischen Objekts in einem Chart
  • width - Breite des grafischen Objekts in einem Chart
  • height - Höhe des grafischen Objekts in einem Chart

Eine weitere Option der Methode CreateBitmap ist eine überladene Methode, die CreateBitmap mit chart_id und window gleich 0 aufruft (was dem aktuellen Chart und Hauptfenster entspricht).

//+------------------------------------------------------------------+
//| Create object on chart with attached dynamic resource            |
//+------------------------------------------------------------------+
bool CCanvas::CreateBitmap(const string name,const datetime time,const double price,
                           const int width,const int height,ENUM_COLOR_FORMAT clrfmt)
  {
   return(CreateBitmap(0,0,name,time,price,width,height,clrfmt));
  }

Die Methode CreateBitmapLabel hat die gleichen Parameter wie CreateBitmap, mit Ausnahme von "time" und "price". Stattdessen sind x und y zu sehen.

Eingaben:

  • chart_id - Chart ID (0 = aktuell)
  • window - Index des Unterfensters des Charts (0 = Hauptfenster)
  • name - Name eines erstellten grafischen Objekts in einem Chart
  • x - X-Koordinate eines grafischen Objekts in einem Chart
  • y - Y-Koordinate eines grafischen Objekts in einem Chart
  • width - Breite des erstellten grafischen Objekts
  • height - Höhe eines erstellten grafischen Objekts
  • clrfmt - Pixel-Farbformat eines erstellten grafischen Objekts

Eine weitere Option der Methode CreateBitmapLabel ist eine überladene Methode, die CreateBitmapLabel mit chart_id und window gleich 0 aufruft (wie CreateBitmap).

//+------------------------------------------------------------------+
//| Create object on chart with attached dynamic resource            |
//+------------------------------------------------------------------+
bool CCanvas::CreateBitmapLabel(const string name,const int x,const int y,
                                const int width,const int height,ENUM_COLOR_FORMAT clrfmt)
  {
   return(CreateBitmapLabel(0,0,name,x,y,width,height,clrfmt));
  }

Wenn das Chart über das Objekt OBJ_BITMAP_LABEL verfügt, kann es mit der Methode Attach an CCanvas angehängt werden. Infolgedessen interagiert das Chart-Objekt mit dem CCanvas-Objekt auf die gleiche Weise wie mit dem Chart-Objekt, das mit der Methode CreateBitmap oder CreateBitmapLabel erstellt wurde.

virtual bool  Attach(const long chart_id,const string objname,const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA);

virtual bool  Attach(const long chart_id,const string objname,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA);


Die Methode fügt CCanvas an das bereits vorhandene Chart-Objekt OBJ_BITMAP_LABEL an.

Eingaben:

  • chart_id - Chart ID (0 = aktuell)
  • objname - Name eines angehängten grafischen Objekts
  • width - Breite eines angehängten grafischen Objekts
  • height - Höhe eines angehängten grafischen Objekts
  • clrfmt - Pixel-Farbformat eines angehängten grafischen Objekts

Die erste Option der Methode Attach impliziert, dass ein Chart-Objekt keine grafische Ressource hat und es selbst erstellt, indem es die Parameter objname, width, height und clrfmt verwendet und alle Daten einträgt, die für die weitere Behandlung des angehängten grafischen Objekts erforderlich sind.

Die zweite Option setzt das Vorhandensein einer grafischen Ressource voraus und liest einfach Bildpixeldaten in das Array m_pixels und füllt auch alle Daten, die für die weitere Behandlung des angehängten grafischen Objekts notwendig sind.

Methoden zum Setzen und Empfangen von Schriftparametern, sowie Methoden zum Setzen und Empfangen eines Linienzeichnungsstils

Nachdem das grafische Objekt mit der CreateBitmap- oder CreateBitmapLabel-Methode erstellt oder mit der Attach-Methode angehängt wurde, sollten wir die Schriftparameter und den primitiven Zeichenstil einstellen, um das erforderliche Bild zu erhalten. Die Schriftparameter werden mit den folgenden Methoden eingestellt.

Einfache Methoden zur Einstellung der Schrifteigenschaften:

bool FontNameSet(string name);  // Set the font name

bool FontSizeSet(int size);     // Set the font size

bool FontFlagsSet(uint flags);  // Set the font attributes

bool FontAngleSet(uint angle);  // Set the font slope angle


Einfache Methoden zum Lesen der Schrifteigenschaften:

string FontNameGet(void) const;   // Return the font name

int    FontSizeGet(void) const;   // Return the font size

uint   FontFlagsGet(void) const;  // Return the font attributes

uint   FontAngleGet(void) const;  // Return the font slope angle


Einfache Methoden zum Setzen der Schrifteigenschaften:

bool FontSet(const string name,const int size,const uint flags=0,const uint angle=0); // Set the font properties

Eingaben:

  • name - Name der Schriftart
  • size - Schriftgröße
  • flags - Attribute der Schriftart
  • angle - Neigungswinkel der Schrift

Wie man im Programmcode sehen können, setzt die Methode den Schriftnamen, die Größe, die Attribute und den Neigungswinkel der Schrift gemäß name, size, flags und angle, die als Parameter übergeben werden.

Methode zum Empfang aller Schrifteigenschaften

void FontGet(string &name,int &size,uint &flags,uint &angle); // Get font properties

Eingaben:

  • name - Name der Schriftart
  • size - Schriftgröße
  • flags - Attribute der Schriftart
  • angle - Neigungswinkel der Schrift

Wie im Programmcode zu sehen ist, schreibt die Methode den Schriftnamen, die Größe, die Attribute und den Neigungswinkel in die entsprechenden Variablen name, size, flags und angle, die ihr als Parameter an bestimmten Adressen übergeben werden.

Methoden zum Lesen und Schreiben eines grafischen Stils zum Erstellen von Linien:

uint LineStyleGet(void) const;       // Return the specified line drawing style

void LineStyleSet(const uint style); // Set the line drawing style


    Eingaben:

    • style - Linienzeichnungsstil

    Methoden zum Zeichnen und Darstellen eines Textes 

    Beginnen wir mit der Methode der Textdarstellung, da sie nur eine ihrer Art ist, während die grafischen primitiven Zeichenmethoden in CCanvas zahlreich sind.

    Text-Anzeige

    void TextOut(int x,int y,string text,const uint clr,uint alignment=0); // Display text to the m_pixels array

    Eingaben:

    • x - X-Koordinate eines angezeigten Textes
    • y - Y-Koordinate eines angezeigten Textes
    • text - angezeigter Text
    • clr - Farbe des angezeigten Textes
    • alignment - Art der Verankerung des angezeigten Textes

    Gemäß des Programmcodes zeigt die Methode Text an den Koordinaten x und y mit der angegebenen clr Farbe und Ausrichtung entsprechend der Methode zur Textverankerung an.

    Ändern von Pixeln

    In CCanvas können Pixel, die sich im Array m_pixels befinden, geändert werden oder ihre Werte entsprechend den angegebenen Koordinaten empfangen werden.

    uint PixelGet(const int x,const int y) const;          // Return the pixel color value according to x and y coordinates
    
    void PixelSet(const int x,const int y,const uint clr); // Change the pixel color value according to x and y coordinates
    
    
    

    Eingaben:

    • x - Pixel X-Koordinate
    • y - Pixel Y-Koordinate
    • clr - Pixelfarbe

    Zeichnen von grafischen Primitiven

    Da CCanvas über zahlreiche Methoden zum Zeichnen von Primitiven verfügt, werde ich in diesem Artikel nur auf die wichtigsten eingehen. Alle anderen Methoden sind in der Dokumentation zu finden.

    Vertikale Linie

    void LineVertical(int x,int y1,int y2,const uint clr); // Draw a vertical line according to specified coordinates and color

    Eingaben:

    • x - X-Koordinate der Vertikalen
    • y1 - Y-Koordinate des ersten Punktes
    • y2 - Y-Koordinate des zweiten Punktes
    • clr - Linienfarbe

    Horizontale Linie

    void LineHorizontal(int x1,int x2,int y,const uint clr); // Draw a horizontal line according to specified coordinates and color

    Eingaben:

    • x1 - X-Koordinate des ersten Punktes
    • x2 - X-Koordinate des zweiten Punktes
    • y - Y-Koordinate der Horizontalen
    • clr - Linienfarbe

    Freihandlinie

    void Line(int x1,int y1,int x2,int y2,const uint clr); // Draw a freehand line according to specified coordinates and color

    Eingaben:

    • x1 - X-Koordinate des ersten Punktes
    • x1 - X-Koordinate des ersten Punktes 
    • x2 - X-Koordinate des zweiten Punktes 
    • y1 - Y-Koordinate des ersten Punktes
    • clr - Linienfarbe

    Polylinie

    void Polyline(int &x[],int &y[],const uint clr); // Draw a polyline according to specified coordinates and color

    Eingaben:

    • x - Punkt im X-Koordinatenfeld
    • y - Punkt im Y-Koordinatenfeld
    • clr - Linienfarbe

    Polygon

    void Polygon(int &x[],int &y[],const uint clr); // Draw a polygon according to specified coordinates and color

    Eingaben:

    • x - Punkt im X-Koordinatenfeld
    • y - Punkt im Y-Koordinatenfeld
    • clr - Farbe des Polygons

    Rechteck

    void Rectangle(int x1,int y1,int x2,int y2,const uint clr); // Draw a rectangle according to specified coordinates and color

    Eingaben:

    • x1 - erste Punkt X-Koordinate
    • y1 - erster Punkt Y-Koordinate
    • x2 - zweiter Punkt X-Koordinate
    • y2 - zweiter Punkt Y-Koordinate
    • clr - Farbe des Rechtecks 

    Dreieck

    void Triangle(int x1,int y1,int x2,int y2,int x3,int y3,const uint clr); // Draw a triangle according to specified coordinates and color

    Eingaben:

    • x1 - erster Punkt X-Koordinate
    • y1 - erster Punkt Y-Koordinate
    • x2 - zweiter Punkt X-Koordinate
    • y2 - zweiter Punkt Y-Koordinate
    • x3 - dritter Punkt X-Koordinate
    • y3 - dritter Punkt Y-Koordinate
    • clr - Rechteckfarbe 

    Kreis

    void Circle(int x,int y,int r,const uint clr); // Draw a circle according to specified coordinates, radius and color

    Eingaben:

    • x - X-Koordinate
    • y - Y-Koordinate
    • r - Kreisradius
    • clr - Farbe des Kreises

    Ellipse

    void Ellipse(int x1,int y1,int x2,int y2,const uint clr); // Draw an ellipse according to specified coordinates and color

    Eingaben:

    • x1 - erster Punkt X-Koordinate
    • y1 - erster Punkt Y-Koordinate
    • x2 - zweiter Punkt X-Koordinate
    • y2 - zweiter Punkt Y-Koordinate
    • clr - Rechteckfarbe 

    Die Beschreibungen der anderen Methoden, einschließlich Arc und Pie, sind zu umfangreich, um sie hier zu zeigen. Sie finden deren Beschreibungen in der Dokumentation, indem Sie den obigen Links folgen.

    Die betrachteten Methoden zeigen einfache Primitive mit einer Linienbreite von 1 Pixel und dem gewöhnlichen Linienstil STYLE_SOLID an. Sie können sie verwenden, wenn Sie mit einfachen Grafiken zurechtkommen.

    Wenn Sie jedoch etwas anderes als STYLE_SOLID benötigen, wählen Sie eine der unten beschriebenen Methoden.

    //--- Methods for drawing primitives with smoothing using antialiasing
        
    void PixelSetAA(const double x,const double y,const uint clr);
        
    void LineAA(const int x1,const int y1,const int x2,const int y2,const uint clr,const uint style=UINT_MAX);
        
    void PolylineAA(int &x[],int &y[],const uint clr,const uint style=UINT_MAX);
        
    void PolygonAA(int &x[],int &y[],const uint clr,const uint style=UINT_MAX);
        
    void TriangleAA(const int x1,const int y1,const int x2,const int y2,const int x3,const int y3,const uint clr,const uint style=UINT_MAX);
        
    void CircleAA(const int x,const int y,const double r,const uint clr,const uint style=UINT_MAX);
        
    void EllipseAA(const double x1,const double y1,const double x2,const double y2,const uint clr,const uint style=UINT_MAX);
        
    //--- Methods for drawing primitives with smoothing using Wu's algorithm
        
    void LineWu(int x1,int y1,int x2,int y2,const uint clr,const uint style=UINT_MAX);
        
    void PolylineWu(const int &x[],const int &y[],const uint clr,const uint style=UINT_MAX);
        
    void PolygonWu(const int &x[],const int &y[],const uint clr,const uint style=UINT_MAX);
        
    void TriangleWu(const int x1,const int y1,const int x2,const int y2,const int x3,const int y3,const uint clr,const uint style=UINT_MAX);
        
    void CircleWu(const int x,const int y,const double r,const uint clr,const uint style=UINT_MAX);
        
    void EllipseWu(const int x1,const int y1,const int x2,const int y2,const uint clr,const uint style=UINT_MAX);
    

    Alle diese Methoden ähneln den oben genannten, haben aber einen zusätzlichen Stil Parameter, der aus der ENUM_LINE_STYLE Aufzählung ausgewählt werden kann. Er ist im Programmcode gelb hervorgehoben. Ist dieser Parameter style nicht gesetzt (also gleich UINT_MAX), verwendet die aufgerufene Methode den Wert m_style, der mit der LineStyleSet Methode gesetzt wurde. Der Wert kann mit der Methode LineStyleGet abgerufen werden.

    Sie haben vielleicht bemerkt, dass die Methoden die Möglichkeit haben, einen Linienstil zu setzen, aber sie sind nicht in der Lage, eine Linienbreite zu ändern. Die folgenden Methoden ermöglichen das Zeichnen von Primitiven mit der Möglichkeit, die Linienstärke einzustellen.

    //--- Methods for drawing primitives with preliminarily set antialiasing filter
    
    void LineThickVertical(const int x,const int y1,const int y2,const uint clr,const int size,const uint style,ENUM_LINE_END end_style);
    
    void LineThickHorizontal(const int x1,const int x2,const int y,const uint clr,const int size,const uint style,ENUM_LINE_END end_style);
    
    void LineThick(const int x1,const int y1,const int x2,const int y2,const uint clr,const int size,const uint style,ENUM_LINE_END end_style);
    
    void PolylineThick(const int &x[],const int &y[],const uint clr,const int size,const uint style,ENUM_LINE_END end_style);
    
    void PolygonThick(const int &x[],const int &y[],const uint clr,const int size,const uint style,ENUM_LINE_END end_style);
    

    Ähnlich wie die oben beschriebenen Methoden bieten die aktuellen Methoden die Möglichkeit, den Zeilenstil style zu setzen. Sie bieten auch die neuen Parameter, wie size zur Festlegung der Linienbreite, sowie end_style, für die Art des Linienendes, der aus der Enumeration ENUM_LINE_END ausgewählt werden kann.

    Außerdem verfügt CCanvas über Methoden zum Zeichnen von geglätteten Primitiven unter Verwendung der Bézier-Methode. Um die endgültige Bildqualität zu verbessern, werden die resultierenden Primitive mit dem Bitmap-Glättungsalgorithmus bearbeitet. Anders als bei den vorherigen Methoden bestehen die Primitive hier aus Bézier-Kurven und nicht aus geraden Linien. Lassen Sie uns diese Methoden betrachten.

    //--- Methods for drawing a smoothed polyline and smoothed polygon
    
    void PolylineSmooth(const int &x[],const int &y[],const uint clr,const int size,
                        ENUM_LINE_STYLE style=STYLE_SOLID,ENUM_LINE_END end_style=LINE_END_ROUND,
                        double tension=0.5,double step=10);
    
    void PolygonSmooth(int &x[],int &y[],const uint clr,const int size,
                       ENUM_LINE_STYLE style=STYLE_SOLID,ENUM_LINE_END end_style=LINE_END_ROUND,
                       double tension=0.5,double step=10);
    

    Zusätzlich zu den bereits bekannten Arrays der primitiven Koordinatenpunkten x und y, der Farbe clr, size für die Linienbreite, style für den Linienstil und end_style für das Ende der Linie, haben wir hier zwei zusätzliche Parameter Spannungs-Parameter, einen Glättungsparameterwert und step für Näherungsschritte.

    Abgesehen von den oben beschriebenen Methoden zum Zeichnen von Primitiven bietet CCanvas auch Methoden zum Zeichnen von gefüllten Primitiven.

    //--- Methods of drawing filled primitives
    
    void FillRectangle(int x1,int y1,int x2,int y2,const uint clr);
    
    void FillTriangle(int x1,int y1,int x2,int y2,int x3,int y3,const uint clr);
    
    void FillPolygon(int &x[],int &y[],const uint clr);
    
    void FillCircle(int x,int y,int r,const uint clr);
    
    void FillEllipse(int x1,int y1,int x2,int y2,const uint clr);
    
    void Fill(int x,int y,const uint clr);
    
    void Fill(int x,int y,const uint clr,const uint threshould);
    

    Es handelt sich um einfache Methoden ohne Glättungsalgorithmen. Die Zuweisung der Parameter erfolgt analog zu den Parametern der bereits betrachteten Methoden und beinhaltet die Erstellung ähnlicher Primitive, die aus Linien bestehen.


    Pixelformat und Farbkomponenten

    Kehren wir zu den oben betrachteten Methoden zurück und richten wir unsere Aufmerksamkeit auf das Pixelformat in den Methoden CreateBitmap und CreateBitmapLabel. Für das Format ist der Parameter clrfmt zuständig. Der Parameter legt das Pixelformat fest, das für die grafische Ressource erstellt wird, die sich später mit dem entsprechenden Chart-Objekt verbindet. Das Pixelformat, das für die grafische Ressource während der Canvas-Erstellung mit Hilfe der Methoden CreateBitmap oder CreateBitmapLabel festgelegt wird, beeinflusst die Methode zur Behandlung eines Bildes durch das Terminal, wenn es im Chart angezeigt wird. Um die Bildbearbeitungsmethoden zu definieren, schauen wir uns ENUM_COLOR_FORMAT an. Hier können wir einen von drei möglichen konstanten Werten auswählen.

    ENUM_COLOR_FORMAT

    • COLOR_FORMAT_XRGB_NOALPHA - XRGB Format (Alphakanal wird ignoriert)
    • COLOR_FORMAT_ARGB_RAW - "rohes" ARGB-Format (Farbkomponenten werden vom Terminal nicht verarbeitet)
    • COLOR_FORMAT_ARGB_NORMALIZE - ARGB-Format (Farbbestandteile werden vom Terminal verarbeitet)

    Betrachten wir nun die Reihenfolge der Bereitstellung von Farbkomponenten in diesen Formaten.

    Pixelformat und Farbkomponenten

    Hier sehen wir im Pixelarray der Zellbytes, m_pixels, die Position der Farbkomponenten, wobei R der rote Kanal, G der grüner Kanal und B der blauer Kanal, A der Alpha-Kanal und X ein unbenutztes Byte ist. uint Pixeldatentyp, 4 Bytes. Ein Byte pro Kanal. Ein Byte kann Zahlen von 0 bis 255 speichern. Wir können die Pixelfarbe einstellen, indem wir die Byte-Werte ändern. Wenn man zum Beispiel 255 für den R-Kanal und 0 für die G- und B-Kanäle einstellt, erhält man die rote Farbe. Setzt man 255 auf den G-Kanal, während die übrigen Werte auf 0 gesetzt werden, erhält man die grüne Farbe. Wenn der B-Kanal auf 255 gesetzt wird, während die übrigen auf 0 gesetzt werden, erhalten wir die blaue Farbe. Auf diese Weise können wir verschiedene Farben erhalten, indem wir die verschiedenen Kombinationen von RGB-Kanalwerten einstellen. Durch Einstellen des Alphakanalwerts von 0 (völlig unsichtbar, transparent) bis 255 (völlig sichtbar, nicht transparent) können wir die Nichttransparenz der Pixel steuern, indem wir den Effekt erzeugen, dass sich die Pixel des Chart-Bildes überlappen. Dies kann im Format COLOR_FORMAT_ARGB_NORMALIZE korrekt durchgeführt werden. Gehen wir nun zu den Farben über und betrachten wir die Farbpalette.

    Farbpalette

    Hier können wir deutlich sehen, wie sich eine Änderung der Kombination von Stufen jedes Kanals auf die resultierende Farbe auswirkt. Die Farben werden in den Formaten RGB und HEX dargestellt. Während ich mich bereits mit RGB beschäftigt habe, bedarf HEX einiger Erläuterungen, insbesondere für Programmieranfänger, die CCanvas beherrschen wollen. Betrachten wir das hexadezimale Zahlensystem, das Zahlen von 0 bis 15 enthält, und vergleichen es mit dem Dezimalsystem (0-9). Das Dezimalsystem hat die folgenden Symbole: 0, 1, 2, 3, 4, 5, 6, 7, 8 und 9. Wie soll man also Zahlen, die größer als 9 sind, im Hexadezimalsystem bezeichnen? In diesem Fall verwendet das System lateinische Buchstaben von A bis F. Das hexadezimale Zahlensystem hat also folgende Symbole: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E und F. Jetzt können wir einfach verstehen, wie das Byte mit dem Kanalwert im HEX-Format aussieht. In C-basierten Programmiersprachen wird HEX als 0x[Wert] bezeichnet. Ein Bereich der einzelnen Bytes haben also folgendes Aussehen: 0x00 bis 0xFF.


    Beispiele für die Erstellung von Grafiken mit CCanvas

    Kehren wir zum Pixelformat zurück und betrachten wir ein einfaches Beispiel für eine Anwendung, die CCanvas verwendet. Außerdem werden wir mit verschiedenen Pixelformaten spielen, indem wir versuchen, die Werte für Bildfarbe und Alphakanal in verschiedenen Pixelformaten festzulegen. Beginnen wir mit dem universellsten Format COLOR_FORMAT_ARGB_NORMALIZE. Wir werden es verwenden, um unsere erste Anwendung zu entwickeln.

    Eine einfache Anwendung mit CCanvas

    Erstellen wir ein Skript und nennen es Erase.mq5.

    #include <Canvas\Canvas.mqh>
    
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
     {
        CCanvas canvas;
        canvas.CreateBitmapLabel("canvas", 15, 30, 300, 250, COLOR_FORMAT_ARGB_NORMALIZE);
        canvas.Erase(ColorToARGB(clrWhite, 255));
        canvas.Update(true);
        Sleep(6000);
        canvas.Destroy();
     }
    

    In der ersten Zeile sehen wir die Einbindung des Moduls Canvas.mqh. Nun ist es möglich, ein Objekt, eine Instanz der Klasse CCanvas, für die weitere Arbeit zu erstellen, wie im Beispiel gezeigt.

    CCanvas  canvas;

    Als Nächstes wird das Canvas selbst mit dem Parameter clrfmt erstellt: COLOR_FORMAT_ARGB_NORMALIZE.

     canvas.CreateBitmapLabel("canvas", 15, 30, 300, 250, COLOR_FORMAT_ARGB_NORMALIZE);

    Und füllen wir es mit der Methode "Erase".

     canvas.Erase(ColorToARGB(clrWhite, 255));

    Nach dem Erstellen von Canvas rufen wir die Methode Update auf.

    canvas.Update(true);

    Mit redraw: true. Dies ist ein optionaler Parameter, da er ohnehin standardmäßig gesetzt ist, aber ich schlage vor, ihn für mehr Klarheit zu setzen. Als Nächstes warten wir 6 Sekunden um das Ergebnis der Bearbeitung der Anwendung auf dem Chart zu sehen.

    Sleep(6000); 

    Als Nächstes rufen wir die Methode Destroy auf, um den von der grafischen Ressource und dem Chart-Objekt belegten Speicher freizugeben.

    canvas.Destroy();

    Danach beendet das Skript seine Arbeit. Das Ergebnis ist in der folgenden Abbildung zu sehen.

    Erase

    Hier sehen wir ein ausgefülltes Rechteck, das als Basis oder Hintergrund für das Zeichnen komplexerer grafischer Konstruktionen verwendet werden kann.

    Pixelformat und Überlappungsmethoden

    Schauen wir uns das bereits betrachtete Beispiel Erase.mq5 an und versuchen wir, die Farbe direkt zu setzen, ohne die Funktion ColorToARGB. Wir kopieren den gesamten Code des Beispiels und erstellen ein Skript namensARGB_NORMALIZE.mq5. Dann legen wir die Farbe fest, indem wir eine der Farben aus der oben erwähnten Palette auswählen, zum Beispiel clrPaleGreen. Wir nehmen den Wert im HEX-Format und fügen den Wert des Alphakanals 0xFF auf der linken Seite hinzu.

    #include <Canvas\Canvas.mqh>
    
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
     {
        CCanvas canvas;
        canvas.CreateBitmapLabel("canvas", 15, 30, 300, 250, COLOR_FORMAT_ARGB_NORMALIZE);
        canvas.Erase(0xFF98FB98);
        canvas.Update(true);
        Sleep(6000);
        canvas.Destroy();
     }
    

    Die RGB-Komponenten in der Auflistung sind grün hervorgehoben, die A-Komponente ist grau hervorgehoben. Wir starten das Skript und sehen uns das Ergebnis an.

    Color change in the COLOR_FORMAT_ARGB_NORMALIZE format

    Wir sehen, wie sich die Farbe des Bildes verändert hat. Versuchen wir nun, die Transparenz zu ändern. Setzen Sie den Wert des Alphakanals auf 0xCC.

    void OnStart()
     {
        CCanvas canvas;
        canvas.CreateBitmapLabel("canvas", 15, 30, 300, 250, COLOR_FORMAT_ARGB_NORMALIZE);
        canvas.Erase(0xCC98FB98);
        canvas.Update(true);
        Sleep(6000);
        canvas.Destroy();
     }
    

    Werfen wir einen Blick auf das Ergebnis.

    Non-transparency change in the COLOR_FORMAT_ARGB_NORMALIZE format

    Hier sehen wir, dass der gefüllte Bereich halbtransparent geworden ist.

    Ändern Sie das Pixelformat in COLOR_FORMAT_ARGB_RAW und machen Sie das Bild wieder völlig undurchsichtig. Dazu erstellen wir ein separates Beispiel ARGB_RAW.mq5.

    void OnStart()
     {
        CCanvas canvas;
        canvas.CreateBitmapLabel("canvas", 15, 30, 300, 250, COLOR_FORMAT_ARGB_RAW);
        canvas.Erase(0xFF98FB98);
        canvas.Update(true);
        Sleep(6000);
        canvas.Destroy();
     }
    

    Führen wir das Beispiel aus und sehen uns das Ergebnis an.

    Nicht-Transparenz im Format COLOR_FORMAT_ARGB_RAW

    Wie wir sehen, unterscheidet sich das Ergebnis nicht von COLOR_FORMAT_ARGB_NORMALIZE.

    Wir setzen den Alphakanal auf 0xFB und ändern Sie die Hintergrundfarbe des Charts auf Weiß.

    void OnStart()
     {
        ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrWhite);
        CCanvas canvas;
        canvas.CreateBitmapLabel("canvas", 15, 30, 300, 250, COLOR_FORMAT_ARGB_RAW);
        canvas.Erase(0xFB98FB98);
        canvas.Update(true);
        Sleep(6000);
        canvas.Destroy();
     }
    

    Hier ist nur eine geringfügige Änderung und kein Unterschied zum COLOR_FORMAT_ARGB_NORMALIZE-Pixelformat zu erkennen.

    Nichttransparente Änderung im Format COLOR_FORMAT_ARGB_RAW

    Verringern wir jedoch den Wert des Alphakanals um eins,

    canvas.Erase(0xFA98FB98);

    stellen wir sofort erhebliche Veränderungen des endgültigen Bildes fest.

    Bildveränderung bei einer geringfügigen Änderung der Nicht-Transparenz im Format COLOR_FORMAT_ARGB_RAW

    Prüfen wir nun, wie sich das Bild bei einer vollständigen Transparenzänderung verhält. Dazu erstellen wir ein neues Beispiel und nennen es ARGB_RAW-2.mq5. Wir kopieren den Code aus dem vorherigen Beispiel dorthin und nehmen leichte Änderungen vor, damit der Alphakanalwert in einem bestimmten Intervall von 255 auf 0 wechseln kann.

    void OnStart()
     {
      ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrWhite);
      CCanvas canvas;
      canvas.CreateBitmapLabel("canvas", 15, 30, 300, 250, COLOR_FORMAT_ARGB_RAW);
      canvas.Erase(0xFF98FB98);
      for(int a = 255; a >= 0; a--)
       {
        canvas.TransparentLevelSet((uchar)a);
        canvas.Update(true);
        Sleep(100);
       }
      canvas.Destroy();
     }
    

    Wie wir im Listing sehen können, verfügt die Anwendung über eine for-Schleife, die die Transparenz (a-Schleifenvariable) durch den Aufruf der Methode TrancparentLevelSet und die Transparenz des gesamten Bildes ändert.

    canvas.TransparentLevelSet((uchar)a);

    Als Nächstes rufen Sie die bereits bekannte Methode Update auf.

    canvas.Update(true);

    Als Nächstes lässt die Funktion Sleep die Schleife 100 Millisekunden lang warten (damit die Nutzer Zeit haben, die Änderung der Transparenz zu sehen).

    Sleep(100);

    Dann entfernt die Anwendung Canvas

    canvas.Destroy();

    und beendet ihre Arbeit. Das Ergebnis ist in der folgenden GIF-Animation zu sehen.

    Vollständige Änderung der Transparenz im Format COLOR_FORMAT_ARGB_RAW

    Aus dem folgenden Beispiel sowie aus allen Beispielen, die das Format COLOR_FORMAT_ARGB_RAW anwenden, geht hervor, dass das Bild mit dem Alphakanalwert 255 korrekt angezeigt wird. Verringert man jedoch den Wert, wird das Bild verzerrt, da das Format keine Normalisierung der RGB-Kanalwerte vorsieht. Die Kanäle können überfüllt werden, was zu Artefakten und Verzerrungen führt, da überfüllte Werte, die 255 überschreiten, einfach verworfen werden. Andererseits beschleunigt die Verwendung dieses Formats die Anzeige des Bildes im Vergleich zum COLOR_FORMAT_ARGB_NORMALIZE-Format.

    Kommen wir zurück zu unserem Beispiel für die Verwendung des Pixelformats COLOR_FORMAT_ARGB_RAW ARGB_RAW.mq5. Erstellen Sie das Skript mit dem Namen XRGB_NOALPHA.mq5 und kopieren Sie den Code aus ARGB_RAW.mq5 dorthin, indem Sie das Pixelformat auf COLOR_FORMAT_XRGB_NOALPHA setzen. Setzen Sie außerdem den Alphakanalwert in der Löschmethode auf Null

    void OnStart()
     {
        CCanvas canvas;
        canvas.CreateBitmapLabel("canvas", 15, 30, 300, 250, COLOR_FORMAT_XRGB_NOALPHA);
        canvas.Erase(0x0098FB98);
        canvas.Update(true);
        Sleep(6000);
        canvas.Destroy();
     }
    

    Starten Sie das Skript, um die Ergebnisse zu sehen.

    Überlagernde Farbe im Format COLOR_FORMAT_XRGB_NOALPHA

    Wie wir sehen können, unterscheidet sich das Ergebnis nicht von den Formaten COLOR_FORMAT_ARGB_NORMALIZE und COLOR_FORMAT_ARGB_RAW mit dem maximalen Alphakanalwert (0xFF) in der Löschmethode. Man sieht also, dass der Alphakanalwert bei diesem Format vollständig ignoriert wird und das Bild einfach über das Chart-Bild gelegt wird. 

    Text-Anzeige

    Wir wissen, dass CCanvas eine Methode zur Anzeige des TextOut Textes bietet. Lassen Sie uns versuchen, den Text anzuzeigen. Wir erstellen das Skript und nennen es TextOut.mq5. Wir verwenden das oben besprochene Erase.mq5 als Grundlage, indem wir seinen Code kopieren. Wir fügen die Methode zum Setzen der FontSet-Schriftparameter hinzu und setzen Sie die notwendigen Werte, Schriftart "Calibri" und Schriftgröße -210.

    Um die Schriftgröße in Pixeln Größe in den Methoden FontSet und FontSizeSet einzustellen, sollte sie mit -10 multipliziert werden. Im Falle einer Schriftgröße von 21 Pixeln ist Größe also gleich -210.

    Die übrigen Parameter werden standardmäßig belassen. Als Nächstes fügen wir die Methode TextOut mit dem Parameter text hinzu: "Text". Alle anderen für die Beispieloperation notwendigen Zeichenfolgen bleiben im kopierten Code.

    void OnStart()
     {
        CCanvas canvas;
        canvas.CreateBitmapLabel("canvas", 15, 30, 300, 250, COLOR_FORMAT_ARGB_NORMALIZE);
        canvas.Erase(ColorToARGB(clrWhite, 255));
        canvas.FontSet("Calibri", -210);
        canvas.TextOut(15, 15, "Text", ColorToARGB(clrLightSlateGray, 255));
        canvas.Update(true);
        Sleep(6000);
        canvas.Destroy();
     }
    

    Schauen wir uns das Ergebnis der Skriptoperation an.

    Anzeige des Textes mit der Methode TextOut

    Der Text erscheint auf dem Bild. 

    Ändern wir das Beispiel so, dass das Bild genau in der Mitte des Charts erscheint und an das Label-Objekt (OBJ_LABEL) erinnert. Erstellen wir jetzt ein neues Beispiel auf der Grundlage des Skripts und nennen es TextOut-2.mq5.

    void OnStart()
     {
      string text = "Text";
      int width, height;
      const int textXDist = 10, textYDist = 5;
      int canvasX, canvasY;
      CCanvas canvas;
      
      canvas.FontSet("Calibri", -210);
      canvas.TextSize(text, width, height);
      
      canvasX = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0) / 2 - width / 2;
      canvasY = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0) / 2 - height / 2;
      
      canvas.CreateBitmapLabel("canvas", canvasX, canvasY,
                               width + textXDist * 2, height + textYDist * 2,
                               COLOR_FORMAT_ARGB_NORMALIZE);
      canvas.Erase(ColorToARGB(clrWhite, 255));
      canvas.TextOut(textXDist, textYDist, text, ColorToARGB(clrLightSlateGray, 255));
      canvas.Update(true);
      Sleep(6000);
      canvas.Destroy();
     }
    

    Dem Programmcode zufolge wird der angezeigte Text der Text-Variablen zugewiesen (da im Skriptcode zweimal auf sie zugegriffen wird).

    string text = "Text";

    Die Variablen Breite und Höhe wurden deklariert, um die angezeigte Textgröße zu speichern.

    int width, height;

    Die beiden deklarierten Konstanten textXDist und textYDist speichern die Abstände des angezeigten Textes vom rechten bzw. oberen Rand des Bildes.

    const int textXDist = 10, textYDist = 5;

    Als Nächstes werden zwei Variablen canvasX und canvasY deklariert, um die Ergebnisse der Berechnungen der Bildkoordinaten zu speichern.

    int canvasX, canvasY;

    Danach kommt die Methode FontSet, die die Schriftparameter definiert (um sie vorher zu setzen).

    canvas.FontSet("Calibri", -210);

    Die Methode TextSize erlaubt es, die Größe eines angezeigten Textes zu definieren (um die Bildgröße zu bestimmen), die in den Variablen width und height gespeichert wird.

    canvas.TextSize(text, width, height);

    Als Nächstes berechnen wir die Bildkoordinaten, um das Bild in der Mitte eines Charts anzuzeigen.

    canvasX = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0) / 2 - width / 2;
    canvasY = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0) / 2 - height / 2;
    

    Als Nächstes erstellen wir das Bild mit der Funktion CreateBitmapLabel, bei der die zuvor berechneten Koordinaten festgelegt werden.

    canvas.CreateBitmapLabel("canvas", canvasX, canvasY,
                               width + textXDist * 2, height + textYDist * 2,
                               COLOR_FORMAT_ARGB_NORMALIZE);
    

    Danach wird das Bild mit Hilfe der Methode Erase mit Farbe gefüllt.

    canvas.Erase(ColorToARGB(clrWhite, 255));

    Anschließend wird der Text (Text) mit der Methode TextOut unter Angabe der zuvor festgelegten Koordinaten textXDist und textYDist angezeigt.

    canvas.TextOut(textXDist, textYDist, text, ColorToARGB(clrLightSlateGray, 255));

    Als Nächstes haben wir die bereits bekannten Zeichenketten, die keiner Erklärung bedürfen. Starten wir das Skript und sehen wir uns das Ergebnis an.

    Beispiel einer Textbeschriftung

    Das Bild zeigt ein Objekt, das einem Textetikett ähnelt. Aber es fehlt der transparente Hintergrund. Der Hintergrund kann in unserem aktuellen Pixelformat COLOR_FORMAT_ARGB_NORMALIZE eingestellt werden. Aber ich werde einen anderen Weg gehen. Wir setzen das Pixelformat COLOR_FORMAT_ARGB_RAW auf unsere Leinwand, da wir in diesem Fall keine Mischfarben benötigen, während die Farben mit dem Alphakanal 0 und 255 im Format ohne Verzerrung dargestellt werden. Die Farbe mit dem Alphakanalwert von 0 beeinflusst das endgültige Bild nicht. Wir kopieren das Skript LabelExample.mq5, das auf dem vorherigen Beispiel basiert.

    void OnStart()
     {
      string text = "Text";
      int width, height;
      const int textXDist = 10, textYDist = 5;
      int canvasX, canvasY;
      CCanvas canvas;
      
      canvas.FontSet("Calibri", -210);
      canvas.TextSize(text, width, height);
      
      canvasX = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0) / 2 - width / 2;
      canvasY = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0) / 2 - height / 2;
      
      canvas.CreateBitmapLabel("canvas", canvasX, canvasY,
                               width + textXDist * 2, height + textYDist * 2,
                               COLOR_FORMAT_ARGB_RAW);
      canvas.Erase(ColorToARGB(clrWhite, 255));
      canvas.FontSet("Calibri", -210);
      canvas.TextOut(textXDist, textYDist, text, ColorToARGB(clrLightSlateGray, 255));
      canvas.Update(true);
      Sleep(6000);
      canvas.Destroy();
     }
    

    Der Alphakanal ist unverändert geblieben, sodass wir sicherstellen können, dass der Alphakanal von 255 die Pixel des Charts vollständig neu zeichnet.

    Prüfung des Formats COLOR_FORMAT_ARGB_RAW

    Jetzt setzen wir den Wert des Alphakanals auf 0.

    void OnStart()
     {
      string text = "Text";
      int width, height;
      const int textXDist = 10, textYDist = 5;
      int canvasX, canvasY;
      CCanvas canvas;
      
      canvas.FontSet("Calibri", -210);
      canvas.TextSize(text, width, height);
      
      canvasX = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0) / 2 - width / 2;
      canvasY = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0) / 2 - height / 2;
      
      canvas.CreateBitmapLabel("canvas", canvasX, canvasY,
                               width + textXDist * 2, height + textYDist * 2,
                               COLOR_FORMAT_ARGB_RAW);
      canvas.Erase(ColorToARGB(clrWhite, 0));
      canvas.FontSet("Calibri", -210);
      canvas.TextOut(textXDist, textYDist, text, ColorToARGB(clrLightSlateGray, 255));
      canvas.Update(true);
      Sleep(6000);
      canvas.Destroy();
     }
    

    Starten Sioe das Skript und schauen sie sich das Ergebnis an.

    Beschriftung analog

    Wir können die Textbeschriftung analog sehen (OBJ_LABEL). So können wir Texte über Bildern anzeigen, ohne sie komplett neu zu zeichnen. Dies wird in verschiedenen Nutzeroberflächen häufig zur Hervorhebung von Steuerelementen und zur Anzeige von Textdaten verwendet.

    Zeichnen einfacher Primitive ohne Glättung

    Schauen wir uns DrawPrimitives.mq5 an, um zu verstehen, wie man Primitive in CCanvas zeichnet.

    void OnStart()
     {
      CCanvas canvas;
      int w, h;
      int minSize, point;
      uint color_ = ColorToARGB(clrDodgerBlue, 255);
      uint fillColor = ColorToARGB(clrRed, 255);
      int plX[] = {6, 8, 9, 11, 12, 14}, plY[] = {14, 12, 13, 11, 12, 10};
      int pgX[] = {15, 14, 15, 17, 18, 17}, pgY[] = {9, 7, 5, 5, 7, 9};
      w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
      h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
      minSize = (int)fmin(w, h);
      point = minSize / 15;
      canvas.CreateBitmapLabel("canvas", 0, 0, w, h, COLOR_FORMAT_XRGB_NOALPHA);
      canvas.Erase(ColorToARGB(clrWhite, 255));
      canvas.LineHorizontal(point, w - point, h - point, color_);
      canvas.LineVertical(point, point, h - point, color_);
      canvas.Line(point * 2, point * 13, point * 8, point * 9, color_);
      for (int i = 0; i < (int)plX.Size(); i++)
        plX[i] *= point;
      for (int i = 0; i < (int)plY.Size(); i++)
        plY[i] *= point;
      canvas.Polyline(plX, plY, color_);
      for (int i = 0; i < (int)pgX.Size(); i++)
        pgX[i] *= point;
      for (int i = 0; i < (int)pgY.Size(); i++)
        pgY[i] *= point;
      canvas.Polygon(pgX, pgY, color_);
      canvas.Rectangle(point * 2, point * 5, point * 7, point, color_);
      canvas.Triangle(point * 2, point * 11, point * 2, point * 6, point * 7, point * 6, color_);
      canvas.Circle(point * 10, point * 3, point * 2, color_);
      canvas.Ellipse(point * 8, point * 9, point * 12, point * 6, color_);
      canvas.Arc(point * 15, point * 2, point * 2, point, 45.0 * M_PI / 180, 180.0 * M_PI / 180, color_);
      canvas.Pie(point * 16, point * 3, point * 2, point, 180.0 * M_PI / 180, 402.5 * M_PI / 180, color_, fillColor);
      canvas.Update(true);
      Sleep(6000);
      canvas.Destroy();
     }
    

    Alle Primitive können bequem entlang der Quadrate gezeichnet werden. Ihre Koordinaten und die übrigen Parameter lassen sich mit der gleichen Methode bequem einstellen. Daher habe ich beschlossen, die Aufgabe zu vereinfachen, indem ich das CCanvas-Bild in Teile mit gleicher Breite und Höhe unterteile, sodass es möglich ist, Koordinaten in Quadraten festzulegen und sie in Pixel umzuwandeln, indem man die Koordinaten in Quadraten mit der Größe eines einzelnen Quadrats multipliziert. Um dies zu erreichen, habe ich die Größe des Charts als CCanvas-Bildgröße genommen und den kleinsten Wert durch 15 geteilt.

      CCanvas canvas;
      int w, h;
      int minSize, point;
      uint color_ = ColorToARGB(clrDodgerBlue, 255);
      uint fillColor = ColorToARGB(clrRed, 255);
      int plX[] = {6, 8, 9, 11, 12, 14}, plY[] = {14, 12, 13, 11, 12, 10};
      int pgX[] = {15, 14, 15, 17, 18, 17}, pgY[] = {9, 7, 5, 5, 7, 9};
      w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
      h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
      minSize = (int)fmin(w, h);
      point = minSize / 15;
    

    Der Code ist in der Auflistung gelb hervorgehoben. Da wir nun die Größe eines einzelnen Quadrats kennen (die Variable Punkt), können wir die in Quadraten festgelegten Koordinaten in ihre tatsächliche Größe in Pixeln umrechnen. Vor der Erstellung des Beispiels habe ich das Chart-Bild in Quadrate unterteilt und diese verwendet, um die Koordinaten aller Primitive im Skript festzulegen.

    int plX[] = {6, 8, 9, 11, 12, 14}, plY[] = {14, 12, 13, 11, 12, 10};
    int pgX[] = {15, 14, 15, 17, 18, 17}, pgY[] = {9, 7, 5, 5, 7, 9};
    

    Hier habe ich die Koordinaten für eine Polylinie (Polylinie) und ein Polygon (Polygon) in das Array gesetzt. Dann habe ich sie in Pixel umgewandelt, indem ich die Arrays in den Schleifen einzeln ausgewählt und eine Polylinie und ein Polygon gezeichnet habe.

      for (int i = 0; i < (int)plX.Size(); i++)
        plX[i] *= point;
      for (int i = 0; i < (int)plY.Size(); i++)
        plY[i] *= point;
      canvas.Polyline(plX, plY, color_);
      for (int i = 0; i < (int)pgX.Size(); i++)
        pgX[i] *= point;
      for (int i = 0; i < (int)pgY.Size(); i++)
        pgY[i] *= point;
      canvas.Polygon(pgX, pgY, color_);
    

    Der Code für den Aufruf der primitiven Zeichenmethoden ist gelb hervorgehoben. Die übrigen Primitive werden auf folgende Weise angezeigt.

      canvas.Polygon(pgX, pgY, color_);
      canvas.Rectangle(point * 2, point * 5, point * 7, point, color_);
      canvas.Triangle(point * 2, point * 11, point * 2, point * 6, point * 7, point * 6, color_);
      canvas.Circle(point * 10, point * 3, point * 2, color_);
      canvas.Ellipse(point * 8, point * 9, point * 12, point * 6, color_);
      canvas.Arc(point * 15, point * 2, point * 2, point, 45.0 * M_PI / 180, 180.0 * M_PI / 180, color_);
      canvas.Pie(point * 16, point * 3, point * 2, point, 180.0 * M_PI / 180, 402.5 * M_PI / 180, color_, fillColor);
    

    Wie wir im Programmcode sehen können, werden alle Koordinaten mit point multipliziert, um sie in Pixel umzuwandeln. Betrachten wir nun das Ergebnis des Skripts.

    Einfache Primitive

    Wir sehen die Primitive mit dem einfachen Linienzeichnungsstil (STYLE_SOLID), der nicht geändert werden kann.

    Zeichnen von Primitiven mit Glättung und veränderbarem Linienstil

    Damit der Linienstil geändert werden kann, verwenden Sie die Methoden mit dem Sortieralgorithmus, der Antialiasing (AA) anwendet. Erstellen Sie das Skript DrawPrimitivesAA.mq5 und kopieren Sie den Code aus dem vorherigen Beispiel hinein. Alle in der CCanvas-Klasse vorhandenen Methoden mit dem Präfix AA (LineAA, PolylineAA, PolygonAA, TriangleAA, CircleAA und EllipseAA) werden vollständig konform gemacht. Andere Methoden werden entfernt. Da wir nun im Vergleich zum vorherigen Beispiel weniger Primitive haben, können wir ihre Position ändern.

    void OnStart()
     {
      CCanvas canvas;
      int w, h;
      int minSize, point;
      uint color_ = ColorToARGB(clrDodgerBlue, 255);
      uint fillColor = ColorToARGB(clrRed, 255);
      int plX[] = {6, 8, 9, 11, 12, 14}, plY[] = {13, 11, 12, 10, 11, 9};
      int pgX[] = {15, 14, 15, 17, 18, 17}, pgY[] = {7, 5, 3, 3, 5, 7};
      ENUM_LINE_STYLE lineStyle = STYLE_SOLID;
      w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
      h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
      minSize = (int)fmin(w, h);
      point = minSize / 15;
      canvas.CreateBitmapLabel("canvas", 0, 0, w, h, COLOR_FORMAT_XRGB_NOALPHA);
      canvas.Erase(ColorToARGB(clrWhite, 255));
      canvas.LineStyleSet(lineStyle);
      canvas.LineAA(point * 2, point * 12, point * 8, point * 8, color_);
      for (int i = 0; i < (int)plX.Size(); i++)
        plX[i] *= point;
      for (int i = 0; i < (int)plY.Size(); i++)
        plY[i] *= point;
      canvas.PolylineAA(plX, plY, color_);
      for (int i = 0; i < (int)pgX.Size(); i++)
        pgX[i] *= point;
      for (int i = 0; i < (int)pgY.Size(); i++)
        pgY[i] *= point;
      canvas.PolygonAA(pgX, pgY, color_);
      canvas.TriangleAA(point * 2, point * 9, point * 2, point * 4, point * 7, point * 4, color_);
      canvas.CircleAA(point * 16, point * 11, point * 2, color_);
      canvas.EllipseAA(point * 8, point * 4, point * 12, point * 7, color_);
      canvas.Update(true);
      Sleep(6000);
      canvas.Destroy();
     }
    

    Die geänderten Zeichenfolgen sind gelb hervorgehoben. Starten wir nun das Skript und schauen wir uns das Ergebnis an.

    Primitive mit Antialiasing

    Das Bild zeigt die Ergebnisse der Methoden mit Antialiasing. Die Linien sehen im Vergleich zum vorherigen Beispiel mit einfachen Primitiven glatter aus.

    Allerdings ist der Linienstil hier noch STYLE_SOLID. Wir beheben dies, indem wir das Skript DrawPrimitivesAA-2.mq5 erstellen und dort den Code des vorherigen Beispiels einfügen. Wir legen sleep als Eingabeparameter fest, der die Verzögerung nach der Änderung des Linienstils und der Anzeige der Primitive bestimmt.

    #property script_show_inputs
    
    //--- input parameters
    input int sleep = 1000;
    

    Alle Zeichenmethoden werden im Makro festgelegt.

    #define drawPrimitives canvas.Erase(ColorToARGB(clrWhite, 255));                               \
      canvas.LineAA(point * 2, point * 12, point * 8, point * 8, color_);                          \
      canvas.PolylineAA(plX, plY, color_);                                                         \
      canvas.PolygonAA(pgX, pgY, color_);                                                          \
      canvas.TriangleAA(point * 2, point * 9, point * 2, point * 4, point * 7, point * 4, color_); \
      canvas.CircleAA(point * 16, point * 11, point * 2, color_);                                  \
      canvas.EllipseAA(point * 8, point * 4, point * 12, point * 7, color_);                       \
      canvas.Update(true);                                                                         \
      Sleep(sleep);
    

    Als Nächstes ändern wir einzeln den Linienstil, indem wir die Methode LineStyleSet und die Anzeige von Primitiven verwenden. Schauen wir uns das Beispiel an.

    #property script_show_inputs
    
    //--- input parameters
    input int sleep = 1000;
    
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    #include <Canvas\Canvas.mqh>
    
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    #define drawPrimitives canvas.Erase(ColorToARGB(clrWhite, 255));                               \
      canvas.LineAA(point * 2, point * 12, point * 8, point * 8, color_);                          \
      canvas.PolylineAA(plX, plY, color_);                                                         \
      canvas.PolygonAA(pgX, pgY, color_);                                                          \
      canvas.TriangleAA(point * 2, point * 9, point * 2, point * 4, point * 7, point * 4, color_); \
      canvas.CircleAA(point * 16, point * 11, point * 2, color_);                                  \
      canvas.EllipseAA(point * 8, point * 4, point * 12, point * 7, color_);                       \
      canvas.Update(true);                                                                         \
      Sleep(sleep);
    
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
     {
      CCanvas canvas;
      int w, h;
      int minSize, point;
      uint color_ = ColorToARGB(clrDodgerBlue, 255);
      uint fillColor = ColorToARGB(clrRed, 255);
      int plX[] = {6, 8, 9, 11, 12, 14}, plY[] = {13, 11, 12, 10, 11, 9};
      int pgX[] = {15, 14, 15, 17, 18, 17}, pgY[] = {7, 5, 3, 3, 5, 7};
      //ENUM_LINE_STYLE lineStyle;
      w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
      h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
      minSize = (int)fmin(w, h);
      point = minSize / 15;
      canvas.CreateBitmapLabel("canvas", 0, 0, w, h, COLOR_FORMAT_XRGB_NOALPHA);
      
      for (int i = 0; i < (int)plX.Size(); i++)
        plX[i] *= point;
      for (int i = 0; i < (int)plY.Size(); i++)
        plY[i] *= point;
      for (int i = 0; i < (int)pgX.Size(); i++)
        pgX[i] *= point;
      for (int i = 0; i < (int)pgY.Size(); i++)
        pgY[i] *= point;
      
      canvas.LineStyleSet(STYLE_SOLID);
      drawPrimitives
      canvas.LineStyleSet(STYLE_DOT);
      drawPrimitives
      canvas.LineStyleSet(STYLE_DASH);
      drawPrimitives
      canvas.LineStyleSet(STYLE_DASHDOTDOT);
      drawPrimitives
      
      Sleep(6000);
      canvas.Destroy();
     }
    

    Das hervorgehobene Codefragment zeigt, wie der Stil geändert wird und die Zeichenmethoden aufgerufen werden. Schauen wir uns das Ergebnis der Skriptoperation an.

    Ändern des Linienzeichnungsstils (AA)

    Die GIF-Animation zeigt die Änderung des primitiven Linienstils mit dem angegebenen sleep-Intervall.

    Nun schlage ich vor, ein Beispiel für das Zeichnen von Primitiven unter Verwendung des Wu-Algorithmus zu erstellen. Erstellen wir das Skript DrawPrimitivesWu.mq5 auf der Grundlage des vorherigen Beispiels. Wir ersetzen die AA-Symbolkombination durch Wu und kommentieren die Zeilen wie im Listing gezeigt aus.

    #property script_show_inputs
    
    //--- input parameters
    input int sleep = 1000;
    
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    #include <Canvas\Canvas.mqh>
    
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    #define drawPrimitives canvas.Erase(ColorToARGB(clrWhite, 255));                               \
      canvas.LineWu(point * 2, point * 12, point * 8, point * 8, color_);                          \
      canvas.PolylineWu(plX, plY, color_);                                                         \
      canvas.PolygonWu(pgX, pgY, color_);                                                          \
      canvas.TriangleWu(point * 2, point * 9, point * 2, point * 4, point * 7, point * 4, color_); \
      canvas.CircleWu(point * 16, point * 11, point * 2, color_);                                  \
      canvas.EllipseWu(point * 8, point * 4, point * 12, point * 7, color_);                       \
      canvas.Update(true);                                                                         \
      Sleep(sleep);
    
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
     {
      CCanvas canvas;
      int w, h;
      int minSize, point;
      uint color_ = ColorToARGB(clrDodgerBlue, 255);
      uint fillColor = ColorToARGB(clrRed, 255);
      int plX[] = {6, 8, 9, 11, 12, 14}, plY[] = {13, 11, 12, 10, 11, 9};
      int pgX[] = {15, 14, 15, 17, 18, 17}, pgY[] = {7, 5, 3, 3, 5, 7};
      //ENUM_LINE_STYLE lineStyle;
      w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
      h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
      minSize = (int)fmin(w, h);
      point = minSize / 15;
    
      canvas.CreateBitmapLabel("canvas", 0, 0, w, h, COLOR_FORMAT_XRGB_NOALPHA);
      
      for (int i = 0; i < (int)plX.Size(); i++)
        plX[i] *= point;
      for (int i = 0; i < (int)plY.Size(); i++)
        plY[i] *= point;
      for (int i = 0; i < (int)pgX.Size(); i++)
        pgX[i] *= point;
      for (int i = 0; i < (int)pgY.Size(); i++)
        pgY[i] *= point;
    
      canvas.LineStyleSet(STYLE_SOLID);
      drawPrimitives
      //canvas.LineStyleSet(STYLE_DOT);
      //drawPrimitives
      //canvas.LineStyleSet(STYLE_DASH);
      //drawPrimitives
      //canvas.LineStyleSet(STYLE_DASHDOTDOT);
      //drawPrimitives
      
      Sleep(6000);
      canvas.Destroy();
     }
    

    Alle hinzugefügten Methoden sind gelb hervorgehoben. Wir führen das Beispiel aus, um das Ergebnis zu sehen.

    Primitive mit Wu-Glättung

    Wir können sehen, dass sich die Qualität der Zeichnung dank der Glättung mit dem Wu-Algorithmus erneut verbessert hat. De-kommentieren Sie nun die Zeilen im Skript und starten Sie es erneut.

    Ändern des Linienstils (Wu's)

    Der Linienstil ändert sich genauso wie im vorherigen Beispiel, aber die Qualität der geglätteten primitiven Bilder ist höher.

    Zeichnen von Primitiven mit veränderbarer Linienbreite

    Wir erstellen das Skript auf der Grundlage des vorherigen Beispiels und nennen es DrawPrimitivesThick.mq5. Dann ersetzen wir die vorhandenen Methoden zum Zeichnen von Primitiven durch Methoden mit dem Präfix "Thick" und entfernen die Methoden, die keine Entsprechung haben, wie in der Auflistung gezeigt. Nun kommentieren wir die unnötigen Zeichenfolgen wie im vorherigen Beispiel aus.

    #property script_show_inputs
    
    //--- input parameters
    input int sleep = 1000;
    
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    #include <Canvas\Canvas.mqh>
    
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    #define drawPrimitives canvas.Erase(ColorToARGB(clrWhite, 255));                                      \
      canvas.LineThickVertical(point, point, h - point, color_, size, lineStyle, endStyle);               \
      canvas.LineThickHorizontal(point, w - point, h - point, color_, size, lineStyle, endStyle);         \
      canvas.LineThick(point * 2, point * 12, point * 8, point * 8, color_, size, lineStyle, endStyle);   \
      canvas.PolylineThick(plX, plY, color_, size, lineStyle, endStyle);                                  \
      canvas.PolygonThick(pgX, pgY, color_, size, lineStyle, endStyle);                                   \
      canvas.Update(true);                                                                                \
      Sleep(sleep);
    
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
     {
      CCanvas canvas;
      int w, h;
      int minSize, point;
      uint color_ = ColorToARGB(clrDodgerBlue, 255);
      uint fillColor = ColorToARGB(clrRed, 255);
      int plX[] = {6, 8, 9, 11, 12, 14}, plY[] = {13, 11, 12, 10, 11, 9};
      int pgX[] = {17, 18, 17, 15, 14, 15, 17}, pgY[] = {7, 5, 3, 3, 5, 7, 7};
      ENUM_LINE_STYLE lineStyle = STYLE_SOLID;
      int size = 3;
      ENUM_LINE_END endStyle = LINE_END_ROUND;
      w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
      h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
      minSize = (int)fmin(w, h);
      point = minSize / 15;
      canvas.CreateBitmapLabel("canvas", 0, 0, w, h, COLOR_FORMAT_XRGB_NOALPHA);
      
      for (int i = 0; i < (int)plX.Size(); i++)
        plX[i] *= point;
      for (int i = 0; i < (int)plY.Size(); i++)
        plY[i] *= point;
      for (int i = 0; i < (int)pgX.Size(); i++)
        pgX[i] *= point;
      for (int i = 0; i < (int)pgY.Size(); i++)
        pgY[i] *= point;
    
      canvas.LineStyleSet(STYLE_SOLID);
      drawPrimitives
      //canvas.LineStyleSet(STYLE_DOT);
      //drawPrimitives
      //canvas.LineStyleSet(STYLE_DASH);
      //drawPrimitives
      //canvas.LineStyleSet(STYLE_DASHDOTDOT);
      drawPrimitives
      
      Sleep(6000);
      canvas.Destroy();
     }
    

    Die hinzugefügten Methoden sind gelb hervorgehoben. Wie Sie vielleicht schon bemerkt haben, verfügen die von uns betrachteten Methoden über zwei zusätzliche Parameter: size, die Zeilenbreite, und end_style, der Stil für das Zeilenende. Führen Sie das Skript aus und sehen Sie sich das Ergebnis an.

    Anzeige der Primitive (Dick)

    Das Bild zeigt die Primitive mit dicken Linien an, deren Breite wie oben beschrieben geändert werden kann. Erstellen wir nun das Skript, das auf dem vorherigen basiert, und nennen es DrawPrimitivesThick-2.mq5. Damit können wir alle möglichen Kombinationen von Linienstärken, Linienstilen und Linienabschlussstilen sehen. Um dies zu erreichen, entfernen wir die zuvor auskommentierten Zeichenfolgen und fügen dem Makro Methode1 hinzu, in dem wir den Stil für den Linienabschluss ändern und das Makro Methode0 nach jeder Änderung aufrufen. Wir rufen die primitiven Zeichenmethoden im Makro Methode0 auf. Im Makro drawPrimitives ändern wir den Linienstil und rufen method1 nach jeder Änderung des Linienstils auf. Das DrawPrimitivesMakro wird in der Schleife aufgerufen, in der die Linienbreite in einem bestimmten Bereich geändert wird.

    #property script_show_inputs
    
    //--- input parameters
    input int sleep = 1000;
    input int beginSize = 1;
    input int endSize = 4;
    
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    #include <Canvas\Canvas.mqh>
    
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    #define drawPrimitives lineStyle = STYLE_SOLID; \
      method1                                       \
      lineStyle = STYLE_DOT;                        \
      method1                                       \
      lineStyle = STYLE_DASH;                       \
      method1                                       \
      lineStyle = STYLE_DASHDOTDOT;                 \
      method1 
      
    #define method1 endStyle = LINE_END_ROUND; \
      method0                                  \
      endStyle = LINE_END_BUTT;                \
      method0                                  \
      endStyle = LINE_END_SQUARE;              \
      method0
    
    #define method0 canvas.Erase(ColorToARGB(clrWhite, 255));                                                                                                      \
      canvas.LineThickVertical(point, point, h - point, color_, size, lineStyle, endStyle);                                                                        \
      canvas.LineThickHorizontal(point, w - point, h - point, color_, size, lineStyle, endStyle);                                                                  \
      canvas.LineThick(point * 2, point * 12, point * 8, point * 8, color_, size, lineStyle, endStyle);                                                            \
      canvas.PolylineThick(plX, plY, color_, size, lineStyle, endStyle);                                                                                           \
      canvas.PolygonThick(pgX, pgY, color_, size, lineStyle, endStyle);                                                                                            \
      canvas.TextOut(point * 2, point, "Size: " + (string)size + "; Style: " + EnumToString(lineStyle) + "; End Style: " + EnumToString(endStyle) + ";", color_);  \
      canvas.Update(true);                                                                                                                                         \
      Sleep(sleep);
    
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
     {
      CCanvas canvas;
      int w, h;
      int minSize, point;
      uint color_ = ColorToARGB(clrDodgerBlue, 255);
      uint fillColor = ColorToARGB(clrRed, 255);
      int plX[] = {6, 8, 9, 11, 12, 14}, plY[] = {13, 11, 12, 10, 11, 9};
      int pgX[] = {17, 18, 17, 15, 14, 15, 17}, pgY[] = {7, 5, 3, 3, 5, 7, 7};
      ENUM_LINE_STYLE lineStyle = STYLE_SOLID;
      int size = 3;
      ENUM_LINE_END endStyle = LINE_END_ROUND;
      w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
      h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
      minSize = (int)fmin(w, h);
      point = minSize / 15;
      canvas.CreateBitmapLabel("canvas", 0, 0, w, h, COLOR_FORMAT_XRGB_NOALPHA);
      canvas.FontSet("Calibri", -120);
      
      for (int i = 0; i < (int)plX.Size(); i++)
        plX[i] *= point;
      for (int i = 0; i < (int)plY.Size(); i++)
        plY[i] *= point;
      for (int i = 0; i < (int)pgX.Size(); i++)
        pgX[i] *= point;
      for (int i = 0; i < (int)pgY.Size(); i++)
        pgY[i] *= point;
    
      for (int i = beginSize; i <= endSize; i++) {
        size = i;
        drawPrimitives
      }
      
      Sleep(6000);
      canvas.Destroy();
     }
    //+------------------------------------------------------------------+
    

    Alle Codeänderungen sind gelb hervorgehoben. Das resultierende Skript nimmt den Bereich der Zeilenbreite aus den Eingabeparametern beginSize und endSize und bewegt sich entlang aller möglichen Kombinationsmöglichkeiten von Linienstil und Abschluss mithilfe der Makros. Als Ergebnis erhalten wir eine Enumeration aller möglichen primitiven Zeilenparameter, deren Kombinationen beim Ausführen des Skripts zu sehen sind. Die folgende GIF-Animation zeigt das Ergebnis der Skriptausführung.

    Primitive mit verschiedenen Parametern

    Die Animation zeigt die Änderungen in der Linienbreite, dem Linienstil und der Art des Linienendes. Alle diese Parameter werden auf dem Canvas angezeigt und sind auch für uns sichtbar.

    Zeichnen von geglätteten Primitiven nach dem Bézier-Algorithmus mit einer veränderbaren Linienbreite

    Verwenden wir eines unserer vorherigen Beispiele DrawPrimitivesThick.mq5 und erstellen ein Skript DrawPrimitivesSmooth.mq5. Wir ersetzen PolylineThick und PolygonThick durch PolylineSmooth und PolygonSmooth im Skript. Verschieben Sie die Koordinaten der Polylinie und des Polygons um zwei Quadrate nach links, sodass die Primitive ungefähr in der Mitte angezeigt werden.

    void OnStart()
     {
      CCanvas canvas;
      int w, h;
      int minSize, point;
      uint color_ = ColorToARGB(clrDodgerBlue, 255);
      uint fillColor = ColorToARGB(clrRed, 255);
      int plX[] = {3, 5, 6, 8, 9, 11}, plY[] = {13, 11, 12, 10, 11, 9};
      int pgX[] = {14, 15, 14, 12, 11, 12}, pgY[] = {7, 5, 3, 3, 5, 7};
    

    Die geänderten Werte sind im Programmcode hervorgehoben. Das resultierende Skript sollte wie folgt aussehen.

    #property script_show_inputs
    
    //--- input parameters
    input int sleep = 1000;
    
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    #include <Canvas\Canvas.mqh>
    
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    #define drawPrimitives canvas.Erase(ColorToARGB(clrWhite, 255));                                      \
      canvas.PolylineSmooth(plX, plY, color_, size, lineStyle, endStyle);                                 \
      canvas.PolygonSmooth(pgX, pgY, color_, size, lineStyle, endStyle);                                  \
      canvas.Update(true);                                                                                \
      Sleep(sleep);
    
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
     {
      CCanvas canvas;
      int w, h;
      int minSize, point;
      uint color_ = ColorToARGB(clrDodgerBlue, 255);
      uint fillColor = ColorToARGB(clrRed, 255);
      int plX[] = {3, 5, 6, 8, 9, 11}, plY[] = {13, 11, 12, 10, 11, 9};
      int pgX[] = {14, 15, 14, 12, 11, 12}, pgY[] = {7, 5, 3, 3, 5, 7};
      ENUM_LINE_STYLE lineStyle = STYLE_SOLID;
      int size = 3;
      ENUM_LINE_END endStyle = LINE_END_BUTT;
      w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
      h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
      minSize = (int)fmin(w, h);
      point = minSize / 15;
      canvas.CreateBitmapLabel("canvas", 0, 0, w, h, COLOR_FORMAT_XRGB_NOALPHA);
      
      for (int i = 0; i < (int)plX.Size(); i++)
        plX[i] *= point;
      for (int i = 0; i < (int)plY.Size(); i++)
        plY[i] *= point;
      for (int i = 0; i < (int)pgX.Size(); i++)
        pgX[i] *= point;
      for (int i = 0; i < (int)pgY.Size(); i++)
        pgY[i] *= point;
    
      canvas.LineStyleSet(STYLE_SOLID);
      drawPrimitives
      
      Sleep(6000);
      canvas.Destroy();
     }
    

    Führen Sie das Beispiel aus und überprüfen Sie das Ergebnis.

    Primitive des Bezier-Algorithmus (geglättet)

    Wir sehen eine Polylinie und ein Polygon, die mit Bézier-Kurven erstellt wurden. Die Methoden PolylineSmooth und PolygonSmooth verfügen über die Parameter tension, Glättung, und step, Näherungsschritt, auf denen Bezier-Kurven basieren. Erstellen wir ein Beispiel, in dem diese Parameter nach bestimmten Intervallen geändert werden und der Bildschirm die Ergebnisse dieser Änderungen anzeigt. Wir verwenden das Beispiel DrawPrimitivesThick-2.mq5 und erstellen darauf basierend das Skript DrawPrimitivesSmooth-2.mq5. Werfen wir einen Blick auf das Ergebnis.

    #property script_show_inputs
    
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    #include <Canvas\Canvas.mqh>
    
    //--- input parameters
    input int sleep = 1000;
    input int lineSize = 3;
    input ENUM_LINE_STYLE lineStyle = STYLE_SOLID;
    input ENUM_LINE_END lineEndStyle = LINE_END_BUTT;
    input double minTension = 0.0;
    input double stepTension = 0.1;
    input double maxTension = 1.0;
    input double minStep = 1.0;
    input double stepStep = 5.0;
    input double maxStep = 21.0;
    
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
     {
      CCanvas canvas;
      int w, h;
      int minSize, point;
      uint color_ = ColorToARGB(clrDodgerBlue, 255);
      uint fillColor = ColorToARGB(clrRed, 255);
      int plX[] = {3, 5, 6, 8, 9, 11}, plY[] = {13, 11, 12, 10, 11, 9};
      int pgX[] = {14, 15, 14, 12, 11, 12}, pgY[] = {7, 5, 3, 3, 5, 7};
      ENUM_LINE_STYLE style = lineStyle;
      int size = lineSize;
      ENUM_LINE_END endStyle = lineEndStyle;
      w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
      h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
      minSize = (int)fmin(w, h);
      point = minSize / 15;
      canvas.CreateBitmapLabel("canvas", 0, 0, w, h, COLOR_FORMAT_XRGB_NOALPHA);
      canvas.FontSet("Calibri", -120);
      
      for (int i = 0; i < (int)plX.Size(); i++)
        plX[i] *= point;
      for (int i = 0; i < (int)plY.Size(); i++)
        plY[i] *= point;
      for (int i = 0; i < (int)pgX.Size(); i++)
        pgX[i] *= point;
      for (int i = 0; i < (int)pgY.Size(); i++)
        pgY[i] *= point;
    
      for (double tension = minTension; tension <= maxTension; tension += stepTension)
        for (double step = minStep; step <= maxStep; step += stepStep)
         {
           canvas.Erase(ColorToARGB(clrWhite, 255));
           canvas.PolylineSmooth(plX, plY, color_, size, style, endStyle, tension, step);
           canvas.PolygonSmooth(pgX, pgY, color_, size, style, endStyle, tension, step);
           canvas.TextOut(point * 2, point, "Size: " + (string)size + "; Style: " + EnumToString(style) + "; End Style: " + EnumToString(endStyle) + ";", color_);
           canvas.TextOut(point * 2, point * 2, "Tension: " + DoubleToString(tension, 2) + ";" + " Step: " + DoubleToString(step, 2) + ";", color_);
           canvas.Update(true);
           Sleep(sleep);
         }
      
      canvas.Destroy();
     }
    

    Wie wir sehen können, wurde der Code fast vollständig geändert. Die folgenden Einstellungen sind unter den Eingabeparametern erschienen: lineSize, Liniengröße, lineStyle, Linienstil und lineEndStyle, Stil für das Linienende (sie nehmen jetzt nicht an der Schleife teil). Außerdem gibt es noch Eingabeparameter für die Einstellung der Zeichenparameter von Bézier-Kurven: minTension, maxTension und stepTension (tension, Bereich des Glättungsparameters und Schrittweite), sowie minStep, maxStep und stepStep (step Wertebereich und Schrittweite der Näherung). Weiterhin wirken diese Parameter in den Schleifen, die alle möglichen Parameterkombinationen von tension und step gemäß den angegebenen stepTension und stepStep entsprechend einstellen. Führen Sie das Beispiel aus und sehen Sie das Ergebnis.

    Primitive mit verschiedenen Parametern

    Die GIF-Animation zeigt geglättete Primitive mit veränderbaren Glättungsparametern, deren Werte in der zweiten Zeile des Bildes angezeigt werden.

    Zeichnet gefüllte Primitive

    Bei der Entwicklung grafischer Anwendungen kann es vorkommen, dass Sie ein Primitiv ausfüllen möchten. Die Klasse CCanvas verfügt über Methoden mit dem Präfix Fill für diesen Fall. Diese Methoden malen die entsprechenden Primitive in einer einheitlichen Farbe. Wir verwenden das vorherige Beispiel DrawPrimitivesWu.mq5, um das Skript DrawPrimitivesFill.mq5 zu erstellen, das alle Koordinaten für die benötigten Primitive enthält, sodass wir nur noch die entsprechenden Methoden einfügen und die Füllfarbe festlegen müssen. Hinzufügen der Methoden FillPolygon, FillTriangle, FillCircle und FillEllipse. Die Methode Fill wird weiter unten beschrieben (wenn wir das Füllen besprechen). Werfen wir einen Blick auf den resultierenden Code.

    void OnStart()
     {
      CCanvas canvas;
      int w, h;
      int minSize, point;
      uint fillColor = ColorToARGB(clrRed, 255);
      int pgX[] = {4, 5, 7, 8, 7, 5}, pgY[] = {12, 10, 10, 12, 14, 14};
      w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
      h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
      minSize = (int)fmin(w, h);
      point = minSize / 15;
      canvas.CreateBitmapLabel("canvas", 0, 0, w, h, COLOR_FORMAT_XRGB_NOALPHA);
      
      for (int i = 0; i < (int)pgX.Size(); i++)
        pgX[i] *= point;
      for (int i = 0; i < (int)pgY.Size(); i++)
        pgY[i] *= point;
    
      canvas.Erase(ColorToARGB(clrWhite, 255));
      canvas.FillPolygon(pgX, pgY, fillColor);
      canvas.FillTriangle(point * 4, point * 8, point * 4, point * 3, point * 9, point * 3, fillColor); 
      canvas.FillCircle(point * 13, point * 12, point * 2, fillColor);
      canvas.FillEllipse(point * 11, point * 3, point * 15, point * 6, fillColor);
      canvas.Update(true);                                                                         
      
      Sleep(6000);
      canvas.Destroy();
     }
    

    Alle geänderten und hinzugefügten Zeichenfolgen sind gelb hervorgehoben. Starten Sie das Skript, um die Ergebnisse zu sehen.

    Starten Sie das Skript, um die Ergebnisse zu sehen. 

    Das Bild zeigt die Primitive, die mit einer einheitlichen Farbe gefüllt sind. Darüber können wir normale Primitive als Rahmen zeichnen oder die Primitive mit einem editierbaren Linienstil, die wir oben betrachtet haben.

    Ausfüllen

    Wenn Sie schon einmal das Werkzeug für das Ausfüllen in grafischen Editoren verwendet haben, wissen Sie vielleicht schon, worüber ich sprechen werde. Es ist an der Zeit, einen Blick auf die oben erwähnte Methode Fill (ausfüllen) zu werfen. Das ist die Methode, mit der wir den Bildbereich einer bestimmten Farbe mit einer anderen Farbe ausfüllen können. Erstellen wir ein neues Beispiel als Indikator (da er im Gegensatz zum Skript die Chart-Ereignisse (OnChartEvent) verarbeitet). Sie fragen sich vielleicht, warum wir Chart-Ereignisse hier behandeln müssen? Wir werden ein Beispiel erstellen, bei dem Sie die Füllfarbe auswählen und auf einen beliebigen Bildpunkt klicken können, um ihn mit der ausgewählten Farbe zu füllen. Wir erstellen einen neuen Indikator und nennen Sie ihn Filling.mq5. Werfen wir einen Blick auf den Code des Indikators.

    #property indicator_chart_window
    #property indicator_plots 0
    
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    #include <Canvas\Canvas.mqh>
    #include <ChartObjects\ChartObjectsTxtControls.mqh>
    
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    CCanvas canvas;
    CChartObjectButton colors[14];
    CChartObjectButton * oldSelected = NULL;
    uint fillColor = 0;
    
    //+------------------------------------------------------------------+
    //| Custom indicator initialization function                         |
    //+------------------------------------------------------------------+
    int OnInit()
     {
      int w, h;
      int minSize, point;
      uint Color1 = ColorToARGB(clrDodgerBlue, 255);
      uint Color2 = ColorToARGB(clrRed, 255);
      int pgX[] = {4, 5, 7, 8, 7, 5}, pgY[] = {12, 10, 10, 12, 14, 14};
      w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
      h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
      minSize = (int)fmin(w, h);
      point = minSize / 15;
    
      canvas.CreateBitmapLabel("canvas", 0, 0, w, h, COLOR_FORMAT_XRGB_NOALPHA);
      
      canvas.FontSet("Calibri", -120);
      
      for (int i = 0; i < (int)pgX.Size(); i++)
        pgX[i] *= point;
      for (int i = 0; i < (int)pgY.Size(); i++)
        pgY[i] *= point;
    
      canvas.Erase(ColorToARGB(clrWhite, 255));
      canvas.Polygon(pgX, pgY, Color1);
      canvas.FillTriangle(point * 4, point * 8, point * 4, point * 3, point * 9, point * 3, Color2);
      canvas.FillCircle(point * 13, point * 12, point * 2, Color2);
      canvas.Ellipse(point * 11, point * 3, point * 15, point * 6, Color1);
      canvas.Update(true); 
      
      ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
      
      for (int i = 0; i < (int)colors.Size(); i++)
       {
        colors[i].Create(0, "color-"+(string)i, 0, (int)((i + 2.8) * point), point, 21, 21);
        colors[i].SetInteger(OBJPROP_BGCOLOR, ColorToARGB(CCanvas::GetDefaultColor(i), 0));
       }
      
      if ((oldSelected = GetPointer(colors[(int)colors.Size()-1])) == NULL)
        return(INIT_FAILED); 
      oldSelected.State(1);  
      fillColor = ColorToARGB((color)oldSelected.GetInteger(OBJPROP_BGCOLOR), 255);
      
      return(INIT_SUCCEEDED);
     }
     
    //+------------------------------------------------------------------+
    //| Custom indicator deinitialization function                       |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
     {
      canvas.Destroy();
     }
     
    //+------------------------------------------------------------------+
    //| Custom indicator iteration function                              |
    //+------------------------------------------------------------------+
    int OnCalculate(const int rates_total,
                    const int prev_calculated,
                    const int begin,
                    const double &price[])
     {
    //---
    //--- return value of prev_calculated for next call
      return(rates_total);
     }
    //+------------------------------------------------------------------+
    //| ChartEvent function                                              |
    //+------------------------------------------------------------------+
    void OnChartEvent(const int id,
                      const long &lparam,
                      const double &dparam,
                      const string &sparam)
     {
      uint mouseState = (uint)sparam;
      int x = (int)lparam, y = (int)dparam;
      CChartObjectButton * colorBtn;
      int left, right, bottom, top;
      
      if (id == CHARTEVENT_MOUSE_MOVE) 
        if ((mouseState & 1) == 1) 
         {
          for (int i = 0; i < (int)colors.Size(); i++) 
           {
            if ((colorBtn = GetPointer(colors[i])) == NULL)
              return;
            left = colorBtn.X_Distance();
            top = colorBtn.Y_Distance();
            right = left + colorBtn.X_Size() - 1;
            bottom = top + colorBtn.Y_Size() - 1;
            if (x >= left && x <= right && y >= top && y <= bottom) {
              fillColor = ColorToARGB((color)colorBtn.GetInteger(OBJPROP_BGCOLOR), 255);
              if (oldSelected == NULL)
                return;
              oldSelected.State(0);
              oldSelected = GetPointer(colorBtn);
              ChartRedraw();
              return;
            }
           }
          canvas.Fill(x, y, fillColor);
          canvas.Update(true);
         }
        
     }
    

    Wie funktioniert das Beispiel? Zunächst wurde die canvas Variable auf globaler Ebene spezifiziert, um für die gesamte Dauer der Indikatoroperation auf sie zugreifen zu können.

    CCanvas canvas;

    Das Array aus 14 Schaltflächen ermöglicht die Auswahl von Farben. Durch Drücken einer der Schaltflächen wird die Ausfüllfarbe festgelegt.

    CChartObjectButton colors[14];

    Als Nächstes wurde der oldSelected Zeiger deklariert.

    CChartObjectButton * oldSelected = NULL;

    Der folgende Parameter wird verwendet, um die gedrückte Schaltfläche zu speichern, sie an eine Ausgangsposition zurückzubringen oder die Ausfüllfarbe zu speichern.

    uint fillColor = 0;

    Als Nächstes sehen wir im OnInit-Handler den bereits bekannten Code, der Canvas erstellt und Primitive darauf zeichnet.

    int OnInit()
     {
      int w, h;
      int minSize, point;
      uint Color1 = ColorToARGB(clrDodgerBlue, 255);
      uint Color2 = ColorToARGB(clrRed, 255);
      int pgX[] = {4, 5, 7, 8, 7, 5}, pgY[] = {12, 10, 10, 12, 14, 14};
      w = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS, 0);
      h = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
      minSize = (int)fmin(w, h);
      point = minSize / 15;
      canvas.CreateBitmapLabel("canvas", 0, 0, w, h, COLOR_FORMAT_XRGB_NOALPHA);
      
      canvas.FontSet("Calibri", -120);
      
      for (int i = 0; i < (int)pgX.Size(); i++)
        pgX[i] *= point;
      for (int i = 0; i < (int)pgY.Size(); i++)
        pgY[i] *= point;
    
      canvas.Erase(ColorToARGB(clrWhite, 255));                                                                                                                 
      canvas.Polygon(pgX, pgY, Color1);                                                          
      canvas.FillTriangle(point * 4, point * 8, point * 4, point * 3, point * 9, point * 3, Color2); 
      canvas.FillCircle(point * 13, point * 12, point * 2, Color2);                                  
      canvas.Ellipse(point * 11, point * 3, point * 15, point * 6, Color1);                     
      canvas.Update(true);
    ...
    

    Die Behandlung von Mausereignissen wird anschließend aktiviert.

    ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);

    Als Nächstes erstellen wir die Schaltflächen in verschiedenen Farben (um die Farbe des Ausfüllens zu ändern).

      for (int i = 0; i < (int)colors.Size(); i++)
       {
        colors[i].Create(0, "color-"+(string)i, 0, (int)((i + 2.8) * point), point, 21, 21);
        colors[i].SetInteger(OBJPROP_BGCOLOR, ColorToARGB(CCanvas::GetDefaultColor(i), 0));
       }
    

    Achten Sie darauf, woher die Farben der Schaltflächen stammen. Das Codefragment ist gelb hervorgehoben. Wir können die statistische Methode CCanvas::GetDefaultColor sehen. Durch das Setzen des Parameters i, beginnend bei 0, können wir die Farbpalette erhalten.

    Danach initialisieren wir den Link, um die Schaltfläche oldSelected an die Startposition zu bringen.

      if ((oldSelected = GetPointer(colors[(int)colors.Size()-1])) == NULL)
        return(INIT_FAILED); 
      oldSelected.State(1);
    

    Die Farbe fillColor zum Ausfüllen wird initialisiert.

    fillColor = ColorToARGB((color)oldSelected.GetInteger(OBJPROP_BGCOLOR), 255);

    Danach werden die Ereignisse auf dem Chart in OnChartEvent behandelt. Darin werden die Variablen für den Empfang und die Speicherung der Mausparameter deklariert.

    void OnChartEvent(const int id,
                      const long &lparam,
                      const double &dparam,
                      const string &sparam)
     {
      uint mouseState = (uint)sparam;
      int x = (int)lparam, y = (int)dparam;
    ...
    

    Als Nächstes wird die Variable deklariert, der Zeiger zum Speichern einer gedrückten Schaltfläche,

    CChartObjectButton * colorBtn;

    die dadurch definiert wird, dass der Cursor die Koordinaten der Schaltflächenseiten eingibt, wenn man mit der linken Maustaste auf die Schaltfläche klickt. Die Koordinaten der Seiten werden in den folgenden Variablen gespeichert.

    int left, right, bottom, top;

    Als Nächstes wird das Ereignis CHARTEVENT_MOUSE_MOVE kontrolliert, ebenso wie das Klicken der linken Maustaste.

      if (id == CHARTEVENT_MOUSE_MOVE) 
        if ((mouseState & 1) == 1) 
         {
          ...
         }
    

    Dies ist der Code, in dem die Farbauswahl und das Ausfüllen des Bildes durchgeführt werden. Hier durchläuft die Schleife nacheinander alle Schaltflächen im Array colors, das die Schaltfläche definiert, die ein Nutzer gedrückt hat, speichert die Farbe der Schaltfläche in der Variablen fillColor und setzt die zuvor gedrückte Schaltfläche oldSelected an die Ausgangsposition. Als Nächstes verlassen wir die Ereignisbehandlung (Return), da der Klick auf die Schaltfläche und nicht auf das auszufüllende Bild erfolgt ist. Wenn der Klick auf das Bild und nicht auf eine der Farb-Schaltflächen erfolgt, wird das Steuerelement weitergereicht und das Ausfüllen wird mit der Methode Fill und der ausgewählten Farbe fillColor mit anschließender Bildaktualisierung (Methode Update) durchgeführt.

      if (id == CHARTEVENT_MOUSE_MOVE) 
        if ((mouseState & 1) == 1) 
         {
          for (int i = 0; i < (int)colors.Size(); i++) 
           {
            if ((colorBtn = GetPointer(colors[i])) == NULL)
              return;
            left = colorBtn.X_Distance();
            top = colorBtn.Y_Distance();
            right = left + colorBtn.X_Size() - 1;
            bottom = top + colorBtn.Y_Size() - 1;
            if (x >= left && x <= right && y >= top && y <= bottom) {
              fillColor = ColorToARGB((color)colorBtn.GetInteger(OBJPROP_BGCOLOR), 255);
              if (oldSelected == NULL)
                return;
              oldSelected.State(0);
              oldSelected = GetPointer(colorBtn);
              ChartRedraw();
              return;
            }
           }
          canvas.Fill(x, y, fillColor);
          canvas.Update(true);
         }
    

    Führen wir das resultierende Beispiel aus und versuchen wir, das Ausfüllen durchzuführen.

    Ausfüllen mit CCanvas

    Entsprechend der GIF-Animation haben wir das Ausfüllen mit dem Mitteln von CCanvas durchgeführt, das ähnlich wie das Hilfsmittel zum Ausfüllen in grafischen Editoren funktioniert.


    Schlussfolgerung

    In diesem Artikel wurde die Klasse CCanvas untersucht und ihre Methoden beschrieben, damit die Nutzer die Funktionsprinzipien der Klasse vollständig verstehen. Die mitgelieferten Beispiele erklären die Prinzipien der Handhabung der Klasse CCanvas und verdeutlichen gleichzeitig einige schwer verständliche theoretische Aspekte.


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

    Beigefügte Dateien |
    MQL5.zip (21.43 KB)
    Letzte Kommentare | Zur Diskussion im Händlerforum (1)
    Ştefan Dascălu
    Ştefan Dascălu | 24 Apr. 2022 in 06:39
    Vielen Dank für diesen nützlichen Artikel
    Lernen Sie, wie man ein Handelssystem mit dem CCI entwickelt Lernen Sie, wie man ein Handelssystem mit dem CCI entwickelt
    In diesem neuen Artikel aus unserer Serie zum Erlernen der Entwicklung von Handelssystemen stelle ich Ihnen den Commodities Channel Index (CCI) vor, erkläre seine Besonderheiten und zeige Ihnen, wie Sie ein Handelssystem auf Basis dieses Indikators erstellen können.
    Mehrere Indikatoren auf einem Chart (Teil 01): Die Konzepte verstehen Mehrere Indikatoren auf einem Chart (Teil 01): Die Konzepte verstehen
    Heute werden wir lernen, wie man mehrere Indikatoren gleichzeitig auf einem Chart anzeigt, ohne einen separaten Bereich zu belegen. Viele Händler fühlen sich sicherer, wenn sie mehrere Indikatoren gleichzeitig beobachten (z.B. RSI, STOCASTIC, MACD, ADX und einige andere), oder in einigen Fällen sogar verschiedene Vermögenswerte, aus denen ein Index besteht.
    Datenwissenschaft und maschinelles Lernen (Teil 02): Logistische Regression Datenwissenschaft und maschinelles Lernen (Teil 02): Logistische Regression
    Die Klassifizierung von Daten ist für einen Algo-Händler und einen Programmierer von entscheidender Bedeutung. In diesem Artikel werden wir uns auf einen logistischen Klassifizierungsalgorithmus konzentrieren, der uns wahrscheinlich helfen kann, die Ja- oder Nein-Stimmen, die Höhen und Tiefen, Käufe und Verkäufe zu identifizieren.
    MVC-Entwurfsmuster und seine Anwendung (Teil 2): Diagramm der Interaktion zwischen den drei Komponenten MVC-Entwurfsmuster und seine Anwendung (Teil 2): Diagramm der Interaktion zwischen den drei Komponenten
    Dieser Artikel ist eine Fortsetzung und Vervollständigung des im vorherigen Artikel behandelten Themas: das MVC-Muster in MQL-Programmen. In diesem Artikel werden wir ein Diagramm der möglichen Interaktion zwischen den drei Komponenten des Musters betrachten.