
Grafiken in der Bibliothek DoEasy (Teil 75): Methoden zur Handhabung von Primitiven und Text im grafischen Grundelement
Inhalt
- Konzept
- Verbesserung der Klassenbibliothek
- Die Methoden für das Arbeit mit den Primitiven
- Die Methoden für die Arbeit mit Texten
- Test
- Was kommt als Nächstes?
Konzept
Ich setze die Entwicklung der Objektklasse des grafischen Basiselements fort, die als Grundlage für die Erstellung komplexerer grafischer Objekte der Bibliothek dient. Im vorherigen Artikel habe ich das Konzept der Konstruktion des grafischen Basisobjekts erstellt, das grafische Element erzeugt und es mit grundlegenden Eigenschaften ausgestattet, die bereits gesetzt, geändert und empfangen werden können.
Da die Klasse CCanvas für das Zeichnen "auf der Leinwand" (canvas) gedacht ist, verfügt sie über die Methoden zum Arbeiten mit grafischen Primitiven und einem Text. In diesem Artikel werde ich die Methoden des Elementobjekts erstellen, mit denen wir auf die Methoden der Klasse CCanvas zum Zeichnen zugreifen und diese behandeln können. Diese Methoden sollen einfach sein. Sie werden verwendet, um erweiterte Zeichenmethoden in den Nachfolgeobjekten der Elementobjektklasse zu erstellen.
Zusätzlich zum Erstellen der Methoden für die Arbeit mit Primitiven werde ich auch die Methoden für die Arbeit mit Dateien erstellen. Unsere grafischen Objekte, die die GUI-Elemente der benutzerdefinierten Programme sind, sollen sich ihre Eigenschaften, ihren Status und ihre Position im Chart "merken", z. B. beim Wechsel zu einem anderen Zeitrahmen. Um dies zu erreichen, werde ich die Objekteigenschaften in einer Datei speichern. Beim Konstruieren eines Objekts werden die Eigenschaften daraus gelesen.
Da Dateien jedoch in der grafischen Objektsammelklasse behandelt werden sollen und ich diese noch nicht entwickelt habe, werde ich einfach die Methoden zum Speichern und Hochladen von grafischen Objekteigenschaften hinzufügen. Beim Erstellen der grafischen Objektsammelklasse werden wir die Methoden zum Speichern und Hochladen der Eigenschaften verwenden, die ich hier für das grafische Elementobjekt entwickeln werde.
Außerdem werde ich die Klasse für die Arbeit mit Farbe benötigen. Ich werde sie ebenfalls in die Bibliothek aufnehmen.
Die Klasse wird aus der MQL5.com-Codebibliothek stammen, die von Dmitry Fedoseev entwickelt und der Community zur Verfügung gestellt wurde.
Das Endergebnis wird ein grafisches Element sein, das als Basis für weitere grafische Objekte der Bibliothek verwendet werden kann.
Verbesserung der Klassenbibliothek
Wenn wir ein Objekt der Klasse CCanvas, das Transparenz besitzt, löschen müssen, sollten wir die Methode Erase() verwenden, die standardmäßig Null erhält:
//--- clear/fill color void Erase(const uint clr=0);
In unserem Fall ist dies eine falsche Lösung. Wenn wir die Leinwand mit Null löschen, verlieren wir seinen Alphakanal (Farbtransparenzkanal), was letztendlich zu Artefakten führt, wenn wir auf der Leinwand mit auf diese Weise gelöschtem Alphakanal zeichnen.
Um die Leinwand mit dem Alphakanal zu löschen, verwenden Sie 0x00FFFFFF anstelle von Null.
Dies ist eine vollständig transparente schwarze Farbe im ARGB-Format (Alpha = 0, Rot = 255, Grün = 255, Blau = 255).
Wir fügen in \MQL5\Include\DoEasy\Defines.mqh die Makro-Ersetzung für die Angabe einer solchen Farbe hinzu:
//--- Canvas parameters #define PAUSE_FOR_CANV_UPDATE (16) // Canvas update frequency #define NULL_COLOR (0x00FFFFFF) // Zero for the canvas with the alpha channel //+------------------------------------------------------------------+
Bei der Anzeige eines Textes auf der Leinwand mit der Methode TextOut() können wir den Ankerwinkel (Textausrichtungspunkt) für die Textnachricht setzen — das begrenzende Rechteck, relativ zu dem sich die Nachricht befinden soll. Die Ankerpunkte werden mit Hilfe von sechs Flags gesetzt — Kombinationen von zwei Flags, die im Folgenden aufgelistet sind:
Flags für die horizontale Textausrichtung:
- TA_LEFT — Ankerpunkt auf der linken Seite des Begrenzungsrechtecks
- TA_CENTER — horizontaler Ankerpunkt in der Mitte des Begrenzungsrechtecks
- TA_RIGHT — Ankerpunkt auf der rechten Seite des Begrenzungsrechtecks
Vertikale Textausrichtungsflags:
- TA_TOP — Ankerpunkt an der oberen Seite des Begrenzungsrechtecks
- TA_VCENTER — vertikaler Ankerpunkt in der Mitte des Begrenzungsrechtecks
- TA_BOTTOM — Ankerpunkt an der Unterseite des Begrenzungsrechtecks
Die möglichen Kombinationen der Flags und der durch sie gesetzten Verankerungsmethoden sind unten dargestellt:
Um eine Verwirrung darüber zu vermeiden, welches Flag zuerst kommt, setzen wir einfach eine nutzerdefinierte Enumeration, die alle möglichen Flag-Kombinationen für die Ausrichtung eines Textes relativ zu seinem Ankerpunkt angibt:
//+------------------------------------------------------------------+ //| Data for handling graphical elements | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| List of anchoring methods | //| (horizontal and vertical text alignment) | //+------------------------------------------------------------------+ enum ENUM_TEXT_ANCHOR { TEXT_ANCHOR_LEFT_TOP = 0, // Text anchor point at the upper left corner of the bounding rectangle TEXT_ANCHOR_CENTER_TOP = 1, // Text anchor point at the top center side of the bounding rectangle TEXT_ANCHOR_RIGHT_TOP = 2, // Text anchor point at the upper right corner of the bounding rectangle TEXT_ANCHOR_LEFT_CENTER = 4, // Text anchor point at the left center side of the bounding rectangle TEXT_ANCHOR_CENTER = 5, // Text anchor point at the center of the bounding rectangle TEXT_ANCHOR_RIGHT_CENTER = 6, // Text anchor point at the right center side of the bounding rectangle TEXT_ANCHOR_LEFT_BOTTOM = 8, // Text anchor point at the bottom left corner of the bounding rectangle TEXT_ANCHOR_CENTER_BOTTOM = 9, // Text anchor point at the bottom center side of the bounding rectangle TEXT_ANCHOR_RIGHT_BOTTOM = 10, // Text anchor point at the bottom right corner of the bounding rectangle }; //+------------------------------------------------------------------+ //| The list of graphical element types | //+------------------------------------------------------------------+
Hier haben wir drei Flags für jede Textverankerungsebene gesetzt:
- oberer vertikaler Ankerpunkt (TA_TOP) — 0:
- linker horizontaler Ankerpunkt (TA_LEFT) — 0,
- mittlerer horizontaler Ankerpunkt (TA_CENTER) — 1,
- rechter horizontaler Ankerpunkt (TA_RIGHT) — 2.
- linker horizontaler Ankerpunkt (TA_LEFT) — 0,
- Vertikaler Ankerpunkt Mitte (TA_VCENTER) — 4:
- linker horizontaler Ankerpunkt (TA_LEFT) — 0,
- mittlerer horizontaler Ankerpunkt (TA_CENTER) — 1,
- rechter horizontaler Ankerpunkt (TA_RIGHT) — 2.
- linker horizontaler Ankerpunkt (TA_LEFT) — 0,
- Unterer vertikaler Ankerpunkt (TA_BOTTOM) — 8:
- linker horizontaler Ankerpunkt (TA_LEFT) — 0,
- mittlerer horizontaler Ankerpunkt (TA_CENTER) — 1,
- rechter horizontaler Ankerpunkt (TA_RIGHT) — 2.
- linker horizontaler Ankerpunkt (TA_LEFT) — 0,
Jeder der Werte der Enumeration ENUM_TEXT_ANCHOR entspricht der oben beschriebenen Kombination von korrekt gesetzten Flags:
- TEXT_ANCHOR_LEFT_TOP = (TA_LEFT | TA_TOP) = 0,
- TEXT_ANCHOR_CENTER_TOP = (TA_CENTER | TA_TOP) = 1,
- TEXT_ANCHOR_RIGHT_TOP = (TA_RIGHT | TA_TOP) = 2,
- TEXT_ANCHOR_LEFT_CENTER = (TA_LEFT | TA_VCENTER) = 4,
- TEXT_ANCHOR_CENTER = (TA_CENTER | TA_VCENTER) = 5,
- TEXT_ANCHOR_RIGHT_CENTER = (TA_RIGHT | TA_VCENTER) = 6,
- TEXT_ANCHOR_LEFT_BOTTOM = (TA_LEFT | TA_BOTTOM) = 8,
- TEXT_ANCHOR_CENTER_BOTTOM = (TA_CENTER | TA_BOTTOM) = 9,
- TEXT_ANCHOR_RIGHT_BOTTOM = (TA_RIGHT | TA_BOTTOM) = 10.
Diese Enumeration werde ich im weiteren Verlauf verwenden, um die Ausrichtung des Textes relativ zu seinem Ankerpunkt festzulegen.
Da ich gerade den grafischen Teil der Bibliothek entwickle, benötige ich verschiedene Methoden für die Arbeit mit Farbe.
Die Bibliothek der MQL5.com-Quellcodes enthält die bemerkenswerte Bibliothek von Funktionen für die Arbeit mit Farben, die freundlicherweise von Dmitry Fedoseev für den allgemeinen Gebrauch bereitgestellt wurde.
Verbessern wir die Klasse CColors noch etwas — wir machen sie statisch, nicht um das Klassenobjekt zu setzen, sondern direkt auf seine Methoden zuzugreifen, zum Beispiel mit dem Kontextauflösungsoperator (::):
class_name::variable
Die in der Bibliothek enthaltene Klasse CColors erlaubt es uns also, auf die Methoden der Klasse überall im Code zuzugreifen (auch in einem benutzerdefinierten Programm), um zum Beispiel zwei Farben zu mischen — Blau mit der Deckkraft von 128 und Rot mit der Deckkraft von 64:
CColors::BlendColors(ColorToARGB(clrBlue,128),ColorToARGB(clrRed,64));
Speichern wir die Klassendatei im Bibliotheksverzeichnis \MQL5\Include\DoEasy\Services\ in Colors.mqh.
//+------------------------------------------------------------------+ //| Colors.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://www.mql5.com/en/users/integer" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Class for working with color | //+------------------------------------------------------------------+ class CColors { private: static double Arctan2(const double x,const double y); static double Hue_To_RGB(double v1,double v2,double vH); public: //+--------------------------------------------------------------------+ //| The list of functions from http://www.easyrgb.com/index.php?X=MATH | //+--------------------------------------------------------------------+ static void RGBtoXYZ(const double aR,const double aG,const double aB,double &oX,double &oY,double &oZ); static void XYZtoRGB(const double aX,const double aY,const double aZ,double &oR,double &oG,double &oB); static void XYZtoYxy(const double aX,const double aY,const double aZ,double &oY,double &ox,double &oy); static void YxyToXYZ(const double aY,const double ax,const double ay,double &oX,double &oY,double &oZ); static void XYZtoHunterLab(const double aX,const double aY,const double aZ,double &oL,double &oa,double &ob); static void HunterLabToXYZ(const double aL,const double aa,const double ab,double &oX,double &oY,double &oZ); static void XYZtoCIELab(const double aX,const double aY,const double aZ,double &oCIEL,double &oCIEa,double &oCIEb); static void CIELabToXYZ(const double aCIEL,const double aCIEa,const double aCIEb,double &oX,double &oY,double &oZ); static void CIELabToCIELCH(const double aCIEL,const double aCIEa,const double aCIEb,double &oCIEL,double &oCIEC,double &oCIEH); static void CIELCHtoCIELab(const double aCIEL,const double aCIEC,const double aCIEH,double &oCIEL,double &oCIEa,double &oCIEb); static void XYZtoCIELuv(const double aX,const double aY,const double aZ,double &oCIEL,double &oCIEu,double &oCIEv); static void CIELuvToXYZ(const double aCIEL,const double aCIEu,const double aCIEv,double &oX,double &oY,double &oZ); static void RGBtoHSL(const double aR,const double aG,const double aB,double &oH,double &oS,double &oL); static void HSLtoRGB(const double aH,const double aS,const double aL,double &oR,double &oG,double &oB); static void RGBtoHSV(const double aR,const double aG,const double aB,double &oH,double &oS,double &oV); static void HSVtoRGB(const double aH,const double aS,const double aV,double &oR,double &oG,double &oB); static void RGBtoCMY(const double aR,const double aG,const double aB,double &oC,double &oM,double &oY); static void CMYtoRGB(const double aC,const double aM,const double aY,double &oR,double &oG,double &oB); static void CMYtoCMYK(const double aC,const double aM,const double aY,double &oC,double &oM,double &oY,double &oK); static void CMYKtoCMY(const double aC,const double aM,const double aY,const double aK,double &oC,double &oM,double &oY); static void RGBtoLab(const double aR,const double aG,const double aB,double &oL,double &oa,double &ob); //+------------------------------------------------------------------+ //| Other functions for working with color | //+------------------------------------------------------------------+ static void ColorToRGB(const color aColor,double &aR,double &aG,double &aB); static double GetR(const color aColor); static double GetG(const color aColor); static double GetB(const color aColor); static double GetA(const color aColor); static color RGBToColor(const double aR,const double aG,const double aB); static color MixColors(const color aCol1,const color aCol2,const double aK); static color BlendColors(const uint lower_color,const uint upper_color); static void Gradient(color &aColors[],color &aOut[],int aOutCount,bool aCycle=false); static void RGBtoXYZsimple(double aR,double aG,double aB,double &oX,double &oY,double &oZ); static void XYZtoRGBsimple(const double aX,const double aY,const double aZ,double &oR,double &oG,double &oB); static color Negative(const color aColor); static color StandardColor(const color aColor,int &aIndex); static double RGBtoGray(double aR,double aG,double aB); static double RGBtoGraySimple(double aR,double aG,double aB); }; //+------------------------------------------------------------------+ //| Class methods | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Arctan2 | //+------------------------------------------------------------------+ double CColors::Arctan2(const double x,const double y) { if(y==0) return(x<0 ? M_PI : 0); else { if(x>0) return(::atan(y/x)); if(x<0) return(y>0 ? atan(y/x)+M_PI : atan(y/x)-M_PI); else return(y<0 ? -M_PI_2 : M_PI_2); } } //+------------------------------------------------------------------+ //| Hue_To_RGB | //+------------------------------------------------------------------+ double CColors::Hue_To_RGB(double v1,double v2,double vH) { if(vH<0) vH+=1.0; if(vH>1.0) vH-=1; if((6.0*vH)<1.0) return(v1+(v2-v1)*6.0*vH); if((2.0*vH)<1.0) return(v2); if((3.0*vH)<2.0) return(v1+(v2-v1)*((2.0/3.0)-vH)*6.0); //--- return(v1); } //+------------------------------------------------------------------+ //| Conversion of RGB into XYZ | //+------------------------------------------------------------------+ void CColors::RGBtoXYZ(const double aR,const double aG,const double aB,double &oX,double &oY,double &oZ) { double var_R=aR/255; double var_G=aG/255; double var_B=aB/255; //--- if(var_R>0.04045) var_R=::pow((var_R+0.055)/1.055,2.4); else var_R=var_R/12.92; //--- if(var_G>0.04045) var_G=::pow((var_G+0.055)/1.055,2.4); else var_G=var_G/12.92; //--- if(var_B>0.04045) var_B=::pow((var_B+0.055)/1.055,2.4); else var_B=var_B/12.92; //--- var_R =var_R*100.0; var_G =var_G*100.0; var_B =var_B*100.0; oX =var_R*0.4124+var_G*0.3576+var_B*0.1805; oY =var_R*0.2126+var_G*0.7152+var_B*0.0722; oZ =var_R*0.0193+var_G*0.1192+var_B*0.9505; } //+------------------------------------------------------------------+ //| Conversion of XYZ into RGB | //+------------------------------------------------------------------+ void CColors::XYZtoRGB(const double aX,const double aY,const double aZ,double &oR,double &oG,double &oB) { double var_X =aX/100; double var_Y =aY/100; double var_Z =aZ/100; double var_R =var_X*3.2406+var_Y*-1.5372+var_Z*-0.4986; double var_G =var_X*(-0.9689)+var_Y*1.8758+var_Z*0.0415; double var_B =var_X*0.0557+var_Y*(-0.2040)+var_Z*1.0570; //--- if(var_R>0.0031308) var_R=1.055*(::pow(var_R,1.0/2.4))-0.055; else var_R=12.92*var_R; //--- if(var_G>0.0031308) var_G=1.055*(::pow(var_G,1.0/2.4))-0.055; else var_G=12.92*var_G; //--- if(var_B>0.0031308) var_B=1.055*(::pow(var_B,1.0/2.4))-0.055; else var_B=12.92*var_B; //--- oR =var_R*255.0; oG =var_G*255.0; oB =var_B*255.0; } //+------------------------------------------------------------------+ //| Conversion of XYZ into Yxy | //+------------------------------------------------------------------+ void CColors::XYZtoYxy(const double aX,const double aY,const double aZ,double &oY,double &ox,double &oy) { oY =aY; ox =aX/(aX+aY+aZ); oy =aY/(aX+aY+aZ); } //+------------------------------------------------------------------+ //| Conversion of Yxy into XYZ | //+------------------------------------------------------------------+ void CColors::YxyToXYZ(const double aY,const double ax,const double ay,double &oX,double &oY,double &oZ) { oX =ax*(aY/ay); oY =aY; oZ =(1.0-ax-ay)*(aY/ay); } //+------------------------------------------------------------------+ //| Conversion of XYZ into HunterLab | //+------------------------------------------------------------------+ void CColors::XYZtoHunterLab(const double aX,const double aY,const double aZ,double &oL,double &oa,double &ob) { oL =10.0*::sqrt(aY); oa =17.5*(((1.02*aX)-aY)/::sqrt(aY)); ob =7.0*((aY-(0.847*aZ))/::sqrt(aY)); } //+------------------------------------------------------------------+ //| Conversion of HunterLab into XYZ | //+------------------------------------------------------------------+ void CColors::HunterLabToXYZ(const double aL,const double aa,const double ab,double &oX,double &oY,double &oZ) { double var_Y =aL/10.0; double var_X =aa/17.5*aL/10.0; double var_Z =ab/7.0*aL/10.0; //--- oY =::pow(var_Y,2); oX =(var_X+oY)/1.02; oZ =-(var_Z-oY)/0.847; } //+------------------------------------------------------------------+ //| Conversion of XYZ into CIELab | //+------------------------------------------------------------------+ void CColors::XYZtoCIELab(const double aX,const double aY,const double aZ,double &oCIEL,double &oCIEa,double &oCIEb) { double ref_X =95.047; double ref_Y =100.0; double ref_Z =108.883; double var_X =aX/ref_X; double var_Y =aY/ref_Y; double var_Z =aZ/ref_Z; //--- if(var_X>0.008856) var_X=::pow(var_X,1.0/3.0); else var_X=(7.787*var_X)+(16.0/116.0); //--- if(var_Y>0.008856) var_Y=::pow(var_Y,1.0/3.0); else var_Y=(7.787*var_Y)+(16.0/116.0); //--- if(var_Z>0.008856) var_Z=::pow(var_Z,1.0/3.0); else var_Z=(7.787*var_Z)+(16.0/116.0); //--- oCIEL =(116.0*var_Y)-16.0; oCIEa =500.0*(var_X-var_Y); oCIEb =200*(var_Y-var_Z); } //+------------------------------------------------------------------+ //| Conversion of CIELab into ToXYZ | //+------------------------------------------------------------------+ void CColors::CIELabToXYZ(const double aCIEL,const double aCIEa,const double aCIEb,double &oX,double &oY,double &oZ) { double var_Y =(aCIEL+16.0)/116.0; double var_X =aCIEa/500.0+var_Y; double var_Z =var_Y-aCIEb/200.0; //--- if(::pow(var_Y,3)>0.008856) var_Y=::pow(var_Y,3); else var_Y=(var_Y-16.0/116.0)/7.787; //--- if(::pow(var_X,3)>0.008856) var_X=::pow(var_X,3); else var_X=(var_X-16.0/116.0)/7.787; //--- if(::pow(var_Z,3)>0.008856) var_Z=::pow(var_Z,3); else var_Z=(var_Z-16.0/116.0)/7.787; //--- double ref_X =95.047; double ref_Y =100.0; double ref_Z =108.883; //--- oX =ref_X*var_X; oY =ref_Y*var_Y; oZ =ref_Z*var_Z; } //+------------------------------------------------------------------+ //| Conversion of CIELab into CIELCH | //+------------------------------------------------------------------+ void CColors::CIELabToCIELCH(const double aCIEL,const double aCIEa,const double aCIEb,double &oCIEL,double &oCIEC,double &oCIEH) { double var_H=Arctan2(aCIEb,aCIEa); //--- if(var_H>0) var_H=(var_H/M_PI)*180.0; else var_H=360.0-(::fabs(var_H)/M_PI)*180.0; //--- oCIEL =aCIEL; oCIEC =::sqrt(::pow(aCIEa,2)+::pow(aCIEb,2)); oCIEH =var_H; } //+------------------------------------------------------------------+ //| Conversion of CIELCH into CIELab | //+------------------------------------------------------------------+ void CColors::CIELCHtoCIELab(const double aCIEL,const double aCIEC,const double aCIEH,double &oCIEL,double &oCIEa,double &oCIEb) { //--- Arguments from 0 to 360° oCIEL =aCIEL; oCIEa =::cos(M_PI*aCIEH/180.0)*aCIEC; oCIEb =::sin(M_PI*aCIEH/180)*aCIEC; } //+------------------------------------------------------------------+ //| Conversion of XYZ into CIELuv | //+------------------------------------------------------------------+ void CColors::XYZtoCIELuv(const double aX,const double aY,const double aZ,double &oCIEL,double &oCIEu,double &oCIEv) { double var_U =(4.0*aX)/(aX+(15.0*aY)+(3.0*aZ)); double var_V =(9.0*aY)/(aX+(15.0*aY)+(3.0*aZ)); double var_Y =aY/100.0; //--- if(var_Y>0.008856) var_Y=::pow(var_Y,1.0/3.0); else var_Y=(7.787*var_Y)+(16.0/116.0); //--- double ref_X =95.047; double ref_Y =100.000; double ref_Z =108.883; double ref_U =(4.0*ref_X)/(ref_X+(15.0*ref_Y)+(3.0*ref_Z)); double ref_V =(9.0*ref_Y)/(ref_X+(15.0*ref_Y)+(3.0*ref_Z)); //--- oCIEL =(116.0*var_Y)-16.0; oCIEu =13.0*oCIEL*(var_U-ref_U); oCIEv =13.0*oCIEL*(var_V-ref_V); } //+------------------------------------------------------------------+ //| Conversion of CIELuv into XYZ | //+------------------------------------------------------------------+ void CColors::CIELuvToXYZ(const double aCIEL,const double aCIEu,const double aCIEv,double &oX,double &oY,double &oZ) { double var_Y=(aCIEL+16.0)/116.0; //--- if(::pow(var_Y,3)>0.008856) var_Y=::pow(var_Y,3); else var_Y=(var_Y-16.0/116.0)/7.787; //--- double ref_X =95.047; double ref_Y =100.000; double ref_Z =108.883; double ref_U =(4.0*ref_X)/(ref_X+(15.0*ref_Y)+(3.0*ref_Z)); double ref_V =(9.0*ref_Y)/(ref_X+(15.0*ref_Y)+(3.0*ref_Z)); double var_U =aCIEu/(13.0*aCIEL)+ref_U; double var_V =aCIEv/(13.0*aCIEL)+ref_V; //--- oY=var_Y*100.0; oX=-(9.0*oY*var_U)/((var_U-4.0)*var_V-var_U*var_V); oZ=(9.0*oY-(15.0*var_V*oY)-(var_V*oX))/(3.0*var_V); } //+------------------------------------------------------------------+ //| Conversion of RGB into HSL | //+------------------------------------------------------------------+ void CColors::RGBtoHSL(const double aR,const double aG,const double aB,double &oH,double &oS,double &oL) { double var_R =(aR/255); double var_G =(aG/255); double var_B =(aB/255); double var_Min =::fmin(var_R,::fmin(var_G,var_B)); double var_Max =::fmax(var_R,::fmax(var_G,var_B)); double del_Max =var_Max-var_Min; //--- oL=(var_Max+var_Min)/2; //--- if(del_Max==0) { oH=0; oS=0; } else { if(oL<0.5) oS=del_Max/(var_Max+var_Min); else oS=del_Max/(2.0-var_Max-var_Min); //--- double del_R =(((var_Max-var_R)/6.0)+(del_Max/2.0))/del_Max; double del_G =(((var_Max-var_G)/6.0)+(del_Max/2.0))/del_Max; double del_B =(((var_Max-var_B)/6.0)+(del_Max/2.0))/del_Max; //--- if(var_R==var_Max) oH=del_B-del_G; else if(var_G==var_Max) oH=(1.0/3.0)+del_R-del_B; else if(var_B==var_Max) oH=(2.0/3.0)+del_G-del_R; //--- if(oH<0) oH+=1.0; //--- if(oH>1) oH-=1.0; } } //+------------------------------------------------------------------+ //| Conversion of HSL into RGB | //+------------------------------------------------------------------+ void CColors::HSLtoRGB(const double aH,const double aS,const double aL,double &oR,double &oG,double &oB) { if(aS==0) { oR=aL*255; oG=aL*255; oB=aL*255; } else { double var_2=0.0; //--- if(aL<0.5) var_2=aL*(1.0+aS); else var_2=(aL+aS)-(aS*aL); //--- double var_1=2.0*aL-var_2; oR =255.0*Hue_To_RGB(var_1,var_2,aH+(1.0/3.0)); oG =255.0*Hue_To_RGB(var_1,var_2,aH); oB =255.0*Hue_To_RGB(var_1,var_2,aH-(1.0/3.0)); } } //+------------------------------------------------------------------+ //| Conversion of RGB into HSV | //+------------------------------------------------------------------+ void CColors::RGBtoHSV(const double aR,const double aG,const double aB,double &oH,double &oS,double &oV) { const double var_R =(aR/255.0); const double var_G =(aG/255.0); const double var_B =(aB/255.0); const double var_Min =::fmin(var_R,::fmin(var_G, var_B)); const double var_Max =::fmax(var_R,::fmax(var_G,var_B)); const double del_Max =var_Max-var_Min; //--- oV=var_Max; //--- if(del_Max==0) { oH=0; oS=0; } else { oS=del_Max/var_Max; const double del_R =(((var_Max-var_R)/6.0)+(del_Max/2))/del_Max; const double del_G =(((var_Max-var_G)/6.0)+(del_Max/2))/del_Max; const double del_B =(((var_Max-var_B)/6.0)+(del_Max/2))/del_Max; //--- if(var_R==var_Max) oH=del_B-del_G; else if(var_G==var_Max) oH=(1.0/3.0)+del_R-del_B; else if(var_B==var_Max) oH=(2.0/3.0)+del_G-del_R; //--- if(oH<0) oH+=1.0; //--- if(oH>1.0) oH-=1.0; } } //+------------------------------------------------------------------+ //| Conversion of HSV into RGB | //+------------------------------------------------------------------+ void CColors::HSVtoRGB(const double aH,const double aS,const double aV,double &oR,double &oG,double &oB) { if(aS==0) { oR =aV*255.0; oG =aV*255.0; oB =aV*255.0; } else { double var_h=aH*6.0; //--- if(var_h==6) var_h=0; //--- int var_i =int(var_h); double var_1 =aV*(1.0-aS); double var_2 =aV*(1.0-aS*(var_h-var_i)); double var_3 =aV*(1.0-aS*(1.0-(var_h-var_i))); double var_r =0.0; double var_g =0.0; double var_b =0.0; //--- if(var_i==0) { var_r =aV; var_g =var_3; var_b =var_1; } else if(var_i==1.0) { var_r=var_2; var_g=aV; var_b=var_1; } else if(var_i==2.0) { var_r=var_1; var_g=aV; var_b=var_3; } else if(var_i==3) { var_r=var_1; var_g=var_2; var_b=aV; } else if(var_i==4) { var_r=var_3; var_g=var_1; var_b=aV; } else { var_r=aV; var_g=var_1; var_b=var_2; } //--- oR =var_r*255.0; oG =var_g*255.0; oB =var_b*255.0; } } //+------------------------------------------------------------------+ //| Conversion of RGB into CMY | //+------------------------------------------------------------------+ void CColors::RGBtoCMY(const double aR,const double aG,const double aB,double &oC,double &oM,double &oY) { oC =1.0-(aR/255.0); oM =1.0-(aG/255.0); oY =1.0-(aB/255.0); } //+------------------------------------------------------------------+ //| Conversion of CMY into RGB | //+------------------------------------------------------------------+ void CColors::CMYtoRGB(const double aC,const double aM,const double aY,double &oR,double &oG,double &oB) { oR =(1.0-aC)*255.0; oG =(1.0-aM)*255.0; oB =(1.0-aY)*255.0; } //+------------------------------------------------------------------+ //| Conversion of CMY into CMYK | //+------------------------------------------------------------------+ void CColors::CMYtoCMYK(const double aC,const double aM,const double aY,double &oC,double &oM,double &oY,double &oK) { double var_K=1; //--- if(aC<var_K) var_K=aC; if(aM<var_K) var_K=aM; if(aY<var_K) var_K=aY; //--- if(var_K==1.0) { oC =0; oM =0; oY =0; } else { oC =(aC-var_K)/(1.0-var_K); oM =(aM-var_K)/(1.0-var_K); oY =(aY-var_K)/(1.0-var_K); } //--- oK=var_K; } //+------------------------------------------------------------------+ //| Conversion of CMYK into CMY | //+------------------------------------------------------------------+ void CColors::CMYKtoCMY(const double aC,const double aM,const double aY,const double aK,double &oC,double &oM,double &oY) { oC =(aC*(1.0-aK)+aK); oM =(aM*(1.0-aK)+aK); oY =(aY*(1.0-aK)+aK); } //+------------------------------------------------------------------+ //| Conversion of RGB into Lab | //+------------------------------------------------------------------+ void CColors::RGBtoLab(const double aR,const double aG,const double aB,double &oL,double &oa,double &ob) { double X=0,Y=0,Z=0; RGBtoXYZ(aR,aG,aB,X,Y,Z); XYZtoHunterLab(X,Y,Z,oL,oa,ob); } //+------------------------------------------------------------------+ //| Getting values of the RGB components | //+------------------------------------------------------------------+ void CColors::ColorToRGB(const color aColor,double &aR,double &aG,double &aB) { aR =GetR(aColor); aG =GetG(aColor); aB =GetB(aColor); } //+------------------------------------------------------------------+ //| Getting the R component value | //+------------------------------------------------------------------+ double CColors::GetR(const color aColor) { return(aColor&0xff); } //+------------------------------------------------------------------+ //| Getting the G component value | //+------------------------------------------------------------------+ double CColors::GetG(const color aColor) { return((aColor>>8)&0xff); } //+------------------------------------------------------------------+ //| Getting the B component value | //+------------------------------------------------------------------+ double CColors::GetB(const color aColor) { return((aColor>>16)&0xff); } //+------------------------------------------------------------------+ //| Getting the A component value | //+------------------------------------------------------------------+ double CColors::GetA(const color aColor) { return(double(uchar((aColor)>>24))); } //+------------------------------------------------------------------+ //| Conversion of RGB into const color | //+------------------------------------------------------------------+ color CColors::RGBToColor(const double aR,const double aG,const double aB) { int int_r =(int)::round(aR); int int_g =(int)::round(aG); int int_b =(int)::round(aB); int Color =0; //--- Color=int_b; Color<<=8; Color|=int_g; Color<<=8; Color|=int_r; //--- return((color)Color); } //+------------------------------------------------------------------+ //| Getting the value of the intermediary color between two colors | //+------------------------------------------------------------------+ color CColors::MixColors(const color aCol1,const color aCol2,const double aK) { //--- aK - from 0 to 1 double R1=0.0,G1=0.0,B1=0.0,R2=0.0,G2=0.0,B2=0.0; //--- ColorToRGB(aCol1,R1,G1,B1); ColorToRGB(aCol2,R2,G2,B2); //--- R1+=(int)::round(aK*(R2-R1)); G1+=(int)::round(aK*(G2-G1)); B1+=(int)::round(aK*(B2-B1)); //--- return(RGBToColor(R1,G1,B1)); } //+------------------------------------------------------------------+ //| Blending two colors considering the transparency of color on top | //+------------------------------------------------------------------+ color CColors::BlendColors(const uint lower_color,const uint upper_color) { double r1=0,g1=0,b1=0; double r2=0,g2=0,b2=0,alpha=0; double r3=0,g3=0,b3=0; //--- Convert the colors in ARGB format uint pixel_color=::ColorToARGB(upper_color); //--- Get the components of the lower and upper colors ColorToRGB(lower_color,r1,g1,b1); ColorToRGB(pixel_color,r2,g2,b2); //--- Get the transparency percentage from 0.00 to 1.00 alpha=GetA(upper_color)/255.0; //--- If there is transparency if(alpha<1.0) { //--- Blend the components taking the alpha channel into account r3=(r1*(1-alpha))+(r2*alpha); g3=(g1*(1-alpha))+(g2*alpha); b3=(b1*(1-alpha))+(b2*alpha); //--- Adjustment of the obtained values r3=(r3>255)? 255 : r3; g3=(g3>255)? 255 : g3; b3=(b3>255)? 255 : b3; } else { r3=r2; g3=g2; b3=b2; } //--- Combine the obtained components and return the color return(RGBToColor(r3,g3,b3)); } //+------------------------------------------------------------------+ //| Getting an array of the specified size with a color gradient | //+------------------------------------------------------------------+ void CColors::Gradient(color &aColors[], // List of colors color &aOut[], // Return array int aOutCount, // Set the size of the return array bool aCycle=false) // Closed-loop cycle. Return array ends with the same color as it starts with { ::ArrayResize(aOut,aOutCount); //--- int InCount =::ArraySize(aColors)+aCycle; int PrevJ =0; int nci =0; double K =0.0; //--- for(int i=1; i<InCount; i++) { int J=(aOutCount-1)*i/(InCount-1); //--- for(int j=PrevJ; j<=J; j++) { if(aCycle && i==InCount-1) { nci =0; K =1.0*(j-PrevJ)/(J-PrevJ+1); } else { nci =i; K =1.0*(j-PrevJ)/(J-PrevJ); } aOut[j]=MixColors(aColors[i-1],aColors[nci],K); } PrevJ=J; } } //+------------------------------------------------------------------+ //| One more variant of conversion of RGB into XYZ and | //| corresponding conversion of XYZ into RGB | //+------------------------------------------------------------------+ void CColors::RGBtoXYZsimple(double aR,double aG,double aB,double &oX,double &oY,double &oZ) { aR/=255; aG/=255; aB/=255; aR*=100; aG*=100; aB*=100; //--- oX=0.431*aR+0.342*aG+0.178*aB; oY=0.222*aR+0.707*aG+0.071*aB; oZ=0.020*aR+0.130*aG+0.939*aB; } //+------------------------------------------------------------------+ //| XYZtoRGBsimple | //+------------------------------------------------------------------+ void CColors::XYZtoRGBsimple(const double aX,const double aY,const double aZ,double &oR,double &oG,double &oB) { oR=3.063*aX-1.393*aY-0.476*aZ; oG=-0.969*aX+1.876*aY+0.042*aZ; oB=0.068*aX-0.229*aY+1.069*aZ; } //+------------------------------------------------------------------+ //| Negative color | //+------------------------------------------------------------------+ color CColors::Negative(const color aColor) { double R=0.0,G=0.0,B=0.0; ColorToRGB(aColor,R,G,B); //--- return(RGBToColor(255-R,255-G,255-B)); } //+------------------------------------------------------------------+ //| Search for the most similar color | //| in the set of standard colors of the terminal | //+------------------------------------------------------------------+ color CColors::StandardColor(const color aColor,int &aIndex) { color m_c[]= { clrBlack,clrDarkGreen,clrDarkSlateGray,clrOlive,clrGreen,clrTeal,clrNavy,clrPurple,clrMaroon,clrIndigo, clrMidnightBlue,clrDarkBlue,clrDarkOliveGreen,clrSaddleBrown,clrForestGreen,clrOliveDrab,clrSeaGreen, clrDarkGoldenrod,clrDarkSlateBlue,clrSienna,clrMediumBlue,clrBrown,clrDarkTurquoise,clrDimGray, clrLightSeaGreen,clrDarkViolet,clrFireBrick,clrMediumVioletRed,clrMediumSeaGreen,clrChocolate,clrCrimson, clrSteelBlue,clrGoldenrod,clrMediumSpringGreen,clrLawnGreen,clrCadetBlue,clrDarkOrchid,clrYellowGreen, clrLimeGreen,clrOrangeRed,clrDarkOrange,clrOrange,clrGold,clrYellow,clrChartreuse,clrLime,clrSpringGreen, clrAqua,clrDeepSkyBlue,clrBlue,clrFuchsia,clrRed,clrGray,clrSlateGray,clrPeru,clrBlueViolet,clrLightSlateGray, clrDeepPink,clrMediumTurquoise,clrDodgerBlue,clrTurquoise,clrRoyalBlue,clrSlateBlue,clrDarkKhaki,clrIndianRed, clrMediumOrchid,clrGreenYellow,clrMediumAquamarine,clrDarkSeaGreen,clrTomato,clrRosyBrown,clrOrchid, clrMediumPurple,clrPaleVioletRed,clrCoral,clrCornflowerBlue,clrDarkGray,clrSandyBrown,clrMediumSlateBlue, clrTan,clrDarkSalmon,clrBurlyWood,clrHotPink,clrSalmon,clrViolet,clrLightCoral,clrSkyBlue,clrLightSalmon, clrPlum,clrKhaki,clrLightGreen,clrAquamarine,clrSilver,clrLightSkyBlue,clrLightSteelBlue,clrLightBlue, clrPaleGreen,clrThistle,clrPowderBlue,clrPaleGoldenrod,clrPaleTurquoise,clrLightGray,clrWheat,clrNavajoWhite, clrMoccasin,clrLightPink,clrGainsboro,clrPeachPuff,clrPink,clrBisque,clrLightGoldenrod,clrBlanchedAlmond, clrLemonChiffon,clrBeige,clrAntiqueWhite,clrPapayaWhip,clrCornsilk,clrLightYellow,clrLightCyan,clrLinen, clrLavender,clrMistyRose,clrOldLace,clrWhiteSmoke,clrSeashell,clrIvory,clrHoneydew,clrAliceBlue,clrLavenderBlush, clrMintCream,clrSnow,clrWhite,clrDarkCyan,clrDarkRed,clrDarkMagenta,clrAzure,clrGhostWhite,clrFloralWhite }; //--- double m_rv=0.0,m_gv=0.0,m_bv=0.0; //--- ColorToRGB(aColor,m_rv,m_gv,m_bv); //--- double m_md=0.3*::pow(255,2)+0.59*::pow(255,2)+0.11*::pow(255,2)+1; aIndex=0; //--- for(int i=0; i<138; i++) { double m_d=0.3*::pow(GetR(m_c[i])-m_rv,2)+0.59*::pow(GetG(m_c[i])-m_gv,2)+0.11*::pow(GetB(m_c[i])-m_bv,2); //--- if(m_d<m_md) { m_md =m_d; aIndex =i; } } //--- return(m_c[aIndex]); } //+------------------------------------------------------------------+ //| Conversion into gray color | //+------------------------------------------------------------------+ double CColors::RGBtoGray(double aR,double aG,double aB) { aR/=255; aG/=255; aB/=255; //--- aR=::pow(aR,2.2); aG=::pow(aG,2.2); aB=::pow(aB,2.2); //--- double rY=0.21*aR+0.72*aG+0.07*aB; rY=::pow(rY,1.0/2.2); //--- return(rY); } //+------------------------------------------------------------------+ //| Simple conversion into gray color | //+------------------------------------------------------------------+ double CColors::RGBtoGraySimple(double aR,double aG,double aB) { aR/=255; aG/=255; aB/=255; double rY=0.3*aR+0.59*aG+0.11*aB; //--- return(rY); } //+------------------------------------------------------------------+
Alle vorgenommenen Änderungen beschränken sich auf das Setzen der Modifikation 'static' für jede der Methoden sowie auf einige rein kosmetische Änderungen (abgesehen von den Variablennamen in den Methodenargumenten), die meinem Codierungsstil entsprechen. Außerdem habe ich die Methode RGBtoLab() zum Konvertieren des RGB-Farbmodells in Lab hinzugefügt. Die Methode konvertiert einfach das RGB-Modell in XYZ, das wiederum in das Lab-Farbmodell umgewandelt wird. Dieser Sachverhalt wurde einst von Anatoli Kazharski in seinem Artikel "Graphical Interfaces IX: The Color Picker Control (Chapter 1)" betrachtet:
Ich habe einfach den Rat des Autors befolgt.
Um die CColors Klasse für die gesamte Bibliothek und darauf basierende Programme sichtbar zu machen, müssen wir die Klassendatei in die Datei der Bibliotheksdienstfunktionen in \MQL5\Include\DoEasy\Services\DELib.mqh einbinden:
//+------------------------------------------------------------------+ //| DELib.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\Defines.mqh" #include "Message.mqh" #include "TimerCounter.mqh" #include "Pause.mqh" #include "Colors.mqh" //+------------------------------------------------------------------+ //| Service functions | //+------------------------------------------------------------------+
Ich werde die Klasse hier nicht benötigen, aber ich werde sie später beim Erstellen der Nachfolgeklassen der grafischen Elementobjekte verwenden.
Jedes grafische Objekt verfügt zumindest über seine Größe und die Koordinaten seiner Position auf dem Chart. Außerdem sind unsere Objekte mit vielen Eigenschaften ausgestattet, die bei laufendem Programm geändert werden können. Wenn wir jedoch das Programm neu starten oder den Zeitrahmen wechseln, werden alle Änderungen, die wir an grafischen Objekten vorgenommen haben, während das Programm läuft, zurückgesetzt. Damit sich jedes Objekt den Zustand seiner Eigenschaften merken kann, müssen wir sie außerhalb speichern. In diesem Fall lesen nach dem Neustart des Programms alle grafischen Objekte, die während seines Betriebs konstruiert und geändert wurden, ihre entsprechenden Eigenschaften (die ab dem Zeitpunkt des Zurücksetzens relevant sind) aus der Datei und stellen sie wieder her. Um dies zu erreichen, müssen wir der Objektklasse des grafischen Elements zwei Methoden hinzufügen — die Methode zum Schreiben der Objekteigenschaften in die Datei und die zum Lesen der Objekteigenschaften aus der Datei.
Um die Objekteigenschaften zu lesen und zu schreiben, werde ich die Objekteigenschaften in der Struktur speichern, während die Struktur mit den Standardfunktionen StructToCharArray() und CharArrayToStruct() sowohl in der Datei gespeichert als auch aus ihr gelesen werden kann.
Jedes grafische Objekt soll die Methoden zum Speichern von Eigenschaften in die Datei und zum Lesen aus der Datei aufweisen, da jedes grafische Objekt, das auf dem Canvas basiert, ein Nachkomme des grafischen Elementobjekts sein soll, in dem die Methoden gesetzt werden sollen. Wenn das Objekt also zusammengesetzt ist (d. h. es besteht aus anderen Objekten, die auf dem grafischen Element basieren), können wir die Zustände aller seiner untergeordneten Objekte nacheinander entsprechend dem Objektindex in der Liste der untergeordneten Objekte wiederherstellen (der Index ist in der Konstante CANV_ELEMENT_PROP_NUM der Enumeration ENUM_CANV_ELEMENT_PROP_INTEGER der Eigenschaften des Elementobjekts gespeichert).
In diesem Artikel werde ich die Eigenschaften nicht in der Datei speichern/aus der Datei lesen, da dies von der Klasse der grafischen Objektkollektion aus geschehen soll. Ich werde dies später — nach der Erstellung des grafischen Elements — berücksichtigen. Wie auch immer, die Schreib- und Lesemethoden werden hier hinzugefügt.
Da das grafische Element vom Basisobjekts aller grafischen Objekte der CGBaseObj-Bibliothek abgeleitet ist, setzen wir zunächst die virtuelle Methode zum Erzeugen der Struktur aus den Objekteigenschaften und die virtuelle Methode zum Wiederherstellen der Objekteigenschaften aus der Struktur in den geschützten Abschnitt der Objektklassendatei (\MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh):
protected: string m_name_prefix; // Object name prefix string m_name; // Object name long m_chart_id; // Chart ID int m_subwindow; // Subwindow index int m_shift_y; // Subwindow Y coordinate shift int m_type; // Object type //--- Create (1) the object structure and (2) the object from the structure virtual bool ObjectToStruct(void) { return true; } virtual void StructToObject(void){;} public:
Diese Methoden machen hier nichts — sie müssten in den abgeleiteten Klassen neu definiert werden. Der nächste Nachfahre der Klasse ist die Klasse des grafischen Elementobjekts in \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh. Deklarieren wir dieselben virtuellen Methoden in seinem geschützten Abschnitt:
//+------------------------------------------------------------------+ //| Class of the base object of the library graphical objects | //+------------------------------------------------------------------+ class CGCnvElement : public CGBaseObj { protected: CCanvas m_canvas; // CCanvas class object CPause m_pause; // Pause class object //--- Return the cursor position relative to the (1) entire element and (2) the element active area bool CursorInsideElement(const int x,const int y); bool CursorInsideActiveArea(const int x,const int y); //--- Create (1) the object structure and (2) the object from the structure virtual bool ObjectToStruct(void); virtual void StructToObject(void); private:
Im privaten Bereich deklarieren wir die Struktur zur Speicherung aller Objekteigenschaften, das Objekt mit dem Strukturtyp und das Objektstruktur-Array:
private: struct SData { //--- Object integer properties int id; // Element ID int type; // Graphical element type int number; // Element index in the list long chart_id; // Chart ID int subwindow; // Chart subwindow index int coord_x; // Form's X coordinate on the chart int coord_y; // Form's Y coordinate on the chart int width; // Element width int height; // Element height int edge_right; // Element right border int edge_bottom; // Element bottom border int act_shift_left; // Active area offset from the left edge of the element int act_shift_top; // Active area offset from the top edge of the element int act_shift_right; // Active area offset from the right edge of the element int act_shift_bottom; // Active area offset from the bottom edge of the element uchar opacity; // Element opacity color color_bg; // Element background color bool movable; // Element moveability flag bool active; // Element activity flag int coord_act_x; // X coordinate of the element active area int coord_act_y; // Y coordinate of the element active area int coord_act_right; // Right border of the element active area int coord_act_bottom; // Bottom border of the element active area //--- Object real properties //--- Object string properties uchar name_obj[64]; // Graphical element object name uchar name_res[64]; // Graphical resource name }; SData m_struct_obj; // Object structure uchar m_uchar_array[]; // uchar array of the object structure long m_long_prop[ORDER_PROP_INTEGER_TOTAL]; // Integer properties double m_double_prop[ORDER_PROP_DOUBLE_TOTAL]; // Real properties string m_string_prop[ORDER_PROP_STRING_TOTAL]; // String properties
Im öffentlichen Abschnitt der Klasse deklarieren wir die Methoden zum Schreiben und Lesen von Objekteigenschaften aus der Datei:
public: //--- Set object (1) integer, (2) real and (3) string properties void SetProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property,long value) { this.m_long_prop[property]=value; } void SetProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE property,double value) { this.m_double_prop[this.IndexProp(property)]=value; } void SetProperty(ENUM_CANV_ELEMENT_PROP_STRING property,string value) { this.m_string_prop[this.IndexProp(property)]=value; } //--- Return object (1) integer, (2) real and (3) string property from the properties array long GetProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property) const { return this.m_long_prop[property]; } double GetProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE property) const { return this.m_double_prop[this.IndexProp(property)];} string GetProperty(ENUM_CANV_ELEMENT_PROP_STRING property) const { return this.m_string_prop[this.IndexProp(property)];} //--- Return the flag of the object supporting this property virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE property) { return false;} virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_STRING property) { return true; } //--- Compare CGCnvElement objects with each other by all possible properties (for sorting the lists by a specified object property) virtual int Compare(const CObject *node,const int mode=0) const; //--- Compare CGCnvElement objects with each other by all properties (to search equal objects) bool IsEqual(CGCnvElement* compared_obj) const; //--- (1) Save the object to file and (2) upload the object from the file virtual bool Save(const int file_handle); virtual bool Load(const int file_handle); //--- Create the element
Da das Objekt keine realen Eigenschaften hat, sollte die virtuelle Methode, die das Flag der Unterstützung realer Eigenschaften durch das Objekt zurückgibt, false zurückgeben.
Implementieren wir die deklarierten Methoden außerhalb des Klassenkörpers.
Die Methode, die die Struktur des Objekts aus seinen Eigenschaften erzeugt:
//+------------------------------------------------------------------+ //| Create the object structure | //+------------------------------------------------------------------+ bool CGCnvElement::ObjectToStruct(void) { //--- Save integer properties this.m_struct_obj.id=(int)this.GetProperty(CANV_ELEMENT_PROP_ID); // Element ID this.m_struct_obj.type=(int)this.GetProperty(CANV_ELEMENT_PROP_TYPE); // Graphical element type this.m_struct_obj.number=(int)this.GetProperty(CANV_ELEMENT_PROP_NUM); // Eleemnt ID in the list this.m_struct_obj.chart_id=this.GetProperty(CANV_ELEMENT_PROP_CHART_ID); // Chart ID this.m_struct_obj.subwindow=(int)this.GetProperty(CANV_ELEMENT_PROP_WND_NUM); // Chart subwindow index this.m_struct_obj.coord_x=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_X); // Form's X coordinate on the chart this.m_struct_obj.coord_y=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_Y); // Form's Y coordinate on the chart this.m_struct_obj.width=(int)this.GetProperty(CANV_ELEMENT_PROP_WIDTH); // Element width this.m_struct_obj.height=(int)this.GetProperty(CANV_ELEMENT_PROP_HEIGHT); // Element height this.m_struct_obj.edge_right=(int)this.GetProperty(CANV_ELEMENT_PROP_RIGHT); // Element right edge this.m_struct_obj.edge_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_BOTTOM); // Element bottom edge this.m_struct_obj.act_shift_left=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT); // Active area offset from the left edge of the element this.m_struct_obj.act_shift_top=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP); // Active area offset from the top edge of the element this.m_struct_obj.act_shift_right=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT); // Active area offset from the right edge of the element this.m_struct_obj.act_shift_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM);// Active area offset from the bottom edge of the element this.m_struct_obj.opacity=(uchar)this.GetProperty(CANV_ELEMENT_PROP_OPACITY); // Element opacity this.m_struct_obj.color_bg=(color)this.GetProperty(CANV_ELEMENT_PROP_COLOR_BG); // Element background color this.m_struct_obj.movable=(bool)this.GetProperty(CANV_ELEMENT_PROP_MOVABLE); // Element moveability flag this.m_struct_obj.active=(bool)this.GetProperty(CANV_ELEMENT_PROP_ACTIVE); // Element activity flag this.m_struct_obj.coord_act_x=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_ACT_X); // X coordinate of the element active area this.m_struct_obj.coord_act_y=(int)this.GetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y); // Y coordinate of the element active area this.m_struct_obj.coord_act_right=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_RIGHT); // Right border of the element active area this.m_struct_obj.coord_act_bottom=(int)this.GetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM); // Bottom border of the element active area //--- Save real properties //--- Save string properties ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_NAME_OBJ),this.m_struct_obj.name_obj);// Graphical element object name ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_NAME_RES),this.m_struct_obj.name_res);// Graphical resource name //--- Save the structure to the uchar array ::ResetLastError(); if(!::StructToCharArray(this.m_struct_obj,this.m_uchar_array)) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_SAVE_OBJ_STRUCT_TO_UARRAY),(string)::GetLastError()); return false; } return true; } //+------------------------------------------------------------------+
Hier ist alles ganz einfach: Jedes ganzzahlige Strukturfeld erhält die entsprechende Objekt-Eigenschaft, während Texteigenschaften des Objekts in einen entsprechenden uchar-Array der Struktur gespeichert werden. Als Nächstes speichern wir einfach die neu erstellte Struktur der Objekteigenschafts in das uchar-Array mit StructToCharArray().
Wenn das Speichern der Struktur in das Array fehlgeschlagen ist, wird über den Fehler informiert und false zurückgegeben. Sonst wird als Ergebnis true zurückgegeben.
Die Methode restauriert die Objekteigenschaften aus der Struktur:
//+------------------------------------------------------------------+ //| Create the object from the structure | //+------------------------------------------------------------------+ void CGCnvElement::StructToObject(void) { //--- Save integer properties this.SetProperty(CANV_ELEMENT_PROP_ID,this.m_struct_obj.id); // Element ID this.SetProperty(CANV_ELEMENT_PROP_TYPE,this.m_struct_obj.type); // Graphical element type this.SetProperty(CANV_ELEMENT_PROP_NUM,this.m_struct_obj.number); // Element index in the list this.SetProperty(CANV_ELEMENT_PROP_CHART_ID,this.m_struct_obj.chart_id); // Chart ID this.SetProperty(CANV_ELEMENT_PROP_WND_NUM,this.m_struct_obj.subwindow); // Chart subwindow index this.SetProperty(CANV_ELEMENT_PROP_COORD_X,this.m_struct_obj.coord_x); // Form's X coordinate on the chart this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,this.m_struct_obj.coord_y); // Form's Y coordinate on the chart this.SetProperty(CANV_ELEMENT_PROP_WIDTH,this.m_struct_obj.width); // Element width this.SetProperty(CANV_ELEMENT_PROP_HEIGHT,this.m_struct_obj.height); // Element height this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.m_struct_obj.edge_right); // Element right edge this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.m_struct_obj.edge_bottom); // Element bottom edge this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,this.m_struct_obj.act_shift_left); // Active area offset from the left edge of the element this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,this.m_struct_obj.act_shift_top); // Active area offset from the upper edge of the element this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,this.m_struct_obj.act_shift_right); // Active area offset from the right edge of the element this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,this.m_struct_obj.act_shift_bottom); // Active area offset from the bottom edge of the element this.SetProperty(CANV_ELEMENT_PROP_OPACITY,this.m_struct_obj.opacity); // Element opacity this.SetProperty(CANV_ELEMENT_PROP_COLOR_BG,this.m_struct_obj.color_bg); // Element background color this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,this.m_struct_obj.movable); // Element moveability flag this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,this.m_struct_obj.active); // Element activity flag this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_X,this.m_struct_obj.coord_act_x); // X coordinate of the element active area this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y,this.m_struct_obj.coord_act_y); // Y coordinate of the element active area this.SetProperty(CANV_ELEMENT_PROP_ACT_RIGHT,this.m_struct_obj.coord_act_right); // Right border of the element active area this.SetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM,this.m_struct_obj.coord_act_bottom); // Bottom border of the element active area //--- Save real properties //--- Save string properties this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,::CharArrayToString(this.m_struct_obj.name_obj));// Graphical element object name this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,::CharArrayToString(this.m_struct_obj.name_res));// Graphical resource name } //+------------------------------------------------------------------+
Hier erhält jede ganzzahlige Objekteigenschaft den Wert aus dem entsprechenden Strukturfeld, während der Inhalt der entsprechenden uchar-Array-Struktur mittels CharArrayToString() als Text in die Objekt-Eigenschaften eingetragen wird.
Die Methode speichert das Objekt in der Datei:
//+------------------------------------------------------------------+ //| Save the object to the file | //+------------------------------------------------------------------+ bool CGCnvElement::Save(const int file_handle) { if(!this.ObjectToStruct()) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_OBJ_STRUCT)); return false; } if(::FileWriteArray(file_handle,this.m_uchar_array)==0) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_WRITE_UARRAY_TO_FILE)); return false; } return true; } //+------------------------------------------------------------------+
Die Methode erhält das Handle der Datei, in der die Objekteigenschaften gespeichert werden sollen. Die Objekteigenschaften werden dann mit der oben betrachteten Methode ObjectToStruct() in der Struktur gespeichert. Das beim Konstruieren der Struktur erzeugte uchar-Array wird mit FileWriteArray() in die Datei geschrieben und true zurückgegeben. Im Falle eines Fehlers zeigt die Methode die Fehlermeldung im Journal an und gibt false zurück.
Die Methode lädt die Objekteigenschaften aus der Datei hoch:
//+------------------------------------------------------------------+ //| Upload the object from the file | //+------------------------------------------------------------------+ bool CGCnvElement::Load(const int file_handle) { if(::FileReadArray(file_handle,this.m_uchar_array)==0) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_LOAD_UARRAY_FROM_FILE)); return false; } if(!::CharArrayToStruct(this.m_struct_obj,this.m_uchar_array)) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_OBJ_STRUCT_FROM_UARRAY)); return false; } this.StructToObject(); return true; } //+------------------------------------------------------------------+
Die Methode erhält das Handle der Datei mit den gespeicherten Objekteigenschaften. Anschließend werden die Objekteigenschaften aus der Datei mit FileReadArray() in das uchar-Array hochgeladen. Die hochgeladenen Eigenschaften werden mit CharArrayToStruct() in die Struktur kopiert. Die aus der Datei ausgefüllte Struktur wird mit der oben genannten Methode StructToObject() in den Objekteigenschaften gesetzt und true zurückgegeben. Wenn das Lesen aus der Datei oder das Kopieren des erhaltenen Arrays in die Struktur mit einem Fehler endet, teilt die Methode dies mit und gibt false zurück.
Der Methodenblock für einen vereinfachten Zugriff auf Objekteigenschaften erhält die Methoden zur Rückgabe des rechten und unteren Elementrandes, die Methoden zum Setzen und zurückgeben der Element-Hintergrundfarbe, sowie die Methoden zur Rückgabe der Element-ID und dessen Index in der Liste der Elemente im zusammengesetzten Objekt:
//+------------------------------------------------------------------+ //| The methods of simplified access to object properties | //+------------------------------------------------------------------+ //--- Set the (1) X, (2) Y coordinates, (3) element width, (4) height, (5) right (6) and bottom edge, bool SetCoordX(const int coord_x); bool SetCoordY(const int coord_y); bool SetWidth(const int width); bool SetHeight(const int height); void SetRightEdge(void) { this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.RightEdge()); } void SetBottomEdge(void) { this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.BottomEdge()); } //--- Set the shift of the (1) left, (2) top, (3) right, (4) bottom edge of the active area relative to the element, //--- (5) all shifts of the active area edges relative to the element, (6) the element background color and (7) the element opacity void SetActiveAreaLeftShift(const int value) { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,fabs(value)); } void SetActiveAreaRightShift(const int value) { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,fabs(value)); } void SetActiveAreaTopShift(const int value) { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,fabs(value)); } void SetActiveAreaBottomShift(const int value) { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,fabs(value)); } void SetActiveAreaShift(const int left_shift,const int bottom_shift,const int right_shift,const int top_shift); void SetColorBG(const color colour) { this.SetProperty(CANV_ELEMENT_PROP_COLOR_BG,colour); } void SetOpacity(const uchar value,const bool redraw=false); //--- Return the shift (1) of the left, (2) right, (3) top and (4) bottom edge of the element active area int ActiveAreaLeftShift(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT); } int ActiveAreaRightShift(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT); } int ActiveAreaTopShift(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP); } int ActiveAreaBottomShift(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM); } //--- Return the coordinate (1) of the left, (2) right, (3) top and (4) bottom edge of the element active area int ActiveAreaLeft(void) const { return int(this.CoordX()+this.ActiveAreaLeftShift()); } int ActiveAreaRight(void) const { return int(this.RightEdge()-this.ActiveAreaRightShift()); } int ActiveAreaTop(void) const { return int(this.CoordY()+this.ActiveAreaTopShift()); } int ActiveAreaBottom(void) const { return int(this.BottomEdge()-this.ActiveAreaBottomShift()); } //--- Return (1) the background color, (2) the opacity, coordinate (3) of the right and (4) bottom element edge color ColorBG(void) const { return (color)this.GetProperty(CANV_ELEMENT_PROP_COLOR_BG); } uchar Opacity(void) const { return (uchar)this.GetProperty(CANV_ELEMENT_PROP_OPACITY); } int RightEdge(void) const { return this.CoordX()+this.m_canvas.Width(); } int BottomEdge(void) const { return this.CoordY()+this.m_canvas.Height(); } //--- Return the (1) X, (2) Y coordinates, (3) element width and (4) height, int CoordX(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_COORD_X); } int CoordY(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_COORD_Y); } int Width(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_WIDTH); } int Height(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_HEIGHT); } //--- Return the element (1) moveability and (2) activity flag bool Movable(void) const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_MOVABLE); } bool Active(void) const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_ACTIVE); } //--- Return (1) the object name, (2) the graphical resource name, (3) the chart ID and (4) the chart subwindow index string NameObj(void) const { return this.GetProperty(CANV_ELEMENT_PROP_NAME_OBJ); } string NameRes(void) const { return this.GetProperty(CANV_ELEMENT_PROP_NAME_RES); } long ChartID(void) const { return this.GetProperty(CANV_ELEMENT_PROP_CHART_ID); } int WindowNum(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_WND_NUM); } //--- Return (1) the element ID and (2) index in the list int ID(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ID); } int Number(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_NUM); } //+------------------------------------------------------------------+
Alle diese Methoden geben einfach die entsprechende Eigenschaft des Elementobjekts zurück.
Die Methoden für das Arbeit mit den Primitiven
Die Klasse CCanvas bietet eine Vielzahl von Möglichkeiten, um verschiedene grafische Primitive auf der Leinwand bzw. Canvas zu zeichnen. Wir sind in der Lage, entweder die Farbe jedes Pixels auszulesen oder ihm die gewünschte Farbe und Transparenz zuzuweisen. Neben dem einfachen Setzen einer Farbe auf ein Pixel bietet die Klasse die Möglichkeit, verschiedene Figuren entweder Pixel für Pixel (ohne Glättung) oder mit Hilfe verschiedener Glättungsmethoden zu zeichnen.
Die Objektklasse des grafischen Elements soll dem Anwender den Zugriff auf die Zeichenmethoden der Klasse CCanvas ermöglichen. Unsere Methoden werden den Aufruf der Methoden der Klasse CCanvas nur geringfügig vereinfachen. Die Vereinfachung besteht darin, dass die Farbe auf die übliche Art und Weise eingestellt wird — durch Angabe der notwendigen Farbe im color Format und Einstellung des Grades der Farbopazität (0 — transparent, 255 — völlig undurchsichtig), während die Methoden der CCanvas-Klasse "bitten", die Farbe sofort im uint ARGB-Format anzugeben, das nur eine Zahl ist. Nicht jeder ist mit der Angabe der gewünschten Farbe in diesem Format zufrieden (halbtransparentes Grau: 0x7F7F7F7F). In den abgeleiteten Klassen, die von dem Objekt des grafischen Elements abgeleitet werden sollen, werde ich die Palette der Zeichenfunktionen erweitern, indem ich die komfortable Funktionalität, die jeder erstellten Klasse innewohnt, hinzufüge. In der gleichen Klasse, die die Grundlage für die Erstellung der übrigen grafischen Objekte bildet, sollen die Zeichenmethoden einfach und überschaubar sein.
Auf den Methodenblock für den vereinfachten Zugriff auf die Objekteigenschaften folgen neue Codeblöcke. Ich habe versucht, sie entsprechend ihrem Zweck zu verteilen.
Methoden zum Empfangen von Daten beginnen mit "Get", während Methoden zum Setzen von Daten mit "Set" beginnen.
Die Methode empfängt die Farbe des Punktes mit den angegebenen Koordinaten:
//+------------------------------------------------------------------+ //| The methods of receiving raster data | //+------------------------------------------------------------------+ //--- Get a color of the dot with the specified coordinates uint GetPixel(const int x,const int y) const { return this.m_canvas.PixelGet(x,y); } //+------------------------------------------------------------------+
Hier wird das Ergebnis des Aufrufs der Methode PixelGet() der Klasse CCanvas zurückgegeben. Die Methode gibt die Farbe im ARGB-Format zurück.
Die Methoden des Füllens, Löschens und Aktualisierens von Rasterdaten:
//+------------------------------------------------------------------+ //| The methods of filling, clearing and updating raster data | //+------------------------------------------------------------------+ //--- Clear the element filling it with color and opacity void Erase(const color colour,const uchar opacity,const bool redraw=false); //--- Clear the element completely void Erase(const bool redraw=false); //--- Update the element void Update(const bool redraw=false) { this.m_canvas.Update(redraw); } //+------------------------------------------------------------------+
Die Methode Update() aktualisiert einfach das Objekt und das Chart mit der Methode Update() der Klasse CCanvas.
Die Erase()-Methoden werden außerhalb des Hauptteils der Klasse implementiert:
//+------------------------------------------------------------------+ //| Clear the element filling it with color and opacity | //+------------------------------------------------------------------+ void CGCnvElement::Erase(const color colour,const uchar opacity,const bool redraw=false) { this.m_canvas.Erase(::ColorToARGB(colour,opacity)); if(redraw) ::ChartRedraw(this.m_chart_id); } //+------------------------------------------------------------------+ //| Clear the element completely | //+------------------------------------------------------------------+ void CGCnvElement::Erase(const bool redraw=false) { this.m_canvas.Erase(NULL_COLOR); if(redraw) ::ChartRedraw(this.m_chart_id); } //+------------------------------------------------------------------+
Dies sind zwei überladene Methoden.
In der ersten übergeben wir die gewünschte Farbe und Transparenz, mit der das gesamte Element mit Hilfe der Erase()-Methode der CCanvas-Klasse gefüllt werden soll. Bitte beachten Sie, dass die Methode die Farbe und die Transparenz, die an die Methode Erase() der Klasse CCanvas übergeben wurden, mit Hilfe der Funktion ColorToARGB() in das ARGB-Format umwandelt. Das werde ich in allen Zeichenmethoden tun.
In der zweiten Methode füllen wir einfach den gesamten Hintergrund mit einer komplett transparenten schwarzen Farbe. Der Wert wurde zuvor mit der Makro-Substitution NULL_COLOR definiert.
Jede der Methoden erhält das Flag, das die Notwendigkeit anzeigt, das Chart neu zu zeichnen. Wenn das Flag gesetzt ist, wird das Chart neu gezeichnet.
Es folgt der Methodenblock zum Zeichnen von Primitiven ohne Glättung. Alle Methoden sind identisch und rufen die entsprechenden Methoden der Klasse CCanvas auf. Diese Methoden erhalten die in den Methodenargumenten angegebenen Parameter und die in das ARGB-Format konvertierte Farbe aus den an die Methoden übergebenen Farb- und Transparenzwerten:
//+------------------------------------------------------------------+ //| The methods of drawing primitives without smoothing | //+------------------------------------------------------------------+ //--- Set the color of the dot with the specified coordinates void SetPixel(const int x,const int y,const color clr,const uchar opacity=255) { this.m_canvas.PixelSet(x,y,::ColorToARGB(clr,opacity)); } //--- Draw a segment of a vertical line void DrawLineVertical(const int x, // X coordinate of the segment const int y1, // Y coordinate of the segment first point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255) // Opacity { this.m_canvas.LineVertical(x,y1,y2,::ColorToARGB(clr,opacity)); } //--- Draw a segment of a horizontal line void DrawLineHorizontal(const int x1, // X coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y, // Segment Y coordinate const color clr, // Color const uchar opacity=255) // Opacity { this.m_canvas.LineHorizontal(x1,x2,y,::ColorToARGB(clr,opacity)); } //--- Draw a segment of a freehand line void DrawLine(const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255) // Opacity { this.m_canvas.Line(x1,y1,x2,y2,::ColorToARGB(clr,opacity)); } //--- Draw a polyline void DrawPolyline(int &array_x[], // Array with the X coordinates of polyline points int & array_y[], // Array with the Y coordinates of polyline points const color clr, // Color const uchar opacity=255) // Opacity { this.m_canvas.Polyline(array_x,array_y,::ColorToARGB(clr,opacity)); } //--- Draw a polygon void DrawPolygon(int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255) // Opacity { this.m_canvas.Polygon(array_x,array_y,::ColorToARGB(clr,opacity)); } //--- Draw a rectangle using two points void DrawRectangle(const int x1, // X coordinate of the first point defining the rectangle const int y1, // Y coordinate of the first point defining the rectangle const int x2, // X coordinate of the second point defining the rectangle const int y2, // Y coordinate of the second point defining the rectangle const color clr, // color const uchar opacity=255) // Opacity { this.m_canvas.Rectangle(x1,y1,x2,y2,::ColorToARGB(clr,opacity)); } //--- Draw a circle void DrawCircle(const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const int r, // Circle radius const color clr, // Color const uchar opacity=255) // Opacity { this.m_canvas.Circle(x,y,r,::ColorToARGB(clr,opacity)); } //--- Draw a triangle void DrawTriangle(const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255) // Opacity { m_canvas.Triangle(x1,y1,x2,y2,x3,y3,::ColorToARGB(clr,opacity)); } //--- Draw an ellipse using two points void DrawEllipse(const int x1, // X coordinate of the first point defining the ellipse const int y1, // Y coordinate of the first point defining the ellipse const int x2, // X coordinate of the second point defining the ellipse const int y2, // Y coordinate of the second point defining the ellipse const color clr, // Color const uchar opacity=255) // Opacity { this.m_canvas.Ellipse(x1,y1,x2,y2,::ColorToARGB(clr,opacity)); } //--- Draw an arc of an ellipse inscribed in a rectangle with corners at (x1,y1) and (x2,y2). //--- The arc boundaries are clipped by lines from the center of the ellipse, which extend to two points with coordinates (x3,y3) and (x4,y4) void DrawArc(const int x1, // X coordinate of the top left corner forming the rectangle const int y1, // Y coordinate of the top left corner forming the rectangle const int x2, // X coordinate of the bottom right corner forming the rectangle const int y2, // Y coordinate of the bottom right corner forming the rectangle const int x3, // X coordinate of the first point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const int y3, // Y coordinate of the first point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const int x4, // X coordinate of the second point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const int y4, // Y coordinate of the second point, to which a line from the rectangle center is drawn in order to obtain the arc boundary const color clr, // Color const uchar opacity=255) // Opacity { m_canvas.Arc(x1,y1,x2,y2,x3,y3,x4,y4,::ColorToARGB(clr,opacity)); } //--- Draw a filled sector of an ellipse inscribed in a rectangle with corners at (x1,y1) and (x2,y2). //--- The sector boundaries are clipped by lines from the center of the ellipse, which extend to two points with coordinates (x3,y3) and (x4,y4) void DrawPie(const int x1, // X coordinate of the upper left corner of the rectangle const int y1, // Y coordinate of the upper left corner of the rectangle const int x2, // X coordinate of the bottom right corner of the rectangle const int y2, // Y coordinate of the bottom right corner of the rectangle const int x3, // X coordinate of the first point to find the arc boundaries const int y3, // Y coordinate of the first point to find the arc boundaries const int x4, // X coordinate of the second point to find the arc boundaries const int y4, // Y coordinate of the second point to find the arc boundaries const color clr, // Line color const color fill_clr, // Fill color const uchar opacity=255) // Opacity { this.m_canvas.Pie(x1,y1,x2,y2,x3,y3,x4,y4,::ColorToARGB(clr,opacity),ColorToARGB(fill_clr,opacity)); } //+------------------------------------------------------------------+
Der Methodenblock zum Zeichnen gefüllter Primitive ohne Glättung:
//+------------------------------------------------------------------+ //| The methods of drawing filled primitives without smoothing | //+------------------------------------------------------------------+ //--- Fill in the area void Fill(const int x, // X coordinate of the filling start point const int y, // Y coordinate of the filling start point const color clr, // Color const uchar opacity=255, // Opacity const uint threshould=0) // Threshold { this.m_canvas.Fill(x,y,::ColorToARGB(clr,opacity),threshould); } //--- Draw a filled rectangle void DrawRectangleFill(const int x1, // X coordinate of the first point defining the rectangle const int y1, // Y coordinate of the first point defining the rectangle const int x2, // X coordinate of the second point defining the rectangle const int y2, // Y coordinate of the second point defining the rectangle const color clr, // Color const uchar opacity=255) // Opacity { this.m_canvas.FillRectangle(x1,y1,x2,y2,::ColorToARGB(clr,opacity)); } //--- Draw a filled circle void DrawCircleFill(const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const int r, // Circle radius const color clr, // Color const uchar opacity=255) // Opacity { this.m_canvas.FillCircle(x,y,r,::ColorToARGB(clr,opacity)); } //--- Draw a filled triangle void DrawTriangleFill(const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255) // Opacity { this.m_canvas.FillTriangle(x1,y1,x2,y2,x3,y3,::ColorToARGB(clr,opacity)); } //--- Draw a filled polygon void DrawPolygonFill(int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255) // Opacity { this.m_canvas.FillPolygon(array_x,array_y,::ColorToARGB(clr,opacity)); } //--- Draw a filled ellipse inscribed in a rectangle with the specified coordinates void DrawEllipseFill(const int x1, // X coordinate of the top left corner forming the rectangle const int y1, // Y coordinate of the top left corner forming the rectangle const int x2, // X coordinate of the bottom right corner forming the rectangle const int y2, // Y coordinate of the bottom right corner forming the rectangle const color clr, // Color const uchar opacity=255) // Opacity { this.m_canvas.FillEllipse(x1,y1,x2,y2,::ColorToARGB(clr,opacity)); } //+------------------------------------------------------------------+
Die Methoden zum Zeichnen von Primitiven mit Glättung:
//+------------------------------------------------------------------+ //| The methods of drawing primitives using smoothing | //+------------------------------------------------------------------+ //--- Draw a point using AntiAliasing algorithm void SetPixelAA(const double x, // Point X coordinate const double y, // Point Y coordinate const color clr, // Color const uchar opacity=255) // Opacity { this.m_canvas.PixelSetAA(x,y,::ColorToARGB(clr,opacity)); } //--- Draw a segment of a freehand line using AntiAliasing algorithm void DrawLineAA(const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255, // Opacity const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value { this.m_canvas.LineAA(x1,y1,x2,y2,::ColorToARGB(clr,opacity),style); } //--- Draw a segment of a freehand line using Wu algorithm void DrawLineWu(const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const color clr, // Color const uchar opacity=255, // Opacity const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value { this.m_canvas.LineWu(x1,y1,x2,y2,::ColorToARGB(clr,opacity),style); } //--- Draws a segment of a freehand line having a specified width using smoothing algorithm with the preliminary filtration void DrawLineThick(const int x1, // X coordinate of the segment first point const int y1, // Y coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y2, // Y coordinate of the segment second point const int size, // Line width const color clr, // Color const uchar opacity=255, // Opacity const uint style=STYLE_SOLID, // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value ENUM_LINE_END end_style=LINE_END_ROUND) // Line style is one of the ENUM_LINE_END enumeration values { this.m_canvas.LineThick(x1,y1,x2,y2,::ColorToARGB(clr,opacity),size,style,end_style); } //--- Draw a vertical segment of a freehand line having a specified width using smoothing algorithm with the preliminary filtration void DrawLineThickVertical(const int x, // X coordinate of the segment const int y1, // Y coordinate of the segment first point const int y2, // Y coordinate of the segment second point const int size, // Line width const color clr, // Color const uchar opacity=255,// Opacity const uint style=STYLE_SOLID, // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value const ENUM_LINE_END end_style=LINE_END_ROUND) // Line style is one of the ENUM_LINE_END enumeration values { this.m_canvas.LineThickVertical(x,y1,y2,::ColorToARGB(clr,opacity),size,style,end_style); } //--- Draw a horizontal segment of a freehand line having a specified width using smoothing algorithm with the preliminary filtration void DrawLineThickHorizontal(const int x1, // X coordinate of the segment first point const int x2, // X coordinate of the segment second point const int y, // Segment Y coordinate const int size, // Line width const color clr, // Color const uchar opacity=255,// Opacity const uint style=STYLE_SOLID, // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value const ENUM_LINE_END end_style=LINE_END_ROUND) // Line style is one of the ENUM_LINE_END enumeration values { this.m_canvas.LineThickHorizontal(x1,x2,y,::ColorToARGB(clr,opacity),size,style,end_style); } //--- Draws a polyline using AntiAliasing algorithm void DrawPolylineAA(int &array_x[], // Array with the X coordinates of polyline points int &array_y[], // Array with the Y coordinates of polyline points const color clr, // Color const uchar opacity=255, // Opacity const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value { this.m_canvas.PolylineAA(array_x,array_y,::ColorToARGB(clr,opacity),style); } //--- Draws a polyline using Wu algorithm void DrawPolylineWu(int &array_x[], // Array with the X coordinates of polyline points int &array_y[], // Array with the Y coordinates of polyline points const color clr, // Color const uchar opacity=255, // Opacity const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value { this.m_canvas.PolylineWu(array_x,array_y,::ColorToARGB(clr,opacity),style); } //--- Draw a polyline with a specified width consecutively using two antialiasing algorithms. //--- First, individual line segments are smoothed based on Bezier curves. //--- Then, the raster antialiasing algorithm is applied to the polyline built from these segments to improve the rendering quality void DrawPolylineSmooth(const int &array_x[], // Array with the X coordinates of polyline points const int &array_y[], // Array with the Y coordinates of polyline points const int size, // Line width const color clr, // Color const uchar opacity=255, // Opacity const double tension=0.5, // Smoothing parameter value const double step=10, // Approximation step const ENUM_LINE_STYLE style=STYLE_SOLID,// Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value const ENUM_LINE_END end_style=LINE_END_ROUND)// Line style is one of the ENUM_LINE_END enumeration values { this.m_canvas.PolylineSmooth(array_x,array_y,::ColorToARGB(clr,opacity),size,style,end_style,tension,step); } //--- Draw a polyline having a specified width using smoothing algorithm with the preliminary filtration void DrawPolylineThick(const int &array_x[], // Array with the X coordinates of polyline points const int &array_y[], // Array with the Y coordinates of polyline points const int size, // Line width const color clr, // Color const uchar opacity=255,// Opacity const uint style=STYLE_SOLID, // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value ENUM_LINE_END end_style=LINE_END_ROUND) // Line style is one of the ENUM_LINE_END enumeration values { this.m_canvas.PolylineThick(array_x,array_y,::ColorToARGB(clr,opacity),size,style,end_style); } //--- Draw a polygon using AntiAliasing algorithm void DrawPolygonAA(int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255, // Opacity const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value { this.m_canvas.PolygonAA(array_x,array_y,::ColorToARGB(clr,opacity),style); } //--- Draw a polygon using Wu algorithm void DrawPolygonWu(int &array_x[], // Array with the X coordinates of polygon points int &array_y[], // Array with the Y coordinates of polygon points const color clr, // Color const uchar opacity=255, // Opacity const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value { this.m_canvas.PolygonWu(array_x,array_y,::ColorToARGB(clr,opacity),style); } //--- Draw a polygon with a specified width consecutively using two smoothing algorithms. //--- First, individual segments are smoothed based on Bezier curves. //--- Then, the raster smoothing algorithm is applied to the polygon built from these segments to improve the rendering quality. void DrawPolygonSmooth(int &array_x[], // Array with the X coordinates of polyline points int &array_y[], // Array with the Y coordinates of polyline points const int size, // Line width const color clr, // Color const uchar opacity=255, // Opacity const double tension=0.5, // Smoothing parameter value const double step=10, // Approximation step const ENUM_LINE_STYLE style=STYLE_SOLID,// Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value const ENUM_LINE_END end_style=LINE_END_ROUND)// Line style is one of the ENUM_LINE_END enumeration values { this.m_canvas.PolygonSmooth(array_x,array_y,::ColorToARGB(clr,opacity),size,style,end_style,tension,step); } //--- Draw a polygon having a specified width using smoothing algorithm with the preliminary filtration void DrawPolygonThick(const int &array_x[], // array with the X coordinates of polygon points const int &array_y[], // array with the Y coordinates of polygon points const int size, // line width const color clr, // Color const uchar opacity=255, // Opacity const uint style=STYLE_SOLID,// line style ENUM_LINE_END end_style=LINE_END_ROUND) // line ends style { this.m_canvas.PolygonThick(array_x,array_y,::ColorToARGB(clr,opacity),size,style,end_style); } //--- Draw a triangle using AntiAliasing algorithm void DrawTriangleAA(const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255, // Opacity const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value { this.m_canvas.TriangleAA(x1,y1,x2,y2,x3,y3,::ColorToARGB(clr,opacity),style); } //--- Draw a triangle using Wu algorithm void DrawTriangleWu(const int x1, // X coordinate of the triangle first vertex const int y1, // Y coordinate of the triangle first vertex const int x2, // X coordinate of the triangle second vertex const int y2, // Y coordinate of the triangle second vertex const int x3, // X coordinate of the triangle third vertex const int y3, // Y coordinate of the triangle third vertex const color clr, // Color const uchar opacity=255, // Opacity const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value { this.m_canvas.TriangleWu(x1,y1,x2,y2,x3,y3,::ColorToARGB(clr,opacity),style); } //--- Draw a circle using AntiAliasing algorithm void DrawCircleAA(const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const double r, // Circle radius const color clr, // Color const uchar opacity=255, // Opacity const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value { this.m_canvas.CircleAA(x,y,r,::ColorToARGB(clr,opacity),style); } //--- Draw a circle using Wu algorithm void DrawCircleWu(const int x, // X coordinate of the circle center const int y, // Y coordinate of the circle center const double r, // Circle radius const color clr, // Color const uchar opacity=255, // Opacity const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value { this.m_canvas.CircleWu(x,y,r,::ColorToARGB(clr,opacity),style); } //--- Draw an ellipse by two points using AntiAliasing algorithm void DrawEllipseAA(const double x1, // X coordinate of the first point defining the ellipse const double y1, // Y coordinate of the first point defining the ellipse const double x2, // X coordinate of the second point defining the ellipse const double y2, // Y coordinate of the second point defining the ellipse const color clr, // Color const uchar opacity=255, // Opacity const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value { this.m_canvas.EllipseAA(x1,y1,x2,y2,::ColorToARGB(clr,opacity),style); } //--- Draw an ellipse by two points using Wu algorithm void DrawEllipseWu(const int x1, // X coordinate of the first point defining the ellipse const int y1, // Y coordinate of the first point defining the ellipse const int x2, // X coordinate of the second point defining the ellipse const int y2, // Y coordinate of the second point defining the ellipse const color clr, // Color const uchar opacity=255, // Opacity const uint style=UINT_MAX) // Line style is one of the ENUM_LINE_STYLE enumeration values or a custom value { this.m_canvas.EllipseWu(x1,y1,x2,y2,::ColorToARGB(clr,opacity),style); } //+------------------------------------------------------------------+
Die Logik aller hinzugefügten Methoden ist vollständig klar. Alle an die Methoden übergebenen Parameter sind signiert. Der Zweck der einzelnen Methoden ist in den Kommentaren angegeben. Ich glaube also, dass hier alles klar ist. In jedem Fall können Sie gerne den Abschnitt Kommentare verwenden.
Die Methoden für die Arbeit mit Texten
Die Klasse CCanvas merkt sich die Einstellungen des zuletzt angezeigten Textes, einschließlich seiner Schriftart, Farbe, Transparenz usw. Um die Textgröße herauszufinden, können wir die Methode TextSize() verwenden, die die aktuellen Schrifteinstellungen verwendet, um die Breite und Höhe des textbegrenzenden Rechtecks zu messen. Das können wir bei verschiedenen Gelegenheiten brauchen, z. B. wenn wir den vorherigen Text mit der Hintergrundfarbe überschreiben und ihn mit neuen Koordinaten schreiben — Text-Offset. In diesem Fall benötigen wir nicht nur Textkoordinaten, sondern auch Textankerpunkte (links-oben, Mitte-oben, rechts-oben usw.). Wir müssen genau wissen, welcher Ankerwinkel des Begrenzungsrechtecks dem Text gegeben wird, sonst werden die Koordinaten des zu löschenden Rechtecks falsch gesetzt. Um dies zu erreichen, müssen wir die Klassenvariable hinzufügen, die den zuletzt angegebenen Ankerpunkt speichert.
Deklarieren wir die Variable im privaten Bereich der Klasse:
long m_long_prop[ORDER_PROP_INTEGER_TOTAL]; // Integer properties double m_double_prop[ORDER_PROP_DOUBLE_TOTAL]; // Real properties string m_string_prop[ORDER_PROP_STRING_TOTAL]; // String properties ENUM_TEXT_ANCHOR m_text_anchor; // Current text alignment //--- Return the index of the array the order (1) double and (2) string properties are located at int IndexProp(ENUM_CANV_ELEMENT_PROP_DOUBLE property) const { return(int)property-CANV_ELEMENT_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_CANV_ELEMENT_PROP_STRING property) const { return(int)property-CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_DOUBLE_TOTAL; }
Ganz am Anfang des parametrischen Klassenkonstruktors initialisieren wir die Werte für den Objekttyp und den Textankerpunkt:
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, const int element_id, const int element_num, const long chart_id, const int wnd_num, const string name, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable=true, const bool activity=true, const bool redraw=false) { this.m_name=this.m_name_prefix+name; this.m_chart_id=chart_id; this.m_subwindow=wnd_num; this.m_type=element_type; this.m_text_anchor=0; if(this.Create(chart_id,wnd_num,this.m_name,x,y,w,h,colour,opacity,redraw)) {
Fügen wir in die Variable m_type der übergeordneten Klasse, die von ihrer virtuellen Methode Type() zurückgegeben wird, den in den Konstruktorparametern übergebenen Objekttyp ein, während die m_text_anchor Variable mit dem Standardwert initialisiert wird — der linken oberen Ecke des begrenzenden Rechtecks.
Ganz am Ende des Hauptteils der Klasse (nämlich nach dem Codeblock für die Arbeit mit Primitiven) fügen wir den Codeblock für die Arbeit mit Text ein:
//+------------------------------------------------------------------+ //| The methods of working with text | //+------------------------------------------------------------------+ //--- Return text the alignment type (anchor method) ENUM_TEXT_ANCHOR TextAnchor(void) const { return this.m_text_anchor; } //--- Set the current font bool SetFont(const string name, // Font name. For example, "Arial" const int size, // Font size const uint flags=0, // Font creation flags const uint angle=0, // Font slope angle in tenths of a degree const bool relative=true) // Relative font size flag { return this.m_canvas.FontSet(name,(relative ? size*-10 : size),flags,angle); } //--- Set a font name bool SetFontName(const string name) // Font name. For example, "Arial" { return this.m_canvas.FontNameSet(name); } //--- Set a font size bool SetFontSize(const int size, // Font size const bool relative=true) // Relative font size flag { return this.m_canvas.FontSizeSet(relative ? size*-10 : size); } //--- Set font flags //--- FONT_ITALIC - Italic, FONT_UNDERLINE - Underline, FONT_STRIKEOUT - Strikeout bool SetFontFlags(const uint flags) // Font creation flags { return this.m_canvas.FontFlagsSet(flags); } //--- Set a font slope angle bool SetFontAngle(const float angle) // Font slope angle in tenths of a degree { return this.m_canvas.FontAngleSet(uint(angle*10)); } //--- Set the font anchor angle (alignment type) void SetTextAnchor(const uint flags=0) { this.m_text_anchor=(ENUM_TEXT_ANCHOR)flags; } //--- Gets the current font parameters and write them to variables void GetFont(string &name, // The reference to the variable for returning a font name int &size, // Reference to the variable for returning a font size uint &flags, // Reference to the variable for returning font flags uint &angle) // Reference to the variable for returning a font slope angle { this.m_canvas.FontGet(name,size,flags,angle); } //--- Return (1) the font name, (2) size, (3) flags and (4) slope angle string FontName(void) const { return this.m_canvas.FontNameGet(); } int FontSize(void) const { return this.m_canvas.FontSizeGet(); } int FontSizeRelative(void) const { return(this.FontSize()<0 ? -this.FontSize()/10 : this.FontSize()); } uint FontFlags(void) const { return this.m_canvas.FontFlagsGet(); } uint FontAngle(void) const { return this.m_canvas.FontAngleGet(); } //--- Return the text (1) width, (2) height and (3) all sizes (the current font is used to measure the text) int TextWidth(const string text) { return this.m_canvas.TextWidth(text); } int TextHeight(const string text) { return this.m_canvas.TextHeight(text); } void TextSize(const string text, // Text for measurement int &width, // Reference to the variable for returning a text width int &height) // Reference to the variable for returning a text height { this.m_canvas.TextSize(text,width,height); } //--- Display the text in the current font void Text(int x, // X coordinate of the text anchor point int y, // Y coordinate of the text anchor point string text, // Display text const color clr, // Color const uchar opacity=255, // Opacity uint alignment=0) // Text anchoring method { this.m_text_anchor=(ENUM_TEXT_ANCHOR)alignment; this.m_canvas.TextOut(x,y,text,::ColorToARGB(clr,opacity),alignment); } }; //+------------------------------------------------------------------+
Hier ist alles so wie beim Umgang mit Primitiven. Alle Methoden sind mit Kommentaren versehen, die ihren Zweck, ihre Eingänge und Ausgänge offenlegen.
Ich möchte die Methode zum Setzen der aktuellen Schriftparameter kommentieren:
//--- Set the current font bool SetFont(const string name, // Font name. For example, "Arial" const int size, // Font size const uint flags=0, // Font creation flags const uint angle=0, // Font slope angle in tenths of a degree const bool relative=true) // Relative font size flag { return this.m_canvas.FontSet(name,(relative ? size*-10 : size),flags,angle);
Hier gibt size die Schriftgröße an und wird immer entsprechend der Schriftgröße gesetzt, die wir bei der Anzeige eines Textes mit Hilfe des gewöhnlichen OBJ_LABEL Text-Label-Objekts einstellen würden. Die Größe wird darin als positiver Integer-Wert gesetzt. Im Gegensatz dazu wird beim Zeichnen des Textes auf der Leinwand die Schriftgröße auf die gleiche Weise gesetzt wie in der TextSetFont()-Funktion:
Die Schriftgröße wird mit positiven oder negativen Werten eingestellt. Das Vorzeichen legt fest, ob die Textgröße von den Einstellungen des Betriebssystems (Schriftskala) abhängt.
- Wenn die Größe positiv ist, wird sie bei der Darstellung des logischen Fonts als physischer Font in die physikalischen Einheiten des Gerätes, in Pixel, umgerechnet. Die Größe entspricht der Höhe von Symbolzellen aus verfügbaren Fonts. Die gemeinsame Verwendung von Texten, die mit der Funktion TextOut() angezeigt werden, und Texten, die mit dem grafischen Objekt OBJ_LABEL ("Textlabel") angezeigt werden, ist nicht empfehlenswert.
- Wenn die Größe negativ ist, wird sie in Zehntel eines logischen Punktes angenommen (der Wert -350 entspricht 35 logischen Punkten) und durch 10 geteilt. Der resultierende Wert wird in physikalische Einheiten des Geräts (Pixel) umgerechnet und entspricht dem absoluten Wert der Zeichenhöhe aus verfügbaren Fonts. Um einen Text der Objektgröße OBJ_LABEL auf dem Bildschirm zu erhalten, multiplizieren Sie die in den Objekteigenschaften angegebene Schriftgröße mit -10.
Das Flag für die relative Schriftgröße wird in der Methode geprüft. Standardmäßig wird der Wert der Größe mit -10 multipliziert, damit die Schriftart mit einem korrekten Wert für die Übergabe der CCanvas-Klasse an die Methode FontSet() angegeben wird.
Im Konstruktor der parametrischen Klasse fügen wir eine Schriftinitialisierung hinzu (setzen des Standardnamen und Größe):
//+------------------------------------------------------------------+ //| Parametric constructor | //+------------------------------------------------------------------+ CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, const int element_id, const int element_num, const long chart_id, const int wnd_num, const string name, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable=true, const bool activity=true, const bool redraw=false) { this.m_name=this.m_name_prefix+name; this.m_chart_id=chart_id; this.m_subwindow=wnd_num; this.m_type=element_type; this.SetFont("Calibri",8); this.m_text_anchor=0; if(this.Create(chart_id,wnd_num,this.m_name,x,y,w,h,colour,opacity,redraw)) {
Dies sind alle Verbesserungen, die ich für den aktuellen Artikel geplant habe. Lassen Sie uns die Ergebnisse testen.
Test
Wir haben den EA aus dem vorherigen Artikel, der zwei grafische Elementobjekte im Chart anzeigt. Nehmen wir den gleichen EA, speichern ihn im neuen Ordner \MQL5\Experts\TestDoEasy\TestDoEasyPart75\ als TestDoEasyPart75.mq5 und gehen wie folgt vor:
Wenn wir auf das erste (obere) Objekt klicken, zeichnen wir abwechselnd ein Rechteck und einen Kreis auf das Objekt. Mit jedem neuen Klick auf das Objekt verringert sich die Größe des Rechtecks um 2 Pixel auf jeder Seite und der Radius des Kreises verringert sich ebenfalls um 2 Pixel. Das Rechteck wird auf die übliche Weise gezeichnet, während der Kreis mit Hilfe der Glättung gezeichnet wird. Mit jedem neuen Klick wird die Transparenz des Objekts in einer Schleife, von 0 auf 255 erhöht.
Wenn man auf das zweite (untere) Objekt klickt, wird ein Text angezeigt, der abwechselnd den Ankerpunkt ändert. Schreiben Sie selbst den Namen des Ankerpunkts in den Text. Die Transparenz des Objekts bleibt unverändert.
Bestimmung der Anzahl der erzeugten Elemente:
//+------------------------------------------------------------------+ //| TestDoEasyPart75.mq5 | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" //--- includes #include <Arrays\ArrayObj.mqh> #include <DoEasy\Services\Select.mqh> #include <DoEasy\Objects\Graph\GCnvElement.mqh> //--- defines #define ELEMENTS_TOTAL (2) // Number of created graphical elements //--- input parameters sinput bool InpMovable = true; // Movable flag //--- global variables CArrayObj list_elements; //+------------------------------------------------------------------+
Um zu vermeiden, dass bei jeder Änderung des Zeitrahmens identische Objekte erzeugt werden, leeren wir die Liste der bereits erzeugten Objekte in OnInit(), bevor neue Objekte erzeugt werden:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Set the permissions to send cursor movement and mouse scroll events ChartSetInteger(ChartID(),CHART_EVENT_MOUSE_MOVE,true); ChartSetInteger(ChartID(),CHART_EVENT_MOUSE_WHEEL,true); //--- Set EA global variables //--- Create the specified number of graphical elements on the canvas list_elements.Clear(); int total=ELEMENTS_TOTAL; for(int i=0;i<total;i++) { //--- When creating an object, pass all the required parameters to it CGCnvElement *element=new CGCnvElement(GRAPH_ELEMENT_TYPE_ELEMENT,i,0,ChartID(),0,"Element_0"+(string)(i+1),300,40+(i*80),100,70,clrSilver,200,InpMovable,true,true); if(element==NULL) continue; //--- Add objects to the list if(!list_elements.Add(element)) { delete element; continue; } } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
Da die Kollektionsklasse der grafische Objekte noch nicht existiert, um zu prüfen, ob es notwendig ist, ein neues Objekt mit einem bestimmten Namen zu erzeugen, werden diese Objekte hier einfach neu erzeugt, indem die Liste der zuvor erzeugten Objekte vorher gelöscht wird.
In OnChartEvent() werden die Mausklicks auf zwei angelegte Objekte abgearbeitet:
//+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- If clicking on an object if(id==CHARTEVENT_OBJECT_CLICK) { //--- In the new list, get the element object with the name corresponding to the sparam string parameter value of the OnChartEvent() handler CArrayObj *obj_list=CSelect::ByGraphCanvElementProperty(GetPointer(list_elements),CANV_ELEMENT_PROP_NAME_OBJ,sparam,EQUAL); //--- If the object is received from the list if(obj_list!=NULL && obj_list.Total()>0) { static uchar try0=0, try1=0; //--- Get the pointer to the object in the list CGCnvElement *obj=obj_list.At(0); //--- If this is the first graphical element if(obj.ID()==0) { //--- Set a new opacity level for the object uchar opasity=obj.Opacity(); if((opasity+5)>255) opasity=0; else opasity+=5; //--- Set a new opacity to the object obj.SetOpacity(opasity); //--- Set rectangle and circle coordinates int x1=2,x2=obj.Width()-3; int y1=2,y2=obj.Height()-3; int xC=(x1+x2)/2; int yC=(y1+y2)/2; int R=yC-y1; //--- Draw a rectangle at each first click if(try0%2==0) obj.DrawRectangle(x1+try0,y1+try0,x2-try0,y2-try0,clrDodgerBlue,obj.Opacity()); //--- Display the circle smoothed using AntiAliasing at each second click else obj.DrawCircleAA(xC,yC,R-try0,clrGreen,obj.Opacity()); //--- If the number of clicks on the object exceeds 30 if(try0>30) { //--- Clear the object setting its current color and transparency obj.Erase(obj.ColorBG(),obj.Opacity()); //--- Re-start the click number countdown try0=0; } //--- Update the chart and the object, and display the comment featuring color values and object opacity obj.Update(true); // 'true' is not needed here since the next Comment command redraws the chart anyway Comment("Object name: ",obj.NameObj(),", opasity=",obj.Opacity(),", Color BG: ",(string)obj.ColorBG()); //--- Increase the counter of mouse clicks by object try0++; } //--- If this is the second object else if(obj.ID()==1) { //--- Set the font parameters for it ("Calibri" size 8) obj.SetFont("Calibri",8); //--- Set the text anchor angle corresponding to the click counter by object obj.SetTextAnchor((ENUM_TEXT_ANCHOR)try1); //--- Create the text out of the anchor angle name string text=StringSubstr(EnumToString(obj.TextAnchor()),12); //--- Set the text coordinates relative to the upper left corner of the graphical element int xT=2,yT=2; //--- Depending on the anchor angle, set the new coordinates of the displayed text //--- LEFT_TOP if(try1==0) { xT=2; yT=2; } //--- CENTER_TOP else if(try1==1) { xT=obj.Width()/2; yT=2; } //--- RIGHT_TOP //--- since the ENUM_TEXT_ANCHOR enumeration features no 3, increase the counter of object clicks by 1 else if(try1==2) { xT=obj.Width()-2; yT=2; try1++; } //--- LEFT_CENTER else if(try1==4) { xT=2; yT=obj.Height()/2; } //--- CENTER else if(try1==5) { xT=obj.Width()/2; yT=obj.Height()/2; } //--- RIGHT_CENTER //--- since the ENUM_TEXT_ANCHOR enumeration features no 7, increase the counter of object clicks by 1 else if(try1==6) { xT=obj.Width()-2; yT=obj.Height()/2; try1++; } //--- LEFT_BOTTOM else if(try1==8) { xT=2; yT=obj.Height()-2; } //--- CENTER_BOTTOM else if(try1==9) { xT=obj.Width()/2; yT=obj.Height()-2; } //--- RIGHT_BOTTOM else if(try1==10) { xT=obj.Width()-2; yT=obj.Height()-2; } //--- Clear the graphical element filling it with the current color and transparency obj.Erase(obj.ColorBG(),obj.Opacity()); //--- Display the text with the calculated coordinates in the cleared element obj.Text(xT,yT,text,clrDodgerBlue,255,obj.TextAnchor()); //--- Update the object and chart obj.Update(true); // 'true' is not needed here since the next Comment command redraws the chart anyway Comment("Object name: ",obj.NameObj(),", opasity=",obj.Opacity(),", Color BG: ",(string)obj.ColorBG()); //--- Increase the counter of object clicks try1++; if(try1>10) try1=0; } } } } //+------------------------------------------------------------------+
Der Code der Funktion ist mit ausführlichen Kommentaren versehen. Ich glaube, seine Logik ist klar. Wenn Sie Fragen zum aktuellen Artikel haben, können Sie diese gerne in den Kommentaren unten stellen.
Kompilieren Sie den EA und starten Sie ihn auf dem Chart. Klicken Sie auf die Objekte:
Als Ergebnis habe ich versehentlich ein lustiges Bild auf dem oberen Objekt erhalten, das einer CD ähnelt :)
Was kommt als Nächstes?
Im nächsten Artikel werde ich mit der Entwicklung der Objekte beginnen, die von dem hier erstellten Grafikelement-Objekt abgeleitet werden.
Alle Dateien der aktuellen Version der Bibliothek sind unten zusammen mit der Test-EA-Datei für MQL5 zum Testen und Herunterladen angehängt.
Ihre Fragen und Vorschläge schreiben Sie bitte in den Kommentarteil.
*Frühere Artikel dieser Serie:
Grafiken in der Bibliothek DoEasy (Teil 73): Das Formularobjekt eines grafischen Elements
Grafiken in der Bibliothek DoEasy (Teil 74): Das grafisches Basiselement, das von der Klasse CCanvas unterstützt wird
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/9515





- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.