Zeichnen von Messuhren mithilfe der Klasse CCanvas

27 Juni 2016, 11:03
Serhii Shevchuk
0
676

Inhaltsverzeichnis

Einleitung

Alles begann, als ich zum ersten Mal auf die Klasse CCanvas stieß. Als ich zur Praxis überging, kam mir die Idee, einen Messuhr-Indikator zu zeichnen. Meine ersten Messuhren waren ziemlich grob, doch letztendlich wurden sie durch neue Elemente ergänzt und wurden optisch ansprechend. Als Ergebnis verfüge ich nun über eine kleine Bibliothek, die das Hinzufügen einer Messuhr zu einem Indikator oder EA auf schnelle und einfache Weise ermöglicht. In diesem Beitrag betrachten wir die Struktur von Messuhren, lernen die Funktionen kennen, die für die Zeichnung und die Optik zuständig sind, und bewerten die Ressourcenintensität.

Messuhren

Abb. 1. Messuhren


1 Koordinaten und Verknüpfung

Es gibt zwei Möglichkeiten, eine Messuhr in einem Diagramm zu platzieren: absolut und relativ.

Bei absoluter Positionierung stellen die Koordinaten den Abstand von einer Verknüpfungsecke entlang der X- und Y-Achse dar.

Bei relativer Positionierung wird der lokale Ursprung von Koordinaten gemäß dem festgelegten Typ der relativen Positionierung erzeugt. Wird der vertikale Typ ausgewählt, befindet sich der Ursprung unter oder über einem Bezugsobjekt (wenn eine entsprechende obere oder untere Ecke einer Verknüpfung ausgewählt wird). Wird der horizontale Typ ausgewählt, wird er ab der Ecke der Verknüpfung nach links oder rechts verschoben. In diesem Fall stellen die angegebenen Koordinaten eine Verschiebung vom lokalen Ursprung dar. Bei einer positiven Verschiebung bewegt sich das Objekt vom Bezugsobjekt weg. Bei negativer Verschiebung nähert sich das Objekt dem Bezugsobjekt.

Das Bezugsobjekt kann nur durch ein Objekt einer anderen Messuhr dargestellt werden. Es ist wichtig, dass beide Objekte die gleiche Verknüpfungsecke haben.

Abb. 2 zeigt ein Beispiel der relativen Positionierung.

Relative Positionierung

Abb. 2. Relative Positionierung von Messuhren

Sehen wir uns die Einstellungen jeder Messuhr an:

  • Messuhr "gg01": relative Positionierung deaktiviert. Horizontale Verschiebung – 40, vertikale Verschiebung – 40.
  • Messuhr "gg02": relative Positionierung – horizontal, Bezugsobjekt – "gg01". Horizontale Verschiebung vom lokalen Ursprung der Koordinaten (Punkt A) – 15, vertikale Verschiebung – 0.
  • Messuhr "gg03": relative Positionierung – vertikal, Bezugsobjekt – "gg01". Horizontale Verschiebung vom lokalen Ursprung der Koordinaten (Punkt B) – 0, vertikale Verschiebung – 15.
  • Messuhr "gg04": relative Positionierung – vertikal, Bezugsobjekt – "gg02". Horizontale Verschiebung vom lokalen Ursprung der Koordinaten (Punkt C) – 50, vertikale Verschiebung – 15.

Die relative Positionierung erleichtert die Einstellung von Eingabeparametern, wenn das Diagramm mehrere Indikatoren mit Messuhren aufweist. Wenn Sie beschließen, die Größe einer Messuhr zu ändern, werden die Koordinaten der anderen Messuhren automatisch neu berechnet.

Die Funktion GaugeCreate() legt den Positionierungstyp und die Koordinaten fest.


2 Elemente der Messuhr

Die Messuhr besteht aus zwei grafischen Objekten. Eins davon ist die Skalaschicht, das andere die Nadelschicht. Beide grafischen Objekte haben die gleichen Koordinaten. Die Nadelschicht wird über der Skalaschicht platziert. Der in den Eingabeparametern festgelegte Name der Messuhr dient als Präfix für die Namen beider Objekte. Lautet der Name der Messuhr beispielsweise "Gauge01", so heißt die Skalaschicht "Gauge01_s" und die Nadelschicht "Gauge01_n".

Abb. 3 illustriert die Struktur der Messuhr.

Abb. 3. Struktur der Messuhr

Abb. 3 Struktur der Messuhr

Die Skalaschicht beinhaltet:

  • Rand (1)
  • Teilstriche der Skala (5, 6, 7)
  • Beschriftung der Teilstriche der Skala (4)
  • hervorgehobene Bereiche (2, 12)
  • Legenden (3, 10, 11)

Legenden werden nach ihrem Zweck unterschieden:

  • Beschreibung der Messuhr (3)
  • Maßeinheiten (11)
  • aktueller Wert (10)
  • Multiplikator der Skalenbeschriftungen (nicht illustriert)

Die Einteilung der Skala ist aufgeteilt in:

  • primär (7)
  • mittel (5)
  • klein (6)

Nur primäre Teilstriche sind beschriftet. Die Schrittweite der Einteilung wird als numerischer Wert festgelegt. Der mittlere Einteilungsschritt wird abhängig von der festgelegten Menge von mittleren Markierungen zwischen primären berechnet. Der kleine Einteilungsschritt wird abhängig von der festgelegten Menge von kleinen Markierungen zwischen mittleren berechnet. Die kleine und mittlere Einteilung können weggelassen werden.

Die Nadelschicht beinhaltet:

  • Nadel (8)
  • Nadelmittelpunkt (9)


2,1 Größen

Abb. 3 zeigt die Größen einiger Elemente der Messuhr:

  • d – Messuhrgröße, die dem Durchmesser der äußeren Konturlinie der Messuhr entspricht
  • b – Größe des Randes
  • g – Größe des Abstands zwischen dem Rand und den Skalenelementen
  • c – Größe des Nadelmittelpunktes.

Beachten Sie: Der Durchmesser der Messuhr stellt die einzige in Pixeln festgelegte Größe dar ("d" in Abb. 3). Alle anderen Elemente und Schriftarten werden in bedingten Einheiten festgelegt. Ihre Größen werden als prozentualer Anteil des Durchmessers berechnet. Damit wird die Skalierung erleichtert. Ändern Sie den Durchmesser und alle anderen Größen werden proportional dazu neu berechnet. Die Koeffizienten für die Berechnung sind im Abschnitt Makroersetzung aufgeführt und können durch den Benutzer modifiziert werden.


2,2 Korpusform

Es gibt zwei Typen der Korpusform einer Messuhr: Kreis und Sektor. Die Sektorform ist praktischer, wenn der Winkel des Skalenbereichs kleiner als 180 Grad ist.

Form der Messuhr

Abb. 4. Form der Messuhr

Abb. 4 zeigt eine kreisförmige Messuhr (a) und zwei sektorförmige Messuhren (b, c). Die Funktion GaugeSetCaseParameters() wird verwendet, um die gewünschte Korpusform festzulegen.


2,3 Skala

Dies ist das wichtigste Element der Messuhr. Die Lesbarkeit von Daten hängt von seinem Aussehen ab. Die Skala sollte nicht übermäßig kompliziert sein, muss aber gleichzeitig genügend Informationen liefern. Die Auswahl der äußeren Werte der Skala und der Schrittweite der primären Markierungen erfordern erhöhte Aufmerksamkeit. Mit der Funktion GaugeSetScaleParameters() werden der Bereich, die Rotation und die äußeren Werte der Skala (Minimum und Maximum) festgelegt. Das Minimum kann sich links (direkte Reihenfolge) oder rechts (umgekehrte Reihenfolge) befinden.

Der Skalenbereich ist der Winkel, der durch die zwei Radiusvektoren der Grenzwerte der Skala gebildet wird. Er wird in Abb. 5 illustriert.

Skalenbereich

Abb. 5. Skalenbereich

Die Rotation der Skala ist der Winkel der Abweichung der Winkelhalbierenden des Winkels des Skalenbereichs ab der Linie, die vom Zentrum der Messuhr vertikal nach oben ausgeht. Sie wird in Abb. 6 illustriert.

Rotationswinkel der Skala

Abb. 6. Rotationswinkel der Skala

Die Kombination des Winkels des Skalenbereichs und des Rotationswinkels kann Ihnen helfen, das Äußere der Messuhr ziemlich flexibel einzustellen. Abb. 4(c) zeigt eine Messuhr mit einem Bereich von 90 Grad und einer Rotation von 45 Grad.

Das Maximum und Minimum der Skala sind wichtige Parameter, die abhängig vom Bereich der zulässigen Werte der anzuzeigenden Variable ausgewählt werden müssen. Die Nullmarkierung kann aus Bequemlichkeitsgründen weggelassen werden. Es hat keinen Sinn, die Skala ab Null zu zeichnen, wenn Ihre Variable sich im Bereich von 400 bis 600 verändert. Abb. 7 zeigt einige Beispiele von Maxima und Minima der Skala.

