
Zeichnen von Messuhren mithilfe der Klasse CCanvas
Inhaltsverzeichnis
- Einleitung
- 1 Koordinaten und Verknüpfung
- 2 Elemente der Messuhr
- 3 Funktionen
- 3,1 GaugeCreate
- 3,2 GaugeSetCaseParameters
- 3,3 GaugeSetScaleParameters
- 3,4 GaugeSetMarkParameters
- 3,5 GaugeSetMarkLabelFont
- 3,6 GaugeSetLegendParameters
- 3,7 GaugeSetRangeParameters
- 3,8 GaugeSetNeedleParameters
- 3,9 GaugeRedraw
- 3,10 GaugeNewValue
- 3,11 GaugeDelete
- 3,12 GaugeCalcLocation
- 3,13 GaugeRelocation
- 4 Aufzählungen
- 4,1 ENUM_CASE_BORDER_STYLE
- 4,2 ENUM_CASE_STYLE
- 4,3 ENUM_GAUGE_LEGEND
- 4,4 ENUM_MARK_STYLE
- 4,5 ENUM_MUL_SCALE
- 4,6 ENUM_NCENTER_STYLE
- 4,7 ENUM_NEEDLE_FILL
- 4,8 ENUM_REL_MODE
- 4,9 ENUM_SCALE_STYLE
- 4,10 ENUM_SIZE
- 5. Makroersetzung
- 6 Modifizieren der Klasse CCanvas
- 6,1 Zeichnen eines Segments mithilfe eines Kantenglättungsalgorithmus
- 6,2 Ausfüllen eines Bereichs mit geglätteten Kanten
- 7 Anwendungsbeispiele
- 8 Bewertung der Ressourcenintensität
- Fazit
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.
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.
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
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)
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.
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.
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.
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.
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.
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.
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
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.
Abb. 8. Positionierung von Strichen und Beschriftungen
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.
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.
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.
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)
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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_STYLE | Stil 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_STYLE | Stil des Mittelpunkts der Nadel |
ENUM_NEEDLE_FILL | Methode zur Füllung des Nadelbereichs |
ENUM_REL_MODE | Methode der relativen Positionierung |
ENUM_SCALE_STYLE | Stil der Skala |
ENUM_SIZE | Größ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_THIN | Dü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_DESCRIPTION | Beschreibung der Messuhr |
LEGEND_UNITS | Maßeinheiten |
LEGEND_MUL | Multiplikator der Skalenbeschriftungen |
LEGEND_VALUE | Aktueller 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_INNER | Ausrichtung der Markierungen am inneren Rand |
MARKS_MIDDLE | Ausrichtung der Markierungen am Mittelpunkt |
MARKS_OUTER | Ausrichtung 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.
Identifikator | Bedeutung | Anzeige |
---|---|---|
MUL_10000 | 10000 | х10k |
MUL_1000 | 1000 | х1k |
MUL_100 | 100 | х100 |
MUL_10 | 10 | х10 |
MUL_1 | 1 | Nicht angezeigt |
MUL_01 | 0,1 | /10 |
MUL_001 | 0,01 | /100 |
MUL_0001 | 0,001 | /1k |
MUL_00001 | 0,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_NONE | Keine Anzeige des Mittelpunkts der Nadel |
NDCS_SMALL | Kleine Anzeige |
NDCS_LARGE | Groß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_FILL | Füllung der Nadel ohne Kantenglättung |
NEEDLE_FILL_AA | Füllung der Nadel mit Kantenglättung |
NEEDLE_NOFILL_AA | Keine 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_NONE | Relative Positionierung deaktiviert |
RELATIVE_MODE_HOR | Horizontal |
RELATIVE_MODE_VERT | Vertikal |
RELATIVE_MODE_DIAG | Diagonal |
Tabelle 10 Werte von ENUM_REL_MODE
4,9 ENUM_SCALE_STYLE
Stil der Skala. Werte werden in Tabelle 11 aufgelistet.
Identifikator | Beschreibung |
---|---|
SCALE_INNER | Skalenbeschriftungen innerhalb der Skala |
SCALE_OUTER | Skalenbeschriftungen 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_SMALL | Klein |
SIZE_MIDDLE | Mittel |
SIZE_LARGE | Groß |
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:
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.
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.
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.
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.
Abb. 15. Aussehen der Messuhr nach zusätzlichen Einstellungen
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.
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 Ltd.
Originalartikel: https://www.mql5.com/ru/articles/1699





- 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.