MQL5-Handelswerkzeuge (Teil 8): Verbessertes informatives Dashboard mit verschiebbaren und minimierbaren Funktionen
Einführung
In unserem letzten Artikel (Teil 7) haben wir ein Informations-Dashboard in MetaQuotes Language 5 (MQL5) entwickelt, um Multi-Symbol-Positionen und Kontowerte wie „Balance“ (Saldo), „Equity“ (Kapital) und „Free Margin“ (freie Marge) mit sortierbaren Spalten und durch Komma getrennte Werte (CSV) Exportfunktionen zu überwachen. In Teil 8 verbessern wir dieses Dashboard, indem wir Funktionen zum Ziehen und Minimieren, interaktive Schaltflächen zum Schließen, Umschalten und Exportieren sowie Hover-Effekte für ein dynamischeres Nutzererlebnis hinzufügen. Mit dieser Verbesserung bleiben die Positionsverfolgung in Echtzeit und der Glüheffekt in der Kopfzeile erhalten. Wir werden die folgenden Themen behandeln:
Am Ende werden Sie über ein vielseitiges, nutzerfreundliches MQL5-Dashboard verfügen, das auf eine effiziente Handelsüberwachung zugeschnitten ist – legen wir los!
Verstehen der erweiterten Dashboard-Architektur
Wir aktualisieren das Informations-Dashboard aus Teil 7, indem wir Funktionen zum Ziehen und Minimieren sowie interaktive Schaltflächen und Hover-Effekte hinzufügen, um es flexibler und nutzerfreundlicher für die Verwaltung mehrerer Positionen zu machen. Diese Verbesserungen sind wichtig, da sie es ermöglichen, das Dashboard an eine beliebige Stelle im Chart zu verschieben und so die Unübersichtlichkeit während der Analyse zu minimieren, während die Minimierungsoption Platz auf dem Bildschirm spart und die interaktiven Elemente ein unmittelbares visuelles Feedback liefern, was die allgemeine Handelserfahrung in schnelllebigen Umgebungen verbessert.
Dies erreichen wir durch die Einbindung von Mausereignissen für das Ziehen und Anklicken von Schaltflächen, um sicherzustellen, dass das Dashboard reaktionsschnell und anpassungsfähig bleibt, ohne dabei seine Kernfunktionen zur Positionsbestimmung zu verlieren. Wir werden auch ein Symbol in der Kopfzeile für die Exportfunktion hinzufügen, damit sie leicht zu finden ist, aber die Tastaturfunktion beibehalten. Wir planen, das sortierbare Raster und die Echtzeit-Updates beizubehalten, während wir diese Verbesserungen hinzufügen, um ein Tool zu schaffen, das sich im täglichen Gebrauch intuitiv und effizient anfühlt. Schauen Sie sich unten an, was wir erreichen wollen, und dann können wir mit der Umsetzung beginnen!