Maxima und Minima der Skala

Abb. 7. Maxima und Minima der Skala

  • a) Werte von 0 bis 500, direkte Reihenfolge
  • b) Werte von -200 bis 400, direkte Reihenfolge
  • c) Werte von -400 bis 0, direkte Reihenfolge
  • d) Werte von 500 bis 0, umgekehrte Reihenfolge
  • e) Werte von 200 bis 800, direkte Reihenfolge
  • f) Werte von 0 bis -800, umgekehrte Reihenfolge


2,4 Teilstriche

Die Einstellung der Teilstriche besteht aus der Auswahl der Größe der Striche und der Ausrichtungsmethode.

Die Ausrichtung kann folgende sein:

  • innerer Rand der Skala
  • äußerer Rand der Skala
  • Mittelpunkt

Abb. 8 zeigt Beispiele der Ausrichtung der Teilstriche der Skala:

  • a – Mittelpunkt
  • b – innerer Rand
  • c – äußerer Rand

Für die Einstellung wird die Funktion GaugeSetMarkParameters() verwendet.

Die Position der Beschriftung der Teilstriche bezieht sich auf die Einstellungen der Skala und wird mithilfe der Funktion GaugeSetScaleParameters() angepasst.

Abb. 8(a) zeigt ein Beispiel der Positionierung von Beschriftungen innerhalb der Skala, Abb. 8(b) und 8(c) zeigen Beschriftungen außerhalb der Skala.

Es wird empfohlen, einen Multiplikator zu nutzen – einen Koeffizienten, durch den alle angezeigten Werte geteilt werden, sodass die Beschriftungen nicht zu viel Platz auf der Skala einnehmen. Der Multiplikator kann Werte zwischen 0,0001 und 10000 annehmen. Abb. 4(c) zeigt ein Beispiel für die Anwendung eines Multiplikators von 100, der die Verwendung einstelliger anstatt dreistelliger Zahlen in Beschriftungen ermöglicht. Abb. 1 illustriert eine Situation, in der wir einen Multiplikator von 0,0001 für ATR verwenden, wodurch das Weglassen des Dezimalpunktes und der Nullen in Beschriftungen ermöglicht wird. Der Multiplikator wird mit der Funktion GaugeSetScaleParameters() festgelegt.

Positionierung von Strichen und Beschriftungen

Abb. 8. Positionierung von Strichen und Beschriftungen


2,5 Legenden

Legenden sind für die Anzeige von ergänzenden Informationen vorgesehen und können vier Typen angehören:

  • Beschreibung der Messuhr
  • Maßeinheiten
  • aktueller Wert
  • Multiplikator

Jede Legende kann ausgeblendet werden. Standardmäßig wird nur die Beschreibung der Messuhr angezeigt.

Die Positionierung der Legende wird durch den Winkel und Radius festgelegt. Der Winkel wird in Grad festgelegt und sein Wert entspricht dem Winkel zwischen der Linie, die vom Zentrum der Messuhr vertikal nach oben ausgeht, und einem imaginären Segment, das den Mittelpunkt der Messuhr und den Mittelpunkt der Legende verbindet. Der Radius wird in bedingten Einheiten festgelegt. Er kann Werte zwischen 0 und 10 annehmen, wobei 0 dem Radius des Mittelpunkts der Nadel und 10 dem äußeren Radius der Skala entspricht.

Abb. 9 zeigt ein Beispiel der Positionierung der Legende.

  • Die Legende "Profit" (Beschreibung der Messuhr) hat die folgenden Koordinaten: Winkel – 0, Radius – 3.
  • Die Legende "0.00" (aktueller Wert) hat die folgenden Koordinaten: Winkel – 225, Radius – 4.
  • Die Legende "USD" (Maßeinheiten) hat die folgenden Koordinaten: Winkel – 215, Radius – 8.

Für die Einstellung der Parameter der Legende wird die Funktion GaugeSetLegendParameters() verwendet.

Koordinaten der Legende

Abb. 9. Koordinaten der Legende

Beachten Sie: Legenden sind nicht auf der Skala fixiert und ihre Winkel sind nicht mit dem Rotationswinkel der Skala verbunden.


2,6 Hervorgehobene Bereiche

Hervorgehobene Datenbereiche stellen einen inhärenten Bestandteil jeder Messuhr dar. Sie helfen beim Erkennen, dass die Variable einen akuten Wert angenommen hat oder in einen speziellen Bereich gekommen ist. Mit der Funktion GaugeSetRangeParameters() können bis zu vier hervorgehobene Bereiche eingerichtet werden. Dazu müssen Sie Extremwerte und die Farbe für die Hervorhebung festlegen. Abb. 1 illustriert den Indikator Profit mit zwei hervorgehobenen Bereichen: von 200 bis 400 ist der grüne Bereich, der signalisiert, dass der Gewinn gebunden werden sollte, und von -200 bis -400 ist der orangefarbene Bereich, der vor einem großen Wertverlust warnt.


2,7 Nadel

Die Funktion GaugeSetNeedleParameters() legt die Größe des Mittelpunkts der Nadel und die Art der Bereichsfüllung fest. Die Art der Bereichsfüllung wirkt sich auf die Ressourcenintensität des Indikators aus, da die Nadelschicht bei jeder Aktualisierung der Daten vollständig neu gezeichnet wird. Abb. 10 zeigt ein Beispiel der Bereichsfüllung.

  • ausgefüllte Nadel mit Kantenglättungsalgorithmus (a)
  • ausgefüllte Nadel ohne Kantenglättungsalgorithmus (b)
  • nicht ausgefüllte Nadel mit geglätteter Konturlinie (c)

Methoden zur Füllung des Nadelbereichs

Abb. 10 Methoden zur Füllung des Nadelbereichs

Die Vor- und Nachteile jeder der Methoden werden in den Abschnitten beschrieben, die sich der Modifizierung der Klasse CCanvas und der Beurteilung der Ressourcenintensität widmen.


3 Funktionen

In Tabelle 1 werden Funktionen für die Zeichnung von Messuhren und die Festlegung ihres Erscheinungsbilds aufgelistet.

Funktion
Verhalten
GaugeCalcLocation
Berechnet die Koordinaten des Mittelpunkts der Messuhr
GaugeCreate
Erzeugt die Messuhr
GaugeDelete
Löscht die Messuhr
GaugeNewValue
Aktualisiert die Position der Nadel und den angezeigten Wert
GaugeRedraw
Zeichnet die Messuhr neu
GaugeRelocation
Ändert die Lage der Messuhrobjekte im Diagramm
GaugeSetCaseParameters
Legt die Korpusparameter der Messuhr fest
GaugeSetLegendParameters
Legt die Parameter der Legende fest
GaugeSetMarkLabelFont
Legt die Schriftart der Beschriftung der Teilstriche fest
GaugeSetMarkParameters
Legt die Parameter der Unterteilung der Skala fest
GaugeSetNeedleParameters
Legt die Parameter der Nadel fest
GaugeSetRangeParameters
Legt die Parameter des Bereichs fest
GaugeSetScaleParameters
Legt die Parameter der Skala fest

Tabelle 1 Liste von Funktionen

Sehen wir uns jede der Funktionen im Detail an. Sie werden in der Reihenfolge aufgelistet, in der wir bei der Initialisierung ihren jeweiligen Aufruf empfehlen.


3,1 GaugeCreate

Erzeugt die Messuhr.

bool GaugeCreate(
   string name,              // gauge name
   GAUGE_STR &g,             // reference to the gauge structure
   int x,                    // horizontal indent from the anchor corner
   int y,                    // vertical indent from the anchor corner
   int size,                 // gauge size
   string ObjRel,            // name of a graphical object relatively to which the position is set
   ENUM_REL_MODE rel_mode,   // relative positioning
   ENUM_BASE_CORNER corner,  // anchor corner
   bool back,                // objects on the background
   uchar scale_transparency, // scale transparency
   uchar needle_transparency // needle transparency 
 );

