
Grafische Interfaces IV: Informierende Interface-Elemente (Kapitel 1)
Inhalt
- Einleitung
- Das Statusbar Element
- Test der Statusbar
- Das Tooltip Element
- Test des Tooltip Elementes
- Privates Array für Tooltips
- Schlussfolgerung
Einleitung
Der erste Artikel Grafische Interfaces I: Vorbereitung der Bibliotheksstruktur (Kapitel 1) betrachtet im Detail den Sinn und Zweck der Bibliothek. Eine vollständige Liste der Links zu den Artikeln finden Sie am Ende von jedem Kapitel. Zudem finden Sie dort eine Möglichkeit das Projekt, entsprechend dem aktuellen Entwicklungszustand, herunterzuladen. Die Dateien müssen in den gleichen Verzeichnissen untergebracht werden, so, wie Sie auch in dem Archiv abgelegt sind.
Zum aktuellen Stand der Entwicklung, beinhaltet die Bibliothek für die Erzeugung von grafischen Interfaces ein Formular und verschiedene Steuerelemente (Controls), welche dem Formular hinzugefügt werden können. Wie zuvor schon erwähnt, sollte sich einer der zukünftigen Artikel mit dem Thema Multi-Window-Modus beschäftigen. Dafür liegt nun alles vor, und wir werden in dem folgenden Kapitel dieses Thema behandeln. In diesem Kapitel schreiben wir Klassen für die Erzeugung der Statusbar und des Tooltip-Elementes.
Das Statusbar Element
Die Statusbar ist eine der informierenden Elemente des grafischen Interfaces. Dieses Element wird für die schnelle Eingabe von wichtigen Daten, Details und Werten verwendet. Auch die MetaTrader Terminals besitzen eine solche Statusbar. Sie besteht aus mehreren Abschnitten (elementen) Das erste Element zeigt Informationen darüber, über welchen Teil des Terminals sich der Mauszeiger befindet oder die Namen der Programm-Kommandos. Es gibt auch Elemente, die die Zeit und die zugehörigen Kursdaten anzeigen, sobald sich der Mauszeiger über dem Bereich eines Charts befindet. Einige Elemente besitzen ein Kontextmenü, welches durch einen Klick mit der linken Maustaste angezeigt werden kann. Auch der MetaEditor Programmeditor besitzt eine Statusbar. Seine Elemente zeigen ebenfalls die Programm-Kommandos an, die Position des Cursors (Zeile/Spalte) und den Text-Eingabemodus (Einfügen/Überschreiben). Weitere Informationen über die Statusbar im Terminal und in dem Programmeditor finden Sie in der dazugehörigen Hilfe (F1).
In diesem Artikel werden wir eine einfache Statusbar, ohne die Möglichkeit ein Kontextmenü hinzuzufügen, erzeugen. Wie auch bei anderen Interfaceelementen, besteht die Statusbar aus verschiedenen einfachen Objekten:
- Hintergrund
- Elemente.
- Trennlinien.
Abbildung 1. Komponenten der Statusbar.
Erzeugen Sie die StatusBar.mqh Datei und beziehen Sie sie in der WndContainer.mqh Datei mit ein, damit sie in der gesamten Bibliothek verfügbar ist. Der nachfolgende Programmcode zeigt die CStatusBar Klasse mit den Virtuellen Standardmethoden, welche in allen Klassen der Controls verwendet werden.
//+------------------------------------------------------------------+ //| StatusBar.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Element.mqh" #include "Window.mqh" //+------------------------------------------------------------------+ //| Klasse für das Erzeugen der Statusbar | //+------------------------------------------------------------------+ class CStatusBar : public CElement { private: //--- Ein Pointer zu der Form zu welchem das Element hinzugefügt worden ist CWindow *m_wnd; //--- public: CStatusBar(void); ~CStatusBar(void); //--- Speichert den Pointer void WindowPointer(CWindow &object) { m_wnd=::GetPointer(object); } //--- public: //--- Chart Eventhandler virtual void OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) {} //--- Timer virtual void OnEventTimer(void) {} //--- Bewegen des Elementes virtual void Moving(const int x,const int y); //--- (1) Anzeigen, (2) verstecken, (3) zurücksetzen, (4) löschen virtual void Show(void); virtual void Hide(void); virtual void Reset(void); virtual void Delete(void); //--- (1) Setzen, (2) Zurücksetzen der Priorität der linken Maustaste virtual void SetZorders(void); virtual void ResetZorders(void); }; //+------------------------------------------------------------------+ //| Konstruktor | //+------------------------------------------------------------------+ CStatusBar::CStatusBar(void) { } //+------------------------------------------------------------------+ //| Destruktor | //+------------------------------------------------------------------+ CStatusBar::~CStatusBar(void) { } //+------------------------------------------------------------------+
Die OnEvent() und OnEventTimer() Methoden für das Behandeln von Events werden in dieser Version nicht verwendet.
Wir betrachten nun die Eigenschaften der Statusbar. Da wir Objekt-Arrays verwenden, wird es gemeinsame und spezielle Eigenschaften geben. Die speziellen Eigenschaften umfassen nur die Breite des Elementes. Die Liste der gemeinsamen Eigenschaften ist um einiges länger und wird nachfolgend gezeigt.
Gemeinsame Eigenschaften:
- Farbe des Hintergrundes und des Rahmens des Hintergrundes.
- Textfarbe.
- Die Farben für die Trennlinie
- Priorität eines Klicks mit der linken Maustaste.
Initialisierung der Eigenschaften-Werte mit Standardwerten innerhalb des Konstruktors. In dem Moment der Erzeugung des Elementes, kann der User diese unter Verwendung der Public-Methoden neu definieren.
class CStatusBar : public CElement { private: //--- Eigenschaften: // Arrays für spezielle Eigenschaften int m_width[]; //--- (1) Farbe des Hintergrundes und (2) Rahmen des Hintergrundes color m_area_color; color m_area_border_color; //--- Die Textfarbe color m_label_color; //--- Priorität eines Klicks mit der linken Maustaste int m_zorder; //--- Farben für die Trennlinien color m_sepline_dark_color; color m_sepline_light_color; //--- public: //--- Farbe des (1) Hintergrundes, (2) Rahmen des Hintergrundes und (3) des Textes void AreaColor(const color clr) { m_area_color=clr; } void AreaBorderColor(const color clr) { m_area_border_color=clr; } void LabelColor(const color clr) { m_label_color=clr; } //--- Farbe der Trennlinien void SeparateLineDarkColor(const color clr) { m_sepline_dark_color=clr; } void SeparateLineLightColor(const color clr) { m_sepline_light_color=clr; } }; //+------------------------------------------------------------------+ //| Konstruktor | //+------------------------------------------------------------------+ CStatusBar::CStatusBar(void) : m_area_color(C'240,240,240'), m_area_border_color(clrSilver), m_label_color(clrBlack), m_sepline_dark_color(C'160,160,160'), m_sepline_light_color(clrWhite) { //--- Abspeichern des namens der Elementklasse in der Basisklasse CElement::ClassName(CLASS_NAME); //--- Setzen der Priorität eines Klicks mit der linken Maustaste m_zorder=2; }
Lassen Sie uns nun die Methoden für die Erzeugung der Statusbar betrachten. Für den Hintergrund verwenden wir das einfache Objekt vom Typ CRectLabel. Ein dynamisches Array von einfachen Objekten des Typs CEdit muss für die Erzeugung des Elementes deklariert werden. The CSeparateLine Klasse wurde zuvor schon entwickelt. Diese Klasse kann als ein unabhängiges Interface Element verwendet werden. In unserem Fall verwenden wir es als eine Komponente unserer Statusbar und dafür benötigen wir ein Array mit diesen Elementen.
Elemente werden mit der Methode CStatusBar::AddItem() vor der Erzeugung der Statusbar hinzugefügt, anderenfalls wird die Erzeugung des grafischen interfaces abgebrochen. Die Anzahl der Elemente kann über den Aufruf der Methode CStatusBar::ItemsTotal() abgefragt werden.
//+------------------------------------------------------------------+ //| StatusBar.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Element.mqh" #include "Window.mqh" #include "SeparateLine.mqh" //+------------------------------------------------------------------+ //| Klasse für das Erzeugen der Statusbar | //+------------------------------------------------------------------+ class CStatusBar : public CElement { private: //--- Objekt für die Erzeugung eines Buttons CRectLabel m_area; CEdit m_items[]; CSeparateLine m_sep_line[]; //--- public: //--- Methoden für das Erzeugen der Statusbar bool CreateStatusBar(const long chart_id,const int subwin,const int x,const int y); //--- private: bool CreateArea(void); bool CreateItems(void); bool CreateSeparateLine(const int line_number,const int x,const int y); //--- public: //--- Anzahl der Elemente int ItemsTotal(void) const { return(::ArraySize(m_items)); } //--- Fügt ein Element mit spezifizierten Eigenschaften vor der Erzeugung der Statusbar hinzu void AddItem(const int width); };
Wir werden nur die CStatusBar::CreateItems() Methode für das Erzeugen der Elemente der Statusbar im Detail besprechen. Alle anderen Methoden haben keine fundamentalen Unterschiede, gegenüber den zuvor schon beschriebenen Methoden.
Die Elemente werden so in dem Bereich des Hintergrundes gesetzt, dass sie nicht ihren Rahmen verdecken. Aus diesem Grunde fügen wir zunächst Ränder mit einem Pixel hinzu. Anschließen wird eine Überprüfung der Anzahl der Elemente durchgeführt. Wenn keine Elemente gesetzt wurden, wird eine entsprechende Nachricht ausgegeben und die Erzeugung des grafischen Interfaces wird abgebrochen.
Wie legen fest, dass wenn die Breite des ersten Element ist nicht definiert ist, sie automatisch berechnet wird. Die Breite der Statusbar sollte der Breite des Formulars entsprechen, zu welchem die Statusbar hinzugefügt wird. (Zwei Pixel kleiner, damit sie sich innerhalb des Formulars befindet) Um die Breite des ersten Elementes ermitteln zu können, wird die Breite der anderen Elemente aufaddiert und dieser Wert von der Breite des Formulars abgezogen.
Anschließend folgt die Schleife, in welcher die Elemente der Statusbar erzeugt werden und dann die Schleife für die Erzeugung der Trennlinien. Die Koordinaten für die Trennlinien werden in Relation zu den Koordinaten von jedem Element berechnen. Für das erste Element wird keine Trennlinie erzeugt. Aus diesem Grunde ist die Anzahl der Trennlinien immer um einen kleiner als die Anzahl der Elemente.
//+------------------------------------------------------------------+ //| Erzeugt eine Liste mit Elementen für die Statusbar | //+------------------------------------------------------------------+ bool CStatusBar::CreateItems(void) { int l_w=0; int l_x=m_x+1; int l_y=m_y+1; //--- Abfrage der Anzahl der Elemente int items_total=ItemsTotal(); //--- Falls es in dieser Gruppe keine Elemente gibt, benachrichtigen und abbrechen if(items_total<1) { ::Print(__FUNCTION__," > This method is to be called, " "if a group contains at least one item! Use the CStatusBar::AddItem() method"); return(false); } //--- Falls die Breite des ersten Element ist nicht definiert ist, dann... if(m_width[0]<1) { //--- ...Berechne sie im Verhältnis zu der gemeinsamen Breite der anderen Elemente for(int i=1; i<items_total; i++) l_w+=m_width[i]; //--- m_width[0]=m_wnd.XSize()-l_w-(items_total+2); } //--- Erzeuge eine spezifizierte Anzahl von Elementen for(int i=0; i<items_total; i++) { //--- Den Objektnamen bilden string name=CElement::ProgramName()+"_statusbar_edit_"+string(i)+"__"+(string)CElement::Id(); //--- X Koordinate l_x=(i>0)? l_x+m_width[i-1] : l_x; //--- Erzeugen eines Objektes if(!m_items[i].Create(m_chart_id,name,m_subwin,l_x,l_y,m_width[i],m_y_size-2)) return(false); //--- Festlegen der Eigenschaften m_items[i].Description(""); m_items[i].TextAlign(ALIGN_LEFT); m_items[i].Font(FONT); m_items[i].FontSize(FONT_SIZE); m_items[i].Color(m_label_color); m_items[i].BorderColor(m_area_color); m_items[i].BackColor(m_area_color); m_items[i].Corner(m_corner); m_items[i].Anchor(m_anchor); m_items[i].Selectable(false); m_items[i].Z_Order(m_zorder); m_items[i].ReadOnly(true); m_items[i].Tooltip("\n"); //--- Ränder von den Kanten des Panels m_items[i].XGap(l_x-m_wnd.X()); m_items[i].YGap(l_y-m_wnd.Y()); //--- Koordinaten m_items[i].X(l_x); m_items[i].Y(l_y); //--- Größe m_items[i].XSize(m_width[i]); m_items[i].YSize(m_y_size-2); //--- Abspeichern des Objekt-Pointers CElement::AddToArray(m_items[i]); } //--- Erzeugen der Trennlinien for(int i=1; i<items_total; i++) { //--- X Koordinate l_x=m_items[i].X(); //--- Erzeugen einer Linie CreateSeparateLine(i,l_x,l_y+2); } //--- return(true); }
Wir benötigen noch eine Public-Methode für die Veränderung des Textes in jedem Element. Wie erzeugen diese Methode und nennen Sie CStatusBar::ValueToItem(). Als Parameter erhält sie die Nummer des Indexes und die Textzeile, die von dem Element angezeigt werden soll.
class CStatusBar : public CElement { public: //--- Setzen des Wertes über den angegebenen Index void ValueToItem(const int index,const string value); }; //+------------------------------------------------------------------+ //| Setzen des Wertes über den angegebenen Index | //+------------------------------------------------------------------+ void CStatusBar::ValueToItem(const int index,const string value) { //--- Überprüfung der Überschreitung des ABCs int array_size=::ArraySize(m_items); if(array_size<1 || index<0 || index>=array_size) return; //--- Setzen des übergebenen Textes m_items[index].Description(value); }
Test der Statusbar
Es ist nun alles bereit für einen ersten Test der Statusbar. Für den Test verwenden wir den ersten EA aus dem vorherigen Teil dieser Serie (dem dritten Teil). Behalten Sie das Hauptmenü und 5 Buttons vom Typ CIconButton Und entfernen Sie alle anderen Controls. die StatusBar.mqh Datei wurde bereits in die Bibliothek mit einbezogen und somit können die Instanzen und Methoden für das Erzeugen der Statusbar in der CProgram Klasse erzeugt werden.
Wir erzeugen eine Statusbar, die aus zwei Elementen besteht. Die Breite des ersten Elementes geben wir nicht an und somit wird diese automatisch berechnet. Nach der Erzeugung der Statusbar, legen Sie den Text «For Help, press F1» in dem ersten Element als Beispiel fest.
//+------------------------------------------------------------------+ //| Kasse für das erzeugende Anwendung | //+------------------------------------------------------------------+ class CProgram : public CWndEvents { private: //--- Status bar CStatusBar m_status_bar; //--- private: //--- Status bar #define STATUSBAR1_GAP_X (1) #define STATUSBAR1_GAP_Y (175) bool CreateStatusBar(void); }; //+------------------------------------------------------------------+ //| Erzeugung des Trading-Panels | //+------------------------------------------------------------------+ bool CProgram::CreateTradePanel(void) { //--- Erzeugen des Formulars 1 für die Controls //---Erzeugung der Controls: // Hauptmenü //--- Kontextmenüs //--- Erzeugen der Statusbar if(!CreateStatusBar()) return(false); //--- Neuzeichnen auf dem Chart m_chart.Redraw(); return(true); } //+------------------------------------------------------------------+ //| Erzeugt die Statusbar | //+------------------------------------------------------------------+ bool CProgram::CreateStatusBar(void) { #define STATUS_LABELS_TOTAL 2 //--- Übergabe des Panel Objektes m_status_bar.WindowPointer(m_window1); //--- Koordinaten int x=m_window1.X()+STATUSBAR1_GAP_X; int y=m_window1.Y()+STATUSBAR1_GAP_Y; //--- Breite int width[]={0,110}; //--- Festlegen der Eigenschaften vor der Erzeugung m_status_bar.YSize(24); //--- Angabe der Anzahl der Abschnitte und deren Eigenschaften for(int i=0; i<STATUS_LABELS_TOTAL; i++) m_status_bar.AddItem(width[i]); //--- Erzeugen eines Controls if(!m_status_bar.CreateStatusBar(m_chart_id,m_subwin,x,y)) return(false); //--- Den Text im ersten Element der Statusbar setzen m_status_bar.ValueToItem(0,"For Help, press F1"); //--- Das Objekt zu dem gemeinsamen Array von Objektgruppen hinzufügen CWndContainer::AddToElementsArray(0,m_status_bar); return(true); }
Das zweite Element der Statusbar soll die lokale Zeit anzeigen. Dafür fügen wir den nachfolgenden Code dem Timer der Anwendung hinzu. Das bedeutet, dass das Element alle 500 Millisekunden aktualisiert wird.
//+------------------------------------------------------------------+ //| Timer | //+------------------------------------------------------------------+ void CProgram::OnTimerEvent(void) { CWndEvents::OnTimerEvent(); //--- Aktualisieren des zweiten Elementes der Statusbar (alle 500 Millisekunden) static int count=0; if(count<500) { count+=TIMER_STEP_MSC; return; } //--- count=0; m_status_bar.ValueToItem(1,TimeToString(TimeLocal(),TIME_DATE|TIME_SECONDS)); m_chart.Redraw(); }
Wenn wir alles richtig gemacht haben, dann sollte das Ergebnis wie in dem nachfolgenden Screenshot gezeigt aussehen:
Abbildung 2. Test des Statusbar-Elementes
Wir haben die Entwicklung der Klasse für die Erzeugung des Statusbar-Elementes abgeschlossen. Sie finden die vollständigen Versionen in den Dateien die diesem Artikel beigefügt sind.
Das Tooltip Element
Jetzt schreiben wir die Klasse für die Erzeugung von Tooltips. In dem vierten Kapitel des ersten Teils dieser Serie, wo wir die Funktionen für Formular-Buttons besprochen haben, haben wir erklärt, warum dieses Element eine separate Klasse benötigt. Kurz gesagt, Tooltips müssen mit einer nicht limitierte Anzahl von Symbolen umgehen können und die Option besitzen, bestimmten Wörter hervorzuheben. Um dieses implementieren zu können, verwenden wir eine Klasse für das Zeichnen von Elementen. Ein Beispiel dafür, wie Interface-Elemente gezeichnet werden können, haben wir zuvor schon gezeigt. Die CSeparateLine Klasse für das Erzeugen der Trennlinie, wurde in dem zweiten Kapitel des zweiten Teils dieser Serie geschrieben. Nun folgen wir dem gleichen Prinzip und erzeugen eine Klasse für Tooltips.
Abbildung 3. Ein Beispiel von einem Tooltip in Word.
Erzeugen Sie die Tooltip.mqh Datei und beziehen Sie sie in der WndContainer.mqh Datei mit ein. Erzeugen sie dann in der neuen Datei eine Klasse mit den bereits bekannten Standardmethoden für alle Interface-Elemente. In dieser Klasse benötigen wir neben dem Pointer zu dem Formular einen Pointer zu dem Element zu welchen das Tooltip hinzugefügt wird und eine Methode für das Abspeichern dieses Pointers.
//+------------------------------------------------------------------+ //| Tooltip.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Element.mqh" #include "Window.mqh" //+------------------------------------------------------------------+ //| Klasse für das Erzeugen des Tooltips | //+------------------------------------------------------------------+ class CTooltip : public CElement { private: //--- Ein Pointer zu der Form zu welchem das Element hinzugefügt worden ist CWindow *m_wnd; //--- Ein Pointer zu dem Element, zu welchem der Tooltip hinzugefügt wird CElement *m_element; //--- public: //--- (1) Speichert den Formular-Pointer, (2) Speichert den Element-Pointer void WindowPointer(CWindow &object) { m_wnd=::GetPointer(object); } void ElementPointer(CElement &object) { m_element=::GetPointer(object); } //--- public: //--- Chart Eventhandler virtual void OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam); //--- Bewegen des Elementes virtual void Moving(const int x,const int y); //--- (1) Anzeigen, (2) verstecken, (3) zurücksetzen, (4) löschen virtual void Show(void); virtual void Hide(void); virtual void Reset(void); virtual void Delete(void); }; //+------------------------------------------------------------------+ //| Konstruktor | //+------------------------------------------------------------------+ CTooltip::CTooltip(void) { } //+------------------------------------------------------------------+ //| Destruktor | //+------------------------------------------------------------------+ CTooltip::~CTooltip(void) { }
Die Eigenschaften, die von dem User vor der Erzeugung des Tooltips festgelegt werden können:
- Kopfteil.
- Array mit Zeilen.
Andere Eigenschaften besitzen Standardwerte. Diese sind:
- Farbverläufe des Tooltip Hintergrundes;
- die Farbe des Rahmens des Hintergrundes;
- Farbe der Kopfzeile;
- Textfarbe;
- Alpha-Kanal. Für das Verwalten der Transparenz eines Tooltips.
Das Array mit Zeilen kann Zeile für Zeile dem Tooltip vor seiner Erzeugung mit der Methode CTooltip::AddString() hinzugefügt werden.
class CTooltip : public CElement { private: //--- Eigenschaften: // Kopfzeile string m_header; //--- Array mit den Zeilen des Textes des Tooltips string m_tooltip_lines[]; //--- Der Wert des Alphakanals (Die Transparenz des Tooltips) uchar m_alpha; //--- Farben von (1) Text, (2) der kopfzeile und (3) des Rahmens des Hintergrundes color m_text_color; color m_header_color; color m_border_color; //--- Array mit Hintergrund-Farbverläufen color m_array_color[]; //--- public: //--- Tooltip Kopfzeile void Header(const string text) { m_header=text; } //--- Fügt dem Tooltip eine Zeile hinzu void AddString(const string text); }; //+------------------------------------------------------------------+ //| Konstruktor | //+------------------------------------------------------------------+ CTooltip::CTooltip(void) : m_header(""), m_alpha(0), m_text_color(clrDimGray), m_header_color(C'50,50,50'), m_border_color(C'118,118,118'), m_gradient_top_color(clrWhite), m_gradient_bottom_color(C'208,208,235') { //--- Abspeichern des namens der Elementklasse in der Basisklasse CElement::ClassName(CLASS_NAME); } //+------------------------------------------------------------------+ //| Fügt eine Zeile hinzu | //+------------------------------------------------------------------+ void CTooltip::AddString(const string text) { //--- Vergrößern des Arrays um ein Element int array_size=::ArraySize(m_tooltip_lines); ::ArrayResize(m_tooltip_lines,array_size+1); //--- Abspeichern des Wertes des übergebenen Parameters m_tooltip_lines[array_size]=text; }
Ähnlich wie bei der Klasse für die Trennlinien, verwenden wir für die Erzeugung des Tooltips die CRectCanvas Klasse und zwei Methoden (public und private).
class CTooltip : public CElement { private: //--- Objekt für die Erzeugung des Tooltips CRectCanvas m_canvas; //--- public: //--- Methode für die Erzeugung des Tooltips bool CreateTooltip (const long chart_id,const int subwin); //--- private: //--- Erzeugt die Grundlage für das Tooltip bool CreateCanvas(void); };
Zu der Überprüfung des Pointers zu dem Formular, zu welchem das Element hinzugefügt wird, überprüft die CTooltip::CreateTooltip() public Methode auch den Pointer zu dem Element, für welches dieses Tooltip bestimmt ist.. Falls es diesen Pointer gibt, dann werden die Koordinaten des Tooltips In Relation zu den Koordinaten des Elementes berechnet. In unserem Fall ist das einen Pixel unterhalb der unteren Grenze des Elementes.
//+------------------------------------------------------------------+ //| Erzeugt das Tooltip-Objekt | //+------------------------------------------------------------------+ bool CTooltip::CreateTooltip(const long chart_id,const int subwin) { //--- Verlassen, wenn es keinen Pointer zu einer Form gibt if(::CheckPointer(m_wnd)==POINTER_INVALID) { ::Print(__FUNCTION__," > Before creating the tooltip, the class must be passed " "the form pointer: CTooltip::WindowPointer(CWindow &object)."); return(false); } //--- Abbrechen, falls es keinen Pointer zu einem Element gibt if(::CheckPointer(m_element)==POINTER_INVALID) { ::Print(__FUNCTION__," > Before creating the tooltip, the class must be passed " "the element pointer: CTooltip::ElementPointer(CElement &object)."); return(false); } //--- Initialisierung der Pointer m_id =m_wnd.LastId()+1; m_chart_id =chart_id; m_subwin =subwin; m_x =m_element.X(); m_y =m_element.Y2()+1; //--- Ränder von den Kanten CElement::XGap(m_x-m_wnd.X()); CElement::YGap(m_y-m_wnd.Y()); //--- Erzeugt das Tooltip if(!CreateTooltip()) return(false); //--- return(true); }
Anders wie bei den ähnliche Methoden in der CSeparateLine Klasse, werden in der CTooltip::CreateCanvas() private Methode für die Erzeugung der Arbeitsfläche, Die folgenden Aktionen ausgeführt anstelle des Zeichnens nach der Erzeugung eines Objektes und der Festlegung der Eigenschaften.
- Festlegen der Größe des Arrays für Farbverläufe für den Hintergrund des Tooltips. Die Größe des Arrays entspricht der Anzahl der Pixel der Höhe des Objektes (Die Größe der Y-Achse).
- Initialisierung des Farbverlauf-Arrays
- Säubern der Arbeitsfläche. Festlegen der Transparenz auf 100%. Der Wert des Alphakanals ist Zero.
Am Ende der Methode wird das Objekt dem gemeinsamen Array der Elemente hinzugefügt. Nachfolgend wird der Programmcode dieser Methode gezeigt.
//+------------------------------------------------------------------+ //| Erzeugt die Arbeitsfläche für das Zeichnen | //+------------------------------------------------------------------+ bool CTooltip::CreateCanvas(void) { //--- Den Objektnamen bilden string name=CElement::ProgramName()+"_help_tooltip_"+(string)CElement::Id(); //--- Erzeugt die Arbeitsfläche if(!m_canvas.CreateBitmapLabel(m_chart_id,m_subwin,name,m_x,m_y,m_x_size,m_y_size,COLOR_FORMAT_ARGB_NORMALIZE)) return(false); //--- Hinzufügen zum Chart if(!m_canvas.Attach(m_chart_id,name,m_subwin,1)) return(false); //--- Festlegen der Eigenschaften m_canvas.Background(false); //--- Ränder von den Kanten m_canvas.XGap(m_x-m_wnd.X()); m_canvas.YGap(m_y-m_wnd.Y()); //--- Festlegen der Größe des Arrays des Farbverlaufes für den Tooltip Hintergrund CElement::GradientColorsTotal(m_y_size); ::ArrayResize(m_array_color,m_y_size); //--- Initialisierung des Arrays CElement::InitColorArray(m_gradient_top_color,m_gradient_bottom_color,m_array_color); //--- Säubern der Arbeitsfläche m_canvas.Erase(::ColorToARGB(clrNONE,0)); m_canvas.Update(); m_alpha=0; //--- Abspeichern des Objekt-Pointers CElement::AddToArray(m_canvas); return(true); }
Lassen Sie uns die Methoden für das Zeichnen eines Vertikalen Farbverlaufs und des Rahmens betrachten. Für den Farbverlauf gibt es bereits in dem Moment der Erzeugung der Arbeitsfläche ein initialisiertes Array mit den benötigten werten. Das ist der Grund warum wir lediglich Zeile für Zeile eine Linie mit der Farbe aus dem Array innerhalb einer Schleife zeichnen können, wobei die Anzahl der Durchläufe gleich der Höhe der Arbeitsfläche ist (Pixel). Für das Zeichnen des Rahmens, werden am Anfang der Methode Arrays mit den Koordinaten für jede Seite der Arbeitsfläche deklariert und initialisiert. Nachdem der Rahmen gezeichnet worden ist, runden wir die Ecken mit einem Pixel ab und dann fügen wir vier weitere Pixel in denvVier Ecken innerhalb der Arbeitsfläche hinzu.
Beide Methoden haben lediglich einen Parameter, den Alphakanal. Das bedeutet, dass wir den Grad der Transparenz des Farbverlaufes und des Rahmens mit dem Aufruf der Methode angeben können.
//+------------------------------------------------------------------+ //| Vertikaler Farbverlauf | //+------------------------------------------------------------------+ void CTooltip::VerticalGradient(const uchar alpha) { //--- X Koordinate int x1=0; int x2=m_x_size; //--- Zeichnen des Farbverlaufs for(int y=0; y<m_y_size; y++) m_canvas.Line(x1,y,x2,y,::ColorToARGB(m_array_color[y],alpha)); } //+------------------------------------------------------------------+ //| Rahmen | //+------------------------------------------------------------------+ void CTooltip::Border(const uchar alpha) { //--- Die Farbe des Rahmens color clr=m_border_color; //--- Umrandung int x_size =m_canvas.X_Size()-1; int y_size =m_canvas.Y_Size()-1; //--- Koordinaten: oben/rechts/unten/links int x1[4]; x1[0]=0; x1[1]=x_size; x1[2]=0; x1[3]=0; int y1[4]; y1[0]=0; y1[1]=0; y1[2]=y_size; y1[3]=0; int x2[4]; x2[0]=x_size; x2[1]=x_size; x2[2]=x_size; x2[3]=0; int y2[4]; y2[0]=0; y2[1]=y_size; y2[2]=y_size; y2[3]=y_size; //--- Zeichnen des Rahmens mit den spezifizierten Koordinaten for(int i=0; i<4; i++) m_canvas.Line(x1[i],y1[i],x2[i],y2[i],::ColorToARGB(clr,alpha)); //--- Abrunden der Ecken mit einem Pixel clr=clrBlack; m_canvas.PixelSet(0,0,::ColorToARGB(clr,0)); m_canvas.PixelSet(0,m_y_size-1,::ColorToARGB(clr,0)); m_canvas.PixelSet(m_x_size-1,0,::ColorToARGB(clr,0)); m_canvas.PixelSet(m_x_size-1,m_y_size-1,::ColorToARGB(clr,0)); //--- Hinzufügen von Pixeln mit angegebenen Koordinaten clr=C'180,180,180'; m_canvas.PixelSet(1,1,::ColorToARGB(clr,alpha)); m_canvas.PixelSet(1,m_y_size-2,::ColorToARGB(clr,alpha)); m_canvas.PixelSet(m_x_size-2,1,::ColorToARGB(clr,alpha)); m_canvas.PixelSet(m_x_size-2,m_y_size-2,::ColorToARGB(clr,alpha)); }
Lassen Sie uns festlegen, dass das Tooltip sofort auftaucht, wenn sich der Mauszeiger über einem Element befindet und wenn der Mauszeiger den Bereich des Elementes verlässt, das Tooltip ausfadet.
Lassen Sie uns die CTooltip::ShowTooltip() Methode für das Anzeigen des Tooltips erzeugen. Zu Beginn der Methode wird eine Überprüfung des Wertes des m_alpha -Kanals durchgeführt. Falls der Wert des Alphakanals gleich oder größer als 255 ist, bedeutet das, dass das Programm bereits an dieser Stelle war und das Tooltip zu 100% sichtbar ist. Andernfalls müssen im nächsten Schritt die Koordinaten und die Ränder für die Kopfzeile des Tooltips gesetzt werden und der Farbverlauf und der Rahmen gezeichnet werden. Falls der Anwender keinen Text für die Kopfzeile angegeben hat, dann wird die Kopfzeile auch nicht gezeichnet. Wenn ein Text angegeben wurde, dann werden die Schriftparameter gesetzt und die Kopfzeile wird gezeichnet.
Anschließend werden die Koordinaten für den Haupttext des Tooltips festgelegt, unter Berücksichtigung der Anwesenheit einer Kopfzeile. Anschließend werden die Parameter für den Haupttext gesetzt. Anders als die fette Schriftart der Kopfzeile (FW_BLACK), erhält die Schrift für den Haupttext keine hervorhebenden Merkmale (FW_THIN). Anschließend wird der Text über eine Schleife auf die Arbeitsfläche gebracht. Die Y Koordinate wird für jede Zeile (Iteration) neu eingestellt. Zum Schluss wird die Arbeitsfläche für die Darstellung der Veränderungen aktualisiert und eine Hinweis für ein komplett sichtbares Tooltip wird gesetzt. Nachfolgend wird der Programmcode dieser Methode gezeigt.
//+------------------------------------------------------------------+ //| Zeigt den Tooltip | //+------------------------------------------------------------------+ void CTooltip::ShowTooltip(void) { //--- Abbrechen, falls das Tooltip zu 100% sichtbar ist if(m_alpha>=255) return; //--- Koordinaten und Ränder für die Kopfzeile int x =5; int y =5; int y_offset =15; //--- Zeichnen des Farbverlaufs VerticalGradient(255); //--- Zeichnen des Rahmens Border(255); //--- Zeichnen der Kopfzeile (falls angegeben) if(m_header!="") { //--- Setzen der Schrift-Parameter m_canvas.FontSet(FONT,-80,FW_BLACK); //--- Zeichnen des Textes der Kopfzeile m_canvas.TextOut(x,y,m_header,::ColorToARGB(m_header_color),TA_LEFT|TA_TOP); } //--- Die Koordinaten für den Haupttext des Tooltips (Unter Berücksichtigung der Anwesenheit einer Kopfzeile) x=(m_header!="")? 15 : 5; y=(m_header!="")? 25 : 5; //--- Setzen der Schrift-Parameter m_canvas.FontSet(FONT,-80,FW_THIN); //--- Zeichnen des Haupttextes des Tooltips int lines_total=::ArraySize(m_tooltip_lines); for(int i=0; i<lines_total; i++) { m_canvas.TextOut(x,y,m_tooltip_lines[i],::ColorToARGB(m_text_color),TA_LEFT|TA_TOP); y=y+y_offset; } //--- Aktualisieren der Arbeitsfläche m_canvas.Update(); //--- Hinweis auf einen komplett sichtbaren Tooltip m_alpha=255; }
Lassen Sie uns eine Methode für das Ausfaden des Tooltips schreiben und sie CTooltip::FadeOutTooltip() nennen. Zu Beginn dieses Verfahren wird eine rückwärts Prüfung für den Wert des Alphakanals durchgeführt. Das bedeutet, wenn es eine komplette Transparenz gibt, dann gibt es keinen Grund fortzufahren. Falls dieses nicht der Fall ist, dann wird die Arbeitsfläche in einer Schleife solange neu gezeichnet, bis die komplette Transparenz erreicht ist. Der vollständige Programmcode diese Methode wird nachfolgend gezeigt.
//+------------------------------------------------------------------+ //| Langsames Faden des Tooltips | //+------------------------------------------------------------------+ void CTooltip::FadeOutTooltip(void) { //--- Abbrechen, wenn das Tooltip zu 100% versteckt ist if(m_alpha<1) return; //--- Abstände für die Kopfzeile int y_offset=15; //--- Schrittweite für die Transparenz uchar fadeout_step=7; //--- Langsames Faden des Tooltips for(uchar a=m_alpha; a>=0; a-=fadeout_step) { //--- Wenn der nächste Schritt die Transparenz negativ macht, dann stoppen if(a-fadeout_step<0) { a=0; m_canvas.Erase(::ColorToARGB(clrNONE,0)); m_canvas.Update(); m_alpha=0; break; } //--- Koordinaten für die Kopfzeile int x =5; int y =5; //--- Zeichnen des Farbverlaufs und des Rahmens VerticalGradient(a); Border(a); //--- Zeichnen der Kopfzeile (falls angegeben) if(m_header!="") { //--- Setzen der Schrift-Parameter m_canvas.FontSet(FONT,-80,FW_BLACK); //--- Zeichnen des Textes der Kopfzeile m_canvas.TextOut(x,y,m_header,::ColorToARGB(m_header_color,a),TA_LEFT|TA_TOP); } //--- Die Koordinaten für den Haupttext des Tooltips (Unter Berücksichtigung der Anwesenheit einer Kopfzeile) x=(m_header!="")? 15 : 5; y=(m_header!="")? 25 : 5; //--- Setzen der Schrift-Parameter m_canvas.FontSet(FONT,-80,FW_THIN); //--- Zeichnen des Haupttextes des Tooltips int lines_total=::ArraySize(m_tooltip_lines); for(int i=0; i<lines_total; i++) { m_canvas.TextOut(x,y,m_tooltip_lines[i],::ColorToARGB(m_text_color,a),TA_LEFT|TA_TOP); y=y+y_offset; } //--- Aktualisieren der Arbeitsfläche m_canvas.Update(); } }
Nun haben wir alles was dafür notwendig ist, um ein Tooltip zu erzeugen und zu zeichnen. Die Methoden müssen in den Eventhandler des Elementes aufgerufen werden. Um herauszufinden, ob auf dem Formular der Button für die Anzeige von Tooltips gedrückt wurde, erzeugen wir in der CWindow-Klasse die >CWindow::TooltipBmpState() Methode.
class CWindow : public CElement { public: //--- Überprüfung des Anzeigemodus für das Tooltip bool TooltipBmpState(void) const { return(m_button_tooltip.State()); } };
Falls sich jetzt der Fokus auf diesem Element befindet, wird der Tooltip angezeigt (unter der Voraussetzung dass der Tooltip-Display-Modus aktiviert ist.) Falls sich der Mauszeiger außerhalb des Bereiches des Elementes befindet oder das Formular gesperrt ist, dann wird der Tooltip wieder versteckt.
//+-----------------------------------------------------------------+ //| Chart Eventhandler | //+-----------------------------------------------------------------+ void CTooltip::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Verarbeiten der Events über die Bewegung des Mauszeigers if(id==CHARTEVENT_MOUSE_MOVE) { //--- Abbrechen, falls das Element versteckt ist if(!CElement::IsVisible()) return; //--- Abbrechen, falls Tooltip-Button auf dem Formular deaktiviert ist if(!m_wnd.TooltipBmpState()) return; //--- Falls das Formular gesperrt ist if(m_wnd.IsLocked()) { //--- Verstecke den Tooltip FadeOutTooltip(); return; } //--- Falls sich der Fokus auf dem Element befindet if(m_element.MouseFocus()) //--- Anzeigen des Tooltips ShowTooltip(); //--- Falls es keinen Fokus gibt else //--- Verstecke den Tooltip FadeOutTooltip(); //--- return; } }
Test des Tooltip Elementes
Jetzt können wir es testen und ansehen wie es funktioniert. Es gibt zurzeit fünf Buttons auf dem Formular unseres Test EA. Lassen Sie uns einen Tooltip für jeden Button erstellen. Deklarieren Sie fünf Instanzen der CTooltip Klasse und fünf Methoden. Als Beispiel zeigen wir hier nur die Implementation für einen dieser Buttons.
class CProgram : public CWndEvents { private: CTooltip m_tooltip1; CTooltip m_tooltip2; CTooltip m_tooltip3; CTooltip m_tooltip4; CTooltip m_tooltip5; //--- private: bool CreateTooltip1(void); bool CreateTooltip2(void); bool CreateTooltip3(void); bool CreateTooltip4(void); bool CreateTooltip5(void); }; //+------------------------------------------------------------------+ //| Erzeugt Tooltip 5 | //+------------------------------------------------------------------+ bool CProgram::CreateTooltip5(void) { #define TOOLTIP5_LINES_TOTAL 3 //--- Abspeichern des Fenster-Pointers m_tooltip5.WindowPointer(m_window1); //--- Abspeichern des Element-Pointers m_tooltip5.ElementPointer(m_icon_button5); //--- Array mit dem Text des Tooltips string text[]= { "Control \"Icon button\" (5).", "This is the second line of the tooltip.", "This is the third line of the tooltip." }; //--- Festlegen der Eigenschaften vor der Erzeugung m_tooltip5.Header("Icon Button 5"); m_tooltip5.XSize(250); m_tooltip5.YSize(80); //--- Hinzufügen von Text, Zeile für Zeile for(int i=0; i<TOOLTIP5_LINES_TOTAL; i++) m_tooltip5.AddString(text[i]); //--- Erzeugen eines Controls if(!m_tooltip5.CreateTooltip(m_chart_id,m_subwin)) return(false); //--- Das Objekt zu dem gemeinsamen Array von Objektgruppen hinzufügen CWndContainer::AddToElementsArray(0,m_tooltip5); return(true); }
Die Erzeugung von Tooltips muss bei dem Verfahren zum Erstellen der grafischen Benutzeroberfläche zuletzt durchgeführt werden, sodass sie am Ende des Elementarrays in der Basis sind. Auf diese Weise sind die Tooltips immer oberhalb der anderen Objekte des Charts.
//+------------------------------------------------------------------+ //| Erzeugung des Trading-Panels | //+------------------------------------------------------------------+ bool CProgram::CreateTradePanel(void) { //--- Erzeugen des Formulars 1 für die Controls //---Erzeugung der Controls: // Hauptmenü //--- Kontextmenüs //--- Erzeugen der Statusbar //--- Icon Buttons //--- Tooltips if(!CreateTooltip1()) return(false); if(!CreateTooltip2()) return(false); if(!CreateTooltip3()) return(false); if(!CreateTooltip4()) return(false); if(!CreateTooltip5()) return(false); //--- Neuzeichnen auf dem Chart m_chart.Redraw(); return(true); }
Um den Tooltip-Button in dem Moment seiner Erzeugung auf dem Formular anzeigen zu können, verwenden Sie die CWindow::UseTooltipsButton() Methode.
//+------------------------------------------------------------------+ //| Erzeugt das Formular 1 für die Controls | //+------------------------------------------------------------------+ bool CProgram::CreateWindow1(const string caption_text) { //--- Hinzufügen des Pointers des Fensters zu dem Fenster-Array CWndContainer::AddWindow(m_window1); //--- Koordinaten int x=1; int y=20; //--- Eigenschaften m_window1.Movable(true); m_window1.XSize(251); m_window1.YSize(200); m_window1.UseTooltipsButton(); m_window1.CaptionBgColor(clrCornflowerBlue); m_window1.CaptionBgColorHover(C'150,190,240'); //--- Erzeugen des Formulars if(!m_window1.CreateWindow(m_chart_id,m_subwin,caption_text,x,y)) return(false); //--- return(true); }
Sie sollten das gleiche Ergebnis, wie es in dem nachfolgenden Screenshot gezeigt ist, erhalten.
Abbildung 4. Tooltip-Test.
Es sieht gut aus und arbeitet einwandfrei! Jetzt benötigt dieses Elements ein privates Array in der Pointer-Basis. Wenn wir den Multi-Window-Modus entwickeln, wird sichtbar, in welchen Fällen wir ein solches Array benötigen.
Privates Array für Tooltips
Gehen Sie zu der CWndContainer Klasse und erweitern Sie sie mit neuen Elementen. Der WindowElements Struktur muss ein privates Array für Tooltips hinzugefügt werden. Wir erzeugen zudem (1) die CWndContainer::TooltipsTotal() Methode für die Abfrage der Nummer des Tooltips und (2) die CWndContainer::AddTooltipElements() Methode um den Tooltip-Pointer dem privaten Array hinzuzufügen.
class CWndContainer { protected: //--- Struktur der Element-Arrays struct WindowElements { //--- Tooltips CTooltip *m_tooltips[]; }; //--- Array mit Arrays von Elementen für jedes Fenster WindowElements m_wnd[]; //--- public: //--- Nummern von Tooltips int TooltipsTotal(const int window_index); //--- private: //--- Speichert die Pointer zu Tooltip-Elementen in der Basis bool AddTooltipElements(const int window_index,CElement &object); }; //+--------------------------------------------------------------------------+ //| Gibt die Anzahl der Tooltips über den angegebenen Fenster-Index zurück | //+--------------------------------------------------------------------------+ int CWndContainer::TooltipsTotal(const int window_index) { if(window_index>=::ArraySize(m_wnd)) { ::Print(PREVENTING_OUT_OF_RANGE); return(WRONG_VALUE); } //--- return(::ArraySize(m_wnd[window_index].m_tooltips)); } //+-----------------------------------------------------------------+ //| Speichert den Tooltip-Pointer in dem privaten Array | //+-----------------------------------------------------------------+ bool CWndContainer::AddTooltipElements(const int window_index,CElement &object) { //--- Abbrechen, falls es kein Tooltip ist if(object.ClassName()!="CTooltip") return(false); //--- Abfrage des Tooltip-Pointers CTooltip *t=::GetPointer(object); //--- Hinzufügen des Pointers zu dem privaten Array AddToRefArray(t,m_wnd[window_index].m_tooltips); return(true); }
Die CWndContainer::AddTooltipElements() Methode muss in der CWndContainer::AddToElementsArray() public Methode aufgerufen werden, welche in der benutzerdefinierten Klasse für das Hinzufügen von Elementen zu der Basis verwendet wird. Eine abgekürzte Version wird in dem nachfolgenden Code gezeigt.
//+------------------------------------------------------------------+ //| Fügt einen Pointer zu dem Element Array hinzu | //+------------------------------------------------------------------+ void CWndContainer::AddToElementsArray(const int window_index,CElement &object) { //--- Falls die Basis keine Formulare für Controls enthält //--- Falls es eine Anfrage für eine nicht existierende Form gibt //--- Hinzufügen zu dem gemeinsamen Array von Elementen //--- Hinzufügen von Element-Objekten zu dem gemeinsamen Array von Objekten //--- Abspeichern der ID von dem letzten Element in allen Forms //--- Erhöhen des Zählers für die Elemente //--- Speichert die Pointer zu den Kontextmenü-Objekten in der Basis //--- Speichert die Pointer zu den Hauptmenü-Objekten in der Basis //--- Speichert die Pointer zu den Splitbutton-Objekten in der Basis //--- Speichert die Pointer zu den Tooltip-Objekten in der Basis if(AddTooltipElements(window_index,object)) return; }
Die Entwicklung der Klasse für die Erzeugung von Tooltips ist nun abgeschlossen. Sie können eine vollständige Version am Ende dieses Artikels herunterladen.
Schlussfolgerung
In diesem Artikel haben wir die Entwicklung einer Statusbar und des Tooltip-Elementes betrachtet. In dem nächsten Kapitel werden wir Multi-Fenster-Interfaces entwickeln und das Managementsystem für die Prioritäten der linken Maustaste besprechen.
Sie können das Material des ersten Teils dieser Serie über die angehängten Dateien herunterladen und testen. Wenn Sie fragen zur Verwendung dieses Materials haben, dann können Sie zunächst auf die detaillierte Beschreibung in dem Artikel zu dieser Bibliothek zurückgreifen oder Sie stellen Ihre Frage(n) in den Kommentaren zu diesem Artikel.
Liste der Artikel (Kapitel) des vierten Teils:
- Grafische Interfaces IV: Informierende Interface-Elemente (Kapitel 1)
- Grafische Interfaces IV: Der Multi-Window-Modus und das System für Prioritäten (Kapitel 2)
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/2307





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