Implementation in MQL5
Um das Programm in MQL5 zu verbessern, müssen wir die neuen Dashboard-Komponenten definieren, normalerweise 4 Stück.
//--- existing components #define HEADER_PANEL_TEXT "HEADER_PANEL_TEXT"//--- Header title label #define CLOSE_BUTTON "BUTTON_CLOSE" //--- Close button identifier #define EXPORT_BUTTON "BUTTON_EXPORT" //--- Export button identifier #define TOGGLE_BUTTON "BUTTON_TOGGLE" //--- Toggle (minimize/maximize) button //--- the rest of the components
Wir beginnen mit dem Hinzufügen neuer Definitionen, um die erweiterten Funktionen des Informations-Dashboards zu unterstützen, indem wir Bezeichner für interaktive Elemente der Benutzerschnittstelle (UI) einführen. Wir definieren „HEADER_PANEL_TEXT“ als „HEADER_PANEL_TEXT“ für die Beschriftung des Dashboards, um eine klare visuelle Überschrift zu erhalten. Der „CLOSE_BUTTON“ ist als „BUTTON_CLOSE“ definiert, wodurch ein Bezeichner für eine Schaltfläche zum Schließen des Dashboards erstellt wird, mit dem wir es aus dem Chart entfernen können. Der „EXPORT_BUTTON“ wird als „BUTTON_EXPORT“ definiert und richtet eine Schaltfläche zum Auslösen des CSV-Exports ein, wodurch die Zugänglichkeit der Daten verbessert wird. Der „TOGGLE_BUTTON“ wird als „BUTTON_TOGGLE“ definiert, wodurch eine Schaltfläche zum Minimieren oder Maximieren des Dashboards aktiviert wird, was die Verwaltung des Bildschirmplatzes verbessert.
Diese Definitionen sorgen für eine geordnete Namensgebung für die neuen interaktiven Komponenten und unterstützen die verschiebbaren und minimierbaren Erweiterungen. Als Nächstes ändern wir die Farbe der vom Header verwendeten Schatten, indem wir sie durch klar definierte MQL5-Konstanten ersetzen.
// Dashboard settings struct DashboardSettings { //--- Structure for dashboard settings int panel_x; //--- X-coordinate of panel int panel_y; //--- Y-coordinate of panel int row_height; //--- Height of each row int font_size; //--- Font size for labels string font; //--- Font type for labels color bg_color; //--- Background color of main panel color border_color; //--- Border color of panels color header_color; //--- Default color for header text color text_color; //--- Default color for text color section_bg_color; //--- Background color for header/footer panels int zorder_panel; //--- Z-order for main panel int zorder_subpanel; //--- Z-order for sub-panels int zorder_labels; //--- Z-order for labels int label_y_offset; //--- Y-offset for label positioning int label_x_offset; //--- X-offset for label positioning int header_x_distances[9]; //--- X-distances for header labels (9 columns) color header_shades[12]; //--- Array of header color shades for glow effect } settings = { //--- Initialize settings with default values 20, //--- panel_x 20, //--- panel_y 24, //--- row_height 11, //--- font_size "Calibri Bold", //--- font C'240,240,240', //--- bg_color (light gray) clrBlack, //--- border_color C'0,50,70', //--- header_color (dark teal) clrBlack, //--- text_color C'200,220,230', //--- section_bg_color (light blue-gray) 100, //--- zorder_panel 101, //--- zorder_subpanel 102, //--- zorder_labels 3, //--- label_y_offset 25, //--- label_x_offset {10, 120, 170, 220, 280, 330, 400, 470, 530}, //--- header_x_distances {clrBlack, clrRed, clrBlue, clrGreen, clrMagenta, clrDarkOrchid, clrDeepPink, clrSkyBlue, clrDodgerBlue, clrDarkViolet, clrOrange, clrCrimson} //--- header_shades }; //--- the previous one was as below /* //--- {C'0,0,0', C'255,0,0', C'0,255,0', C'0,0,255', C'255,255,0', C'0,255,255', C'255,0,255', C'255,255,255', C'255,0,255', C'0,255,255', C'255,255,0', C'0,0,255'} */
Hier erweitern wir die Struktur „DashboardSettings“, indem wir das Array „header_shades“ aktualisieren, um den Glüheffekt in der Kopfzeile zu verbessern und so ein optisch ansprechendes Erlebnis zu schaffen. Zuvor verwendete „header_shades“ eine Mischung der Grundfarben von RGB (z. B. reines Schwarz, Rot, Grün, Blau, Gelb, Cyan, Magenta, Weiß) für den Glühzyklus. Wir definieren nun „header_shades“ mit einer ausgewählte Palette von 12 Farben: „clrBlack“, „clrRed“, „clrBlue“, „clrGreen“, „clrMagenta“, „clrDarkOrchid“, „clrDeepPink“, „clrSkyBlue“, „clrDodgerBlue“, „clrDarkViolet“, „clrOrange“, und „clrCrimson“.
Dieses Upgrade bietet eine reichhaltigere, vielfältigere Palette, die durch lebendige und nuancierte Farbtöne wechselt und die Ästhetik des Armaturenbretts verbessert, während die Funktionalität des Glüheffekts zur Hervorhebung von Überschriften erhalten bleibt. Schließlich fügen wir weitere globale Variablen hinzu, die uns helfen, ein dynamisches Dashboard zu haben, um den Hover- und Zieh-Status zu steuern.
//--- added global variables int prev_num_symbols = 0; //--- Previous number of active symbols bool panel_is_visible = true; //--- Flag to control panel visibility bool panel_minimized = false; //--- Flag to control minimized state bool panel_dragging = false; //--- Flag to track if panel is being dragged int panel_drag_x = 0; //--- Mouse x-coordinate when drag starts int panel_drag_y = 0; //--- Mouse y-coordinate when drag starts int panel_start_x = 0; //--- Panel x-coordinate when drag starts int panel_start_y = 0; //--- Panel y-coordinate when drag starts int prev_mouse_state = 0; //--- Previous mouse state bool header_hovered = false; //--- Header hover state bool toggle_hovered = false; //--- Toggle button hover state bool close_hovered = false; //--- Close button hover state bool export_hovered = false; //--- Export button hover state int last_mouse_x = 0; //--- Last mouse x position int last_mouse_y = 0; //--- Last mouse y position bool prev_header_hovered = false; //--- Previous header hover state bool prev_toggle_hovered = false; //--- Previous toggle hover state bool prev_close_hovered = false; //--- Previous close button hover state bool prev_export_hovered = false; //--- Previous export button hover state
Schließlich führen wir zusätzliche globale Variablen ein, um die erweiterte Interaktivität und die Funktionen zum Ziehen zu unterstützen. Wir definieren „prev_num_symbols“ als 0, um die vorherige Anzahl aktiver Symbole für die dynamische Größenanpassung zu verfolgen, „panel_is_visible“ als true, um die Sichtbarkeit des Dashboards zu kontrollieren, und „panel_minimized“ als false, um den minimierten Zustand zu verwalten. Um das Ziehen zu aktivieren, fügen wir „panel_dragging“ als false hinzu, um den Status des Ziehens zu verfolgen, „panel_drag_x“ und „panel_drag_y“ als 0 für die Mauskoordinaten beim Start des Ziehens und „panel_start_x“ und „panel_start_y“ als 0 für die Panelkoordinaten beim Start des Ziehens.
Wir fügen „prev_mouse_state“ als 0 ein, um Mausklickzustände zu überwachen, und für Hover-Effekte definieren wir „header_hovered“, „toggle_hovered“, „close_hovered“ und „export_hovered“ als false, um Hover-Zustände für die Kopfzeile und die Schaltflächen zu verfolgen, wobei „last_mouse_x“ und „last_mouse_y“ als 0, um die letzte Mausposition zu speichern, und „prev_header_hovered“, „prev_toggle_hovered“, „prev_close_hovered“ und „prev_export_hovered“ als false, um Änderungen des Hover-Zustands zu erkennen.
Diese Variablen ermöglichen dynamische UI-Interaktionen wie Ziehen, Minimieren und Hover-Feedback. Da wir nun die aktualisierten Variablen haben, sollten wir auch die Funktionen aktualisieren, um die Objekterstellung zu standardisieren, da dies ein fortschrittlicher Weg zur Modularisierung ist. Beginnen wir mit der Funktion zum Erstellen einer Beschriftung und zum Hinzufügen einer sogenannten Funktion Tooltip-Funktion.
//+------------------------------------------------------------------+ //| Creating label object | //+------------------------------------------------------------------+ bool createLABEL(string objName, string txt, int xD, int yD, color clrTxt, int fontSize, string font, int anchor, string tooltip = "", bool selectable = false) { if(!ObjectCreate(0, objName, OBJ_LABEL, 0, 0, 0)) { //--- Creating label object Print(__FUNCTION__, ": Failed to create label '", objName, "'. Error code = ", GetLastError()); //--- Logging creation failure return(false); //--- Returning failure } ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); //--- Setting x-coordinate ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); //--- Setting y-coordinate ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); //--- Setting corner alignment ObjectSetString(0, objName, OBJPROP_TEXT, txt); //--- Setting text content ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); //--- Setting font size ObjectSetString(0, objName, OBJPROP_FONT, font); //--- Setting font type ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); //--- Setting text color ObjectSetInteger(0, objName, OBJPROP_BACK, false); //--- Setting to foreground ObjectSetInteger(0, objName, OBJPROP_STATE, selectable); //--- Setting selectable state ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, selectable); //--- Setting selectability ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); //--- Setting not selected ObjectSetInteger(0, objName, OBJPROP_ANCHOR, anchor); //--- Setting anchor point ObjectSetInteger(0, objName, OBJPROP_ZORDER, settings.zorder_labels); //--- Setting z-order //--- added this tooltip feature ObjectSetString(0, objName, OBJPROP_TOOLTIP, tooltip == "" ? (selectable ? "Click to sort" : "Position data") : tooltip); //--- Setting tooltip text //--- //--- the existing was a hardcoded to this // ObjectSetString(0, objName, OBJPROP_TOOLTIP, selectable ? "Click to sort" : "Position data"); //--- Set tooltip //--- ChartRedraw(0); //--- Redrawing chart return(true); //--- Returning success }
Für die Funktion „createLABEL“ verbessern wir die Logik des Tooltip, um sie flexibler und für verschiedene UI-Elemente wiederverwendbar zu machen. Zuvor war die QuickInfo mit ObjectSetString auf die Einstellung OBJPROP_TOOLTIP entweder auf „Zum Sortieren klicken“ für auswählbare Beschriftungen oder „Daten positionieren“ für nicht auswählbare Beschriftungen fest kodiert worden, was die Anpassungsmöglichkeiten einschränkte. Wir ändern dies nun, indem wir einen Parameter „tooltip“ mit einer leeren Standardzeichenkette hinzufügen und eine ternäre Bedingung in „ObjectSetString“ für „OBJPROP_TOOLTIP“ verwenden: Wenn „tooltip“ leer ist, wird standardmäßig „Click to sort“ für auswählbare Beschriftungen oder „Position data“ für andere verwendet; andernfalls wird der angegebene „tooltip“-Wert verwendet. Diese Änderung ermöglicht spezifische Tooltips für Elemente wie Schaltflächen (z. B. „Dashboard minimieren“ oder „Dashboard schließen“), während die Standardwerte für Überschriften und Datenbeschriftungen beibehalten werden, was die Nutzerführung und die Übersichtlichkeit der Interaktion verbessert.
Um die Erstellung von Panels zu standardisieren, werden wir die Inline-Aufrufe zur Objekterstellung in OnInit durch eine Funktion ersetzen, um die Wartung zu erleichtern. Hier ist die Logik, die wir dafür anwenden.
//+------------------------------------------------------------------+ //| Creating rectangle object | //+------------------------------------------------------------------+ bool createRectangle(string object_name, int x_distance, int y_distance, int x_size, int y_size, color background_color, color border_color = clrBlack) { if(!ObjectCreate(0, object_name, OBJ_RECTANGLE_LABEL, 0, 0, 0)) { //--- Creating rectangle object Print(__FUNCTION__, ": Failed to create Rectangle: '", object_name, "'. Error code = ", GetLastError()); //--- Logging creation failure return(false); //--- Returning failure } ObjectSetInteger(0, object_name, OBJPROP_XDISTANCE, x_distance); //--- Setting x-coordinate ObjectSetInteger(0, object_name, OBJPROP_YDISTANCE, y_distance); //--- Setting y-coordinate ObjectSetInteger(0, object_name, OBJPROP_XSIZE, x_size); //--- Setting width ObjectSetInteger(0, object_name, OBJPROP_YSIZE, y_size); //--- Setting height ObjectSetInteger(0, object_name, OBJPROP_CORNER, CORNER_LEFT_UPPER); //--- Setting corner alignment ObjectSetInteger(0, object_name, OBJPROP_BGCOLOR, background_color); //--- Setting background color ObjectSetInteger(0, object_name, OBJPROP_BORDER_COLOR, border_color); //--- Setting border color ObjectSetInteger(0, object_name, OBJPROP_BORDER_TYPE, BORDER_FLAT); //--- Setting border type ObjectSetInteger(0, object_name, OBJPROP_BACK, false); //--- Setting to foreground ObjectSetInteger(0, object_name, OBJPROP_ZORDER, settings.zorder_subpanel); //--- Setting z-order return(true); //--- Returning success }
Hier erstellen wir einfach eine boolesche Funktion „createRectangle“ und verwenden eine ähnliche Struktur zur Definition wie bei den Beschriftungen. Da dies für Sie nicht neu ist, sparen wir uns die Zeit und gehen zum nächsten Update über, bei dem es sich um eine kleine Korrektur in den Zählfunktionen handelt, um die Schleifenrichtung zu ändern.
//+------------------------------------------------------------------+ //| Counting total positions for a symbol | //+------------------------------------------------------------------+ string countPositionsTotal(string symbol) { int totalPositions = 0; //--- Initializing position counter int count_Total_Pos = PositionsTotal(); //--- Getting total positions for(int i = count_Total_Pos - 1; i >= 0; i--) { //--- Iterating through positions ulong ticket = PositionGetTicket(i); //--- Getting position ticket if(ticket > 0 && PositionSelectByTicket(ticket)) { //--- Checking if position selected if(PositionGetString(POSITION_SYMBOL) == symbol && (MagicNumber < 0 || PositionGetInteger(POSITION_MAGIC) == MagicNumber)) totalPositions++; //--- Checking symbol and magic } } return IntegerToString(totalPositions); //--- Returning total as string } //+------------------------------------------------------------------+ //| Counting buy or sell positions for a symbol | //+------------------------------------------------------------------+ string countPositions(string symbol, ENUM_POSITION_TYPE pos_type) { int totalPositions = 0; //--- Initializing position counter int count_Total_Pos = PositionsTotal(); //--- Getting total positions for(int i = count_Total_Pos - 1; i >= 0; i--) { //--- Iterating through positions ulong ticket = PositionGetTicket(i); //--- Getting position ticket if(ticket > 0 && PositionSelectByTicket(ticket)) { //--- Checking if position selected if(PositionGetString(POSITION_SYMBOL) == symbol && PositionGetInteger(POSITION_TYPE) == pos_type && (MagicNumber < 0 || PositionGetInteger(POSITION_MAGIC) == MagicNumber)) { //--- Checking symbol, type, magic totalPositions++; //--- Incrementing counter } } } return IntegerToString(totalPositions); //--- Returning total as string } //+------------------------------------------------------------------+ //| Counting pending orders for a symbol | //+------------------------------------------------------------------+ string countOrders(string symbol) { int total = 0; //--- Initializing counter int tot = OrdersTotal(); //--- Getting total orders for(int i = tot - 1; i >= 0; i--) { //--- Iterating through orders ulong ticket = OrderGetTicket(i); //--- Getting order ticket if(ticket > 0 && OrderSelect(ticket)) { //--- Checking if order selected if(OrderGetString(ORDER_SYMBOL) == symbol && (MagicNumber < 0 || OrderGetInteger(ORDER_MAGIC) == MagicNumber)) total++; //--- Checking symbol and magic } } return IntegerToString(total); //--- Returning total as string } //+------------------------------------------------------------------+ //| Summing double property for positions of a symbol | //+------------------------------------------------------------------+ string sumPositionDouble(string symbol, ENUM_POSITION_PROPERTY_DOUBLE prop) { double total = 0.0; //--- Initializing total int count_Total_Pos = PositionsTotal(); //--- Getting total positions for(int i = count_Total_Pos - 1; i >= 0; i--) { //--- Iterating through positions ulong ticket = PositionGetTicket(i); //--- Getting position ticket if(ticket > 0 && PositionSelectByTicket(ticket)) { //--- Checking if position selected if(PositionGetString(POSITION_SYMBOL) == symbol && (MagicNumber < 0 || PositionGetInteger(POSITION_MAGIC) == MagicNumber)) { //--- Checking symbol and magic total += PositionGetDouble(prop); //--- Adding property value } } } return DoubleToString(total, 2); //--- Returning total as string } //+------------------------------------------------------------------+ //| Summing commission for positions of a symbol from history | //+------------------------------------------------------------------+ double sumPositionCommission(string symbol) { double total_comm = 0.0; //--- Initializing total commission int pos_total = PositionsTotal(); //--- Getting total positions for(int p = 0; p < pos_total; p++) { //--- Iterating through positions ulong ticket = PositionGetTicket(p); //--- Getting position ticket if(ticket > 0 && PositionSelectByTicket(ticket)) { //--- Checking if selected if(PositionGetString(POSITION_SYMBOL) == symbol && (MagicNumber < 0 || PositionGetInteger(POSITION_MAGIC) == MagicNumber)) { //--- Checking symbol and magic long pos_id = PositionGetInteger(POSITION_IDENTIFIER); //--- Getting position ID if(HistorySelectByPosition(pos_id)) { //--- Selecting history by position int deals_total = HistoryDealsTotal(); //--- Getting total deals for(int d = 0; d < deals_total; d++) { //--- Iterating through deals ulong deal_ticket = HistoryDealGetTicket(d); //--- Getting deal ticket if(deal_ticket > 0) { //--- Checking valid total_comm += HistoryDealGetDouble(deal_ticket, DEAL_COMMISSION); //--- Adding commission } } } } } } return total_comm; //--- Returning total commission } //+------------------------------------------------------------------+ //| Collecting active symbols with positions or orders | //+------------------------------------------------------------------+ void CollectActiveSymbols() { string symbols_temp[]; //--- Temporary array for symbols int added = 0; //--- Counter for added symbols // Collecting from positions int pos_total = PositionsTotal(); //--- Getting total positions for(int i = pos_total - 1; i >= 0; i--) { //--- Iterating through positions ulong ticket = PositionGetTicket(i); //--- Getting position ticket if(ticket > 0 && PositionSelectByTicket(ticket)) { //--- Checking if position selected if(MagicNumber < 0 || PositionGetInteger(POSITION_MAGIC) == MagicNumber) { //--- Checking magic number string sym = PositionGetString(POSITION_SYMBOL); //--- Getting symbol bool found = false; //--- Flag for symbol found for(int k = 0; k < added; k++) { //--- Checking existing symbols if(symbols_temp[k] == sym) { //--- Symbol already added found = true; //--- Setting found flag break; //--- Exiting loop } } if(!found) { //--- If not found ArrayResize(symbols_temp, added + 1); //--- Resizing array symbols_temp[added] = sym; //--- Adding symbol added++; //--- Incrementing counter } } } } // Collecting from orders int ord_total = OrdersTotal(); //--- Getting total orders for(int i = ord_total - 1; i >= 0; i--) { //--- Iterating through orders ulong ticket = OrderGetTicket(i); //--- Getting order ticket if(ticket > 0 && OrderSelect(ticket)) { //--- Checking if order selected if(MagicNumber < 0 || OrderGetInteger(ORDER_MAGIC) == MagicNumber) { //--- Checking magic number string sym = OrderGetString(ORDER_SYMBOL); //--- Getting symbol bool found = false; //--- Flag for symbol found for(int k = 0; k < added; k++) { //--- Checking existing symbols if(symbols_temp[k] == sym) { //--- Symbol already added found = true; //--- Setting found flag break; //--- Exiting loop } } if(!found) { //--- If not found ArrayResize(symbols_temp, added + 1); //--- Resizing array symbols_temp[added] = sym; //--- Adding symbol added++; //--- Incrementing counter } } } } // Setting symbol_data ArrayResize(symbol_data, added); //--- Resizing symbol data array for(int i = 0; i < added; i++) { //--- Iterating through added symbols symbol_data[i].name = symbols_temp[i]; //--- Setting symbol name symbol_data[i].buys = 0; //--- Initializing buys symbol_data[i].sells = 0; //--- Initializing sells symbol_data[i].trades = 0; //--- Initializing trades symbol_data[i].lots = 0.0; //--- Initializing lots symbol_data[i].profit = 0.0; //--- Initializing profit symbol_data[i].pending = 0; //--- Initializing pending symbol_data[i].swaps = 0.0; //--- Initializing swaps symbol_data[i].comm = 0.0; //--- Initializing commission symbol_data[i].buys_str = "0"; //--- Initializing buys string symbol_data[i].sells_str = "0"; //--- Initializing sells string symbol_data[i].trades_str = "0"; //--- Initializing trades string symbol_data[i].lots_str = "0.00"; //--- Initializing lots string symbol_data[i].profit_str = "0.00"; //--- Initializing profit string symbol_data[i].pending_str = "0"; //--- Initializing pending string symbol_data[i].swaps_str = "0.00"; //--- Initializing swaps string symbol_data[i].comm_str = "0.00"; //--- Initializing commission string } }
Um die Logik der Zählfunktionen zu verstärken, ändern wir die Schleifenrichtung von einer inkrementellen zu einer dekrementellen, was in MQL5 in der Regel sicherer ist, aber Sie können auch das Original beibehalten. Wir haben jedoch Ticketprüfungen vor der Auswahl von Aufträgen und Positionen hinzugefügt, um mögliche Fehler durch ungültige Tickets oder Listenänderungen während der Iterationen zu vermeiden. Da wir volle Dynamik wollen, verschieben wir die anfängliche Erstellung des Dashboards in eine Funktion und unterteilen sie in zwei, eine für die maximierte und eine für die minimierte Erstellung.
//+------------------------------------------------------------------+ //| Creating full dashboard UI | //+------------------------------------------------------------------+ void createFullDashboard() { CollectActiveSymbols(); //--- Collecting active symbols int num_rows = ArraySize(symbol_data); //--- Getting number of rows int num_columns = ArraySize(headers); //--- Getting number of columns int column_width_sum = 0; //--- Initializing column width sum for(int i = 0; i < num_columns; i++) //--- Iterating through columns column_width_sum += column_widths[i]; //--- Adding column width int panel_width = MathMax(settings.header_x_distances[num_columns - 1] + column_widths[num_columns - 1], column_width_sum) + 20 + settings.label_x_offset; //--- Calculating panel width // Creating main panel string panel_name = PREFIX + PANEL; //--- Defining main panel name createRectangle(panel_name, settings.panel_x, settings.panel_y, panel_width, (num_rows + 3) * settings.row_height, settings.bg_color, settings.border_color); //--- Creating main panel // Creating header panel string header_panel = PREFIX + HEADER_PANEL; //--- Defining header panel name createRectangle(header_panel, settings.panel_x, settings.panel_y, panel_width, settings.row_height, settings.section_bg_color, settings.border_color); //--- Creating header panel // Creating header title createLABEL(PREFIX + HEADER_PANEL_TEXT, "Trading Dashboard", settings.panel_x + 10, settings.panel_y + 8 + settings.label_y_offset, clrBlack, 14, settings.font, ANCHOR_LEFT, "Dashboard Title"); //--- Creating header title // Creating export button createLABEL(PREFIX + EXPORT_BUTTON, CharToString(60), settings.panel_x + panel_width - 90, settings.panel_y + 12, clrBlack, 18, "Wingdings", ANCHOR_CENTER, "Click to export or press 'E' key to export", true); //--- Creating export button // Creating toggle button createLABEL(PREFIX + TOGGLE_BUTTON, CharToString('r'), settings.panel_x + panel_width - 60, settings.panel_y + 12, clrBlack, 18, "Wingdings", ANCHOR_CENTER, "Minimize dashboard", true); //--- Creating toggle button // Creating close button createLABEL(PREFIX + CLOSE_BUTTON, CharToString('r'), settings.panel_x + panel_width - 30, settings.panel_y + 12, clrBlack, 18, "Webdings", ANCHOR_CENTER, "Close dashboard", true); //--- Creating close button // Creating headers int header_y = settings.panel_y + settings.row_height + 8 + settings.label_y_offset; //--- Calculating header y-coordinate for(int i = 0; i < num_columns; i++) { //--- Iterating through headers string header_name = PREFIX + HEADER + IntegerToString(i); //--- Defining header label name int header_x = settings.panel_x + settings.header_x_distances[i] + settings.label_x_offset; //--- Calculating header x-coordinate createLABEL(header_name, headers[i], header_x, header_y, settings.header_color, 12, settings.font, ANCHOR_LEFT, "Click to sort", true); //--- Creating header label } // Creating symbol and data labels int first_row_y = header_y + settings.row_height; //--- Calculating first row y-coordinate int symbol_x = settings.panel_x + 10 + settings.label_x_offset; //--- Setting symbol x-coordinate for(int i = 0; i < num_rows; i++) { //--- Iterating through rows string symbol_name = PREFIX + SYMB + IntegerToString(i); //--- Defining symbol label name createLABEL(symbol_name, symbol_data[i].name, symbol_x, first_row_y + i * settings.row_height + settings.label_y_offset, settings.text_color, settings.font_size, settings.font, ANCHOR_LEFT, "Symbol name"); //--- Creating symbol label int x_offset = settings.panel_x + 10 + column_widths[0] + settings.label_x_offset; //--- Setting data x-offset for(int j = 0; j < num_columns - 1; j++) { //--- Iterating through data columns string data_name = PREFIX + DATA + IntegerToString(i) + "_" + IntegerToString(j); //--- Defining data label name color initial_color = data_default_colors[j]; //--- Setting initial color string initial_txt = (j <= 2 || j == 5) ? "0" : "0.00"; //--- Setting initial text createLABEL(data_name, initial_txt, x_offset, first_row_y + i * settings.row_height + settings.label_y_offset, initial_color, settings.font_size, settings.font, ANCHOR_RIGHT, "Data value"); //--- Creating data label x_offset += column_widths[j + 1]; //--- Updating x-offset } } // Creating footer panel int footer_y = settings.panel_y + (num_rows + 3) * settings.row_height - settings.row_height; //--- Calculating footer y-coordinate string footer_panel = PREFIX + FOOTER_PANEL; //--- Defining footer panel name createRectangle(footer_panel, settings.panel_x, footer_y, panel_width, settings.row_height, settings.section_bg_color, settings.border_color); //--- Creating footer panel // Creating footer text and data int footer_text_x = settings.panel_x + 10 + settings.label_x_offset; //--- Setting footer text x-coordinate createLABEL(PREFIX + FOOTER_TEXT, "Total:", footer_text_x, footer_y + 8 + settings.label_y_offset, settings.text_color, settings.font_size, settings.font, ANCHOR_LEFT, "Totals"); //--- Creating footer text label int x_offset = settings.panel_x + 10 + column_widths[0] + settings.label_x_offset; //--- Setting footer data x-offset for(int j = 0; j < num_columns - 1; j++) { //--- Iterating through footer data string footer_data_name = PREFIX + FOOTER_DATA + IntegerToString(j); //--- Defining footer data label name color footer_color = data_default_colors[j]; //--- Setting footer data color string initial_txt = (j <= 2 || j == 5) ? "0" : "0.00"; //--- Setting initial text createLABEL(footer_data_name, initial_txt, x_offset, footer_y + 8 + settings.label_y_offset, footer_color, settings.font_size, settings.font, ANCHOR_RIGHT, "Total value"); //--- Creating footer data label x_offset += column_widths[j + 1]; //--- Updating x-offset } // Creating account panel int account_panel_y = footer_y + settings.row_height + 5; //--- Calculating account panel y-coordinate string account_panel_name = PREFIX + ACCOUNT_PANEL; //--- Defining account panel name createRectangle(account_panel_name, settings.panel_x, account_panel_y, panel_width, settings.row_height, settings.section_bg_color, settings.border_color); //--- Creating account panel // Creating account text and data labels int acc_x = settings.panel_x + 10 + settings.label_x_offset; //--- Setting account label x-coordinate int acc_data_offset = 160; //--- Setting data offset int acc_spacing = (panel_width - 45) / ArraySize(account_items); //--- Calculating spacing for(int k = 0; k < ArraySize(account_items); k++) { //--- Iterating through account items string acc_text_name = PREFIX + ACC_TEXT + IntegerToString(k); //--- Defining account text label name int text_x = acc_x + k * acc_spacing; //--- Calculating text x-coordinate createLABEL(acc_text_name, account_items[k] + ":", text_x, account_panel_y + 8 + settings.label_y_offset, settings.text_color, settings.font_size, settings.font, ANCHOR_LEFT, "Account info"); //--- Creating account text label string acc_data_name = PREFIX + ACC_DATA + IntegerToString(k); //--- Defining account data label name int data_x = text_x + acc_data_offset; //--- Calculating data x-coordinate createLABEL(acc_data_name, "0.00", data_x, account_panel_y + 8 + settings.label_y_offset, settings.text_color, settings.font_size, settings.font, ANCHOR_RIGHT, "Account value"); //--- Creating account data label } ChartRedraw(0); //--- Redrawing chart } //+------------------------------------------------------------------+ //| Creating minimized dashboard UI | //+------------------------------------------------------------------+ void createMinimizedDashboard() { int num_columns = ArraySize(headers); //--- Getting number of columns int column_width_sum = 0; //--- Initializing column width sum for(int i = 0; i < num_columns; i++) //--- Iterating through columns column_width_sum += column_widths[i]; //--- Adding column width int panel_width = MathMax(settings.header_x_distances[num_columns - 1] + column_widths[num_columns - 1], column_width_sum) + 20 + settings.label_x_offset; //--- Calculating panel width // Creating header panel createRectangle(PREFIX + HEADER_PANEL, settings.panel_x, settings.panel_y, panel_width, settings.row_height, settings.section_bg_color, settings.border_color); //--- Creating header panel // Creating header title createLABEL(PREFIX + HEADER_PANEL_TEXT, "Trading Dashboard", settings.panel_x + 10, settings.panel_y + 8 + settings.label_y_offset, clrBlack, 14, settings.font, ANCHOR_LEFT, "Dashboard Title"); //--- Creating header title // Creating export button createLABEL(PREFIX + EXPORT_BUTTON, CharToString(60), settings.panel_x + panel_width - 90, settings.panel_y + 12, clrBlack, 18, "Wingdings", ANCHOR_CENTER, "Click to export or press 'E' key to export", true); //--- Creating export button // Creating toggle button (maximize) createLABEL(PREFIX + TOGGLE_BUTTON, CharToString('o'), settings.panel_x + panel_width - 60, settings.panel_y + 12, clrBlack, 18, "Wingdings", ANCHOR_CENTER, "Maximize dashboard", true); //--- Creating toggle button // Creating close button createLABEL(PREFIX + CLOSE_BUTTON, CharToString('r'), settings.panel_x + panel_width - 30, settings.panel_y + 12, clrBlack, 18, "Webdings", ANCHOR_CENTER, "Close dashboard", true); //--- Creating close button ChartRedraw(0); //--- Redrawing chart } //+------------------------------------------------------------------+ //| Deleting all dashboard objects | //+------------------------------------------------------------------+ void deleteAllObjects() { ObjectsDeleteAll(0, PREFIX, -1, -1); //--- Deleting all objects with prefix }
Wir implementieren die Funktionen „createFullDashboard“, „createMinimizedDashboard“ und „deleteAllObjects“ zur Verwaltung der Nutzeroberfläche, die vollständige und minimierte Ansichten mit interaktiven Elementen unterstützt. In „createFullDashboard“ rufen wir „CollectActiveSymbols“ auf, um „symbol_data“ zu füllen, „num_rows“ und „num_columns“ mit ArraySize zu berechnen und „panel_width“ mit „column_widths“ und „settings.header_x_distances“. Wir erstellen das Hauptpanel mit „createRectangle“ für „PREFIX + PANEL“, das Kopfpanel mit „PREFIX + HEADER_PANEL“ und den Kopftitel mit „createLABEL“ für „PREFIX + HEADER_PANEL_TEXT“ als „Trading Dashboard“.
Wir fügen Schaltflächen mit „createLABEL“ für „PREFIX + EXPORT_BUTTON“ (Wingdings 60), „PREFIX + TOGGLE_BUTTON“ (Wingdings 'r' für minimieren) und „PREFIX + CLOSE_BUTTON“ (Webdings 'r') hinzu, alle mit spezifischen Tooltips und auswählbar true. Die Wahl der Icon-Stile hängt von Ihnen ab. Hier finden Sie eine kompakte Übersicht über die möglichen Schriftarten, die Sie verwenden können. Verwenden Sie einfach das richtige Symbol und den richtigen Zeichentyp.