Parameter

 name

   [in]  Name der Messuhr. Wird als Präfix für Namen von grafischen Objekten verwendet, aus denen die Messuhr besteht.

 g

   [out]  Bezug für die Struktur der Messuhr.

 x

   [in]  Abstand von der Ecke der Verknüpfung entlang der X-Achse in Pixeln. Bei relativer Positionierung: Abstand vom lokalen Ursprung der Koordinaten.

 y

   [in]  Abstand von der Ecke der Verknüpfung entlang der Y-Achse in Pixeln. Bei relativer Positionierung: Abstand vom lokalen Ursprung der Koordinaten.

 size

   [in]  Größe der Messuhr. Dargestellt als Durchmesser des Korpus.

 ObjRel

   [in]  Name eines anderen grafischen Objekts, das den Bezug für die Festlegung der Position bildet. Bleibt nur bei relativer Positionierung relevant.

 rel_mode

   [in]  Methode der relativen Positionierung. Kann jeden der Werte aus ENUM_REL_MODE annehmen.

 corner

   [in]  Diagrammecke für die Verknüpfung des grafischen Objekts. Kann jeden der Werte aus ENUM_BASE_CORNER annehmen.

 back

   [in]  Objekte im Hintergrund.

 scale_transparency

   [in]  Durchsichtigkeit der Skala. Kann Werte zwischen 0 und 255 annehmen.

 needle_transparency

   [in]  Durchsichtigkeit der Nadel. Kann Werte zwischen 0 und 255 annehmen.

Ausgegebener Wert

  Gibt true aus, wenn die Objekte der Skalaschicht und der Nadelschicht erstellt wurden. Gibt andernfalls false aus.


3,2 GaugeSetCaseParameters

Legt die Korpusparameter der Messuhr fest.

void GaugeSetCaseParameters(
   GAUGE_STR &g,                  // reference to the gauge structure
   ENUM_CASE_STYLE style,         // body style
   color ccol,                    // body color
   ENUM_CASE_BORDER_STYLE bstyle, // border style
   color bcol,                    // border color
   ENUM_SIZE border_gap_size      // size of space between a border and scale elements
);

Parameter

 g

   [out]  Bezug für die Struktur der Messuhr.

 style

   [in]  Stil des Korpus. Kann jeden der Werte aus ENUM_CASE_STYLE annehmen.

 ccol

   [in]  Farbe des Korpus.

 bstyle

   [in]  Stil des Randes. Kann jeden der Werte aus ENUM_CASE_BORDER_STYLE annehmen.

 bcol

   [in]  Farbe des Randes.

 gap_size

   [in]  Bereich zwischen der inneren Linie des Randes und dem nächstgelegenen Element der Skala ("g" in Abb. 3). Kann jeden der Werte aus ENUM_SIZE annehmen.


3,3 GaugeSetScaleParameters

Legt die Parameter der Skala fest.

void GaugeSetScaleParameters(
   GAUGE_STR &g,           // reference to the gauge structure
   int range,              // scale range
   int rotation,           // angle of rotation
   double min,             // minimum value (left)
   double max,             // maximum value (right)
   ENUM_MUL_SCALE mul,     // multiplier of scale labels
   ENUM_SCALE_STYLE style, // scale style
   color col,              // scale color
   bool display_arc // display scale line
);

Parameter

 g

   [out]  Bezug für die Struktur der Messuhr.

 range

   [in]  Skalenbereich. Festlegung als Winkel, der durch die zwei Radiusvektoren der Grenzwerte der Skala gebildet wird. Kann Werte zwischen 30 und 320 Grad annehmen (Abb. 5).

 rotation

   [in]  Rotationswinkel der Skala (Abb. 6).

 min

   [in]  Minimum der Skala bei direkter Nummerierung.

 max

   [in]  Maximum der Skala bei direkter Nummerierung.

 mul

   [in]  Multiplikator der Skalenbeschriftungen. Kann jeden der Werte aus ENUM_MUL_SCALE annehmen.

 style

   [in]  Stil der Skala. Kann jeden der Werte aus ENUM_SCALE_STYLE annehmen.

 col

   [in]  Farbe der Skala.

 display_arc=false

   [in]  Anzeige der Linie der Skala.


3,4 GaugeSetMarkParameters

Legt die Parameter der Unterteilung der Skala fest.

void GaugeSetMarkParameters(  
   GAUGE_STR &g,          // reference to the gauge structure
   ENUM_MARK_STYLE style, // style of scale marks
   ENUM_SIZE size,        // size of marks
   double major_tmi,      // major mark interval
   int middle_tmarks,     // number of middle marks between major ones
   int minor_tmarks       // number of minor marks between middle ones
);

Parameter

 g

   [out]  Bezug für die Struktur der Messuhr.

 style

   [in]  Stil der Unterteilung der Skala. Kann jeden der Werte aus ENUM_MARK_STYLE annehmen.

 size

   [in]  Größe der Striche. Kann jeden der Werte aus ENUM_SIZE annehmen.

 major_tmi

   [in]  Schrittweite der primären Teilstriche. Primäre Teilstriche werden durch Beschriftungen mit relevanten Werten begleitet.

 middle_tmarks

   [in]  Anzahl der mittleren Teilstriche zwischen den primären Teilstrichen. Kann jeden positiven Wert annehmen. Keine Beschränkung der Größe. Falls 0, werden keine mittleren Teilstriche angezeigt.

 minor_tmarks

   [in]  Anzahl der kleinen Teilstriche zwischen den mittleren (oder zwischen primären, falls keine mittleren angezeigt werden). Kann jeden positiven Wert annehmen. Keine Beschränkung der Größe. Falls 0, werden keine kleinen Teilstriche angezeigt.


3,5 GaugeSetMarkLabelFont

Legt die Schriftart der Beschriftung der Teilstriche fest.

void GaugeSetMarkLabelFont(
   GAUGE_STR &g,        // reference to the gauge structure
   ENUM_SIZE font_size, // font size 
   string font,         // font
   bool italic,         // italic
   bool bold,           // bold
   color col            // color
);

Parameter

 g

   [out]  Bezug für die Struktur der Messuhr.

 font_size

   [in]  Schriftgröße der Beschriftung der Teilstriche. Kann jeden der Werte aus ENUM_SIZE annehmen.

 font

   [in]  Schriftart.

 italic

   [in]  Kursiv.

 bold

   [in]  Fett.

 col

   [in]  Schriftfarbe.


3,6 GaugeSetLegendParameters

Legt die Parameter der Legende fest.

void GaugeSetLegendParameters(
   GAUGE_STR &g,         // reference to the gauge structure
   ENUM_GAUGE_LEGEND gl, // legend type
   bool enable,          // display legend
   string str,           // string (or a complementary parameter)
   int radius,           // coordinates - radius
   double angle,         // coordinates - angle
   uint font_size,       // font size
   string font,          // font
   bool italic,          // italic
   bool bold,            // bold
   color col             // color
);

Parameter

 g

   [out]  Bezug für die Struktur der Messuhr

 gl

   [in]  Legendentyp. Kann jeden der Werte aus ENUM_GAUGE_LEGEND annehmen.

 enable

   [in]  Anzeige der Legende.

 str

   [in]  Dies ist ein anzuzeigender String für Legenden der Typen LEGEND_DESCRIPTION oder LEGEND_UNITS. Dieser Parameter wird für Legenden des Typen LEGEND_MUL ignoriert. Anzahl der Nachkommastellen für Legenden des Typen LEGEND_VALUE. Kann Werte zwischen "0" und "8" annehmen. Alle anderen Werte werden als "0" dargestellt. Der String "2" bedeutet beispielsweise zwei Nachkommastellen. Der String "hello" bedeutet 0 Nachkommastellen.

 radius

   [in]  Radius. Abstand vom Mittelpunkt der Messuhr zum Mittelpunkt der Legende in bedingten Einheiten (Abb. 9).

 angle

   [in]  Winkelkoordinaten. Sein Wert entspricht dem Winkel zwischen der Linie, die vom Zentrum der Messuhr vertikal nach oben ausgeht, und einem imaginären Segment, das den Mittelpunkt der Messuhr und den Mittelpunkt der Legende verbindet (Abb. 9).

 font_size

   [in]  Schriftgröße der Legende.

 font

   [in]  Schriftart.

 italic

   [in]  Kursiv.

 bold

   [in]  Fett.

 col

   [in]  Schriftfarbe.


3,7 GaugeSetRangeParameters

Legt die Parameter des hervorgehobenen Bereichs fest.

void GaugeSetRangeParameters(
   GAUGE_STR &g, // reference to the gauge structure
   int index,    // range index
   bool enable,  // display range
   double start, // initial value
   double end,   // final value
   color col     // color
);

Parameter

 g

   [out]  Bezug für die Struktur der Messuhr.

 index

   [in]  Bereichsindex. Kann Werte zwischen 0 und 3 annehmen.

 enable

   [in]  Anzeigebereich.

 start

   [in]  Ausgangswert.

 end

   [in]  Finaler Wert.

 col

   [in]  Farbe zum Hervorheben des Bereichs.


3,8 GaugeSetNeedleParameters

Legt die Parameter der Nadel fest.

void GaugeSetNeedleParameters(
   GAUGE_STR &g,                     // reference to the gauge structure
   ENUM_NCENTER_STYLE ncenter_style, // needle center style
   color ncenter_col,                // needle center color
   color needle_col,                 // needle color
ENUM_NEEDLE_FILL needle_fill      // method of needle area fill
);

