Grafiken in der Bibliothek DoEasy (Teil 76): Das Formularobjekt und vordefinierte Farbschemata
Inhalt
- Konzept
- Verbesserung der Klassenbibliothek
- Typen der Farbschemata und der Formulare
- Objektklasse des Formulars
- Test
- Was kommt als Nächstes?
Konzept
Im vorherigen Artikel habe ich die Basis-Objektklasse für grafische Elemente entwickelt, die als Grundlage für die Erstellung komplexerer grafischer Objekte der Bibliothek dient, sowie die Methoden zum Zeichnen grafischer Primitive und Texte erstellt. Heute werde ich dieses grafische Element-Objekt als Grundlage für seine abgeleitete Objektklasse — das Formular-Objekt — verwenden. Das Formularobjekt kann bereits eine absolut unabhängige Einheit für die Gestaltung und Darstellung einiger Steuerelemente und die Durchführung der Visualisierung in Programmen sein, die auf der Bibliothek basieren.
Aber bevor wir das Formularobjekt erstellen, sollten wir über die GUI und ihre Design-Methoden sprechen, sowie einen Anfangssatz von Farbschemata und Typen der grafischen Objektpräsentation erstellen.
Viele Programme, die eine grafische Datendarstellung verwenden und über ihre Grafik-Engine eine Interaktion mit der Außenwelt ermöglichen, erlauben es dem Nutzer, das Aussehen und Design der grafischen Objekte schnell zu ändern. Ich werde einen Satz von Schemata verwenden, um das Aussehen und das Farbschema schnell zu ändern. Die Parameter der erstellten Schemata werden in einer separaten Bibliotheksdatei enthalten sein, in der ein Programmbenutzer oder ein Programmierer schnell verschiedene Einstellungen für das Aussehen und die Farbe des grafischen Objekts ändern kann.
In diesem Artikel beginne ich mit der Entwicklung von zwei Schemata, die nach und nach immer mehr unterschiedliche Parameter und deren Werte enthalten werden, wenn ich neue Bibliotheksobjekte und Funktionen weiter entwickle.
Es besteht keine Notwendigkeit, die in der Bibliothek entwickelten Schemata für die Erstellung eigener grafischer Objekte zu verwenden, aber sie können als gutes Beispiel dafür dienen, wie genau bestimmte Objekte für die spätere Verwendung erstellt werden müssen.
Verbesserung der Klassenbibliothek
Fügen wir wie gewohnt die Indizes der neuen Nachrichten in \MQL5\Include\DoEasy\Data.mqh ein:
MSG_LIB_SYS_FAILED_ADD_SYM_OBJ, // Failed to add symbol MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ, // Failed to create the graphical element object MSG_LIB_SYS_OBJ_ALREADY_IN_LIST, // Such an object is already present in the list MSG_LIB_SYS_FAILED_GET_DATA_GRAPH_RES, // Failed to receive graphical resource data
...
MSG_LIB_SYS_FAILED_ADD_BUFFER, // Failed to add buffer object to the list MSG_LIB_SYS_FAILED_CREATE_BUFFER_OBJ, // Failed to create \"Indicator buffer\" object MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST, // Could not add object to the list
und die Nachrichtentexte, die den neu hinzugefügten Indizes entsprechen:
{"Не удалось добавить символ ","Failed to add "}, {"Не удалось создать объект-графический элемент ","Failed to create graphic element object "}, {"Такой объект уже есть в списке","Such an object is already in the list"}, {"Не удалось получить данные графического ресурса","Failed to get graphic resource data"},
...
{"Не удалось добавить объект-буфер в список","Failed to add buffer object to list"}, {"Не удалось создать объект \"Индикаторный буфер\"","Failed to create object \"Indicator buffer\""}, {"Не удалось добавить объект в список","Failed to add object to the list"},
Bei der Entwicklung des Formularobjekts werde ich ein Muster für die spätere Erzeugung von Schatten, die das Formular auf die Objekte wirft, über denen sich das Formular befindet, erstellen. Das Formular benötigt einen kleinen Abstand um sich herum. Dieser Raum soll zum Zeichnen des Schattens verwendet werden. Um die Größe des Raums festzulegen, benötigen wir eine Makro-Substitution, die die Größe einer Seite dieses Raums in Pixeln angibt. Wenn wir fünf Pixel einstellen, wird das Formular oben, unten, links und rechts freien Raum haben — fünf Pixel auf jeder Seite.
Der Umgang mit dem grafischen Elementobjekt zeigt, dass einige seiner Eigenschaften in der Eigenschaftsliste nicht benötigt werden. Sie werden nicht zum Suchen und Sortieren von Objekten verwendet. Daher sollten sie aus der Enumeration der Integer-Eigenschaften des Elementobjekts entfernt werden. Sie werden in gewöhnlichen geschützten Klassenvariablen enthalten sein.
Öffnen wir nun \MQL5\Include\DoEasy\Defines.mqh und implementieren die oben genannten Änderungen:
In der Liste der Canvas-Parameter fügen wir den Abstand einer Seite zum Einfügen von Schatten hinzu:
//--- Parameters of the DOM snapshot series #define MBOOKSERIES_DEFAULT_DAYS_COUNT (1) // The default required number of days for DOM snapshots in the series #define MBOOKSERIES_MAX_DATA_TOTAL (200000) // Maximum number of stored DOM snapshots of a single symbol //--- Canvas parameters #define PAUSE_FOR_CANV_UPDATE (16) // Canvas update frequency #define NULL_COLOR (0x00FFFFFF) // Zero for the canvas with the alpha channel #define OUTER_AREA_SIZE (5) // Size of one side of the outer area around the workspace //+------------------------------------------------------------------+ //| Enumerations | //+------------------------------------------------------------------+
Wir entfernen zwei unnötige Eigenschaften aus der Liste der ganzzahligen Eigenschaften des grafischen Elements:
CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM, // Active area offset from the bottom edge of the element CANV_ELEMENT_PROP_OPACITY, // Element opacity CANV_ELEMENT_PROP_COLOR_BG, // Element background color CANV_ELEMENT_PROP_MOVABLE, // Element moveability flag
Jetzt sieht die Liste der Integer-Eigenschaften wie folgt aus:
//+------------------------------------------------------------------+ //| Integer properties of the graphical element on the canvas | //+------------------------------------------------------------------+ enum ENUM_CANV_ELEMENT_PROP_INTEGER { CANV_ELEMENT_PROP_ID = 0, // Element ID CANV_ELEMENT_PROP_TYPE, // Graphical element type CANV_ELEMENT_PROP_NUM, // Element index in the list CANV_ELEMENT_PROP_CHART_ID, // Chart ID CANV_ELEMENT_PROP_WND_NUM, // Chart subwindow index CANV_ELEMENT_PROP_COORD_X, // Form's X coordinate on the chart CANV_ELEMENT_PROP_COORD_Y, // Form's Y coordinate on the chart CANV_ELEMENT_PROP_WIDTH, // Element width CANV_ELEMENT_PROP_HEIGHT, // Element height CANV_ELEMENT_PROP_RIGHT, // Element right border CANV_ELEMENT_PROP_BOTTOM, // Element bottom border CANV_ELEMENT_PROP_ACT_SHIFT_LEFT, // Active area offset from the left edge of the element CANV_ELEMENT_PROP_ACT_SHIFT_TOP, // Active area offset from the upper edge of the element CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT, // Active area offset from the right edge of the element CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM, // Active area offset from the bottom edge of the element CANV_ELEMENT_PROP_MOVABLE, // Element moveability flag CANV_ELEMENT_PROP_ACTIVE, // Element activity flag CANV_ELEMENT_PROP_COORD_ACT_X, // X coordinate of the element active area CANV_ELEMENT_PROP_COORD_ACT_Y, // Y coordinate of the element active area CANV_ELEMENT_PROP_ACT_RIGHT, // Right border of the element active area CANV_ELEMENT_PROP_ACT_BOTTOM, // Bottom border of the element active area }; #define CANV_ELEMENT_PROP_INTEGER_TOTAL (21) // Total number of integer properties #define CANV_ELEMENT_PROP_INTEGER_SKIP (0) // Number of integer properties not used in sorting //+------------------------------------------------------------------+
Die Gesamtzahl der ganzzahligen Eigenschaften wird um 2 reduziert — wir setzen 21 anstelle von 23.
Außerdem werden zwei unnötige Konstanten aus der Enumeration der möglichen Sortierkriterien für Canvas-basierte grafische Elemente entfernt:
SORT_BY_CANV_ELEMENT_ACT_SHIFT_BOTTOM, // Sort by the active area offset from the bottom edge of the element SORT_BY_CANV_ELEMENT_OPACITY, // Sort by the element opacity SORT_BY_CANV_ELEMENT_COLOR_BG, // Sort by the element background color SORT_BY_CANV_ELEMENT_MOVABLE, // Sort by the element moveability flag
Die vollständige Liste soll wie folgt lauten:
//+------------------------------------------------------------------+ //| Possible sorting criteria of graphical elements on the canvas | //+------------------------------------------------------------------+ #define FIRST_CANV_ELEMENT_DBL_PROP (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP) #define FIRST_CANV_ELEMENT_STR_PROP (CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP+CANV_ELEMENT_PROP_DOUBLE_TOTAL-CANV_ELEMENT_PROP_DOUBLE_SKIP) enum ENUM_SORT_CANV_ELEMENT_MODE { //--- Sort by integer properties SORT_BY_CANV_ELEMENT_ID = 0, // Sort by element ID SORT_BY_CANV_ELEMENT_TYPE, // Sort by graphical element type SORT_BY_CANV_ELEMENT_NUM, // Sort by form index in the list SORT_BY_CANV_ELEMENT_CHART_ID, // Sort by chart ID SORT_BY_CANV_ELEMENT_WND_NUM, // Sort by chart window index SORT_BY_CANV_ELEMENT_COORD_X, // Sort by the element X coordinate on the chart SORT_BY_CANV_ELEMENT_COORD_Y, // Sort by the element Y coordinate on the chart SORT_BY_CANV_ELEMENT_WIDTH, // Sort by the element width SORT_BY_CANV_ELEMENT_HEIGHT, // Sort by the element height SORT_BY_CANV_ELEMENT_RIGHT, // Sort by the element right border SORT_BY_CANV_ELEMENT_BOTTOM, // Sort by the element bottom border SORT_BY_CANV_ELEMENT_ACT_SHIFT_LEFT, // Sort by the active area offset from the left edge of the element SORT_BY_CANV_ELEMENT_ACT_SHIFT_TOP, // Sort by the active area offset from the top edge of the element SORT_BY_CANV_ELEMENT_ACT_SHIFT_RIGHT, // Sort by the active area offset from the right edge of the element SORT_BY_CANV_ELEMENT_ACT_SHIFT_BOTTOM, // Sort by the active area offset from the bottom edge of the element SORT_BY_CANV_ELEMENT_MOVABLE, // Sort by the element moveability flag SORT_BY_CANV_ELEMENT_ACTIVE, // Sort by the element activity flag SORT_BY_CANV_ELEMENT_COORD_ACT_X, // Sort by X coordinate of the element active area SORT_BY_CANV_ELEMENT_COORD_ACT_Y, // Sort by Y coordinate of the element active area SORT_BY_CANV_ELEMENT_ACT_RIGHT, // Sort by the right border of the element active area SORT_BY_CANV_ELEMENT_ACT_BOTTOM, // Sort by the bottom border of the element active area //--- Sort by real properties //--- Sort by string properties SORT_BY_CANV_ELEMENT_NAME_OBJ = FIRST_CANV_ELEMENT_STR_PROP,// Sort by an element object name SORT_BY_CANV_ELEMENT_NAME_RES, // Sort by the graphical resource name }; //+------------------------------------------------------------------+
Alle erzeugten grafischen Objekte basieren auf dem Objekt des grafischen Elements. Das Objekt selbst ist abgeleitet vom Basisobjekt aller grafischen Objekte der Bibliothek (das wiederum ein Nachkomme der Basisklasse CObject der Standardbibliothek ist). Alle Eigenschaften jeder Elternklasse werden an ihre Nachkommen vererbt. Wenn wir also Eigenschaften benötigen, die allen grafischen Objekten gemeinsam sind, sollten sie sich in den Basisobjekten des gesamten Ableitungsbaums befinden. Das Klassenobjekt CGBaseObj dient als Objekt für grafische Bibliotheksobjekte.
Wir werden die Sichtbarkeit von grafischen Objekten im Diagramm verwalten müssen. Dazu müssen wir das grafische Objekt nicht entfernen oder ausblenden. Alles, was wir tun müssen, ist, die notwendigen Flags in der Eigenschaft OBJPROP_TIMEFRAMES des grafischen Objekts zu spezifizieren, damit es entfernt oder im Diagramm über allen anderen angezeigt wird. So können wir die Sichtbarkeit des Objekts im Diagramm verwalten und das gewünschte Objekt über allen anderen platzieren. Es wird als das aktuelle Objekt dienen, mit dem der Programmnutzer arbeiten soll.
Aus dem gesamten Satz der Objekt-Flags benötigen wir die folgenden: OBJ_NO_PERIODS — zum Ausblenden des Objekts und OBJ_ALL_PERIODS — zum Anzeigen des Objekts in einem Chart. Um das Objekt in den Vordergrund zu bringen, verstecken wir es einfach und zeigen es dann wieder an.
Fügen wir der Basisobjektdatei \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh neue Eigenschaften und Methoden hinzu.
Im geschützten Teil der Klasse wird die Variable zum Speichern der Eigenschaft Objektsichtbarkeit deklariert:
//+------------------------------------------------------------------+ //| Class of the base object of the library graphical objects | //+------------------------------------------------------------------+ class CGBaseObj : public CObject { private: 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 bool m_visible; // Object visibility //--- Create (1) the object structure and (2) the object from the structure virtual bool ObjectToStruct(void) { return true; } virtual void StructToObject(void){;} public:
Im öffentlichen Abschnitt der Klasse wird die Methode zum Setzen des Sichtbarkeitsflags des Objekts sowie zum gleichzeitigen Setzen der Eigenschaft selbst an das Objekt und die Methode zum Zurückgeben der Objektsichtbarkeit im Chart eingefügt:
public: //--- Return the values of class variables string Name(void) const { return this.m_name; } long ChartID(void) const { return this.m_chart_id; } int SubWindow(void) const { return this.m_subwindow; } //--- (1) Set and (2) return the object visibility void SetVisible(const bool flag) { long value=(flag ? OBJ_ALL_PERIODS : 0); if(::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_TIMEFRAMES,value)) this.m_visible=flag; } bool IsVisible(void) const { return this.m_visible; } //--- The virtual method returning the object type virtual int Type(void) const { return this.m_type; }
Die Methode, die die Sichtbarkeit des Objekts setzt, prüft zunächst den Wert des Flags. Abhängig vom übergebenen Wert (true oder false) sendet sie eine Anforderung zum Setzen eines Wertes an das Objekt, oder OBJ_ALL_PERIODS zum Anzeigen eines Objekts im Chart, oder 0 zum Ausblenden. Wenn eine Anforderung erfolgreich in die Warteschlange der Chart-Ereignisse gestellt wird, erhält die Variable m_visible den an die Methode übergebenen Flag-Wert. Der Wert kann mit der Methode IsVisible() abgerufen werden, die den Variablenwert zurückgibt.
In der Initialisierungsliste des Klassenkonstruktors initialisieren wir eine neue Variable mit false:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CGBaseObj::CGBaseObj() : m_shift_y(0), m_type(0),m_visible(false), m_name_prefix(::MQLInfoString(MQL_PROGRAM_NAME)+"_") { } //+------------------------------------------------------------------+
Verbessern wir die Objektklasse Grafisches Element in \MQL5\Include\DoEasy\Objects\Graph\GCnvElement.mqh.
Im geschützten Abschnitt der Klasse deklarieren wir die Flag-Variable, in der das Objekt Schattenanwesenheit/-fehlen gesetzt wird, und die Variable zum Speichern der Chart-Hintergrundfarbe — wir werden sie in Zukunft beim Zeichnen von Schatten benötigen:
//+------------------------------------------------------------------+ //| 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 bool m_shadow; // Shadow presence color m_chart_color_bg; // Chart background color //--- Return the cursor position relative to the (1) entire element and (2) the element's 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:
Da wir zwei Konstanten aus der Enumeration der Objekt-Ganzzahl-Eigenschaften entfernt haben, müssen wir sie nun in den Klassenvariablen speichern.
Deklarieren wir sie im privaten Abschnitt:
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 color m_color_bg; // Element background color uchar m_opacity; // Element opacity //--- Return the index of the array the order's (1) double and (2) string properties are located at
In diesen Variablen werden nun die "Element-Hintergrundfarbe" und die "Element-Transparenz" eingestellt und ausgelesen.
Um das Aussehen von grafischen Objekten zu gestalten, benötigen wir eine Methode, die es erlaubt, die Farbhelligkeit zu verändern.
Dies ist eine der Komponenten des HSL-Farbmodells:
HSL, HLS oder HSI (Ton, Sättigung, Helligkeit (Intensität)) ist ein Farbmodell, in dem Farbton, Sättigung und Helligkeit als Farbkoordinaten verwendet werden. HSV und HSL sind zwei verschiedene Farbmodelle (Helligkeit (lightness) sollte nicht mit Glanz (brightness) verwechselt werden).
Wenn wir grafische Primitive zeichnen, müssen wir die konventionell beleuchteten Teile der Zeichnung aufhellen und die konventionell schattierten Teile abdunkeln. Die Farbe des Bildes selbst sollte davon nicht betroffen sein. Um dies zu gewährleisten, werde ich die Methode anwenden, die das ARGB-Farbmodell in HSL umwandelt und die Helligkeit der Pixel des gewünschten Teils des Bildes ändert.
Deklarieren wir diese Methode im privaten Bereich der Klasse:
//--- Update the coordinates (shift the canvas) bool Move(const int x,const int y,const bool redraw=false); //--- Change the color lightness by the specified amount uint ChangeColorLightness(const uint clr,const double change_value); protected:
Das grafische Elementobjekt ist ein Hauptobjekt für die Erstellung komplexerer grafischer Objekte, die davon abgeleitet werden. Unter Berücksichtigung des Konzepts der Konstruktion von Bibliotheksobjekten (wobei die übergeordnete Klasse einen geschützten Konstruktor besitzt, der die Parameter des erstellten Nachfolgeobjekts angibt), müssen wir einen geschützten parametrischen Konstruktor für das Elementobjekt erstellen. Er soll klärende Parameter über den Typ des zu erzeugenden Nachfolgeobjekts auf der Basis des grafischen Elements (hier ist es das Formularobjekt) erhalten.
Im geschützten Bereich der Klasse deklarieren wir einen neuen geschützten parametrischen Konstruktor:
protected: //--- Protected constructor CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, const long chart_id, const int wnd_num, const string name, const int x, const int y, const int w, const int h); public:
Es soll nur grundlegende Parameter für die Erstellung des Objekts erhalten. Alle anderen Parameter werden erst nach seiner erfolgreichen Erstellung gesetzt. Dies wird in der Kollektionsklasse für grafische Objekte der Bibliothek geschehen, mit der ich noch nicht begonnen habe.
Fügen wir die Initialisierung des Flag für das Vorhandensein von Schatten und Hintergrundfarbe des Charts zum Standard-Konstruktor (nicht-parametrisch) hinzu, und zwar zu seiner Initialisierungsliste:
public: //--- Parametric constructor 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); //--- Default constructor/Destructor CGCnvElement() : m_shadow(false),m_chart_color_bg((color)::ChartGetInteger(::ChartID(),CHART_COLOR_BACKGROUND)) {;}
Ergänzen wir die neuen Methoden zum Setzen von Objekteigenschaften in den Methodenblöcken für einen vereinfachten Zugriff auf Objektparameter:
//+------------------------------------------------------------------+ //| 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 SetColorBackground(const color colour) { this.m_color_bg=colour; } void SetOpacity(const uchar value,const bool redraw=false); //--- Set the flag of (1) object moveability, (2) activity, (3) element ID, (4) element index in the list and (5) shadow presence void SetMovable(const bool flag) { this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,flag); } void SetActive(const bool flag) { this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,flag); } void SetID(const int id) { this.SetProperty(CANV_ELEMENT_PROP_ID,id); } void SetNumber(const int number) { this.SetProperty(CANV_ELEMENT_PROP_NUM,number); } void SetShadow(const bool flag); //--- Return the shift (1) of the left, (2) right, (3) top and (4) bottom edge of the element active area
Die Methoden zur Rückgabe der Hintergrundfarbe und Transparenz geben jetzt die Werte zurück, die in den neu deklarierten Variablen gesetzt sind:
//--- Return (1) the background color, (2) the opacity, coordinate (3) of the right and (4) bottom element edge color ColorBackground(void) const { return this.m_color_bg; } uchar Opacity(void) const { return this.m_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,
Am Ende der Liste fügen wir die Methode zur Rückgabe des Flags zum Zeichnen des vom Objekt geworfenen Schattens, die Methode zur Rückgabe der Chart-Hintergrundfarbe
und die Methode, um das Objekt in den Vordergrund zu rücken (über alle anderen grafischen Objekte im Chart):
//--- Return (1) the element ID, (2) element index in the list, (3) flag of the form shadow presence and (4) the chart background color int ID(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_ID); } int Number(void) const { return (int)this.GetProperty(CANV_ELEMENT_PROP_NUM); } bool IsShadow(void) const { return this.m_shadow; } color ChartColorBackground(void) const { return this.m_chart_color_bg; } //--- Set the object above all void BringToTop(void) { CGBaseObj::SetVisible(false); CGBaseObj::SetVisible(true); } //+------------------------------------------------------------------+
Wie wir sehen, sollten wir das Objekt mit Hilfe der oben betrachteten Methoden der Elternklasse ausblenden und sofort wieder einblenden, um es vor alle anderen zu positionieren.
Wir löschen die Zeilen, die die nun entfernten Eigenschaften setzen, aus dem parametrischen Konstruktor:
this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,0); // Active area offset from the bottom edge of the element this.SetProperty(CANV_ELEMENT_PROP_OPACITY,opacity); // Element opacity this.SetProperty(CANV_ELEMENT_PROP_COLOR_BG,colour); // Element color this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,movable); // Element moveability flag
Nun werden diese, sowie neue Eigenschaften, in den neuen Variablen gesetzt:
//+------------------------------------------------------------------+ //| 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) : m_shadow(false) { this.m_chart_color_bg=(color)::ChartGetInteger(chart_id,CHART_COLOR_BACKGROUND); 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; this.m_color_bg=colour; this.m_opacity=opacity; if(this.Create(chart_id,wnd_num,this.m_name,x,y,w,h,colour,opacity,redraw)) { this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,this.m_canvas.ResourceName()); // Graphical resource name this.SetProperty(CANV_ELEMENT_PROP_CHART_ID,CGBaseObj::ChartID()); // Chart ID this.SetProperty(CANV_ELEMENT_PROP_WND_NUM,CGBaseObj::SubWindow()); // Chart subwindow index this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,CGBaseObj::Name()); // Element object name this.SetProperty(CANV_ELEMENT_PROP_TYPE,element_type); // Graphical element type this.SetProperty(CANV_ELEMENT_PROP_ID,element_id); // Element ID this.SetProperty(CANV_ELEMENT_PROP_NUM,element_num); // Element index in the list this.SetProperty(CANV_ELEMENT_PROP_COORD_X,x); // Element's X coordinate on the chart this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,y); // Element's Y coordinate on the chart this.SetProperty(CANV_ELEMENT_PROP_WIDTH,w); // Element width this.SetProperty(CANV_ELEMENT_PROP_HEIGHT,h); // Element height this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,0); // Active area offset from the left edge of the element this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,0); // Active area offset from the upper edge of the element this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,0); // Active area offset from the right edge of the element this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,0); // Active area offset from the bottom edge of the element this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,movable); // Element moveability flag this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,activity); // Element activity flag this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.RightEdge()); // Element right border this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.BottomEdge()); // Element bottom border this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_X,this.ActiveAreaLeft()); // X coordinate of the element active area this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y,this.ActiveAreaTop()); // Y coordinate of the element active area this.SetProperty(CANV_ELEMENT_PROP_ACT_RIGHT,this.ActiveAreaRight()); // Right border of the element active area this.SetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM,this.ActiveAreaBottom()); // Bottom border of the element active area } else { ::Print(CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.m_name); } } //+------------------------------------------------------------------+
Der neue geschützte paramterische Konstruktor unterscheidet sich nicht von dem oben besprochenen:
//+------------------------------------------------------------------+ //| Protected constructor | //+------------------------------------------------------------------+ CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type, const long chart_id, const int wnd_num, const string name, const int x, const int y, const int w, const int h) : m_shadow(false) { this.m_chart_color_bg=(color)::ChartGetInteger(chart_id,CHART_COLOR_BACKGROUND); 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; this.m_color_bg=NULL_COLOR; this.m_opacity=0; if(this.Create(chart_id,wnd_num,this.m_name,x,y,w,h,this.m_color_bg,this.m_opacity,false)) { this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,this.m_canvas.ResourceName()); // Graphical resource name this.SetProperty(CANV_ELEMENT_PROP_CHART_ID,CGBaseObj::ChartID()); // Chart ID this.SetProperty(CANV_ELEMENT_PROP_WND_NUM,CGBaseObj::SubWindow()); // Chart subwindow index this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,CGBaseObj::Name()); // Element object name this.SetProperty(CANV_ELEMENT_PROP_TYPE,element_type); // Graphical element type this.SetProperty(CANV_ELEMENT_PROP_ID,0); // Element ID this.SetProperty(CANV_ELEMENT_PROP_NUM,0); // Element index in the list this.SetProperty(CANV_ELEMENT_PROP_COORD_X,x); // Element's X coordinate on the chart this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,y); // Element's Y coordinate on the chart this.SetProperty(CANV_ELEMENT_PROP_WIDTH,w); // Element width this.SetProperty(CANV_ELEMENT_PROP_HEIGHT,h); // Element height this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,0); // Active area offset from the left edge of the element this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,0); // Active area offset from the upper edge of the element this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,0); // Active area offset from the right edge of the element this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,0); // Active area offset from the bottom edge of the element this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,false); // Element moveability flag this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,false); // Element activity flag this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.RightEdge()); // Element right border this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.BottomEdge()); // Element bottom border this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_X,this.ActiveAreaLeft()); // X coordinate of the element active area this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y,this.ActiveAreaTop()); // Y coordinate of the element active area this.SetProperty(CANV_ELEMENT_PROP_ACT_RIGHT,this.ActiveAreaRight()); // Right border of the element active area this.SetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM,this.ActiveAreaBottom()); // Bottom border of the element active area } else { ::Print(CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.m_name); } } //+------------------------------------------------------------------+
Es werden weniger Werte übergeben, die Hintergrundfarbe des Elements auf ein transparentes Weiß gesetzt und die volle Element-Transparenz gesetzt.
Entfernen wir die jetzt unnötigen Zeilen aus der Methode zur Erstellung der Objektstruktur:
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
und fügen das Speichern der Parameter aus den neuen Variablen unten hinzu:
//+------------------------------------------------------------------+ //| 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.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 this.m_struct_obj.color_bg=this.m_color_bg; // Element background color this.m_struct_obj.opacity=this.m_opacity; // Element opacity //--- 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; } //+------------------------------------------------------------------+
Das Gleiche geschieht in der Methode zum Erzeugen des Objekts 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_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 this.m_color_bg=this.m_struct_obj.color_bg; // Element background color this.m_opacity=this.m_struct_obj.opacity; // Element opacity //--- 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 } //+------------------------------------------------------------------+
In der Methode, die das grafische Objektelement erstellt, wird der Objekthintergrund nun komplett gelöscht und mit weißer, transparenter Farbe gefüllt:
//+------------------------------------------------------------------+ //| Create the graphical element object | //+------------------------------------------------------------------+ bool CGCnvElement::Create(const long chart_id, // Chart ID const int wnd_num, // Chart subwindow const string name, // Element name const int x, // X coordinate const int y, // Y coordinate const int w, // Width const int h, // Height const color colour, // Background color const uchar opacity, // Opacity const bool redraw=false) // Flag indicating the need to redraw { if(this.m_canvas.CreateBitmapLabel(chart_id,wnd_num,name,x,y,w,h,COLOR_FORMAT_ARGB_NORMALIZE)) { this.Erase(NULL_COLOR); this.m_canvas.Update(redraw); this.m_shift_y=(int)::ChartGetInteger(chart_id,CHART_WINDOW_YDISTANCE,wnd_num); return true; } return false; } //+------------------------------------------------------------------+
In der Methode, die die Transparenz des Elements setzt, wird die Transparenz nun in der Variable gesetzt, anstatt eine Eigenschaft des Objekts zu entfernen:
//+------------------------------------------------------------------+ //| Set the element opacity | //+------------------------------------------------------------------+ void CGCnvElement::SetOpacity(const uchar value,const bool redraw=false) { this.m_canvas.TransparentLevelSet(value); this.m_opacity=value; this.m_canvas.Update(redraw); } //+------------------------------------------------------------------+
Die neue Methode ändert die Farbhelligkeit um den angegebenen Wert:
//+------------------------------------------------------------------+ //| Change the color lightness by the specified amount | //+------------------------------------------------------------------+ uint CGCnvElement::ChangeColorLightness(const uint clr,const double change_value) { if(change_value==0.0) return clr; double a=GETRGBA(clr); double r=GETRGBR(clr); double g=GETRGBG(clr); double b=GETRGBB(clr); double h=0,s=0,l=0; CColors::RGBtoHSL(r,g,b,h,s,l); double nl=l+change_value; if(nl>1.0) nl=1.0; if(nl<0.0) nl=0.0; CColors::HSLtoRGB(h,s,nl,r,g,b); return ARGB(a,r,g,b); } //+------------------------------------------------------------------+
Hier:
Wird der übergebene Wert geprüft, um den die Helligkeit verändert werden soll. Wenn Null übergeben wird, soll nichts geändert werden — Farbe wird unverändert zurückgeben.
Als Nächstes wird jede der ARGB-Farbkomponenten der an die Methode übergebenen Farbe empfangen und die RGB-Komponenten in das HSL-Farbmodell konvertiert.
Nach der Konvertierung der Werte der einzelnen Komponenten werden die HSL-Modelle in die entsprechenden Variablen gesetzt (wir benötigen die l-Komponente).
Der an die Methode übergebene Wert (Änderungswert kann von -1,0 bis 1,0 variieren) und anpassen, wenn er über akzeptable Wertebereiche hinausgeht.
Als Nächste wird das HSL-Modells zurück nach RGB konvertiert und das ARGB-Modells zurückgegeben, das sich aus den neuen Farbkomponenten ergibt, die durch die Konvertierung vom HSL-Modell nach RGB erzeugt wurden.
Typen der Farbschemata und der Formulare
Die Bibliothek soll die Erstellung verschiedener Objekte unterstützen — grafische Elemente, entsprechende Formulare, Fenster usw. Jedes Formular, Fenster oder Bild (Rahmen, Trennlinien, Dropdown-Listen usw.) kann unterschiedliche Darstellungsstile haben. Es wäre jedoch seltsam, verschiedene Objekte mit unterschiedlichen Zeichenstilen, Farben und Dekorationen innerhalb eines einzigen Programms zu haben.
Um die Entwicklung von identisch aussehenden Objekten, die zu einem einzigen Programm gehören, zu vereinfachen, werde ich Zeichenstile, Objekttypen und Farbschemata einführen. Dies wird es dem Endanwender ermöglichen, den gewünschten Stil und das Farbschema in den Programmeinstellungen auszuwählen, während der Programmierer sich um all das nicht allzu sehr kümmern muss — ausgewählte Themen und Stile werden sofort alle Objekte nach einem einzigen Kriterium umgestalten. Ich muss nur die notwendigen Änderungen und Ergänzungen in der Datei mit den grafischen Einstellungen vornehmen, in der alle notwendigen Farben sowie Objekt- und Primitivparameter enthalten sind.
Eine solche Taktik habe ich bereits bei der Entwicklung der Bibliothek Nachrichtenklasse angewendet. Sie enthält die Liste der Nachrichtenindizes und das Array der Texte, die den Nachrichtenindizes entsprechen. Das erste, was ich normalerweise zu Beginn eines jeden Artikels mache, ist das Setzen neuer Daten.
Die Datei der grafischen Einstellungen wird auf die gleiche Weise aufgebaut: Sie enthält die Enumeration der Farbthemen und Objektstile sowie die entsprechenden Arrays, die nach und nach neue Parameter und deren Werte für jede neu hinzugefügte Eigenschaft, jedes Thema oder jede Farbe erhalten.
Legen wir im Stammordner \MQL5\Include\DoEasy\ die neue Include-Datei GraphINI.mqh an und ergänzen die Anzahl der Farbschemata:
//+------------------------------------------------------------------+ //| GraphINI.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/en/users/artmedia70" //+------------------------------------------------------------------+ //| Macro substitutions | //+------------------------------------------------------------------+ #define TOTAL_COLOR_THEMES (2) // Number of color schemes //+------------------------------------------------------------------+
Zwei Farbschemata werden ausreichen, um die Verwendung der Einstellungsdatei zu demonstrieren. Ich werde ihre Anzahl später erhöhen.
Unten setze ich die Indizes der Farbschemata und die Indizes der Parameter eines Themas. Jedes Schema wird die gleiche Anzahl von Parametern haben:
//+------------------------------------------------------------------+ //| GraphINI.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/en/users/artmedia70" //+------------------------------------------------------------------+ //| Macro substitutions | //+------------------------------------------------------------------+ #define TOTAL_COLOR_THEMES (2) // Number of color schemes //+------------------------------------------------------------------+ //| Enumerations | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| List of color scheme indices | //+------------------------------------------------------------------+ enum ENUM_COLOR_THEMES { COLOR_THEME_BLUE_STEEL, // Blue steel COLOR_THEME_LIGHT_CYAN_GRAY, // Light cyan gray }; //+------------------------------------------------------------------+ //| List of indices of color scheme parameters | //+------------------------------------------------------------------+ enum ENUM_COLOR_THEME_COLORS { COLOR_THEME_COLOR_FORM_BG, // Form background color COLOR_THEME_COLOR_FORM_FRAME, // Form frame color COLOR_THEME_COLOR_FORM_FRAME_OUTER, // Form outer frame color COLOR_THEME_COLOR_FORM_SHADOW, // Form shadow color }; #define TOTAL_COLOR_THEME_COLORS (4) // Number of parameters in the color theme //+------------------------------------------------------------------+
Der Zugriff auf die einzelnen Farbschemata und die erforderlichen Parameter erfolgt bequem über die Namen der Enumeration-Konstanten.
Im Folgenden schreiben wir ein zweidimensionales Array, das in der ersten Dimension Farbschemata und in der zweiten Dimension Farbparameter-Indizes zum Zeichnen verschiedener Objekteigenschaften enthält:
//+------------------------------------------------------------------+ //| GraphINI.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/en/users/artmedia70" //+------------------------------------------------------------------+ //| Macro substitutions | //+------------------------------------------------------------------+ #define TOTAL_COLOR_THEMES (2) // Number of color schemes //+------------------------------------------------------------------+ //| Enumerations | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| List of color scheme indices | //+------------------------------------------------------------------+ enum ENUM_COLOR_THEMES { COLOR_THEME_BLUE_STEEL, // Blue steel COLOR_THEME_LIGHT_CYAN_GRAY, // Light cyan gray }; //+------------------------------------------------------------------+ //| List of indices of color scheme parameters | //+------------------------------------------------------------------+ enum ENUM_COLOR_THEME_COLORS { COLOR_THEME_COLOR_FORM_BG, // Form background color COLOR_THEME_COLOR_FORM_FRAME, // Form frame color COLOR_THEME_COLOR_FORM_FRAME_OUTER, // Form outer frame color COLOR_THEME_COLOR_FORM_SHADOW, // Form shadow color }; #define TOTAL_COLOR_THEME_COLORS (4) // Number of parameters in the color theme //+------------------------------------------------------------------+ //| The array containing color schemes | //+------------------------------------------------------------------+ color array_color_themes[TOTAL_COLOR_THEMES][TOTAL_COLOR_THEME_COLORS]= { //--- Parameters of the "Blue steel" color scheme { C'134,160,181', // Form background color C'134,160,181', // Form frame color clrDimGray, // Form outer frame color C'46,85,117', // Form shadow color }, //--- Parameters of the "Light cyan gray" color scheme { C'181,196,196', // Form background color C'181,196,196', // Form frame color clrGray, // Form outer frame color C'130,147,153', // Form shadow color }, }; //+------------------------------------------------------------------+
Dies ist das Array, das nach und nach neue Farben für jeden neu hinzugefügten grafischen Objektparameter erhalten soll, dessen Farbe vom gewählten Farbschema abhängen soll.
Als Nächstes fügen wir die Enumerationen von Glättungsarten beim Zeichnen von Primitiven, Rahmenstilen, Typen und Formularstilen hinzu, gefolgt von den Enumerationen von Formularstil-Eigenschaftsindizes und ihren Parametern, ähnlich denen, die für Farbschemata entworfen wurden:
//+------------------------------------------------------------------+ //| Smoothing types | //+------------------------------------------------------------------+ enum ENUM_SMOOTHING_TYPE { SMOOTHING_TYPE_NONE, // No smoothing SMOOTHING_TYPE_AA, // Anti-aliasing SMOOTHING_TYPE_WU, // Wu SMOOTHING_TYPE_THICK, // Thick SMOOTHING_TYPE_DUAL, // Dual }; //+------------------------------------------------------------------+ //| Frame styles | //+------------------------------------------------------------------+ enum ENUM_FRAME_STYLE { FRAME_STYLE_SIMPLE, // Simple frame FRAME_STYLE_FLAT, // Flat frame FRAME_STYLE_BEVEL, // Embossed (convex) FRAME_STYLE_STAMP, // Embossed (concave) }; //+------------------------------------------------------------------+ //| Form types | //+------------------------------------------------------------------+ enum ENUM_FORM_TYPE { FORM_TYPE_SQUARE, // Rectangular }; //+------------------------------------------------------------------+ //| Form styles | //+------------------------------------------------------------------+ enum ENUM_FORM_STYLE { FORM_STYLE_FLAT, // Flat form FORM_STYLE_BEVEL, // Embossed form }; #define TOTAL_FORM_STYLES //+------------------------------------------------------------------+ //| List of form style parameter indices | //+------------------------------------------------------------------+ enum ENUM_FORM_STYLE_PARAMS { FORM_STYLE_FRAME_WIDTH_LEFT, // Form frame width to the left FORM_STYLE_FRAME_WIDTH_RIGHT, // Form frame width to the right FORM_STYLE_FRAME_WIDTH_TOP, // Form frame width on top FORM_STYLE_FRAME_WIDTH_BOTTOM, // Form frame width below FORM_STYLE_FRAME_SHADOW_OPACITY, // Shadow opacity }; #define TOTAL_FORM_STYLE_PARAMS (5) // Number of form style parameters //+------------------------------------------------------------------+ //| Array containing form style parameters | //+------------------------------------------------------------------+ int array_form_style[TOTAL_FORM_STYLES][TOTAL_FORM_STYLE_PARAMS]= { //--- "Flat form" style parameters { 3, // Form frame width to the left 3, // Form frame width to the right 3, // Form frame width on top 3, // Form frame width below 80, // Shadow opacity }, //--- "Embossed form" style parameters { 4, // Form frame width to the left 4, // Form frame width to the right 4, // Form frame width on top 4, // Form frame width below 100, // Shadow opacity }, }; //+------------------------------------------------------------------+
Das zweite Array, dessen Konstruktionslogik mit dem Farbschema-Array identisch ist, wird nach und nach neue Parameter für die Konstruktion von Elementen, Formularen, Fenstern und anderen Objekten erhalten, deren Parameter von einem gewählten Stil für die Gestaltung des Erscheinungsbildes der Objekte abhängen sollen.
Die neuen Enumerationen für Programmeingaben fügen wir der Datei \MQL5\Include\DoEasy\InpData.mqh hinzu, um den erforderlichen Stil der Konstruktion von Objekten und ein Farbschema auswählen zu können. Gleich zu Beginn binden wir die neu erstellte Datei GraphINI.mqh ein:
//+------------------------------------------------------------------+ //| InpData.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" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "GraphINI.mqh" //+------------------------------------------------------------------+
Im Codeblock für die Kompilierung in Englisch und Russisch fügen wir die neuen Eingaben der Enumerationen für die Auswahl eines Farbschemas hinzu:
//+------------------------------------------------------------------+ //| InpData.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" //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "GraphINI.mqh" //+------------------------------------------------------------------+ //| Macro substitutions | //+------------------------------------------------------------------+ #define COMPILE_EN // Comment out the string for compilation in Russian //+------------------------------------------------------------------+ //| Input enumerations | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| English language inputs | //+------------------------------------------------------------------+ #ifdef COMPILE_EN //+------------------------------------------------------------------+ //| Modes of working with symbols | //+------------------------------------------------------------------+ enum ENUM_SYMBOLS_MODE { SYMBOLS_MODE_CURRENT, // Work only with the current Symbol SYMBOLS_MODE_DEFINES, // Work with a given list of Symbols SYMBOLS_MODE_MARKET_WATCH, // Working with Symbols from the "Market Watch" window SYMBOLS_MODE_ALL // Work with a complete list of Symbols }; //+------------------------------------------------------------------+ //| Mode of working with timeframes | //+------------------------------------------------------------------+ enum ENUM_TIMEFRAMES_MODE { TIMEFRAMES_MODE_CURRENT, // Work only with the current timeframe TIMEFRAMES_MODE_LIST, // Work with a given list of timeframes TIMEFRAMES_MODE_ALL // Work with a complete list of timeframes }; //+------------------------------------------------------------------+ //| "Yes"/"No" | //+------------------------------------------------------------------+ enum ENUM_INPUT_YES_NO { INPUT_NO = 0, // No INPUT_YES = 1 // Yes }; //+------------------------------------------------------------------+ //| Select color themes | //+------------------------------------------------------------------+ enum ENUM_INPUT_COLOR_THEME { INPUT_COLOR_THEME_BLUE_STEEL, // Blue steel INPUT_COLOR_THEME_LIGHT_CYAN_GRAY, // Light cyan gray }; //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Russian language inputs | //+------------------------------------------------------------------+ #else //+------------------------------------------------------------------+ //| Modes of working with symbols | //+------------------------------------------------------------------+ enum ENUM_SYMBOLS_MODE { SYMBOLS_MODE_CURRENT, // Работа только с текущим символом SYMBOLS_MODE_DEFINES, // Работа с заданным списком символов SYMBOLS_MODE_MARKET_WATCH, // Работа с символами из окна "Обзор рынка" SYMBOLS_MODE_ALL // Работа с полным списком символов }; //+------------------------------------------------------------------+ //| Mode of working with timeframes | //+------------------------------------------------------------------+ enum ENUM_TIMEFRAMES_MODE { TIMEFRAMES_MODE_CURRENT, // Работа только с текущим таймфреймом TIMEFRAMES_MODE_LIST, // Работа с заданным списком таймфреймов TIMEFRAMES_MODE_ALL // Работа с полным списком таймфреймов }; //+------------------------------------------------------------------+ //| "Да"/"Нет" | //+------------------------------------------------------------------+ enum ENUM_INPUT_YES_NO { INPUT_NO = 0, // Нет INPUT_YES = 1 // Да }; //+------------------------------------------------------------------+ //| Select color themes | //+------------------------------------------------------------------+ enum ENUM_COLOR_THEME { COLOR_THEME_BLUE_STEEL, // Голубая сталь COLOR_THEME_LIGHT_CYAN_GRAY, // Светлый серо-циановый }; //+------------------------------------------------------------------+ #endif //+------------------------------------------------------------------+
Damit können wir beim Start des Programms das gewünschte Farbschema auswählen. Später werde ich die Auswahl von Objektzeichnungsstilen und Konstruktionstypen hinzufügen.
Objektklasse des Formulars
Das Formularobjekt ist eine erweiterte Version eines grafischen Objekts. Es erlaubt das Zeichnen von "dimensionalen" Rahmen und anderen Primitiven, sowie das Anhängen anderer Elemente an es. Natürlich können Sie alles, was Sie brauchen, "von Hand" zeichnen, aber das Formular erlaubt Ihnen, Ihre Arbeit zu automatisieren.
In E:\MetaQuotes\MetaTrader 5\MQL5\Include\DoEasy\Objects\Graph\ erstellen wir die neue Datei Form.mqh der Klasse CForm. Die Klasse sollte vom grafischen Elementobjekt geerbt werden, d. h. die Elementobjektdatei sollte ebenfalls enthalten sein:
//+------------------------------------------------------------------+ //| Form.mqh | //| 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" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "GCnvElement.mqh" //+------------------------------------------------------------------+ //| Form object class | //+------------------------------------------------------------------+ class CForm : public CGCnvElement { }
Im privaten Bereich der Klasse deklarieren wir die benötigten Objekte, Variablen und Klassenhilfsmethoden:
//+------------------------------------------------------------------+ //| Form object class | //+------------------------------------------------------------------+ class CForm : public CGCnvElement { private: CArrayObj m_list_elements; // List of attached elements CGCnvElement *m_shadow_obj; // Pointer to the shadow object color m_color_frame; // Form frame color color m_color_shadow; // Form shadow color int m_frame_width_left; // Form frame width to the left int m_frame_width_right; // Form frame width to the right int m_frame_width_top; // Form frame width at the top int m_frame_width_bottom; // Form frame width at the bottom //--- Initialize the variables void Initialize(void); //--- Create a new graphical object CGCnvElement *CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type, const int element_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, const bool activity); public:
Der öffentliche Teil der Klasse enthält die für die Bibliotheksobjekte standardmäßigen Methoden und mehrere Konstruktoren — die Standardmethoden sowie die, die es dem Nutzer ermöglichen, ein Formular-Objekt für ein bestimmtes Chart und Unterfenster zu erstellen, für ein bestimmtes Unterfenster des aktuellen Charts und für das aktuelle Chart im Hauptfenster:
public: //--- Constructors CForm(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h); CForm(const int subwindow, const string name, const int x, const int y, const int w, const int h); CForm(const string name, const int x, const int y, const int w, const int h); CForm() { this.Initialize(); } //--- Destructor ~CForm(); //--- Supported form properties (1) integer and (2) string ones virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_CANV_ELEMENT_PROP_STRING property) { return true; } //--- Return (1) the list of attached objects and (2) the shadow object CArrayObj *GetList(void) { return &this.m_list_elements; } CGCnvElement *GetShadowObj(void) { return this.m_shadow_obj; }
Im Folgenden werden die Methoden zur Arbeit mit dem Formularobjekt beschrieben:
//--- Set the form (1) color scheme and (2) style virtual void SetColorTheme(const ENUM_COLOR_THEMES theme,const uchar opacity); virtual void SetFormStyle(const ENUM_FORM_STYLE style, const ENUM_COLOR_THEMES theme, const uchar opacity, const bool shadow=false, const bool redraw=false); //--- Create a new attached element bool CreateNewElement(const int element_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, const bool activity); //--- Create a shadow object void CreateShadow(const uchar opacity); //--- Draw an object shadow void DrawShadow(const uchar opacity); //--- Draw the form frame void DrawFormFrame(const int wd_top, // Frame upper segment width const int wd_bottom, // Frame lower segment width const int wd_left, // Frame left segment width const int wd_right, // Frame right segment width const color colour, // Frame color const uchar opacity, // Frame opacity const ENUM_FRAME_STYLE style); // Frame style //--- Draw a simple frame void DrawFrameSimple(const int x, // X coordinate relative to the form const int y, // Y coordinate relative to the form const int width, // Frame width const int height, // Frame height const int wd_top, // Frame upper segment width const int wd_bottom, // Frame lower segment width const int wd_left, // Frame left segment width const int wd_right, // Frame right segment width const color colour, // Frame color const uchar opacity); // Frame opacity //--- Draw a flat frame void DrawFrameFlat(const int x, // X coordinate relative to the form const int y, // Y coordinate relative to the form const int width, // Frame width const int height, // Frame height const int wd_top, // Frame upper segment width const int wd_bottom, // Frame lower segment width const int wd_left, // Frame left segment width const int wd_right, // Frame right segment width const color colour, // Frame color const uchar opacity); // Frame opacity //--- Draw an embossed (convex) frame void DrawFrameBevel(const int x, // X coordinate relative to the form const int y, // Y coordinate relative to the form const int width, // Frame width const int height, // Frame height const int wd_top, // Frame upper segment width const int wd_bottom, // Frame lower segment width const int wd_left, // Frame left segment width const int wd_right, // Frame right segment width const color colour, // Frame color const uchar opacity); // Frame opacity //--- Draw an embossed (concave) frame void DrawFrameStamp(const int x, // X coordinate relative to the form const int y, // Y coordinate relative to the form const int width, // Frame width const int height, // Frame height const int wd_top, // Frame upper segment width const int wd_bottom, // Frame lower segment width const int wd_left, // Frame left segment width const int wd_right, // Frame right segment width const color colour, // Frame color const uchar opacity); // Frame opacity //--- Draw a simple field void DrawFieldFlat(const int x, // X coordinate relative to the form const int y, // Y coordinate relative to the form const int width, // Field width const int height, // Field height const color colour, // Field color const uchar opacity); // Field opacity //--- Draw an embossed (convex) field void DrawFieldBevel(const int x, // X coordinate relative to the form const int y, // Y coordinate relative to the form const int width, // Field width const int height, // Field height const color colour, // Field color const uchar opacity); // Field opacity //--- Draw an embossed (concave) field void DrawFieldStamp(const int x, // X coordinate relative to the form const int y, // Y coordinate relative to the form const int width, // Field width const int height, // Field height const color colour, // Field color const uchar opacity); // Field opacity //+------------------------------------------------------------------+ //| Methods of simplified access to object properties | //+------------------------------------------------------------------+ //--- (1) Set and (2) get the form frame color void SetColorFrame(const color colour) { this.m_color_frame=colour; } color ColorFrame(void) const { return this.m_color_frame; } //--- (1) Set and (2) return the form shadow color void SetColorShadow(const color colour) { this.m_color_shadow=colour; } color ColorShadow(void) const { return this.m_color_shadow; } }; //+------------------------------------------------------------------+
Betrachten wir die deklarierten Methoden im Detail.
Der Konstruktor, der die ID des Charts und des Unterfensters angibt:
//+------------------------------------------------------------------+ //| Constructor indicating the chart and subwindow ID | //+------------------------------------------------------------------+ CForm::CForm(const long chart_id, const int subwindow, const string name, const int x, const int y, const int w, const int h) : CGCnvElement(GRAPH_ELEMENT_TYPE_FORM,chart_id,subwindow,name,x,y,w,h) { this.Initialize(); } //+------------------------------------------------------------------+
Der Konstruktor erhält die ID des Charts und den Index des Unterfensters, mit dem ein Formularobjekt erzeugt wird, seinen Namen, die Koordinaten des linken oberen Formularwinkels und seine Größe. In der Initialisierung wird der Konstruktor der Elementobjektklasse aufgerufen und der Typ des Formularobjekts angegeben. Im Hauptteil der Klasse wird die Initialisierungsmethode aufgerufen.
Aktueller Chart-Konstruktor mit Angabe des Unterfensters:
//+------------------------------------------------------------------+ //| Current chart constructor specifying the subwindow | //+------------------------------------------------------------------+ CForm::CForm(const int subwindow, const string name, const int x, const int y, const int w, const int h) : CGCnvElement(GRAPH_ELEMENT_TYPE_FORM,::ChartID(),subwindow,name,x,y,w,h) { this.Initialize(); } //+------------------------------------------------------------------+
Der Konstruktor erhält die Nummer des Teilfensters, auf dem das Formularobjekt erstellt werden soll (das aktuelle Chart), den Namen des Formularobjekts, die Koordinaten des linken oberen Formularwinkels und seine Größe. In der Initialisierungsliste wird der Konstruktor der Elementobjektklasse aufgerufen und dabei der Typ des Formularobjekts und die aktuelle Chart-ID angegeben. Im Hauptteil der Klasse wird die Initialisierungsmethode aufgerufen.
Der Konstruktor auf dem aktuellen Chart im Hauptfenster des Charts:
//+------------------------------------------------------------------+ //| Constructor on the current chart in the main chart window | //+------------------------------------------------------------------+ CForm::CForm(const string name, const int x, const int y, const int w, const int h) : CGCnvElement(GRAPH_ELEMENT_TYPE_FORM,::ChartID(),0,name,x,y,w,h) { this.Initialize(); } //+------------------------------------------------------------------+
Der Konstruktor erhält den Namen des Formularobjekts, die Koordinaten des linken oberen Formularwinkels und seine Größe. In der Initialisierung wird der Konstruktor der Elementobjektklasse aufgerufen und der Typ des Formularobjekts und die aktuelle Chart-ID sowie den Hauptfensterindex (0) angegeben. Im Hauptteil der Klasse wird die Initialisierungsmethode aufgerufen.
Im Destruktor der Klasse wird die Gültigkeit des Zeigers auf das Schattenobjekt geprüft und das Objekt, falls es existiert, entfernt:
//+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CForm::~CForm() { if(m_shadow_obj!=NULL) delete m_shadow_obj; } //+------------------------------------------------------------------+
Die Methode der Variableninitialisierung:
//+------------------------------------------------------------------+ //| Initialize the variables | //+------------------------------------------------------------------+ void CForm::Initialize(void) { this.m_list_elements.Clear(); this.m_list_elements.Sort(); this.m_shadow_obj=NULL; this.m_shadow=false; this.m_frame_width_right=2; this.m_frame_width_left=2; this.m_frame_width_top=2; this.m_frame_width_bottom=2; } //+------------------------------------------------------------------+
Hier lösche ich die Liste der an das Formular angehängten Elemente, setze das Flag für die sortierte Liste und gebe die Standardwerte für den Zeiger auf das Formularobjekt (NULL), das Flag für das Zeichnen des Schattens (false) und die Größe des Formularrahmens (zwei Pixel auf jeder Seite) an.
Die private Methode, die ein neues grafisches Objekt erzeugt:
//+------------------------------------------------------------------+ //| Create a new graphical object | //+------------------------------------------------------------------+ CGCnvElement *CForm::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type, const int obj_num, const string obj_name, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable, const bool activity) { int pos=::StringLen(::MQLInfoString(MQL_PROGRAM_NAME)); string pref=::StringSubstr(NameObj(),pos+1); string name=pref+"_"+obj_name; CGCnvElement *element=new CGCnvElement(type,this.ID(),obj_num,this.ChartID(),this.SubWindow(),name,x,y,w,h,colour,opacity,movable,activity); if(element==NULL) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),": ",name); return element; } //+------------------------------------------------------------------+
Die Methode erhält alle Parameter, die für die Erstellung eines neuen Objekts notwendig sind — seinen Typ, seinen Index in der Liste der angehängten Objekte, Name, Koordinaten, Größe, Farbe, Transparenz und die Flags der Beweglichkeit und Aktivität des Objekts.
Im Klassenkörper wird das Ende aus dem Namensobjekt abgefragt (der Name besteht aus dem Programmnamen und dem Objektnamen, der bei der Erstellung vergeben wurde). Wir müssen den Objektnamen bei seiner Erstellung abfragen und den an die Methode übergebenen Namen hinzufügen.
Im Falle eines Namens wie beispielsweise "Programm_name_Form01" extrahieren wir die Teilzeichenkette "Form01" und fügen den an die Methode übergebenen Namen hinzu. Wenn wir ein Shadow-Objekt erzeugen und den Namen"Shadow" übergeben, lautet der Objektname "Form01_ Shadow", während der endgültige Name des erzeugten Objekts wie folgt lautet: "Programm_name_Form01_Schatten".
Als Nächstes erstellen wir ein neues Objekt, wobei wir seinen Typ, die Parameter des Charts (auf dem das aktuelle Formularobjekt erstellt wurde), den spezifischen Namen und andere an die Methode übergebene Parameter angeben. Es wird der Zeiger auf das erstellte Objekt in der Methode zurückgegeben zurück oder NULL im Falle eines Fehlers.
Die Methode, die ein neues angehängtes Element erstellt:
//+------------------------------------------------------------------+ //| Create a new attached element | //+------------------------------------------------------------------+ bool CForm::CreateNewElement(const int element_num, const string element_name, const int x, const int y, const int w, const int h, const color colour, const uchar opacity, const bool movable, const bool activity) { CGCnvElement *obj=this.CreateNewGObject(GRAPH_ELEMENT_TYPE_ELEMENT,element_num,element_name,x,y,w,h,colour,opacity,movable,activity); if(obj==NULL) return false; this.m_list_elements.Sort(SORT_BY_CANV_ELEMENT_NAME_OBJ); int index=this.m_list_elements.Search(obj); if(index>WRONG_VALUE) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_OBJ_ALREADY_IN_LIST),": ",obj.NameObj()); delete obj; return false; } if(!this.m_list_elements.Add(obj)) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST),": ",obj.NameObj()); delete obj; return false; } return true; } //+------------------------------------------------------------------+
Die Methode erzeugt ein neues Grafikelement-Objekt mit Hilfe der obigen Methode und fügt es der Liste der angehängten Objekte im Formularobjekt hinzu. Wenn das Erstellen eines neuen Objekts oder das Hinzufügen zur Liste der angehängten Objekte fehlschlägt, wird eine Fehlermeldung angezeigt und false zurückgegeben. Wenn ein neues Element erfolgreich erstellt und der Liste hinzugefügt wurde, wird true zurückgegeben.
Die Methode, die das Schattenobjekt erzeugt:
//+------------------------------------------------------------------+ //| Create the shadow object | //+------------------------------------------------------------------+ void CForm::CreateShadow(const uchar opacity) { //--- If the shadow flag is disabled, exit if(!this.m_shadow) return; //--- Calculate the shadow object coordinates according to the offset from the top and left int x=this.CoordX()-OUTER_AREA_SIZE; int y=this.CoordY()-OUTER_AREA_SIZE; //--- Calculate the width and height in accordance with the top, bottom, left and right offsets int w=this.Width()+OUTER_AREA_SIZE*2; int h=this.Height()+OUTER_AREA_SIZE*2; //--- Create a new element object and set the pointer to it in the variable this.m_shadow_obj=this.CreateNewGObject(GRAPH_ELEMENT_TYPE_ELEMENT,-1,"Shadow",x,y,w,h,this.m_chart_color_bg,opacity,Movable(),false); if(this.m_shadow_obj==NULL) return; //--- Move the form object to the foreground this.BringToTop(); } //+------------------------------------------------------------------+
Die Methodenlogik ist im Code kommentiert. Kurz gesagt, da das Elementobjekt, auf das der Schatten gezeichnet werden soll, größer sein sollte als das Formularobjekt, für das es erstellt wird (wir benötigen oben, unten, links und rechts freien Platz, um den Schatten zu zeichnen), wird die Größe des neuen Objekts in Abhängigkeit vom Makro-Substitution OUTER_AREA_SIZE berechnet.
Nachdem das Objekt erfolgreich erstellt wurde, wird es automatisch über das Formularobjekt gesetzt, auf dem es erstellt wurde. Daher müssen wir das Formularobjekt zwangsweise in den Vordergrund stellen. Dies geschieht ganz am Ende der Methode.
Die Methode zum Zeichnen von Schatten:
//+------------------------------------------------------------------+ //| Draw the shadow | //+------------------------------------------------------------------+ void CForm::DrawShadow(const uchar opacity) { //--- If the shadow flag is disabled, exit if(!this.m_shadow) return; //--- Calculate rectangle coordinates relative to the shadow object borders int x=OUTER_AREA_SIZE+1; int y=OUTER_AREA_SIZE+1; //--- Draw a filled rectangle starting from the calculated coordinates and having the size of the current form object m_shadow_obj.DrawRectangleFill(x,y,x+Width(),y+Height(),this.ColorShadow(),opacity); //--- Update the shadow object for displaying changes m_shadow_obj.Update(); return; } //+------------------------------------------------------------------+
Die Methodenlogik ist mit Kommentaren im Code versehen. Derzeit ist diese Methode nur ein Werkstück für die Erstellung einer vollwertigen Methode zum Zeichnen von Objektschatten. Derzeit zeichnet die Methode lediglich ein einfaches, nach rechts unten verschobenes Rechteck "unter" dem aktuellen Objekt auf das zum Zeichnen von Schatten angelegte Elementobjekt.
Die Methode setzt ein Farbschema:
//+------------------------------------------------------------------+ //| Set a color scheme | //+------------------------------------------------------------------+ void CForm::SetColorTheme(const ENUM_COLOR_THEMES theme,const uchar opacity) { this.SetOpacity(opacity); this.SetColorBackground(array_color_themes[theme][COLOR_THEME_COLOR_FORM_BG]); this.SetColorFrame(array_color_themes[theme][COLOR_THEME_COLOR_FORM_FRAME]); this.SetColorShadow(array_color_themes[theme][COLOR_THEME_COLOR_FORM_SHADOW]); } //+------------------------------------------------------------------+
Die Methode dient zum Einstellen eines bestimmten Farbschemas für das Objekt. Die Methode erhält das gewünschte Schema und den Wert für die Transparenz des Formularobjekts. Die Transparenz des Formulars, die Hintergrundfarbe des Formulars, die Rahmenfarbe des Formulars und die Schattenfarbe des Formulars werden dann anhand der Werte im Array der Farbschemata, das ich oben erstellt habe, eingestellt.
Die Methode setzt den Stil des Formulars:
//+------------------------------------------------------------------+ //| Set the form style | //+------------------------------------------------------------------+ void CForm::SetFormStyle(const ENUM_FORM_STYLE style, const ENUM_COLOR_THEMES theme, const uchar opacity, const bool shadow=false, const bool redraw=false) { //--- Set opacity parameters and the size of the form frame side this.m_shadow=shadow; this.m_frame_width_top=array_form_style[style][FORM_STYLE_FRAME_WIDTH_TOP]; this.m_frame_width_bottom=array_form_style[style][FORM_STYLE_FRAME_WIDTH_BOTTOM]; this.m_frame_width_left=array_form_style[style][FORM_STYLE_FRAME_WIDTH_LEFT]; this.m_frame_width_right=array_form_style[style][FORM_STYLE_FRAME_WIDTH_RIGHT]; //--- Set the color scheme this.SetColorTheme(theme,opacity); //--- Create the shadow object and draw a simple distinct shadow this.CreateShadow((uchar)array_form_style[style][FORM_STYLE_FRAME_SHADOW_OPACITY]); this.DrawShadow((uchar)array_form_style[style][FORM_STYLE_FRAME_SHADOW_OPACITY]); //--- Fill in the form background with color and opacity this.Erase(this.ColorBackground(),this.Opacity()); //--- Depending on the selected form style, draw the corresponding form frame and the outer bounding frame switch(style) { case FORM_STYLE_BEVEL : this.DrawFormFrame(this.m_frame_width_top,this.m_frame_width_bottom,this.m_frame_width_left,this.m_frame_width_right,this.ColorFrame(),this.Opacity(),FRAME_STYLE_BEVEL); this.DrawRectangle(0,0,Width()-1,Height()-1,array_color_themes[theme][COLOR_THEME_COLOR_FORM_FRAME_OUTER],this.Opacity()); break; //---FORM_STYLE_FLAT default: this.DrawFormFrame(this.m_frame_width_top,this.m_frame_width_bottom,this.m_frame_width_left,this.m_frame_width_right,this.ColorFrame(),this.Opacity(),FRAME_STYLE_FLAT); this.DrawRectangle(0,0,Width()-1,Height()-1,array_color_themes[theme][COLOR_THEME_COLOR_FORM_FRAME_OUTER],this.Opacity()); break; } } //+------------------------------------------------------------------+
Die Methodenlogik ist im Code kommentiert.
In der Tat ist diese Methode ein Beispiel für das Erstellen eines Formularobjekts mit den erforderlichen Parametern.
Die Methode zeichnet einen Formularrahmen:
//+------------------------------------------------------------------+ //| Draw the form frame | //+------------------------------------------------------------------+ void CForm::DrawFormFrame(const int wd_top, // Frame upper segment width const int wd_bottom, // Frame lower segment width const int wd_left, // Frame left segment width const int wd_right, // Frame right segment width const color colour, // Frame color const uchar opacity, // Frame opacity const ENUM_FRAME_STYLE style) // Frame style { //--- Depending on the passed frame style switch(style) { //--- draw a dimensional (convex) frame case FRAME_STYLE_BEVEL : DrawFrameBevel(0,0,Width(),Height(),wd_top,wd_bottom,wd_left,wd_right,colour,opacity); break; //--- draw a dimensional (concave) frame case FRAME_STYLE_STAMP : DrawFrameStamp(0,0,Width(),Height(),wd_top,wd_bottom,wd_left,wd_right,colour,opacity); break; //--- draw a flat frame case FRAME_STYLE_FLAT : DrawFrameFlat(0,0,Width(),Height(),wd_top,wd_bottom,wd_left,wd_right,colour,opacity); break; //--- draw a simple frame default: //---FRAME_STYLE_SIMPLE DrawFrameSimple(0,0,Width(),Height(),wd_top,wd_bottom,wd_left,wd_right,colour,opacity); break; } } //+------------------------------------------------------------------+
Je nach Rahmenstil zeichnen wir den entsprechenden Formularrahmen.
Die Methode Zeichnen eines einfachen Rahmens:
//+------------------------------------------------------------------+ //| Draw a simple frame | //+------------------------------------------------------------------+ void CForm::DrawFrameSimple(const int x, // X coordinate relative to the form const int y, // Y coordinate relative to the form const int width, // Frame width const int height, // Frame height const int wd_top, // Frame upper segment width const int wd_bottom, // Frame lower segment width const int wd_left, // Frame left segment width const int wd_right, // Frame right segment width const color colour, // Frame color const uchar opacity) // Frame opacity { //--- Set rectangle coordinates int x1=x, y1=y; int x2=x1+width-1; int y2=y1+height-1; //--- Draw the first rectangle CGCnvElement::DrawRectangle(x1,y1,x2,y2,colour,opacity); //--- If the frame width exceeds 1 on all sides, draw the second rectangle if(wd_left>1 || wd_right>1 || wd_top>1 || wd_bottom>1) CGCnvElement::DrawRectangle(x1+wd_left-1,y1+wd_top-1,x2-wd_right+1,y2-wd_bottom+1,colour,opacity); //--- Search for "voids" between the lines of two rectangles and fill them with color if(wd_left>2 && wd_right>2 && wd_top>2 && wd_bottom>2) this.Fill(x1+1,y1+1,colour,opacity); else if(wd_left>2 && wd_top>2) this.Fill(x1+1,y1+1,colour,opacity); else if(wd_right>2 && wd_bottom>2) this.Fill(x2-1,y2-1,colour,opacity); else if(wd_left<3 && wd_right<3) { if(wd_top>2) this.Fill(x1+1,y1+1,colour,opacity); if(wd_bottom>2) this.Fill(x1+1,y2-1,colour,opacity); } else if(wd_top<3 && wd_bottom<3) { if(wd_left>2) this.Fill(x1+1,y1+1,colour,opacity); if(wd_right>2) this.Fill(x2-1,y1+1,colour,opacity); } } //+------------------------------------------------------------------+
Die Methodenlogik ist im Code ausführlich kommentiert. Kurz gesagt, es werden zwei Rechtecke gezeichnet — eines im anderen. Wenn zwischen den Rechtecken an den Stellen, die die Seiten des zukünftigen Rahmens bilden, eine Lücke ist (die Seiten der Rechtecke überlappen sich nicht), füllen wir diese mit der Farbe, die zum Zeichnen der Rechtecke verwendet wurde.
Die Methode zum Zeichnen eines flachen Rahmens:
//+------------------------------------------------------------------+ //| Draw the flat frame | //+------------------------------------------------------------------+ void CForm::DrawFrameFlat(const int x, const int y, const int width, const int height, const int wd_top, const int wd_bottom, const int wd_left, const int wd_right, const color colour, const uchar opacity) { //--- Draw a simple frame this.DrawFrameSimple(x,y,width,height,wd_top,wd_bottom,wd_left,wd_right,colour,opacity); //--- If the width of the frame top and bottom exceeds one pixel if(wd_top>1 && wd_bottom>1) { //--- Darken the horizontal sides of the frame for(int i=0;i<width;i++) { this.m_canvas.PixelSet(x+i,y,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y),-0.05)); this.m_canvas.PixelSet(x+i,y+height-1,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+height-1),-0.07)); } } //--- If the width of the frame left and right sides exceeds one pixel if(wd_left>1 && wd_right>1) { //--- Darken the vertical sides of the frame for(int i=1;i<height-1;i++) { this.m_canvas.PixelSet(x,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x,y+1),-0.01)); this.m_canvas.PixelSet(x+width-1,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+width-1,y+1),-0.02)); } } } //+------------------------------------------------------------------+
Das Verfahren zeichnet einen geprägten (konvex) Rahmen:
//+------------------------------------------------------------------+ //| Draw an embossed (convex) frame | //+------------------------------------------------------------------+ void CForm::DrawFrameBevel(const int x, const int y, const int width, const int height, const int wd_top, const int wd_bottom, const int wd_left, const int wd_right, const color colour, const uchar opacity) { //--- Draw a simple frame this.DrawFrameSimple(x,y,width,height,wd_top,wd_bottom,wd_left,wd_right,colour,opacity); //--- If the width of the frame top and bottom exceeds one pixel if(wd_top>1 && wd_bottom>1) { //--- Lighten and darken the required sides of the frame edges for(int i=0;i<width;i++) { this.m_canvas.PixelSet(x+i,y,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y),0.25)); this.m_canvas.PixelSet(x+i,y+height-1,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+height-1),-0.2)); } for(int i=wd_left;i<width-wd_right;i++) { this.m_canvas.PixelSet(x+i,y+wd_top-1,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+wd_top-1),-0.2)); this.m_canvas.PixelSet(x+i,y+height-wd_bottom,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+height-wd_bottom),0.1)); } } //--- If the width of the frame left and right sides exceeds one pixel if(wd_left>1 && wd_right>1) { //--- Lighten and darken the required sides of the frame edges for(int i=1;i<height-1;i++) { this.m_canvas.PixelSet(x,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x,y+i),0.1)); this.m_canvas.PixelSet(x+width-1,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+width-1,y+i),-0.1)); } for(int i=wd_top;i<height-wd_bottom;i++) { this.m_canvas.PixelSet(x+wd_left-1,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+wd_left-1,y+i),-0.1)); this.m_canvas.PixelSet(x+width-wd_right,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+width-wd_right,y+i),0.1)); } } } //+------------------------------------------------------------------+
Das Verfahren zeichnet einen geprägten (konkav) Rahmen:
//+------------------------------------------------------------------+ //| Draw an embossed (concave) frame | //+------------------------------------------------------------------+ void CForm::DrawFrameStamp(const int x, const int y, const int width, const int height, const int wd_top, const int wd_bottom, const int wd_left, const int wd_right, const color colour, const uchar opacity) { //--- Draw a simple frame this.DrawFrameSimple(x,y,width,height,wd_top,wd_bottom,wd_left,wd_right,colour,opacity); //--- If the width of the frame top and bottom exceeds one pixel if(wd_top>1 && wd_bottom>1) { //--- Lighten and darken the required sides of the frame edges for(int i=0;i<width;i++) { this.m_canvas.PixelSet(x+i,y,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y),-0.25)); this.m_canvas.PixelSet(x+i,y+height-1,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+height-1),0.2)); } for(int i=wd_left;i<width-wd_right;i++) { this.m_canvas.PixelSet(x+i,y+wd_top-1,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+wd_top-1),0.2)); this.m_canvas.PixelSet(x+i,y+height-wd_bottom,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+height-wd_bottom),-0.25)); } } //--- If the width of the frame left and right sides exceeds one pixel if(wd_left>1 && wd_right>1) { //--- Lighten and darken the required sides of the frame edges for(int i=1;i<height-1;i++) { this.m_canvas.PixelSet(x,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x,y+i),-0.1)); this.m_canvas.PixelSet(x+width-1,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+width-1,y+i),0.2)); } for(int i=wd_top;i<height-wd_bottom;i++) { this.m_canvas.PixelSet(x+wd_left-1,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+wd_left-1,y+i),0.2)); this.m_canvas.PixelSet(x+width-wd_right,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+width-wd_right,y+i),-0.2)); } } } //+------------------------------------------------------------------+
Die Methoden zum Zeichnen der Felder (einfache und geprägte):
//+------------------------------------------------------------------+ //| Draw a simple field | //+------------------------------------------------------------------+ void CForm::DrawFieldFlat(const int x,const int y,const int width,const int height,const color colour,const uchar opacity) { //--- Draw a filled rectangle CGCnvElement::DrawRectangleFill(x,y,x+width-1,y+height-1,colour,opacity); //--- Darken all its edges for(int i=0;i<width;i++) { this.m_canvas.PixelSet(x+i,y,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y),-0.05)); this.m_canvas.PixelSet(x+i,y+height-1,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+height-1),-0.05)); } for(int i=1;i<height-1;i++) { this.m_canvas.PixelSet(x,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x,y+1),-0.05)); this.m_canvas.PixelSet(x+width-1,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+width-1,y+1),-0.05)); } } //+------------------------------------------------------------------+ //| Draw an embossed (convex) field | //+------------------------------------------------------------------+ void CForm::DrawFieldBevel(const int x,const int y,const int width,const int height,const color colour,const uchar opacity) { //--- Draw a filled rectangle CGCnvElement::DrawRectangleFill(x,y,x+width-1,y+height-1,colour,opacity); //--- Lighten its top and left and darken its bottom and right for(int i=0;i<width;i++) { this.m_canvas.PixelSet(x+i,y,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y),0.1)); this.m_canvas.PixelSet(x+i,y+height-1,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+height-1),-0.1)); } for(int i=1;i<height-1;i++) { this.m_canvas.PixelSet(x,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x,y+1),0.05)); this.m_canvas.PixelSet(x+width-1,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+width-1,y+1),-0.05)); } } //+------------------------------------------------------------------+ //| Draw an embossed (concave) field | //+------------------------------------------------------------------+ void CForm::DrawFieldStamp(const int x,const int y,const int width,const int height,const color colour,const uchar opacity) { //--- Draw a filled rectangle CGCnvElement::DrawRectangleFill(x,y,x+width-1,y+height-1,colour,opacity); //--- Darken its top and left and lighten its bottom and right for(int i=0;i<width;i++) { this.m_canvas.PixelSet(x+i,y,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y),-0.1)); this.m_canvas.PixelSet(x+i,y+height-1,CGCnvElement::ChangeColorLightness(this.GetPixel(x+i,y+height-1),0.1)); } for(int i=1;i<height-1;i++) { this.m_canvas.PixelSet(x,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x,y+1),-0.05)); this.m_canvas.PixelSet(x+width-1,y+i,CGCnvElement::ChangeColorLightness(this.GetPixel(x+width-1,y+1),0.05)); } } //+------------------------------------------------------------------+
Die Logik aller oben genannten Methoden ist nahezu identisch und mit Kommentaren im Methodencode versehen. Ich denke, die Methoden sind gut nachvollziehbar und leicht zu verstehen. In jedem Fall können Sie gerne den Abschnitt Kommentare verwenden.
Damit ist die Erstellung des Formularobjekts vorerst abgeschlossen.
Test
Der heutige Test wird ziemlich einfach sein. Ich werde zwei verschiedene Formulare mit unterschiedlichen Konstruktionsstilen und Farbschemata erstellen. Nachdem die Formulare erstellt sind, werde ich ihnen die Felder hinzufügen. Das obere Formular wird ein dimensionales konkaves Feld erhalten, während das zweite Formular ein halbtransparentes dimensionales konkaves Feld erhalten wird.
Um den Test durchzuführen, verwenden wir den EA aus dem vorherigen Artikel und speichern ihn in \MQL5\Experts\TestDoEasy\Teil76\ als TestDoEasyPart76.mq5.
Die Bibliotheks-Formularobjektdatei wird in den EA eingebunden und die Liste der Elementobjekte in die Liste der Formularobjekte umbenannt:
//+------------------------------------------------------------------+ //| TestDoEasyPart76.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\Form.mqh> //--- defines #define FORMS_TOTAL (2) // Number of created forms //--- input parameters sinput bool InpMovable = true; // Movable flag //--- global variables CArrayObj list_forms; //+------------------------------------------------------------------+
In OnInit() werden zwei Formulare erstellt und konkave Felder darauf gezeichnet:
//+------------------------------------------------------------------+ //| 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 form objects list_forms.Clear(); int total=FORMS_TOTAL; for(int i=0;i<total;i++) { //--- When creating an object, pass all the required parameters to it CForm *form=new CForm("Form_0"+(string)(i+1),300,40+(i*80),100,70); if(form==NULL) continue; //--- Set activity and moveability flags for the form form.SetActive(true); form.SetMovable(false); //--- Set the form ID equal to the loop index and the index in the list of objects form.SetID(i); form.SetNumber(0); // (0 - main form object) Auxiliary objects may be attached to the main one. The main object is able to manage them //--- Set the full opacity for the top form and the partial opacity for the bottom one uchar opacity=(i==0 ? 255 : 250); //--- Set the form style and its color theme depending on the loop index ENUM_FORM_STYLE style=(ENUM_FORM_STYLE)i; ENUM_COLOR_THEMES theme=(ENUM_COLOR_THEMES)i; //--- Set the form style and theme form.SetFormStyle(style,theme,opacity,true); //--- If this is the first (top) form if(i==0) { //--- Draw a concave field slightly shifted from the center of the form downwards form.DrawFieldStamp(3,10,form.Width()-6,form.Height()-13,form.ColorBackground(),form.Opacity()); form.Update(true); } //--- If this is the second (bottom) form if(i==1) { //--- Draw a concave semi-transparent "tainted glass" field in the center form.DrawFieldStamp(10,10,form.Width()-20,form.Height()-20,clrWheat,200); form.Update(true); } //--- Add objects to the list if(!list_forms.Add(form)) { delete form; continue; } } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
Wir entfernen die Behandlung von Mausklicks auf Objekte aus der Methode OnChartEvent():
//+------------------------------------------------------------------+ //| 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) { } } //+------------------------------------------------------------------+
Kompilieren Sie den EA und starten Sie ihn auf dem Chart:
Wie Sie sehen können, haben wir es geschafft, zwei verschiedene Formulare mit unterschiedlichen Farben der Komponenten und des Zeichenstils zu erstellen, indem wir einfach den gewünschten Stil und das Farbschema angeben.
Was kommt als Nächstes?
Im nächsten Artikel werde ich die Entwicklung des Formularobjekts fortsetzen und seine Funktionalität ergänzen.
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
Grafiken in der Bibliothek DoEasy (Teil 75): Methoden zur Handhabung von Primitiven und Text im grafischen Grundelement
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/9553
- 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.