Dann erstellen wir Kopfzeilenbeschriftungen für „headers“ an den berechneten Positionen, Symbolbeschriftungen für „symbol_data[i].name“, Datenetiketten mit Anfangswerten, Fußzeilenfeld mit „PREFIX + FOOTER_PANEL“, Fußzeilentext „Total:“, Fußzeilenbeschriftungen, Kontopanel mit „PREFIX + ACCOUNT_PANEL“ und Kontobeschriftungen für „account_items“, alles mit „createRectangle“ und „createLABEL“ mit entsprechenden Koordinaten und Farben, gefolgt von der Funktion ChartRedraw.
In „createMinimizedDashboard“ erstellen wir eine kompakte Nutzeroberfläche, die nur die Kopfzeile mit „createRectangle“ für „PREFIX + HEADER_PANEL“, die Überschrift mit „createLABEL“ für „PREFIX + HEADER_PANEL_TEXT“ und Schaltflächen für Export (Wingdings 60), Umschalten (Wingdings 'o' für Maximieren) und Schließen (Webdings 'r'), um die Bildschirmnutzung und das Neuzeichnen zu minimieren.
Die Funktion „deleteAllObjects“ entfernt alle Dashboard-Objekte mit ObjectsDeleteAll unter Verwendung von „PREFIX“ für alle Charts und Typen und sorgt so für eine saubere Basis für UI-Aktualisierungen oder das Schließen. Diese Funktionen ermöglichen ein flexibles Dashboard mit vollen und minimierten Zuständen und unterstützen Nutzerinteraktionen wie Ziehen und Umschalten. Wir werden nun fortfahren, die Dashboard-Funktion mit Hilfe dieser dynamischen Funktionen zu aktualisieren.
//+------------------------------------------------------------------+ //| Updating dashboard data and visuals | //+------------------------------------------------------------------+ void UpdateDashboard() { bool needs_redraw = false; //--- Initializing redraw flag CollectActiveSymbols(); //--- Collecting active symbols int current_num = ArraySize(symbol_data); //--- Getting current number of symbols if(current_num != prev_num_symbols) { //--- Checking if symbol count changed deleteAllObjects(); //--- Deleting all objects if(panel_minimized) { //--- Checking if minimized createMinimizedDashboard(); //--- Creating minimized dashboard } else { createFullDashboard(); //--- Creating full dashboard } prev_num_symbols = current_num; //--- Updating previous symbol count needs_redraw = true; //--- Setting redraw flag } if(!panel_is_visible || panel_minimized) return; //--- Exiting if not visible or minimized // Resetting totals totalBuys = 0; //--- Resetting total buys totalSells = 0; //--- Resetting total sells totalTrades = 0; //--- Resetting total trades totalLots = 0.0; //--- Resetting total lots totalProfit = 0.0; //--- Resetting total profit totalPending = 0; //--- Resetting total pending totalSwap = 0.0; //--- Resetting total swap totalComm = 0.0; //--- Resetting total commission // Calculating symbol data and totals for(int i = 0; i < current_num; i++) { //--- Iterating through symbols string symbol = symbol_data[i].name; //--- Getting symbol name for(int j = 0; j < 8; j++) { //--- Iterating through data columns string value = ""; //--- Initializing value color data_color = data_default_colors[j]; //--- Setting default color double dval = 0.0; //--- Initializing double value int ival = 0; //--- Initializing integer value switch(j) { //--- Handling data type case 0: // Buy positions value = countPositions(symbol, POSITION_TYPE_BUY); //--- Getting buy positions ival = (int)StringToInteger(value); //--- Converting to integer if(value != symbol_data[i].buys_str) { //--- Checking if changed symbol_data[i].buys_str = value; //--- Updating buys string symbol_data[i].buys = ival; //--- Updating buys count } totalBuys += ival; //--- Adding to total buys break; case 1: // Sell positions value = countPositions(symbol, POSITION_TYPE_SELL); //--- Getting sell positions ival = (int)StringToInteger(value); //--- Converting to integer if(value != symbol_data[i].sells_str) { //--- Checking if changed symbol_data[i].sells_str = value; //--- Updating sells string symbol_data[i].sells = ival; //--- Updating sells count } totalSells += ival; //--- Adding to total sells break; case 2: // Total trades value = countPositionsTotal(symbol); //--- Getting total trades ival = (int)StringToInteger(value); //--- Converting to integer if(value != symbol_data[i].trades_str) { //--- Checking if changed symbol_data[i].trades_str = value; //--- Updating trades string symbol_data[i].trades = ival; //--- Updating trades count } totalTrades += ival; //--- Adding to total trades break; case 3: // Lots value = sumPositionDouble(symbol, POSITION_VOLUME); //--- Getting total lots dval = StringToDouble(value); //--- Converting to double if(value != symbol_data[i].lots_str) { //--- Checking if changed symbol_data[i].lots_str = value; //--- Updating lots string symbol_data[i].lots = dval; //--- Updating lots value } totalLots += dval; //--- Adding to total lots break; case 4: // Profit value = sumPositionDouble(symbol, POSITION_PROFIT); //--- Getting total profit dval = StringToDouble(value); //--- Converting to double data_color = (dval > 0) ? clrGreen : (dval < 0) ? clrRed : clrGray; //--- Setting color based on value if(value != symbol_data[i].profit_str) { //--- Checking if changed symbol_data[i].profit_str = value; //--- Updating profit string symbol_data[i].profit = dval; //--- Updating profit value } totalProfit += dval; //--- Adding to total profit break; case 5: // Pending value = countOrders(symbol); //--- Getting pending orders ival = (int)StringToInteger(value); //--- Converting to integer if(value != symbol_data[i].pending_str) { //--- Checking if changed symbol_data[i].pending_str = value; //--- Updating pending string symbol_data[i].pending = ival; //--- Updating pending count } totalPending += ival; //--- Adding to total pending break; case 6: // Swap value = sumPositionDouble(symbol, POSITION_SWAP); //--- Getting total swap dval = StringToDouble(value); //--- Converting to double data_color = (dval > 0) ? clrGreen : (dval < 0) ? clrRed : clrPurple; //--- Setting color based on value if(value != symbol_data[i].swaps_str) { //--- Checking if changed symbol_data[i].swaps_str = value; //--- Updating swap string symbol_data[i].swaps = dval; //--- Updating swap value } totalSwap += dval; //--- Adding to total swap break; case 7: // Comm dval = sumPositionCommission(symbol); //--- Getting total commission value = DoubleToString(dval, 2); //--- Formatting commission data_color = (dval > 0) ? clrGreen : (dval < 0) ? clrRed : clrBrown; //--- Setting color based on value if(value != symbol_data[i].comm_str) { //--- Checking if changed symbol_data[i].comm_str = value; //--- Updating commission string symbol_data[i].comm = dval; //--- Updating commission value } totalComm += dval; //--- Adding to total commission break; } } } // Sort after calculating values SortDashboard(); //--- Sorting dashboard data // Update header breathing effect glow_counter += MathMax(UpdateIntervalMs, 10); //--- Incrementing glow counter if(glow_counter >= GLOW_INTERVAL_MS) { //--- Checking if glow interval reached if(glow_direction) { //--- Checking if glowing forward glow_index++; //--- Incrementing glow index if(glow_index >= ArraySize(settings.header_shades) - 1) //--- Checking if at end glow_direction = false; //--- Reversing glow direction } else { //--- Glow backward glow_index--; //--- Decrementing glow index if(glow_index <= 0) //--- Checking if at start glow_direction = true; //--- Reversing glow direction } glow_counter = 0; //--- Resetting glow counter } color header_shade = settings.header_shades[glow_index]; //--- Getting current header shade for(int i = 0; i < ArraySize(headers); i++) { //--- Iterating through headers string header_name = PREFIX + HEADER + IntegerToString(i); //--- Defining header name ObjectSetInteger(0, header_name, OBJPROP_COLOR, header_shade); //--- Updating header color needs_redraw = true; //--- Setting redraw flag } // Update symbol and data labels bool labels_updated = false; //--- Initializing label update flag for(int i = 0; i < current_num; i++) { //--- Iterating through symbols string symbol = symbol_data[i].name; //--- Getting symbol name string symb_name = PREFIX + SYMB + IntegerToString(i); //--- Defining symbol label name string current_symb_txt = ObjectGetString(0, symb_name, OBJPROP_TEXT); //--- Getting current symbol text if(current_symb_txt != symbol) { //--- Checking if symbol changed ObjectSetString(0, symb_name, OBJPROP_TEXT, symbol); //--- Updating symbol text labels_updated = true; //--- Setting label updated flag } for(int j = 0; j < 8; j++) { //--- Iterating through data columns string data_name = PREFIX + DATA + IntegerToString(i) + "_" + IntegerToString(j); //--- Defining data label name string value; //--- Initializing value color data_color = data_default_colors[j]; //--- Setting default color switch(j) { //--- Handling data type case 0: //--- Buy positions value = symbol_data[i].buys_str; //--- Getting buys string data_color = clrRed; //--- Setting color to red break; case 1: //--- Sell positions value = symbol_data[i].sells_str; //--- Getting sells string data_color = clrGreen; //--- Setting color to green break; case 2: // Total trades value = symbol_data[i].trades_str; data_color = clrDarkGray; break; case 3: //--- Lots value = symbol_data[i].lots_str; //--- Getting lots string data_color = clrOrange; //--- Setting color to orange break; case 4: //--- Profit value = symbol_data[i].profit_str; //--- Getting profit string data_color = (symbol_data[i].profit > 0) ? clrGreen : (symbol_data[i].profit < 0) ? clrRed : clrGray; //--- Setting color based on profit break; case 5: //--- Pending value = symbol_data[i].pending_str; //--- Getting pending string data_color = clrBlue; //--- Setting color to blue break; case 6: //--- Swap value = symbol_data[i].swaps_str; //--- Getting swap string data_color = (symbol_data[i].swaps > 0) ? clrGreen : (symbol_data[i].swaps < 0) ? clrRed : clrPurple; //--- Setting color based on swap break; case 7: //--- Comm value = symbol_data[i].comm_str; //--- Getting commission string data_color = (symbol_data[i].comm > 0) ? clrGreen : (symbol_data[i].comm < 0) ? clrRed : clrBrown; //--- Setting color based on commission break; } if(updateLABEL(data_name, value, data_color)) labels_updated = true; //--- Updating label if changed } } if(labels_updated) needs_redraw = true; //--- Setting redraw flag if labels updated // Updating totals string new_total_buys = IntegerToString(totalBuys); //--- Formatting total buys if(new_total_buys != total_buys_str) { //--- Checking if changed total_buys_str = new_total_buys; //--- Updating buys string if(updateLABEL(PREFIX + FOOTER_DATA + "0", new_total_buys, clrRed)) needs_redraw = true; //--- Updating label } string new_total_sells = IntegerToString(totalSells); //--- Formatting total sells if(new_total_sells != total_sells_str) { //--- Checking if changed total_sells_str = new_total_sells; //--- Updating sells string if(updateLABEL(PREFIX + FOOTER_DATA + "1", new_total_sells, clrGreen)) needs_redraw = true; //--- Updating label } string new_total_trades = IntegerToString(totalTrades); //--- Formatting total trades if(new_total_trades != total_trades_str) { //--- Checking if changed total_trades_str = new_total_trades; //--- Updating trades string if(updateLABEL(PREFIX + FOOTER_DATA + "2", new_total_trades, clrDarkGray)) needs_redraw = true; //--- Updating label } string new_total_lots = DoubleToString(totalLots, 2); //--- Formatting total lots if(new_total_lots != total_lots_str) { //--- Checking if changed total_lots_str = new_total_lots; //--- Updating lots string if(updateLABEL(PREFIX + FOOTER_DATA + "3", new_total_lots, clrOrange)) needs_redraw = true; //--- Updating label } string new_total_profit = DoubleToString(totalProfit, 2); //--- Formatting total profit color total_profit_color = (totalProfit > 0) ? clrGreen : (totalProfit < 0) ? clrRed : clrGray; //--- Setting color based on profit if(new_total_profit != total_profit_str) { //--- Checking if changed total_profit_str = new_total_profit; //--- Updating profit string if(updateLABEL(PREFIX + FOOTER_DATA + "4", new_total_profit, total_profit_color)) needs_redraw = true; //--- Updating label } string new_total_pending = IntegerToString(totalPending); //--- Formatting total pending if(new_total_pending != total_pending_str) { //--- Checking if changed total_pending_str = new_total_pending; //--- Updating pending string if(updateLABEL(PREFIX + FOOTER_DATA + "5", new_total_pending, clrBlue)) needs_redraw = true; //--- Updating label } string new_total_swap = DoubleToString(totalSwap, 2); //--- Formatting total swap color total_swap_color = (totalSwap > 0) ? clrGreen : (totalSwap < 0) ? clrRed : clrPurple; //--- Setting color based on swap if(new_total_swap != total_swap_str) { //--- Checking if changed total_swap_str = new_total_swap; //--- Updating swap string if(updateLABEL(PREFIX + FOOTER_DATA + "6", new_total_swap, total_swap_color)) needs_redraw = true; //--- Updating label } string new_total_comm = DoubleToString(totalComm, 2); //--- Formatting total commission color total_comm_color = (totalComm > 0) ? clrGreen : (totalComm < 0) ? clrRed : clrBrown; //--- Setting color based on commission if(new_total_comm != total_comm_str) { //--- Checking if changed total_comm_str = new_total_comm; //--- Updating commission string if(updateLABEL(PREFIX + FOOTER_DATA + "7", new_total_comm, total_comm_color)) needs_redraw = true; //--- Updating label } // Updating account info double balance = AccountInfoDouble(ACCOUNT_BALANCE); //--- Getting account balance double equity = AccountInfoDouble(ACCOUNT_EQUITY); //--- Getting account equity double free_margin = AccountInfoDouble(ACCOUNT_MARGIN_FREE); //--- Getting free margin string new_bal = DoubleToString(balance, 2); //--- Formatting balance if(new_bal != acc_bal_str) { //--- Checking if changed acc_bal_str = new_bal; //--- Updating balance string if(updateLABEL(PREFIX + ACC_DATA + "0", new_bal, clrBlack)) needs_redraw = true; //--- Updating label } string new_eq = DoubleToString(equity, 2); //--- Formatting equity color eq_color = (equity > balance) ? clrGreen : (equity < balance) ? clrRed : clrBlack; //--- Setting color based on equity if(new_eq != acc_eq_str) { //--- Checking if changed acc_eq_str = new_eq; //--- Updating equity string if(updateLABEL(PREFIX + ACC_DATA + "1", new_eq, eq_color)) needs_redraw = true; //--- Updating label } string new_free = DoubleToString(free_margin, 2); //--- Formatting free margin if(new_free != acc_free_str) { //--- Checking if changed acc_free_str = new_free; //--- Updating free margin string if(updateLABEL(PREFIX + ACC_DATA + "2", new_free, clrBlack)) needs_redraw = true; //--- Updating label } if(needs_redraw) { //--- Checking if redraw needed ChartRedraw(0); //--- Redrawing chart } }
Der Funktion „UpdateDashboard“ fügen wir am Anfang einen Aufruf an „CollectActiveSymbols“ hinzu, sodass wir die Felder für die Summen und dem Saldo immer aktualisieren. Wenn sich dann die Anzahl der Symbole ändert, rufen wir die Funktion „deleteAllObjects“ auf, um das Dashboard zu zerstören und es mit der Funktion „createFullDashboard“ oder „createMinimizedDashboard“ neu zu erstellen. In der Datenberechnungsschleife haben wir eine Farblogik für Gewinn/Tausch/Provision hinzugefügt, die vorher nur teilweise vorhanden war. Wir haben die Bereiche, in denen sich etwas ändert, hervorgehoben, damit sie leichter zu erkennen sind und mehr Klarheit herrscht. Schließlich können wir nun unsere Logik bei der Initialisierung aufrufen, um das Erreichen des Meilensteins zu sehen.
//+------------------------------------------------------------------+ //| Initializing expert | //+------------------------------------------------------------------+ int OnInit() { createFullDashboard(); //--- Creating full dashboard prev_num_symbols = ArraySize(symbol_data); //--- Setting initial symbol count ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true); //--- Enabling mouse move events EventSetMillisecondTimer(MathMax(UpdateIntervalMs, 10)); //--- Setting timer with minimum 10ms UpdateDashboard(); //--- Updating dashboard return(INIT_SUCCEEDED); //--- Returning success } //+------------------------------------------------------------------+ //| Deinitializing expert | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { deleteAllObjects(); //--- Deleting all objects ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false); //--- Disabling mouse move events EventKillTimer(); //--- Stopping timer } //+------------------------------------------------------------------+ //| Handling timer for millisecond-based updates | //+------------------------------------------------------------------+ void OnTimer() { if(panel_is_visible && !panel_minimized) { //--- Checking if visible and not minimized UpdateDashboard(); //--- Updating dashboard } }
Hier implementieren wir die Ereignisbehandlungen von OnInit, OnDeinit und OnTimer, um den Lebenszyklus und die Aktualisierungen des Dashboards zu verwalten und seine interaktive und dynamische Funktionalität zu ermöglichen. In der Funktion „OnInit“ rufen wir „createFullDashboard“ auf, um die komplette Nutzeroberfläche zu erstellen, setzen „prev_num_symbols“ auf die Größe von „symbol_data“ und setzen „prev_num_symbols“ auf die Größe von „symbol_data“ mit ArraySize, um die anfänglichen Symbole zu verfolgen, aktivieren Mausbewegungsereignisse mit ChartSetInteger und setzen CHART_EVENT_MOUSE_MOVE auf true für Zieh- und Schwebeeffekte, setzen einen Timer mit EventSetMillisecondTimer und verwenden das Maximum von „UpdateIntervalMs“ und 10ms für periodische Aktualisierungen, und rufen Sie „UpdateDashboard“ auf, um die anfänglichen Daten aufzufüllen und geben Sie „INIT_SUCCEEDED“ für eine erfolgreiche Initialisierung zurück.
Die Funktion OnDeinit räumt auf, indem sie „deleteAllObjects“ aufruft, um alle Dashboard-Objekte mit „PREFIX“ zu entfernen, Mausbewegungsereignisse mit „ChartSetInteger“ deaktiviert, „CHART_EVENT_MOUSE_MOVE“ auf false setzt und den Timer mit EventKillTimer anhält, um Ressourcen freizugeben.
In der Funktion OnTimer wird geprüft, ob panel_is_visible“ wahr und „panel_minimized“ falsch ist, und dann „UpdateDashboard“ aufgerufen, um die Daten nur dann zu aktualisieren, wenn das Dashboard vollständig sichtbar ist, wodurch effiziente Aktualisierungen ohne Verarbeitung im minimierten oder ausgeblendeten Zustand gewährleistet werden. Nach der Kompilierung erhalten wir folgendes Ergebnis.