Parameter

 g

   [out]  Bezug für die Struktur der Messuhr.

 ncenter_style

   [in]  Stil des Mittelpunkts der Nadel. Kann jeden der Werte aus ENUM_NCENTER_STYLE annehmen.

 ncenter_col

   [in]  Farbe des Mittelpunkts der Nadel.

 needle_col

   [in]  Farbe der Nadel.

 needle_fill

   [in]  Methode zur Füllung des Nadelbereichs. Kann jeden der Werte aus ENUM_NEEDLE_FILL annehmen.


3,9 GaugeRedraw

Zeichnet die Messuhr neu. Diese Funktion muss nach der Veränderung jedes beliebigen Parameters aufgerufen werden, um diese Änderungen anzuwenden.

void GaugeRedraw(
   GAUGE_STR &g       // reference to the gauge structure
); 

Parameter

 g

   [in]  Bezug für die Struktur der Messuhr.


3,10 GaugeNewValue

Aktualisiert die Position der Nadel und den angezeigten Wert.

void GaugeNewValue(
   GAUGE_STR &g,     // reference to the gauge structure
   double v          // variable value
);

Parameter

 g

   [in]  Bezug für die Struktur der Messuhr.

 v

   [in]  Aktueller Wert der Variable.


3,11 GaugeDelete

Löscht grafische Objekte, aus denen sich die Messuhr zusammensetzt. Rufen Sie die Funktion aus dem Handler OnDeinit() auf.

void GaugeDelete(
   GAUGE_STR &g      // reference to the gauge structure
);

Parameter

 g

   [in]  Bezug für die Struktur der Messuhr.


3,12 GaugeCalcLocation

Berechnet die Koordinaten von Objekten der Messuhr. Wenn die relative Positionierung deaktiviert ist, gibt sie immer die gleichen Koordinaten aus. Ansonsten können sich die Koordinaten von vorhergehenden Werten unterscheiden, wenn das Bezugsobjekt verschoben wurde oder seine Größe geändert hat.

bool GaugeCalcLocation( 
   GAUGE_STR& g         // reference to the gauge structure
);

Parameter

 g

   [in]  Bezug für die Struktur der Messuhr.

Ausgegebener Wert

  Gibt true aus, wenn die Koordinatenwerte sich von den vorhergehenden unterscheiden. Gibt andernfalls false aus. Wenn die Funktion true ausgibt, rufen Sie die Funktion GaugeRelocation() auf, um die berechneten Parameter anzuwenden.


3,13 GaugeRelocation

Verteilt grafische Objekte, aus denen sich die Messuhr zusammensetzt, am entsprechenden Ort im Diagramm. Muss aufgerufen werden, wenn die relative Positionierung aktiviert ist und die Funktion GaugeCalcLocation() true ausgegeben hat.

void GaugeRelocation(
   GAUGE_STR &g       // reference to the gauge structure
);

Parameter

 g

   [in]  Bezug für die Struktur der Messuhr.


4 Aufzählungen

In Tabelle 2 werden die als Funktionsparameter verwendeten Aufzählungen aufgelistet.

Aufzählung
Beschreibung
ENUM_CASE_BORDER_STYLEStil des Randes
ENUM_CASE_STYLE
Stil des Korpus
ENUM_GAUGE_LEGEND
Legendentyp
ENUM_MARK_STYLE
Stil der Unterteilung der Skala
ENUM_MUL_SCALE
Multiplikator der Skalenbeschriftungen
ENUM_NCENTER_STYLEStil des Mittelpunkts der Nadel
ENUM_NEEDLE_FILLMethode zur Füllung des Nadelbereichs
ENUM_REL_MODEMethode der relativen Positionierung
ENUM_SCALE_STYLEStil der Skala
ENUM_SIZEGröße

Tabelle 2 Liste der Aufzählungen


4,1 ENUM_CASE_BORDER_STYLE

Stil des Randes. Werte werden in Tabelle 3 aufgelistet.

Identifikator
Beschreibung
CASE_BORDER_NONE
Kein Rand
CASE_BORDER_THINDünner Rand
CASE_BORDER_THICK
Dicker Rand

Tabelle 3 Werte von ENUM_CASE_BORDER_STYLE

4,2 ENUM_CASE_STYLE

Stil des Korpus. Werte werden in Tabelle 4 aufgelistet.

Identifikator
Beschreibung
CASE_ROUND
Runder Korpus
CASE_SECTOR
Sektorartiger Korpus

Tabelle 4 Werte von ENUM_CASE_STYLE

4,3 ENUM_GAUGE_LEGEND

Legendentyp. Werte werden in Tabelle 5 aufgelistet.

Identifikator
 Beschreibung
LEGEND_DESCRIPTIONBeschreibung der Messuhr
LEGEND_UNITSMaßeinheiten
LEGEND_MULMultiplikator der Skalenbeschriftungen
LEGEND_VALUEAktueller Wert der Variable

Tabelle 5 Werte von ENUM_GAUGE_LEGEND

4,4 ENUM_MARK_STYLE

Stil der Unterteilung der Skala. Werte werden in Tabelle 6 aufgelistet.

Identifikator
 Beschreibung
MARKS_INNERAusrichtung der Markierungen am inneren Rand
MARKS_MIDDLEAusrichtung der Markierungen am Mittelpunkt
MARKS_OUTERAusrichtung der Markierungen am äußeren Rand

Tabelle 6 Werte von ENUM_MARK_STYLE

4,5 ENUM_MUL_SCALE

Multiplikator der Skalenbeschriftungen. Werte werden in Tabelle 7 aufgelistet.

 IdentifikatorBedeutung
Anzeige
MUL_1000010000
 х10k
MUL_10001000
 х1k
MUL_100100
 х100
MUL_1010
 х10
MUL_11
 Nicht angezeigt
MUL_010,1
 /10
MUL_0010,01
 /100
MUL_00010,001
 /1k
MUL_000010,0001
 /10k

Tabelle 7 Werte von ENUM_MUL_SCALE

4,6 ENUM_NCENTER_STYLE

Stil des Mittelpunkts der Nadel. Werte werden in Tabelle 8 aufgelistet.

Identifikator
Beschreibung
NDCS_NONEKeine Anzeige des Mittelpunkts der Nadel
NDCS_SMALLKleine Anzeige
NDCS_LARGEGroße Anzeige

Tabelle 8 Werte von ENUM_NCENTER_STYLE

4,7 ENUM_NEEDLE_FILL

Methode zur Füllung des Nadelbereichs. Werte werden in Tabelle 9 aufgelistet.

 Identifikator Beschreibung
NEEDLE_FILLFüllung der Nadel ohne Kantenglättung
NEEDLE_FILL_AAFüllung der Nadel mit Kantenglättung
NEEDLE_NOFILL_AAKeine Füllung der Nadel, aber Kantenglättung

Tabelle 9 Werte von ENUM_NEEDLE_FILL

4,8 ENUM_REL_MODE

Methode der relativen Positionierung. Werte werden in Tabelle 10 aufgelistet.

 Identifikator Beschreibung
RELATIVE_MODE_NONERelative Positionierung deaktiviert
RELATIVE_MODE_HORHorizontal
RELATIVE_MODE_VERTVertikal
RELATIVE_MODE_DIAGDiagonal

Tabelle 10 Werte von ENUM_REL_MODE

4,9 ENUM_SCALE_STYLE

Stil der Skala. Werte werden in Tabelle 11 aufgelistet.

Identifikator
 Beschreibung
SCALE_INNERSkalenbeschriftungen innerhalb der Skala
SCALE_OUTERSkalenbeschriftungen außerhalb der Skala

Tabelle 11 Werte von ENUM_SCALE_STYLE

4,10 ENUM_SIZE

Größe. Werte werden in Tabelle 12 aufgelistet.

Identifikator
 Beschreibung
SIZE_SMALLKlein
SIZE_MIDDLEMittel
SIZE_LARGEGroß

Tabelle 12 Werte von ENUM_SIZE


5 Makroersetzung

Koeffizienten für Größen:

#define DIAM_TO_NDCSL_RATIO   5   //needle center diameter (small) as percentage of the body diameter
#define DIAM_TO_NDCSB_RATIO   7.5 //needle center diameter (large) as percentage of the body diameter
//---
#define DIAM_TO_BD_SIZE_S     2 //border width (small) as percentage of the body diameter
#define DIAM_TO_BD_SIZE_B     5 //border width (large) as percentage of the body diameter
//---
#define DIAM_TO_BD_GAP_S      2.0 //space from the body border to inner elements of the gauge (small) as percentage of the body diameter
#define DIAM_TO_BD_GAP_M      3.0 //space from the body border to inner elements of the gauge (middle) as percentage of the body diameter
#define DIAM_TO_BD_GAP_L      7.0 //space from the body border to inner elements of the gauge (large) as percentage of the body diameter
//---
#define DIAM_TO_MSZ_MJ_S      3.3 //size of major scale (small) graduation as percentage of the body diameter
#define DIAM_TO_MSZ_MD_S      2.3 //size of middle scale (small) graduation as percentage of the body diameter
#define DIAM_TO_MSZ_MN_S      1.3 //size of minor scale (small) graduation as percentage of the body diameter
//---
#define DIAM_TO_MSZ_MJ_M      6.5 //size of major scale (middle) graduation as percentage of the body diameter
#define DIAM_TO_MSZ_MD_M      4.8 //size of middle scale (middle) graduation as percentage of the body diameter
#define DIAM_TO_MSZ_MN_M      3.0 //size of minor scale (middle) graduation as percentage of the body diameter
//---
#define DIAM_TO_MSZ_MJ_L      10.0 //size of major scale (large) graduation as percentage of the body diameter
#define DIAM_TO_MSZ_MD_L      7.5  //size of middle scale (large) graduation as percentage of the body diameter
#define DIAM_TO_MSZ_MN_L      5.0  //size of minor scale (large) graduation as percentage of the body diameter
//---
#define DIAM_TO_MFONT_SZ_S    4   //font size of scale (small) graduation labels as percentage of the body diameter
#define DIAM_TO_MFONT_SZ_M    6.5 //font size of scale (middle) graduation labels as percentage of the body diameter
#define DIAM_TO_MFONT_SZ_L    10  //font size of scale (large) graduation labels as percentage of the body diameter

Standardfarben:

#define DEF_COL_SCALE      clrBlack
#define DEF_COL_MARK_FONT  clrBlack
#define DEF_COL_CASE       clrMintCream
#define DEF_COL_BORDER     clrLightSteelBlue
#define DEF_COL_LAB        clrDarkGray
#define DEF_COL_NCENTER    clrLightSteelBlue
#define DEF_COL_NEEDLE     clrDimGray


6 Modifizieren der Klasse CCanvas


6,1 Zeichnen eines Segments mithilfe eines Kantenglättungsalgorithmus

Mit der Methode LineAA kann ein Segment mithilfe des Kantenglättungsalgorithmus (Antialiasing) gezeichnet werden. Ein Problem tritt jedoch auf, wenn wir kreisförmig angeordnete Skalenmarkierungen zeichnen. Wenn wir die Koordinaten der Ausgangs- und Endpunkte des Segments vom polaren zum rechteckigen Koordinatensystem konvertieren, erhalten wir Brüche, die zu ganzen Zahlen gerundet werden. Infolgedessen sehen die Markierungen "krumm" aus, wie in Abb. 11(b) illustriert.

Deshalb haben wir die Methode LineAA2 hinzugefügt, die sich dadurch von LineAA unterscheidet, dass der Typ der Eingabeparameter x1, y1, x2, y2 zu double geändert wurde. Damit können wir Teilwerte von Koordinaten übergeben und das erwähnte Problem beheben, was in Abbildung 11(c) deutlich illustriert wird.

//+------------------------------------------------------------------+
//| Draw line with antialiasing (with style) v.2                     |
//+------------------------------------------------------------------+
void CCanvas::LineAA2(const double x1,const double y1,const double x2,const double y2,const uint clr,const uint style)
  {
//--- line is out of image boundaries
   if((x1<0 && x2<0) || (y1<0 && y2<0))
      return;
   if(x1>=m_width && x2>=m_width)
      return;
   if(y1>=m_height && y2>=m_height)
      return;
//--- check
   if(x1==x2 && y1==y2)
     {
      PixelSet(int(x1),int(y1),clr);
      return;
     }
//--- set the line style
   if(style!=UINT_MAX)
      LineStyleSet(style);
//--- preliminary calculations
   double dx=x2-x1;
   double dy=y2-y1;
   double xy=sqrt(dx*dx+dy*dy);
   double xx=x1;
   double yy=y1;
   uint   mask=1<<m_style_idx;
//--- set pixels
   dx/=xy;
   dy/=xy;
   do
     {
      if((m_style&mask)==mask)
         PixelSetAA(xx,yy,clr);
      xx+=dx;
      yy+=dy;
      mask<<=1;
      if(mask==0x1000000)
         mask=1;
     }
   while(fabs(x2-xx)>=fabs(dx) && fabs(y2-yy)>=fabs(dy));
  } 

Abb. 11 illustriert Beispiele für verschiedene Methoden der Zeichnung von Skalenmarkierungen:

Methode der Zeichnung von Skalenmarkierungen

Abb. 11 Verschiedene Methoden der Zeichnung von Skalenmarkierungen (Zoomfaktor 200 %)


6,2 Ausfüllen eines Bereichs mit geglätteten Kanten

Die Methode Fill ist für das Ausfüllen von Bereichen vorgesehen, die durch ohne Kantenglättung gezeichnete Segmente begrenzt sind. Wenn wir diese Methode nutzen, um einen Bereich zu füllen, der durch Segmente begrenzt wird, die mithilfe der Methode LineAA gezeichnet wurden, wird der Bereich nur unvollständig ausgefüllt, was in Abb. 12(a) illustriert wird.

Ausfüllen eines Bereichs mit geglätteten Kanten

Abb. 12 Ausfüllen eines Bereichs mit geglätteten Kanten (Zoomfaktor 200 %)

Also haben wir die Methode Fill2 hinzugefügt. Der Unterschied liegt darin, dass er nicht die Hintergrundfarbe einfärbt, sondern jegliche Farbe, die sich von der Farbe der durch den Bereich eingegrenzten Segmente unterscheidet. Sie ermöglicht das Einfärben von Untertönen, die nicht durch die Methode Fill eingefärbt werden können. Abb. 12(b) zeigt ein Beispiel.

//+------------------------------------------------------------------+
//| Fill closed region with color (v.2)                              |
//+------------------------------------------------------------------+
void CCanvas::Fill2(int x,int y,const uint clr)
  {
//--- check
   if(x<0 || x>=m_width || y<0 || y>=m_height)
      return;
//---
   int  index=y*m_width+x;
   uint old_clr=m_pixels[index];
//--- check if replacement is necessary
   if(old_clr==clr)
      return;
//--- use pseudo stack to emulate deeply-nested recursive calls
   int  stack[];
   uint count=1;
   int  idx;
   int  total=ArraySize(m_pixels);
//--- allocate memory for stack
   if(ArrayResize(stack,total)==-1)
      return;
   stack[0]=index;
   m_pixels[index]=clr;
   for(uint i=0;i<count;i++)
     {
      index=stack[i];
      x=index%m_width;
      //--- left adjacent point
      idx=index-1;
      if(x>0 && m_pixels[idx]!=clr)
        {
         stack[count]=idx;
         if(m_pixels[idx]==old_clr)
            count++;
         m_pixels[idx]=clr;
        }
      //--- top adjacent point
      idx=index-m_width;
      if(idx>=0 && m_pixels[idx]!=clr)
        {
         stack[count]=idx;
         if(m_pixels[idx]==old_clr)
            count++;
         m_pixels[idx]=clr;
        }
      //--- right adjacent point
      idx=index+1;
      if(x<m_width-1 && m_pixels[idx]!=clr)
        {
         stack[count]=idx;
         if(m_pixels[idx]==old_clr)
            count++;
         m_pixels[idx]=clr;
        }
      //--- bottom adjacent point
      idx=index+m_width;
      if(idx<total && m_pixels[idx]!=clr)
        {
         stack[count]=idx;
         if(m_pixels[idx]==old_clr)
            count++;
         m_pixels[idx]=clr;
        }
     }
//--- deallocate memory
   ArrayFree(stack);
  }  

Dennoch hat diese Methode auch ihre Nachteile. Bei einem kleinen spitzen Winkel bleibt ein Teil unausgefüllt, was in Abb. 12(c) illustriert wird. Wir haben eine Lösung für dieses Problem gefunden.

   1) Zuerst wird die gesamte Zeichenfläche (Nadelschicht) mit der für die Nadel vorgesehenen Farbe ausgefüllt:

n.canvas.Fill(10, 10, ColorToARGB(n.needle.c, n.transparency));

   2) Dann zeichnen wir die Nadel aus drei Segmenten mithilfe der Methode LineAA2:

n.canvas.LineAA2(db_xbuf[0], db_ybuf[0], db_xbuf[1], db_ybuf[1], 0);
n.canvas.LineAA2(db_xbuf[1], db_ybuf[1], db_xbuf[2], db_ybuf[2], 0);
n.canvas.LineAA2(db_xbuf[2], db_ybuf[2], db_xbuf[0], db_ybuf[0], 0);

  3) Anschließend füllen wir den Bereich um die Nadel mithilfe der Methode Fill2 durchsichtig aus:

n.canvas.Fill2(10, 10, 0);