Auf dem Bild können wir sehen, dass die neuen Funktionen erfolgreich auftauchen. Wir werden nun eine Funktion zum Aktualisieren der Panelpositionen beim Ziehen erstellen, um zu vermeiden, dass die Objekte neu erstellt werden.
//+------------------------------------------------------------------+ //| Updating panel object positions | //+------------------------------------------------------------------+ void updatePanelPositions() { int num_rows = ArraySize(symbol_data); //--- Getting number of rows int num_columns = ArraySize(headers); //--- Getting number of columns int column_width_sum = 0; //--- Initializing column width sum for(int i = 0; i < num_columns; i++) //--- Iterating through columns column_width_sum += column_widths[i]; //--- Adding column width int panel_width = MathMax(settings.header_x_distances[num_columns - 1] + column_widths[num_columns - 1], column_width_sum) + 20 + settings.label_x_offset; //--- Calculating panel width // Updating header panel and buttons ObjectSetInteger(0, PREFIX + HEADER_PANEL, OBJPROP_XDISTANCE, settings.panel_x); //--- Updating header panel x-coordinate ObjectSetInteger(0, PREFIX + HEADER_PANEL, OBJPROP_YDISTANCE, settings.panel_y); //--- Updating header panel y-coordinate ObjectSetInteger(0, PREFIX + HEADER_PANEL_TEXT, OBJPROP_XDISTANCE, settings.panel_x + 10); //--- Updating header text x-coordinate ObjectSetInteger(0, PREFIX + HEADER_PANEL_TEXT, OBJPROP_YDISTANCE, settings.panel_y + 8 + settings.label_y_offset); //--- Updating header text y-coordinate ObjectSetInteger(0, PREFIX + EXPORT_BUTTON, OBJPROP_XDISTANCE, settings.panel_x + panel_width - 90); //--- Updating export button x-coordinate ObjectSetInteger(0, PREFIX + EXPORT_BUTTON, OBJPROP_YDISTANCE, settings.panel_y + 12); //--- Updating export button y-coordinate ObjectSetInteger(0, PREFIX + TOGGLE_BUTTON, OBJPROP_XDISTANCE, settings.panel_x + panel_width - 60); //--- Updating toggle button x-coordinate ObjectSetInteger(0, PREFIX + TOGGLE_BUTTON, OBJPROP_YDISTANCE, settings.panel_y + 12); //--- Updating toggle button y-coordinate ObjectSetInteger(0, PREFIX + CLOSE_BUTTON, OBJPROP_XDISTANCE, settings.panel_x + panel_width - 30); //--- Updating close button x-coordinate ObjectSetInteger(0, PREFIX + CLOSE_BUTTON, OBJPROP_YDISTANCE, settings.panel_y + 12); //--- Updating close button y-coordinate if(!panel_minimized) { //--- Checking if not minimized // Updating main panel ObjectSetInteger(0, PREFIX + PANEL, OBJPROP_XDISTANCE, settings.panel_x); //--- Updating main panel x-coordinate ObjectSetInteger(0, PREFIX + PANEL, OBJPROP_YDISTANCE, settings.panel_y); //--- Updating main panel y-coordinate // Updating headers int header_y = settings.panel_y + settings.row_height + 8 + settings.label_y_offset; //--- Calculating header y-coordinate for(int i = 0; i < num_columns; i++) { //--- Iterating through headers string header_name = PREFIX + HEADER + IntegerToString(i); //--- Defining header name int header_x = settings.panel_x + settings.header_x_distances[i] + settings.label_x_offset; //--- Calculating header x-coordinate ObjectSetInteger(0, header_name, OBJPROP_XDISTANCE, header_x); //--- Updating header x-coordinate ObjectSetInteger(0, header_name, OBJPROP_YDISTANCE, header_y); //--- Updating header y-coordinate } // Updating symbol and data labels int first_row_y = header_y + settings.row_height; //--- Calculating first row y-coordinate int symbol_x = settings.panel_x + 10 + settings.label_x_offset; //--- Setting symbol x-coordinate for(int i = 0; i < num_rows; i++) { //--- Iterating through rows string symbol_name = PREFIX + SYMB + IntegerToString(i); //--- Defining symbol label name ObjectSetInteger(0, symbol_name, OBJPROP_XDISTANCE, symbol_x); //--- Updating symbol x-coordinate ObjectSetInteger(0, symbol_name, OBJPROP_YDISTANCE, first_row_y + i * settings.row_height + settings.label_y_offset); //--- Updating symbol y-coordinate int x_offset = settings.panel_x + 10 + column_widths[0] + settings.label_x_offset; //--- Setting data x-offset for(int j = 0; j < num_columns - 1; j++) { //--- Iterating through data columns string data_name = PREFIX + DATA + IntegerToString(i) + "_" + IntegerToString(j); //--- Defining data label name ObjectSetInteger(0, data_name, OBJPROP_XDISTANCE, x_offset); //--- Updating data x-coordinate ObjectSetInteger(0, data_name, OBJPROP_YDISTANCE, first_row_y + i * settings.row_height + settings.label_y_offset); //--- Updating data y-coordinate x_offset += column_widths[j + 1]; //--- Updating x-offset } } // Updating footer panel and labels int footer_y = settings.panel_y + (num_rows + 3) * settings.row_height - settings.row_height; //--- Calculating footer y-coordinate ObjectSetInteger(0, PREFIX + FOOTER_PANEL, OBJPROP_XDISTANCE, settings.panel_x); //--- Updating footer panel x-coordinate ObjectSetInteger(0, PREFIX + FOOTER_PANEL, OBJPROP_YDISTANCE, footer_y); //--- Updating footer panel y-coordinate ObjectSetInteger(0, PREFIX + FOOTER_TEXT, OBJPROP_XDISTANCE, settings.panel_x + 10 + settings.label_x_offset); //--- Updating footer text x-coordinate ObjectSetInteger(0, PREFIX + FOOTER_TEXT, OBJPROP_YDISTANCE, footer_y + 8 + settings.label_y_offset); //--- Updating footer text y-coordinate int x_offset = settings.panel_x + 10 + column_widths[0] + settings.label_x_offset; //--- Setting footer data x-offset for(int j = 0; j < num_columns - 1; j++) { //--- Iterating through footer data string footer_data_name = PREFIX + FOOTER_DATA + IntegerToString(j); //--- Defining footer data name ObjectSetInteger(0, footer_data_name, OBJPROP_XDISTANCE, x_offset); //--- Updating footer data x-coordinate ObjectSetInteger(0, footer_data_name, OBJPROP_YDISTANCE, footer_y + 8 + settings.label_y_offset); //--- Updating footer data y-coordinate x_offset += column_widths[j + 1]; //--- Updating x-offset } // Updating account panel and labels int account_panel_y = footer_y + settings.row_height + 5; //--- Calculating account panel y-coordinate ObjectSetInteger(0, PREFIX + ACCOUNT_PANEL, OBJPROP_XDISTANCE, settings.panel_x); //--- Updating account panel x-coordinate ObjectSetInteger(0, PREFIX + ACCOUNT_PANEL, OBJPROP_YDISTANCE, account_panel_y); //--- Updating account panel y-coordinate int acc_x = settings.panel_x + 10 + settings.label_x_offset; //--- Setting account label x-coordinate int acc_data_offset = 160; //--- Setting data offset int acc_spacing = (panel_width - 45) / ArraySize(account_items); //--- Calculating spacing for(int k = 0; k < ArraySize(account_items); k++) { //--- Iterating through account items string acc_text_name = PREFIX + ACC_TEXT + IntegerToString(k); //--- Defining account text name int text_x = acc_x + k * acc_spacing; //--- Calculating text x-coordinate ObjectSetInteger(0, acc_text_name, OBJPROP_XDISTANCE, text_x); //--- Updating account text x-coordinate ObjectSetInteger(0, acc_text_name, OBJPROP_YDISTANCE, account_panel_y + 8 + settings.label_y_offset); //--- Updating account text y-coordinate string acc_data_name = PREFIX + ACC_DATA + IntegerToString(k); //--- Defining account data name int data_x = text_x + acc_data_offset; //--- Calculating data x-coordinate ObjectSetInteger(0, acc_data_name, OBJPROP_XDISTANCE, data_x); //--- Updating account data x-coordinate ObjectSetInteger(0, acc_data_name, OBJPROP_YDISTANCE, account_panel_y + 8 + settings.label_y_offset); //--- Updating account data y-coordinate } } ChartRedraw(0); //--- Redrawing chart }
Hier implementieren wir die Funktion „updatePanelPositions“, um die Funktion zum Ziehen des erweiterten Dashboards zu aktivieren und sicherzustellen, dass sich alle UI-Elemente zusammenhängend bewegen, wenn das Dashboard verschoben wird. Wir berechnen „num_rows“ und „num_columns“ mit „ArraySize“ auf „symbol_data“ und „headers“, und berechnen „panel_width“ durch Summierung von „column_widths“ und Verwendung von MathMax mit „settings.header_x_distances“ plus padding. Wir aktualisieren die Kopfleiste und die Schaltflächen, indem wir OBJPROP_XDISTANCE und „OBJPROP_YDISTANCE“ für „PREFIX + HEADER_PANEL“, „PREFIX + HEADER_PANEL_TEXT“, „PREFIX + EXPORT_BUTTON“, „PREFIX + TOGGLE_BUTTON“ und „PREFIX + CLOSE_BUTTON“ unter Verwendung von ObjectSetInteger mit „settings.panel_x“ und „settings.panel_y“ Koordinaten.
Wenn „panel_minimized“ falsch ist, wird die Position des Hauptpanels mit „PREFIX + PANEL“ aktualisiert, Kopfzeilen an „header_y“, berechnet aus „settings.panel_y + settings.row_height + 8 + settings.label_y_offset“, Symbol- und Datenbeschriftungen an „first_row_y“ mit „symbol_x“ und „x_offset“ angepasst durch „column_widths“, Fußbereich und Beschriftungen an „footer_y“ berechnet für „num_rows + 3“, und Kontopanel und Beschriftungen bei „account_panel_y“ mit „acc_x“ und „acc_spacing“ für die Ausrichtung, alles unter Verwendung der ObjectSetInteger-Funktion. Wir rufen ChartRedraw auf, um die Anzeige zu aktualisieren. Dadurch wird sichergestellt, dass das gesamte Armaturenbrett beim Ziehen nahtlos verschoben wird und das Layout erhalten bleibt. Wir müssen eine Logik definieren, um die Position des Cursors über der Kopfzeile oder den Schaltflächen für die Berücksichtigung des Hovers zu kontrollieren. Hier ist die Logik, die wir verwendet haben, um das umzusetzen.
//+------------------------------------------------------------------+ //| Checking if cursor is inside header or buttons | //+------------------------------------------------------------------+ bool isCursorInHeaderOrButtons(int mouse_x, int mouse_y) { int header_x = (int)ObjectGetInteger(0, PREFIX + HEADER_PANEL, OBJPROP_XDISTANCE); //--- Getting header x-coordinate int header_y = (int)ObjectGetInteger(0, PREFIX + HEADER_PANEL, OBJPROP_YDISTANCE); //--- Getting header y-coordinate int header_width = (int)ObjectGetInteger(0, PREFIX + HEADER_PANEL, OBJPROP_XSIZE); //--- Getting header width int header_height = (int)ObjectGetInteger(0, PREFIX + HEADER_PANEL, OBJPROP_YSIZE); //--- Getting header height bool in_header = (mouse_x >= header_x && mouse_x <= header_x + header_width && mouse_y >= header_y && mouse_y <= header_y + header_height); //--- Checking if in header int close_x = (int)ObjectGetInteger(0, PREFIX + CLOSE_BUTTON, OBJPROP_XDISTANCE); //--- Getting close button x-coordinate int close_y = (int)ObjectGetInteger(0, PREFIX + CLOSE_BUTTON, OBJPROP_YDISTANCE); //--- Getting close button y-coordinate int close_width = 20; //--- Setting close button width int close_height = 20; //--- Setting close button height bool in_close = (mouse_x >= close_x && mouse_x <= close_x + close_width && mouse_y >= close_y && mouse_y <= close_y + close_height); //--- Checking if in close button int export_x = (int)ObjectGetInteger(0, PREFIX + EXPORT_BUTTON, OBJPROP_XDISTANCE); //--- Getting export button x-coordinate int export_y = (int)ObjectGetInteger(0, PREFIX + EXPORT_BUTTON, OBJPROP_YDISTANCE); //--- Getting export button y-coordinate int export_width = 20; //--- Setting export button width int export_height = 20; //--- Setting export button height bool in_export = (mouse_x >= export_x && mouse_x <= export_x + export_width && mouse_y >= export_y && mouse_y <= export_y + export_height); //--- Checking if in export button int toggle_x = (int)ObjectGetInteger(0, PREFIX + TOGGLE_BUTTON, OBJPROP_XDISTANCE); //--- Getting toggle button x-coordinate int toggle_y = (int)ObjectGetInteger(0, PREFIX + TOGGLE_BUTTON, OBJPROP_YDISTANCE); //--- Getting toggle button y-coordinate int toggle_width = 20; //--- Setting toggle button width int toggle_height = 20; //--- Setting toggle button height bool in_toggle = (mouse_x >= toggle_x && mouse_x <= toggle_x + toggle_width && mouse_y >= toggle_y && mouse_y <= toggle_y + toggle_height); //--- Checking if in toggle button return in_header || in_close || in_export || in_toggle; //--- Returning combined check }
Wir implementieren die Funktion „isCursorInHeaderOrButtons“, um das Vorhandensein des Mauszeigers über interaktiven Elementen zu erkennen, was das Ziehen und die Interaktion mit Schaltflächen ermöglicht. Wir holen uns die Koordinaten und Abmessungen für das Header-Panel mit ObjectGetInteger für „PREFIX + HEADER_PANEL“ mit „OBJPROP_XDISTANCE“, „OBJPROP_YDISTANCE“, „OBJPROP_XSIZE“ und „OBJPROP_YSIZE“ und speichern sie in „header_x“, „header_y“, „header_width“ und „header_height“ und prüfen mit „in_header“, ob sich der Cursor („mouse_x“, „mouse_y“) innerhalb der Kopfzeilengrenzen befindet.
In ähnlicher Weise werden die Koordinaten für „PREFIX + CLOSE_BUTTON“, „PREFIX + EXPORT_BUTTON“ und „PREFIX + TOGGLE_BUTTON“ mit „OBJPROP_XDISTANCE“ und OBJPROP_YDISTANCE ermittelt, wobei „close_width“, „close_height“, „export_width“, „export_height“, „toggle_width“ und „toggle_height“ auf 20 gesetzt und mit „in_close“, „in_export“ und „in_toggle“ überprüft, ob sich der Cursor innerhalb der Begrenzungen der einzelnen Schaltflächen befindet. Wir geben true zurück, wenn sich der Cursor in der Kopfzeile oder einer beliebigen Schaltfläche befindet, indem wir Bedingungen mit dem Operator OR kombinieren. Nach der Hover-Erkennung müssen wir die erkannten Kopfzeilen oder Schaltflächen für visuelles Feedback aktualisieren. Hier ist die Logik, mit der wir das erreichen.
//+------------------------------------------------------------------+ //| Updating button hover states | //+------------------------------------------------------------------+ void updateButtonHoverStates(int mouse_x, int mouse_y) { int close_x = (int)ObjectGetInteger(0, PREFIX + CLOSE_BUTTON, OBJPROP_XDISTANCE); //--- Getting close button x-coordinate int close_y = (int)ObjectGetInteger(0, PREFIX + CLOSE_BUTTON, OBJPROP_YDISTANCE); //--- Getting close button y-coordinate int close_width = 20; //--- Setting close button width int close_height = 20; //--- Setting close button height bool is_close_hovered = (mouse_x >= close_x && mouse_x <= close_x + close_width && mouse_y >= close_y && mouse_y <= close_y + close_height); //--- Checking if close button hovered if(is_close_hovered != prev_close_hovered) { //--- Checking hover change ObjectSetInteger(0, PREFIX + CLOSE_BUTTON, OBJPROP_COLOR, is_close_hovered ? clrRed : clrBlack); //--- Updating close button color ObjectSetInteger(0, PREFIX + CLOSE_BUTTON, OBJPROP_BGCOLOR, is_close_hovered ? clrDodgerBlue : clrNONE); //--- Updating close button background prev_close_hovered = is_close_hovered; //--- Updating previous hover state ChartRedraw(0); //--- Redrawing chart } int export_x = (int)ObjectGetInteger(0, PREFIX + EXPORT_BUTTON, OBJPROP_XDISTANCE); //--- Getting export button x-coordinate int export_y = (int)ObjectGetInteger(0, PREFIX + EXPORT_BUTTON, OBJPROP_YDISTANCE); //--- Getting export button y-coordinate int export_width = 20; //--- Setting export button width int export_height = 20; //--- Setting export button height bool is_export_hovered = (mouse_x >= export_x && mouse_x <= export_x + export_width && mouse_y >= export_y && mouse_y <= export_y + export_height); //--- Checking if export button hovered if(is_export_hovered != prev_export_hovered) { //--- Checking hover change ObjectSetInteger(0, PREFIX + EXPORT_BUTTON, OBJPROP_COLOR, is_export_hovered ? clrOrange : clrBlack); //--- Updating export button color ObjectSetInteger(0, PREFIX + EXPORT_BUTTON, OBJPROP_BGCOLOR, is_export_hovered ? clrDodgerBlue : clrNONE); //--- Updating export button background prev_export_hovered = is_export_hovered; //--- Updating previous hover state ChartRedraw(0); //--- Redrawing chart } int toggle_x = (int)ObjectGetInteger(0, PREFIX + TOGGLE_BUTTON, OBJPROP_XDISTANCE); //--- Getting toggle button x-coordinate int toggle_y = (int)ObjectGetInteger(0, PREFIX + TOGGLE_BUTTON, OBJPROP_YDISTANCE); //--- Getting toggle button y-coordinate int toggle_width = 20; //--- Setting toggle button width int toggle_height = 20; //--- Setting toggle button height bool is_toggle_hovered = (mouse_x >= toggle_x && mouse_x <= toggle_x + toggle_width && mouse_y >= toggle_y && mouse_y <= toggle_y + toggle_height); //--- Checking if toggle button hovered if(is_toggle_hovered != prev_toggle_hovered) { //--- Checking hover change ObjectSetInteger(0, PREFIX + TOGGLE_BUTTON, OBJPROP_COLOR, is_toggle_hovered ? clrBlue : clrBlack); //--- Updating toggle button color ObjectSetInteger(0, PREFIX + TOGGLE_BUTTON, OBJPROP_BGCOLOR, is_toggle_hovered ? clrDodgerBlue : clrNONE); //--- Updating toggle button background prev_toggle_hovered = is_toggle_hovered; //--- Updating previous hover state ChartRedraw(0); //--- Redrawing chart } } //+------------------------------------------------------------------+ //| Updating header hover state | //+------------------------------------------------------------------+ void updateHeaderHoverState(int mouse_x, int mouse_y) { int header_x = (int)ObjectGetInteger(0, PREFIX + HEADER_PANEL, OBJPROP_XDISTANCE); //--- Getting header x-coordinate int header_y = (int)ObjectGetInteger(0, PREFIX + HEADER_PANEL, OBJPROP_YDISTANCE); //--- Getting header y-coordinate int header_width = (int)ObjectGetInteger(0, PREFIX + HEADER_PANEL, OBJPROP_XSIZE); //--- Getting header width int header_height = (int)ObjectGetInteger(0, PREFIX + HEADER_PANEL, OBJPROP_YSIZE); //--- Getting header height int close_x = (int)ObjectGetInteger(0, PREFIX + CLOSE_BUTTON, OBJPROP_XDISTANCE); //--- Getting close button x-coordinate int close_y = (int)ObjectGetInteger(0, PREFIX + CLOSE_BUTTON, OBJPROP_YDISTANCE); //--- Getting close button y-coordinate int close_width = 20; //--- Setting close button width int close_height = 20; //--- Setting close button height int export_x = (int)ObjectGetInteger(0, PREFIX + EXPORT_BUTTON, OBJPROP_XDISTANCE); //--- Getting export button x-coordinate int export_y = (int)ObjectGetInteger(0, PREFIX + EXPORT_BUTTON, OBJPROP_YDISTANCE); //--- Getting export button y-coordinate int export_width = 20; //--- Setting export button width int export_height = 20; //--- Setting export button height int toggle_x = (int)ObjectGetInteger(0, PREFIX + TOGGLE_BUTTON, OBJPROP_XDISTANCE); //--- Getting toggle button x-coordinate int toggle_y = (int)ObjectGetInteger(0, PREFIX + TOGGLE_BUTTON, OBJPROP_YDISTANCE); //--- Getting toggle button y-coordinate int toggle_width = 20; //--- Setting toggle button width int toggle_height = 20; //--- Setting toggle button height bool is_header_hovered = (mouse_x >= header_x && mouse_x <= header_x + header_width && mouse_y >= header_y && mouse_y <= header_y + header_height && !(mouse_x >= close_x && mouse_x <= close_x + close_width && mouse_y >= close_y && mouse_y <= close_y + close_height) && !(mouse_x >= export_x && mouse_x <= export_x + export_width && mouse_y >= export_y && mouse_y <= export_y + export_height) && !(mouse_x >= toggle_x && mouse_x <= toggle_x + toggle_width && mouse_y >= toggle_y && mouse_y <= toggle_y + toggle_height)); //--- Checking if header hovered if(is_header_hovered != prev_header_hovered && !panel_dragging) { //--- Checking hover change ObjectSetInteger(0, PREFIX + HEADER_PANEL, OBJPROP_BGCOLOR, is_header_hovered ? clrRed : settings.section_bg_color); //--- Updating header background prev_header_hovered = is_header_hovered; //--- Updating previous hover state ChartRedraw(0); //--- Redrawing chart } updateButtonHoverStates(mouse_x, mouse_y); //--- Updating button hover states }
Schließlich implementieren wir die Funktionen „updateButtonHoverStates“ und „updateHeaderHoverState“, um visuelles Feedback für Nutzerinteraktionen hinzuzufügen und die Reaktionsfähigkeit von Schaltflächen und Kopfzeilen zu verbessern. In „updateButtonHoverStates“ werden die Hover-Zustände der Schaltflächen überprüft, indem die Koordinaten für „PREFIX + CLOSE_BUTTON“, „PREFIX + EXPORT_BUTTON“ und „PREFIX + TOGGLE_BUTTON“ mithilfe von ObjectGetInteger mit „OBJPROP_XDISTANCE“ und „OBJPROP_YDISTANCE“, wobei „close_width“, „close_height“, „export_width“, „export_height“, „toggle_width“ und „toggle_height“ auf 20 gesetzt werden.
Für die Schaltfläche zum Schließen setzen wir „is_close_hovered“, wenn „mouse_x“ und „mouse_y“ innerhalb ihrer Grenzen liegen, und wenn sie sich von „prev_close_hovered“ unterscheidet, aktualisieren wir „OBJPROP_COLOR“ auf „clrRed“ oder „clrBlack“ und „OBJPROP_BGCOLOR“ auf „clrDodgerBlue“ oder „clrNONE“ mit „ObjectSetInteger“ aktualisieren, „prev_close_hovered“ aktualisieren und die Funktion ChartRedraw aufrufen. Ähnlich setzen wir für die Export-Schaltfläche „is_export_hovered“ auf „clrOrange“ oder „clrBlack“ und „clrDodgerBlue“ oder „clrNONE“gesetzt, „prev_export_hovered“ aktualisiert und neu gezeichnet; für die Umschalttaste wird „clrBlue“ oder „clrBlack“ verwendet, „prev_toggle_hovered“ aktualisiert und neu gezeichnet.
In „updateHeaderHoverState“ werden „header_x“, „header_y“, „header_width“ und „header_height“ für „PREFIX + HEADER_PANEL“ und die Schaltflächenkoordinaten ermittelt, wobei „is_header_hovered“ geprüft wird, wenn sich der Cursor innerhalb der Kopfzeile, aber außerhalb der Schaltflächenbegrenzungen befindet. Wenn sich „is_header_hovered“ von „prev_header_hovered“ unterscheidet und „panel_dragging“ falsch ist, aktualisieren wir „OBJPROP_BGCOLOR“ von „PREFIX + HEADER_PANEL“ auf „clrRed“ oder „settings.section_bg_color“ mit ObjectSetInteger, aktualisieren „prev_header_hovered“, rufen „ChartRedraw“ auf und rufen „updateButtonHoverStates“ auf. Diese Funktionen bieten dynamische Hover-Effekte für eine intuitive Nutzerinteraktion. Um die Funktionen zu nutzen, werden wir OnChartEvent erweitern, um die visuelle Feedback-Logik unterzubringen.
//+------------------------------------------------------------------+ //| Handling chart events for sorting, export, and UI interactions | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if(id == CHARTEVENT_OBJECT_CLICK) { //--- Handling object click if(sparam == PREFIX + CLOSE_BUTTON) { //--- Checking close button click Print("Closing the dashboard"); //--- Logging closing PlaySound("alert.wav"); //--- Playing alert sound panel_is_visible = false; //--- Setting panel invisible deleteAllObjects(); //--- Deleting all objects ChartRedraw(0); //--- Redrawing chart } else if(sparam == PREFIX + EXPORT_BUTTON) { //--- Checking export button click Print("Exporting dashboard to CSV"); //--- Logging exporting ExportToCSV(); //--- Exporting to CSV ChartRedraw(0); //--- Redrawing chart } else if(sparam == PREFIX + TOGGLE_BUTTON) { //--- Checking toggle button click deleteAllObjects(); //--- Deleting all objects panel_minimized = !panel_minimized; //--- Toggling minimized state if(panel_minimized) { //--- Checking if minimized Print("Minimizing the dashboard"); //--- Logging minimizing createMinimizedDashboard(); //--- Creating minimized dashboard } else { Print("Maximizing the dashboard"); //--- Logging maximizing createFullDashboard(); //--- Creating full dashboard // Resetting string variables to force update total_buys_str = ""; //--- Resetting buys string total_sells_str = ""; //--- Resetting sells string total_trades_str = ""; //--- Resetting trades string total_lots_str = ""; //--- Resetting lots string total_profit_str = ""; //--- Resetting profit string total_pending_str = ""; //--- Resetting pending string total_swap_str = ""; //--- Resetting swap string total_comm_str = ""; //--- Resetting commission string acc_bal_str = ""; //--- Resetting balance string acc_eq_str = ""; //--- Resetting equity string acc_free_str = ""; //--- Resetting free margin string UpdateDashboard(); //--- Updating dashboard } prev_header_hovered = false; //--- Resetting header hover prev_close_hovered = false; //--- Resetting close hover prev_export_hovered = false; //--- Resetting export hover prev_toggle_hovered = false; //--- Resetting toggle hover ObjectSetInteger(0, PREFIX + HEADER_PANEL, OBJPROP_BGCOLOR, settings.section_bg_color); //--- Resetting header background ObjectSetInteger(0, PREFIX + CLOSE_BUTTON, OBJPROP_COLOR, clrBlack); //--- Resetting close button color ObjectSetInteger(0, PREFIX + CLOSE_BUTTON, OBJPROP_BGCOLOR, clrNONE); //--- Resetting close button background ObjectSetInteger(0, PREFIX + EXPORT_BUTTON, OBJPROP_COLOR, clrBlack); //--- Resetting export button color ObjectSetInteger(0, PREFIX + EXPORT_BUTTON, OBJPROP_BGCOLOR, clrNONE); //--- Resetting export button background ObjectSetInteger(0, PREFIX + TOGGLE_BUTTON, OBJPROP_COLOR, clrBlack); //--- Resetting toggle button color ObjectSetInteger(0, PREFIX + TOGGLE_BUTTON, OBJPROP_BGCOLOR, clrNONE); //--- Resetting toggle button background ChartRedraw(0); //--- Redrawing chart } else { for(int i = 0; i < ArraySize(headers); i++) { //--- Iterating through headers if(sparam == PREFIX + HEADER + IntegerToString(i)) { //--- Checking header click if(sort_column == i) //--- Checking if same column sort_ascending = !sort_ascending; //--- Toggling sort direction else { sort_column = i; //--- Setting new sort column sort_ascending = true; //--- Setting to ascending } UpdateDashboard(); //--- Updating dashboard break; //--- Exiting loop } } } } else if(id == CHARTEVENT_KEYDOWN && lparam == 'E') { //--- Handling 'E' key press ExportToCSV(); //--- Exporting to CSV } else if(id == CHARTEVENT_MOUSE_MOVE && panel_is_visible) { //--- Handling mouse move int mouse_x = (int)lparam; //--- Getting mouse x-coordinate int mouse_y = (int)dparam; //--- Getting mouse y-coordinate int mouse_state = (int)sparam; //--- Getting mouse state if(mouse_x == last_mouse_x && mouse_y == last_mouse_y && !panel_dragging) { //--- Checking if mouse moved return; //--- Exiting if no movement } last_mouse_x = mouse_x; //--- Updating last mouse x last_mouse_y = mouse_y; //--- Updating last mouse y updateHeaderHoverState(mouse_x, mouse_y); //--- Updating header hover state int header_x = (int)ObjectGetInteger(0, PREFIX + HEADER_PANEL, OBJPROP_XDISTANCE); //--- Getting header x-coordinate int header_y = (int)ObjectGetInteger(0, PREFIX + HEADER_PANEL, OBJPROP_YDISTANCE); //--- Getting header y-coordinate int header_width = (int)ObjectGetInteger(0, PREFIX + HEADER_PANEL, OBJPROP_XSIZE); //--- Getting header width int header_height = (int)ObjectGetInteger(0, PREFIX + HEADER_PANEL, OBJPROP_YSIZE);//--- Getting header height int close_x = (int)ObjectGetInteger(0, PREFIX + CLOSE_BUTTON, OBJPROP_XDISTANCE); //--- Getting close button x-coordinate int close_width = 20; //--- Setting close button width int export_x = (int)ObjectGetInteger(0, PREFIX + EXPORT_BUTTON, OBJPROP_XDISTANCE);//--- Getting export button x-coordinate int export_width = 20; //--- Setting export button width int toggle_x = (int)ObjectGetInteger(0, PREFIX + TOGGLE_BUTTON, OBJPROP_XDISTANCE);//--- Getting toggle button x-coordinate int toggle_width = 20; //--- Setting toggle button width if(prev_mouse_state == 0 && mouse_state == 1) { //--- Checking mouse click start if(mouse_x >= header_x && mouse_x <= header_x + header_width && mouse_y >= header_y && mouse_y <= header_y + header_height && !(mouse_x >= close_x && mouse_x <= close_x + close_width) && !(mouse_x >= export_x && mouse_x <= export_x + export_width) && !(mouse_x >= toggle_x && mouse_x <= toggle_x + toggle_width)) { //--- Checking if in draggable area panel_dragging = true; //--- Starting dragging panel_drag_x = mouse_x; //--- Setting drag start x panel_drag_y = mouse_y; //--- Setting drag start y panel_start_x = header_x; //--- Setting panel start x panel_start_y = header_y; //--- Setting panel start y ObjectSetInteger(0, PREFIX + HEADER_PANEL, OBJPROP_BGCOLOR, clrMediumBlue); //--- Setting dragging color ChartSetInteger(0, CHART_MOUSE_SCROLL, false); //--- Disabling chart scroll } } if(panel_dragging && mouse_state == 1) { //--- Handling dragging int dx = mouse_x - panel_drag_x; //--- Calculating x change int dy = mouse_y - panel_drag_y; //--- Calculating y change settings.panel_x = panel_start_x + dx; //--- Updating panel x settings.panel_y = panel_start_y + dy; //--- Updating panel y int chart_width = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS); //--- Getting chart width int chart_height = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS); //--- Getting chart height int num_columns = ArraySize(headers); //--- Getting number of columns int column_width_sum = 0; //--- Initializing column width sum for(int i = 0; i < num_columns; i++) column_width_sum += column_widths[i]; //--- Adding column width int panel_width = MathMax(settings.header_x_distances[num_columns - 1] + column_widths[num_columns - 1], column_width_sum) + 20 + settings.label_x_offset; //--- Calculating panel width int panel_height = panel_minimized ? settings.row_height : (ArraySize(symbol_data) + 3) * settings.row_height; //--- Calculating panel height settings.panel_x = MathMax(0, MathMin(chart_width - panel_width, settings.panel_x)); //--- Constraining x settings.panel_y = MathMax(0, MathMin(chart_height - panel_height, settings.panel_y)); //--- Constraining y updatePanelPositions(); //--- Updating object positions ChartRedraw(0); //--- Redrawing chart } if(mouse_state == 0 && prev_mouse_state == 1) { //--- Handling mouse release if(panel_dragging) { //--- Checking if was dragging panel_dragging = false; //--- Stopping dragging updateHeaderHoverState(mouse_x, mouse_y); //--- Updating hover state ChartSetInteger(0, CHART_MOUSE_SCROLL, true); //--- Enabling chart scroll ChartRedraw(0); //--- Redrawing chart } } prev_mouse_state = mouse_state; //--- Updating previous mouse state } }
Hier wird die Funktion OnChartEvent erweitert, um interaktive Ereignisse zu behandeln und Klicks zum Schließen, Exportieren, Umschalten, Sortieren und Mausbewegungen zum Ziehen zu verwalten. Für CHARTEVENT_OBJECT_CLICK prüfen wir, ob „sparam“ „PREFIX + CLOSE_BUTTON“ ist, protokollieren mit „Print“, spielen „alert.wav“ mit PlaySound ab, setzen „panel_is_visible“ auf false, rufen „deleteAllObjects“ auf und zeichnen mit „ChartRedraw“ neu. Wenn „sparam“ „PREFIX + EXPORT_BUTTON“ ist, protokollieren wir und rufen „ExportToCSV“ auf.
Für „PREFIX + TOGGLE_BUTTON“ löschen wir Objekte, schalten „panel_minimized“ um, protokollieren „Minimieren“ oder „Maximieren“ mit „Drucken“, rufen „createMinimizedDashboard“ oder „createFullDashboard“ aufrufen, String-Variablen wie „total_buys_str“ und „acc_bal_str“ zurücksetzen, „UpdateDashboard“ aufrufen, Hover-Zustände zurücksetzen („prev_header_hovered“, „prev_close_hovered“, etc.) und die Farben für „PREFIX + HEADER_PANEL“, „PREFIX + CLOSE_BUTTON“, „PREFIX + EXPORT_BUTTON“ und „PREFIX + TOGGLE_BUTTON“ mit der Funktion ObjectSetInteger zurücksetzen. Daraus ergibt sich folgendes Bild.