Diese Methode ist nicht optimal, doch sie ermöglicht die korrekte Zeichnung der Nadel.

Methoden zur Füllung des Nadelbereichs

Abb. 13. Mithilfe verschiedener Methoden ausgefüllte Nadeln

Abb. 13 illustriert mithilfe verschiedener Methoden ausgefüllte Nadeln.

  • a) Die aus drei Segmenten bestehende Nadel, die mithilfe der Methode LineAA2 gezeichnet und mithilfe der Methode Fill2 ausgefüllt wurde.
  • b) Die Nadel, die mithilfe der Methode FillTriangle gezeichnet wurde.
  • c) Die nicht ausgefüllte Nadel aus drei Segmenten, die mithilfe der Methode LineAA2 gezeichnet wurde.

Wie wir sehen können, ist die Nadel in Abb. 13(b) uneben und weicht geringfügig von durch 90 Grad teilbaren Winkeln ab. Außerdem können wir sehen, dass die Nadel vom Mittelpunkt verschoben wird, was durch die Rundung der Koordinatenwerte verursacht wird, wenn wir sie vom polaren zum rechteckigen Koordinatensystem konvertieren. Doch gleichzeitig stellt diese Methode bezüglich der Ressourcenintensität die praktischste Möglichkeit dar (wir kommen später nochmal auf dieses Thema zurück). Die in Abb. 13(c) illustrierte Nadel ist ein Kompromiss zwischen zwei oben beschriebenen Methoden. Sie besteht aus drei Segmenten, die mithilfe der Methode LineAA2 gezeichnet, aber nicht ausgefüllt wurden.


7 Anwendungsbeispiele

Sehen wir uns die Anwendung der Bibliothek von Messuhren anhand von mehreren Beispielen an.


7,1 Indikator des aktuellen Gewinns

Fangen wir mit dem einfachsten Beispiel an. Das folgende Beispiel demonstriert das erforderliche Minimum zum Hinzufügen der Messuhr zu einem EA oder Indikator.

//+------------------------------------------------------------------+
//|                                       profit_gauge_indicator.mq5 |
//|                                         Copyright 2015, Decanium |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Decanium"
#property version   "1.00"
#property indicator_plots 0
#property indicator_chart_window

#include <Gauge\gauge_graph.mqh>

input string inp_gauge_name="gg01";                  // Indicator name
input int inp_x = 40;                                // Horizontal offset
input int inp_y = 40;                                // Vertical offset
input int inp_size=300;                              // Indicator size
input string inp_ObjRel="";                          // Name of the base indicator in case of relative positioning
input ENUM_REL_MODE inp_rel_mode=RELATIVE_MODE_NONE; // Relative positioning mode
input ENUM_BASE_CORNER inp_corner=CORNER_LEFT_UPPER; // Anchor corner
input bool inp_back=false;                           // Indicator is in the background
input uchar inp_scale_transparency=0;                // Scale transparency level, 0..255
input uchar inp_needle_transparency=0;               // Needle transparency level, 0..255

//--- declaration of the gauge structure
GAUGE_STR g0;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- creating the gauge
   if(GaugeCreate(inp_gauge_name,g0,inp_x,inp_y,inp_size,inp_ObjRel,inp_rel_mode,
      inp_corner,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- drawing the gauge
   GaugeRedraw(g0);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- deleting the gauge
   GaugeDelete(g0);
   ChartRedraw();
  }    
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
//--- updating readings
   double profit=AccountInfoDouble(ACCOUNT_PROFIT);
   GaugeNewValue(g0,profit);
//---
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//---
   if(id==CHARTEVENT_CHART_CHANGE)
     {
      if(GaugeCalcLocation(g0)==true)
         GaugeRelocation(g0);
     }
  }
//+------------------------------------------------------------------+

Zuerst müssen wir die Struktur der Messuhr deklarieren. Dann fahren wir mit der Initialisierungsfunktion fort, in der wir die Messuhr mithilfe der Funktion GaugeCreate() erstellen und die Zeichenfunktion GaugeRedraw() aufrufen. GaugeNewValue() kann genutzt werden, um die Werte zu aktualisieren. In diesem Beispiel wird sie aus dem Handler OnCalculate() aufgerufen.

Die Messuhr sieht aus, wie in Abb. 14 illustriert.

Äußeres des Indikators, Standardparameter

Abb. 14. Standard-Erscheinungsform der Messuhr

Nun fügen wir die Möglichkeit hinzu, den Skalenbereich und den Rotationswinkel festzulegen. Damit wird die Liste der Eingabeparameter um zwei Parameter erweitert.

input int inp_scale_range=270;   // Scale range, 30..320 degrees
input int inp_rotation=45;       // Scale rotation, 0..359 degrees

Nun erweitern wir den Code der Initialisierung um den Aufruf der Funktion zum Festlegen der Parameter der Skala.

//--- setting parameters of the scale and marks on the scale
   GaugeSetScaleParameters(g0,inp_scale_range,inp_rotation,-200,400,MUL_1,SCALE_INNER,clrBlack);

Zusätzlich zu den neuen Eingabeparametern legen wir Folgendes fest:

  • neue Maxima und Minima (-200 bzw. 400)
  • Multiplikator der Skalenbeschriftungen (MUL_1)
  • Stil der Skala (SCALE_INNER – Skalenbeschriftungen innen)
  • Farbe der Beschriftungen (clrBlack)

Da wir die Grenzwerte der Skala verändert haben, empfiehlt es sich, die Schrittweite der primären Markierungen zu korrigieren. Um eine Überfülle an Text zu vermeiden, ist der Wert 100 dazu am besten geeignet. Dabei platzieren wir eine mittlere Markierung zwischen zwei primären und 4 kleine zwischen zwei mittleren. Somit erhalten wir einen Mindestschritt zwischen Markierungen, der 10 entspricht.

   GaugeSetMarkParameters(g0,MARKS_INNER,SIZE_MIDDLE,100,1,4);

Nun markieren wir zwei Datenbereiche. Der Bereich mit dem Index 0, der bei 200 beginnt und bei 400 endet, wird mit der Farbe clrLimeGreen markiert. Der Bereich mit dem Index 1, der bei -100 beginnt und bei -200 endet, wird mit der Farbe clrCoral markiert.

//--- highlighting ranges on the scale
   GaugeSetRangeParameters(g0,0,true,200,400,clrLimeGreen);
   GaugeSetRangeParameters(g0,1,true,-100,-200,clrCoral);

Nun stellen wir die Legenden ein. Wir bestimmen die Beschreibung der Messuhr, die Maßeinheiten und den aktuellen Wert mit einer Nachkommastelle. Sehen wir uns das nacheinander an.

Beschreibung der Messuhr:

   GaugeSetLegendParameters(g0,LEGEND_DESCRIPTION,true,"Profit",3,0,14,"Arial",false,false);

Der angezeigte String ist "Profit", der Radius ist 3, der Winkel ist 0, die Schriftgröße beträgt 14 bedingte Einheiten.

Maßeinheiten:

   GaugeSetLegendParameters(g0,LEGEND_UNITS,true,"USD",8,215,10,"Arial",true,false);

Der angezeigte String ist "USD", der Radius ist 8, der Winkel ist 215, die Schriftgröße beträgt 10 bedingte Einheiten.

Aktueller Wert:

   GaugeSetLegendParameters(g0,LEGEND_VALUE,true,"1",4,225,20,"Arial",true,false);

Hier steht der String "1" für das Anzeigeformat (eine Nachkommastelle). Koordinaten: Der Radius ist 4, der Winkel ist 255. Die Schriftgröße beträgt 20 bedingte Einheiten.

Nachdem wir einige zusätzliche Einstellungen vorgenommen haben, sieht die Messuhr aus, wie in Abb. 15 illustriert.

Indikator des aktuellen Gewinns

Abb. 15. Aussehen der Messuhr nach zusätzlichen Einstellungen


7,2 Indikator Dashboard

Nun sehen wir uns ein komplizierteres Beispiel an: den Indikator Dashboard. Er wird in Abb. 1 illustriert. Der Indikator zeigt den aktuellen Gewinn, den Spread, die freie Marge als prozentualen Anteil und die aktuellen Werte der Indikatoren ATR, Force Index und RSI an.

Zuerst deklarieren wir das Array der Struktur der Messuhr. 

//--- declaration of the gauge structure array
GAUGE_STR gg[6];

Dann erstellen wir Messuhren und richten sie ein.

Der Indikator des Niveaus der Marge wird in der unteren linken Ecke platziert. Er hat absolute Koordinaten und alle anderen Indikatoren werden mit Bezug zu diesem oder dem benachbarten Indikator platziert.

//--- building the gg00 gauge, margin level
   if(GaugeCreate("gg00",gg[0],5,-90,240,"",RELATIVE_MODE_NONE,
      CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- setting body parameters
   GaugeSetCaseParameters(gg[0],CASE_SECTOR,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- setting parameters of the scale and marks on the scale
   GaugeSetScaleParameters(gg[0],120,35,800,2000,MUL_100,SCALE_INNER,clrBlack);
   GaugeSetMarkParameters(gg[0],MARKS_INNER,SIZE_MIDDLE,200,1,4);
   GaugeSetMarkLabelFont(gg[0],SIZE_MIDDLE,"Arial",false,false,DEF_COL_MARK_FONT);
//--- highlighting ranges on the scale
   GaugeSetRangeParameters(gg[0],0,true,1400,2000,clrLimeGreen);
   GaugeSetRangeParameters(gg[0],1,true,1000,800,clrCoral);
//--- setting text labels
   GaugeSetLegendParameters(gg[0],LEGEND_DESCRIPTION,true,"Margin lvl",4,15,12,"Arial",false,false);
   GaugeSetLegendParameters(gg[0],LEGEND_VALUE,true,"0",3,80,16,"Arial",true,false);
   GaugeSetLegendParameters(gg[0],LEGEND_MUL,true,"",4,55,8,"Arial",true,false);
//--- setting needle parameters
   GaugeSetNeedleParameters(gg[0],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

Wir fahren mit der Anordnung der unteren Reihe fort. Der nächste Indikator ist der Indikator des aktuellen Gewinns.

//--- building the gg01 gauge, current profit
   if(GaugeCreate("gg01",gg[1],-80,20,320,"gg00",RELATIVE_MODE_HOR,
      CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- setting body parameters
   GaugeSetCaseParameters(gg[1],CASE_SECTOR,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- setting parameters of the scale and marks on the scale
   GaugeSetScaleParameters(gg[1],200,0,-400,400,MUL_1,SCALE_INNER,clrBlack);
   GaugeSetMarkParameters(gg[1],MARKS_INNER,SIZE_MIDDLE,100,1,4);
   GaugeSetMarkLabelFont(gg[1],SIZE_MIDDLE,"Arial",false,false,DEF_COL_MARK_FONT);
//--- highlighting ranges on the scale
   GaugeSetRangeParameters(gg[1],0,true,200,400,clrLimeGreen);
   GaugeSetRangeParameters(gg[1],1,true,-200,-400,clrCoral);
//--- setting text labels
   GaugeSetLegendParameters(gg[1],LEGEND_DESCRIPTION,true,"Profit",3,0,16,"Arial",false,false);
   GaugeSetLegendParameters(gg[1],LEGEND_UNITS,true,"USD",3,-90,10,"Arial",true,false);
   GaugeSetLegendParameters(gg[1],LEGEND_VALUE,true,"1",3,90,12,"Arial",true,false);
//--- setting needle parameters
   GaugeSetNeedleParameters(gg[1],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

Mit dem Spread-Indikator wird die untere Reihe abgeschlossen.

//--- building the gg02 gauge, spread
   if(GaugeCreate("gg02",gg[2],-80,-20,240,"gg01",RELATIVE_MODE_HOR,
      CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- setting body parameters
   GaugeSetCaseParameters(gg[2],CASE_SECTOR,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- setting parameters of the scale and marks on the scale
   GaugeSetScaleParameters(gg[2],120,-35,60,0,MUL_1,SCALE_INNER,clrBlack);
   GaugeSetMarkParameters(gg[2],MARKS_INNER,SIZE_MIDDLE,10,1,4);
   GaugeSetMarkLabelFont(gg[2],SIZE_MIDDLE,"Arial",false,false,DEF_COL_MARK_FONT);
//--- highlighting ranges on the scale
   GaugeSetRangeParameters(gg[2],0,true,35,10,clrLimeGreen);
   GaugeSetRangeParameters(gg[2],1,true,50,60,clrCoral);
//--- setting text labels
   GaugeSetLegendParameters(gg[2],LEGEND_DESCRIPTION,true,"Spread",4,-15,14,"Arial",false,false);
   GaugeSetLegendParameters(gg[2],LEGEND_VALUE,true,"0",3,-80,16,"Arial",true,false);
//--- setting needle parameters
   GaugeSetNeedleParameters(gg[2],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

Der Indikator ATR (der linke in der oberen Reihe) wird mit Bezug zum Indikator der freien Marge platziert.

//--- building the gg03 gauge, ATR
   if(GaugeCreate("gg03",gg[3],30,0,180,"gg00",RELATIVE_MODE_VERT,
      CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- setting body parameters
   GaugeSetCaseParameters(gg[3],CASE_ROUND,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- setting parameters of the scale and marks on the scale
   GaugeSetScaleParameters(gg[3],270,45,0.001,0.004,MUL_00001,SCALE_INNER,clrBlack);
   GaugeSetMarkParameters(gg[3],MARKS_INNER,SIZE_LARGE,0.001,9,3);
   GaugeSetMarkLabelFont(gg[3],SIZE_LARGE,"Arial",false,false,DEF_COL_MARK_FONT);
//--- highlighting ranges on the scale
   GaugeSetRangeParameters(gg[3],0,true,0.002,0.001,clrYellow);
//--- setting text labels
   GaugeSetLegendParameters(gg[3],LEGEND_DESCRIPTION,true,"ATR",7,-140,26,"Arial",false,false);
//GaugeSetLegendParameters(gg[3],LEGEND_UNITS,true,"USD",8,180,5,"Arial",true,false);
   GaugeSetLegendParameters(gg[3],LEGEND_VALUE,true,"5",2,180,14,"Arial",true,false);
   GaugeSetLegendParameters(gg[3],LEGEND_MUL,true,"",2,0,20,"Arial",true,false);
//--- setting needle parameters
   GaugeSetNeedleParameters(gg[3],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

Der Indikator RSI wird mit Bezug zum Spread-Indikator (oben) platziert.

//--- building the gg04 gauge, RSI
   if(GaugeCreate("gg04",gg[4],-30,0,180,"gg02",RELATIVE_MODE_VERT,
      CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- setting body parameters
   GaugeSetCaseParameters(gg[4],CASE_ROUND,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- setting parameters of the scale and marks on the scale
   GaugeSetScaleParameters(gg[4],270,45,0,100,MUL_10,SCALE_INNER,clrBlack);
   GaugeSetMarkParameters(gg[4],MARKS_INNER,SIZE_LARGE,10,1,4);
   GaugeSetMarkLabelFont(gg[4],SIZE_LARGE,"Arial",false,false,DEF_COL_MARK_FONT);
//--- setting text labels
   GaugeSetLegendParameters(gg[4],LEGEND_DESCRIPTION,true,"RSI",7,-140,26,"Arial",false,false);
   GaugeSetLegendParameters(gg[4],LEGEND_VALUE,true,"2",2,180,16,"Arial",true,false);
   GaugeSetLegendParameters(gg[4],LEGEND_MUL,true,"",2,0,20,"Arial",true,false);
//--- setting needle parameters
   GaugeSetNeedleParameters(gg[4],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

Der Indikator Force Index wird über dem Indikator des aktuellen Gewinns platziert.

//--- building the gg05 gauge, Force
   if(GaugeCreate("gg05",gg[5],-10,60,180,"gg03",RELATIVE_MODE_HOR,
      CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- setting body parameters
   GaugeSetCaseParameters(gg[5],CASE_ROUND,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- setting parameters of the scale and marks on the scale
   GaugeSetScaleParameters(gg[5],270,45,-4,4,MUL_1,SCALE_INNER,clrBlack);
   GaugeSetMarkParameters(gg[5],MARKS_INNER,SIZE_LARGE,1,1,4);
   GaugeSetMarkLabelFont(gg[5],SIZE_LARGE,"Arial",false,false,DEF_COL_MARK_FONT);
//--- highlighting ranges on the scale
   GaugeSetRangeParameters(gg[5],0,true,-1,-4,clrMediumSeaGreen);
   GaugeSetRangeParameters(gg[5],1,true,1,4,clrCrimson);
//--- setting text labels
   GaugeSetLegendParameters(gg[5],LEGEND_DESCRIPTION,true,"Force",7,-140,20,"Arial",false,false);
   GaugeSetLegendParameters(gg[5],LEGEND_VALUE,true,"5",2,180,14,"Arial",true,false);
   GaugeSetLegendParameters(gg[5],LEGEND_MUL,true,"",3,0,10,"Arial",true,false);
//--- setting needle parameters
   GaugeSetNeedleParameters(gg[5],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

Messuhren lassen sich zyklisch zeichnen.

//--- drawing gauges
   for(int i=0; i<6;i++)
     {
      GaugeRedraw(gg[i]);
      GaugeNewValue(gg[i],0);
     }

Wenn das Ereignis OnCalculate() eintritt, berechnen wir die aktuellen Werte neu und rufen die Funktion GaugeNewValue() für jeden Indikator auf.

//--- updating readings
//--- spread
   GaugeNewValue(gg[2],spread[rates_total-1]);
//--- current profit   
   double profit=AccountInfoDouble(ACCOUNT_PROFIT);
   GaugeNewValue(gg[1],profit);
//--- margin level
   double margin_level=AccountInfoDouble(ACCOUNT_MARGIN_LEVEL);
   GaugeNewValue(gg[0],margin_level);
//--- the ATR indicator
   calculated=BarsCalculated(handle_ATR);
   if(calculated>0)
     {
      double ival[1];
      if(CopyBuffer(handle_ATR,0,0,1,ival)<0)
         Print("ATR CopyBuffer error");
      else
         GaugeNewValue(gg[3],ival[0]);
     }
//--- the RSI indicator
   calculated=BarsCalculated(handle_RSI);
   if(calculated>0)
     {
      double ival[1];
      if(CopyBuffer(handle_RSI,0,0,1,ival)<0)
         Print("RSI CopyBuffer error");
      else
         GaugeNewValue(gg[4],ival[0]);
     }
//--- the Force Index indicator
   calculated=BarsCalculated(handle_Force);
   if(calculated>0)
     {
      double ival[1];
      if(CopyBuffer(handle_Force,0,0,1,ival)<0)
         Print("Force Index CopyBuffer error");
      else
         GaugeNewValue(gg[5],ival[0]);
     }

Bitte beachten Sie, dass es in diesem Beispiel keinen Sinn ergibt, GaugeRelocation() aus dem Ereignis OnChartEvent() aufzurufen. Obwohl hier die relative Positionierung angewendet wird, müssen wir die Koordinaten der Messuhren nicht neu berechnen, wenn sich die Position oder Größe von einer davon ändert, da die Messuhren alle gleichzeitig initialisiert werden.


8 Bewertung der Ressourcenintensität

Die Nadelschicht wird vollständig neu gezeichnet, sobald Werte aktualisiert werden. Das kann ziemlich oft passieren, in einigen Fällen bis zu mehrere Male pro Sekunde. Deshalb ist das Problem der Ressourcenintensität beim Zeichnen der Nadel ziemlich akut. Wir schreiben ein kleines Script, um den CPU-Overhead zum Zeichnen der Nadel mithilfe verschiedener Methoden der Bereichsfüllung zu bewerten.

//+------------------------------------------------------------------+
//|                                                    test_fill.mq5 |
//|                                         Copyright 2015, Decanium |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Decanium"
#property version   "1.00"

#include <Canvas/Canvas2.mqh>

CCanvas canvas;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   Print("***** start test *****");
//---
   string ObjName="test";
   ObjectDelete(0,ObjName);
   canvas.CreateBitmapLabel(ObjName,10,10,400,400,COLOR_FORMAT_ARGB_NORMALIZE);
//---
   int x[3]={200,185,215};
   int y[3]={70, 250,250};
   int cycles=1000;
   uint col=ColorToARGB(clrRed,255);
   uint c1,c2;
//--- testing the area fill with antialiased edges
   canvas.Erase();
   c1=GetTickCount();
   for(int i=0; i<cycles; i++)
     {
      canvas.Fill(10, 10, col);
      canvas.LineAA2(x[0], y[0], x[1], y[1], 0);
      canvas.LineAA2(x[1], y[1], x[2], y[2], 0);
      canvas.LineAA2(x[2], y[2], x[0], y[0], 0);
      canvas.Fill2(10, 10, 0);
     }
   c2=GetTickCount();
   canvas.Update(true);
   Print("Filled AA: ",c2-c1," ms, ",cycles," cycles, ",
         DoubleToString(double(c2-c1)/double(cycles),2)," ms per cycle");
//--- testing the antialiased contour without filling
   canvas.Erase();
   c1=GetTickCount();
   for(int i=0; i<cycles; i++)
     {
      canvas.LineAA2(x[0], y[0], x[1], y[1], col);
      canvas.LineAA2(x[1], y[1], x[2], y[2], col);
      canvas.LineAA2(x[2], y[2], x[0], y[0], col);
     }
   c2=GetTickCount();
   canvas.Update(true);
   Print("Not filled AA: ",c2-c1," ms, ",cycles," cycles, ",
         DoubleToString(double(c2-c1)/double(cycles),2)," ms per cycle");
//--- testing the area fill without antialiasing
   canvas.Erase();
   c1=GetTickCount();
   for(int i=0; i<cycles; i++)
     {
      canvas.FillTriangle(x[0],y[0],x[1],y[1],x[2],y[2],col);
      canvas.LineAA2(x[0], y[0], (x[1]+x[2])/2, y[1], col);
     }
   c2=GetTickCount();
   canvas.Update(true);
   Print("Filled: ",c2-c1," ms, ",cycles," cycles, ",
         DoubleToString(double(c2-c1)/double(cycles),2)," ms per cycle");
  }
//+------------------------------------------------------------------+

Das Script führt jede Methode zum Zeichnen der Nadel 1000 Mal in einem Zyklus aus und misst die für diesen Prozess aufgewendete Zeit in Millisekunden.

Test der Ressourcenintensität

Abb. 16. Ergebnisse des Tests der Ressourcenintensität

Wie Sie anhand der Ergebnisse sehen, dauert die Zeichnung der ausgefüllten Nadel mit geglätteten Kanten hunderte Male länger als die der ausgefüllten Nadel ohne Kantenglättung und Dutzende Male länger als die einer unausgefüllten, geglätteten Konturlinie. In diesem Fall hat Schönheit wirklich ihren Preis.


Fazit

In diesem Beitrag haben wir den Satz der Funktionen zum Zeichnen von Messuhren betrachtet. Das Hauptziel der Erstellung dieser Bibliothek war es, das Hinzufügen von Messuhren zu einem EA oder Indikator einfach zu machen, ohne sich mit den Details des Zeichnens und der Geometrie zu beschäftigen. Die Entscheidung, ob dieses Ziel erreicht wurde, liegt bei Ihnen.

Achten Sie besonders auf die Ressourcenintensität. Zeitraubende Berechnungen im Handler OnCalculate() können dazu führen, dass sich das Terminal aufhängt. Wir empfehlen also, beim Zeichnen der Nadel einen Kompromiss einzugehen (Kantenglättung ohne Ausfüllen).

Übersetzt aus dem Russischen von MetaQuotes Software Corp.
Originalartikel: https://www.mql5.com/ru/articles/1699

Beigefügte Dateien |
dashboard.mq5 (22.69 KB)
canvas2.mqh (171.52 KB)
gauge_graph.mqh (154.69 KB)
Zeichnen von Widerstands- und Unterstützungsebenen mithilfe von MQL5 Zeichnen von Widerstands- und Unterstützungsebenen mithilfe von MQL5

Dieser Beitrag beschreibt eine Methode zum Finden von vier Extrema zum Zeichnen von darauf basierenden Unterstützungs- und Widerstandsebenen. Zum Finden der Extrema in einem Diagramm eines Währungspaars wird der Indikator RSI verwendet. Als Beispiel wird ein Indikatorcode bereitgestellt, der die Unterstützungs- und Widerstandsebenen anzeigt.

Tipps zum Kauf eines Produkts auf dem Market. Schritt für Schritt Tipps zum Kauf eines Produkts auf dem Market. Schritt für Schritt

Diese Schritt-für-Schritt-Anleitung liefert Tipps und Tricks zum besseren Verstehen und Suchen nach einem benötigten Produkt. Dieser Beitrag versucht, verschiedene Methoden zum Suchen nach einem geeigneten Produkt, zum Aussortieren unerwünschter Produkte und zum Bestimmen der Wirksamkeit und Bedeutung eines Produkts für Sie zu erschaffen. Einleitung

Sichern Ihres Expert Advisors beim Handel auf der Moskauer Börse Sichern Ihres Expert Advisors beim Handel auf der Moskauer Börse

Dieser Beitrag behandelt Handelsmethoden, mit denen die Sicherheit von Handelsoperationen auf Aktienbörsen und Märkten mit geringer Liquidität gewährleistet wird, anhand des Beispiels des Terminmarktes der Moskauer Börse. Er liefert einen praktischen Ansatz für die Handelstheorie aus dem Beitrag "Grundlagen der Preisbildung von Börsen anhand des Beispiels des Terminmarktes der Moskauer Börse".

Bau einer interaktiven Anwendung zur Anzeige von RSS-Feeds in MetaTrader 5 Bau einer interaktiven Anwendung zur Anzeige von RSS-Feeds in MetaTrader 5

In diesem Beitrag betrachten wir die Möglichkeit, eine Anwendung zur Anzeige von RSS-Feeds zu erzeugen. Es wird gezeigt, wie Aspekte der Standard Library dazu verwendet werden können, interaktive Programme für MetaTrader 5 zu erstellen.