Für Kopfzeilenklicks durchlaufen wir eine Schleife durch „headers“, schalten „sort_ascending“ um, wenn „sort_column“ übereinstimmt, oder setzen neue „sort_column“ und „sort_ascending“ auf true, und rufen dann „UpdateDashboard“ auf. Für CHARTEVENT_KEYDOWN mit 'E' rufen wir „ExportToCSV“ auf. Für CHARTEVENT_MOUSE_MOVE, wenn „panel_is_visible“, erhalten wir „mouse_x“, „mouse_y“, und „mouse_state“, beenden, wenn unverändert und nicht ziehend, aktualisieren „last_mouse_x“ und „last_mouse_y“, und rufen „updateHeaderHoverState“.
Wenn „prev_mouse_state“ gleich 0 und „mouse_state“ gleich 1 ist, prüfen wir, ob ein Bereich mit der Maus gezogen werden kann (außer Schaltflächen), setzen „panel_dragging“ auf true, speichern die Koordinaten, setzen die Farbe der Kopfzeile auf „clrMediumBlue“ und deaktivieren den Bildlauf mit der Funktion ChartSetInteger. Wenn die Maus gezogen wird und „mouse_state“ gleich 1 ist, werden „dx“ und „dy“ berechnet, „settings.panel_x“ und „settings.panel_y“ innerhalb der Chartgrenzen aktualisiert, „updatePanelPositions“ aufgerufen und neu gezeichnet. Wenn die Maus losgelassen wird, wird das Ziehen gestoppt, der Schwebezustand aktualisiert, der Bildlauf wieder aktiviert und das Bild neu gezeichnet. Dies ermöglicht dynamische UI-Interaktionen für ein nutzerfreundliches Dashboard. Nach der Kompilierung erhalten wir das folgende Ergebnis für den Schwebezustand der Schaltfläche.

Das Ergebnis für einen maximierten und widerstandsfähigen Zustand ist wie folgt.

Aus dem Bild können wir ersehen, dass wir die Dashboard-Komponenten für die Hover-, Zieh- und Minimierungslogik hinzugefügt und damit unsere Ziele erreicht haben. Nun bleibt nur noch die Prüfung der Durchführbarkeit des Projekts, die im vorangegangenen Abschnitt behandelt wurde.
Backtests
Wir haben die Tests durchgeführt, und unten sehen Sie die kompilierte Visualisierung in einem einzigen Graphics Interchange Format (GIF) Bitmap-Bildformat.

Schlussfolgerung
Abschließend haben wir das Informations-Dashboard in MQL5 für Teil 8 verbessert, indem wir ziehbare und minimierbare Funktionen, interaktive Schaltflächen wie „CLOSE_BUTTON“ und „TOGGLE_BUTTON“ sowie Hover-Effekte hinzugefügt haben, um die Nutzerfreundlichkeit zu verbessern und gleichzeitig eine robuste Multi-Symbol-Positions- und Kontoüberwachung zu gewährleisten. Wir haben die Architektur und Implementierung detailliert beschrieben und Funktionen wie „createFullDashboard“, „updatePanelPositions“ und OnChartEvent verwendet, um ein flexibles, visuell ansprechendes Tool mit Echtzeit-Updates und CSV-Export für Excel zu liefern. Sie können dieses Dashboard anpassen, um Ihren Handelsablauf zu optimieren und die Positionsanalyse intuitiver und effizienter zu gestalten.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/19059
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.
Einführung in MQL5 (Teil 20): Einführung in „Harmonic Patterns“
MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 78): Gator- und AD-Oszillator-Strategien für Marktresilienz
CRUD-Operationen in Firebase mit MQL
Einführung in MQL5 (Teil 19): Automatisiertes Erkennen von Wolfe-Wellen
- 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.