English Русский 中文 Deutsch 日本語 Português
preview
Creación de un Panel de administración de operaciones en MQL5 (Parte III): Mejora de la interfaz gráfica de usuario con estilización visual (I)

Creación de un Panel de administración de operaciones en MQL5 (Parte III): Mejora de la interfaz gráfica de usuario con estilización visual (I)

MetaTrader 5Ejemplos |
321 1
Clemence Benjamin
Clemence Benjamin

Contenido:

  1. Introducción
  2. Importancia de una interfaz gráfica de usuario visualmente atractiva
  3. Aplicación de las funciones de estilo de la GUI MQL5
  4. Personalización de colores y fuentes
  5. Lógica de gestión de temas
  6. Ajustar el diseño de los nuevos botones
  7. Mejora avanzada de la interfaz gráfica de usuario
  8. Conclusión

Introducción

Si examinamos los objetivos esbozados en nuestro artículo anterior, ¿podemos decir con seguridad que hemos hecho lo suficiente? En mi opinión, lo que veo inspira un impulso para avanzar más allá de nuestras ofertas actuales. Imagina lo beneficioso que sería implementar una alternancia entre temas oscuros y claros para nuestro Panel de Administración. Además, podríamos mejorar la experiencia del usuario añadiendo botones elegantes, ofreciendo una selección variada de fuentes y permitiendo el cambio de idioma entre las principales lenguas. Esto haría que nuestro panel fuera más fácil de usar para todos.

Nuestro objetivo es ofrecer a los administradores de operaciones una solución de comunicación completa integrada en la plataforma de operaciones. Los conceptos que pretendemos incorporar se inspiran en influyentes investigaciones y desarrollos de interfaces gráficas de usuario (GUI) desde los años setenta. Entre los colaboradores más destacados se encuentran Alan Kay, Xerox PARC, Apple (macOS), Microsoft (Windows), CSS (Cascading Style Sheets), y Material Design de Google. Aprovechando esta información, podemos crear un panel de administración que satisfaga las necesidades de los usuarios y mejore su experiencia general.



Administración sencilla con botones de mensaje rápido

El panel de administración básico que hemos desarrollado hasta ahora.

Resumen de lo que hemos conseguido hasta ahora:

  •  Creación de un Panel de administración de operaciones en MQL5 (Parte I): Creación de una interfaz de mensajería.
  •  Agregar botones esenciales a la interfaz, como minimizar, maximizar, cerrar y botones de mensajes rápidos.

Al final de este artículo, tendremos un panel de administrador de operaciones totalmente personalizado y con estilo visual en MQL5. Aprenderá a implementar diversas técnicas de estilo que mejoran tanto la apariencia como la funcionalidad de la interfaz, creando un entorno profesional y fácil de usar para los comerciantes.

Estos son los principales objetivos de este artículo:
  • Aplicación de técnicas básicas de estilo utilizando MQL5.
  • Personalización de fuentes, colores y diseños.
  • Mejorar la interacción del usuario con elementos visuales.
  • Incorporando personalización entre el modo de tema claro y oscuro.
  • Agregar funciones dinámicas como animaciones y transiciones.

MQL5 proporciona varias funciones y características para diseñar la GUI de su aplicación comercial. Estos incluyen opciones para personalizar colores, fuentes y diseños para adaptarse a las necesidades de sus usuarios y la estética general del diseño que desea lograr.

Dar estilo a la GUI en MQL5 implica el uso de varias funciones y técnicas clave. Analizaremos las funciones que permiten cambiar las propiedades de objetos gráficos, como botones, etiquetas y paneles. Con estos, podemos personalizar el color de fondo, el estilo del borde, el tamaño de la fuente y otros aspectos visuales para crear una apariencia cohesiva.

  1. Personalización de colores y fuentes
  2. Lógica de gestión de temas
  3. Ajustar el diseño de los nuevos botones

    Personalización de colores y fuentes:

    Matriz e índice de fuentes:

    Comenzamos definiendo un array availableFonts y un currentFontIndex para gestionar las selecciones de fuentes para el Panel de Administración. La matriz availableFonts incluye nombres de fuentes como «Arial», «Courier New», «Verdana» y «Times New Roman», que ofrecen a los usuarios diversas opciones para personalizar el aspecto del panel. El currentFontIndex mantiene la pista de la fuente seleccionada indexando en este array. Esta configuración nos permite recorrer fácilmente los tipos de letra y aplicarlos a los componentes de la interfaz de usuario cada vez que el usuario cambia el tipo de letra, asegurándonos de que la experiencia del usuario sigue siendo dinámica y coherente.


    // Array of available fonts
    string availableFonts[] = {"Arial", "Courier New", "Verdana", "Times New Roman"};
    // Index of the current font in use
    int currentFontIndex = 0;
    


    Creación del botón de cambio de fuente:

    Vamos a crear un botón llamado «Font<>», que posicionaremos estratégicamente dentro del Panel de Administración. Este botón no es un botón cualquiera; es clave para cambiar de fuente. Nos aseguramos de que encaje bien en el diseño del panel y nos ocupamos de cualquier problema relacionado con su creación. Al añadir este botón, proporcionamos a los usuarios una forma intuitiva de pasar de un tipo de letra a otro, lo que mejora la facilidad de uso y la flexibilidad estética del panel. Si se produce algún contratiempo al crear el botón, imprimimos un mensaje de error para controlar cualquier problema.
    // Create a button for changing the font
    CButton changeFontButton;
    changeFontButton.Create(panel, "ChangeFontButton", 0, 10, 10, 100, 30);
    changeFontButton.Text("Font<>");
    
    // Verify button creation and handle errors
    if(!changeFontButton.IsCreated())
    {
        Print("Error creating Font<> button.");
    }
    

    Manejo del botón de cambio de fuente:

    Cuando implementamos la función OnChangeFontButtonClick, nuestro objetivo es gestionar el proceso de cambio de fuente sin problemas. Esta función actualiza currentFontIndex para seleccionar la siguiente fuente en la matriz availableFonts, volviendo al principio si es necesario. Después de actualizar el índice, aplicamos la nueva fuente a todos los componentes de la interfaz de usuario relevantes, como el cuadro de entrada, el botón de borrar y el botón de enviar, lo que garantiza una apariencia uniforme en todo el panel. Para finalizar los cambios, utilizamos ChartRedraw para actualizar la pantalla e imprimir un mensaje de confirmación, haciendo saber a los usuarios que el cambio de fuente se ha realizado correctamente.
    // Function to handle the font change button click
    void OnChangeFontButtonClick()
    {
        // Update the font index, wrapping around if necessary
        currentFontIndex = (currentFontIndex + 1) % ArraySize(availableFonts);
        string newFont = availableFonts[currentFontIndex];
        
        // Apply the new font to UI components
        inputBox.Font(newFont);
        clearButton.Font(newFont);
        sendButton.Font(newFont);
    
        // Refresh the display to apply the changes
        ChartRedraw();
    
        // Print confirmation of the font change
        Print("Font changed to ", newFont);
    }
    

    OnChartEvent para manejar los clics de los botones:

    En la función OnChartEvent, manejamos las interacciones del usuario con varios objetos del gráfico, incluyendo nuestro botón de cambio de fuente. Esta función escucha eventos de clic de botón y comprueba qué botón se ha pulsado inspeccionando la cadena sparam. Cuando se pulsa el botón «ChangeFontButton», llamamos a la función OnChangeFontButtonClick para gestionar el cambio de fuente. Este enfoque basado en eventos mantiene nuestra interfaz de usuario receptiva e interactiva, garantizando que las acciones del usuario desencadenen las respuestas adecuadas y mantengan una interfaz atractiva.
    // Function to handle chart events
    void OnChartEvent(const int id, const int sub_id, const int type, const int x, const int y, const int state)
    {
        // Handle button clicks
        if(type == CHARTEVENT_OBJECT_CLICK)
        {
            string buttonName = ObjectGetString(0, "ChangeFontButton", OBJPROP_TEXT);
            if(buttonName == "Font<>")
            {
                OnChangeFontButtonClick();
            }
        }
    }
    

    El cambio de fuentes funciona correctamente

    Funcionamiento del cambio de fuentes.

    Lógica de gestión de temas:


    Lógica de cambio de tema:
    Comenzamos configurando el sistema de gestión de temas con dos temas distintos: claro y oscuro. Para gestionar el cambio, utilizamos una variable booleana, isDarkMode, que realiza un seguimiento de qué tema está activo actualmente. La conmutación es sencilla: cuando el usuario hace clic en el botón del tema, el valor isDarkMode cambia, modificando por completo el aspecto del Panel de administración. Al definir los colores de cada tema por separado, agilizamos el proceso, facilitando el mantenimiento y la aplicación de nuevos estilos siempre que sea necesario.
    bool isDarkMode = false; // Tracks the current theme mode (light or dark)
    color lightBackgroundColor = clrWhite;  // Background color for light mode
    color darkBackgroundColor = clrBlack;   // Background color for dark mode
    color lightTextColor = clrBlack;        // Text color for light mode
    color darkTextColor = clrWhite;         // Text color for dark mode
    

    Crear el botón de cambio de tema:
    Pasemos a crear un botón llamado "Theme<>". Este botón se coloca dentro del Panel de administración, y proporciona a los usuarios una forma sencilla de cambiar entre los modos claro y oscuro. Si algo sale mal durante su creación, nos aseguramos de gestionar el error con un mensaje impreso. Esto facilita la resolución de problemas y garantiza que la interfaz siga siendo intuitiva y receptiva.
    //Creating the theme switch button
    if(!CreateButton("ToggleThemeButton", "Theme<>", 50, 220, 100, 30)) 
    {
       Print("Error: Failed to create theme toggle button"); // Error handling if button creation fails
    }
    

    Manejar el botón de cambio de tema:
    A continuación, manejamos el cambio de tema real implementando la función OnToggleModeButtonClick. Esta función invierte la variable isDarkMode, cambiando entre los temas claro y oscuro. Una vez que sabemos qué tema está activo, aplicamos los colores de fondo y texto correspondientes a todos los elementos de la interfaz de usuario, como el panel, los botones y el texto. El cambio de tema se produce en tiempo real gracias a una actualización rápida, lo que hace que la interfaz sea fluida y sensible. También imprimimos un mensaje de confirmación para que los usuarios sepan cuándo ha cambiado el modo.
    //Theme switching handler
    void OnToggleModeButtonClick()
    {
        isDarkMode = !isDarkMode; // Toggle the theme mode
        if(isDarkMode)
        {
            ApplyTheme(darkBackgroundColor, darkTextColor); // Apply dark mode colors
        }
        else
        {
            ApplyTheme(lightBackgroundColor, lightTextColor); // Apply light mode colors
        }
        Print("Theme has been switched"); // Inform the user that the theme has changed
    }
    


    OnChartEvent para gestionar los clics del botón de alternancia de tema:
    En la función OnChartEvent, detectamos cuando un usuario hace clic en el botón «Toggle Theme» y activamos la función OnToggleModeButtonClick. Este enfoque basado en eventos garantiza que el panel responda instantáneamente a las acciones del usuario. Al escuchar los eventos de clic de los botones, nos aseguramos de que el panel de administración siga siendo interactivo y atractivo, permitiendo a los usuarios cambiar fácilmente entre temas claros y oscuros según sea necesario.
    //The OneChartEvent for  the theme
    void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
    {
        if(id == CHARTEVENT_OBJECT_CLICK) // Check if the event is a button click
        {
            if(sparam == "ToggleThemeButton") // Check if the clicked button is the theme toggle button
            {
                OnToggleModeButtonClick(); // Call the function to handle the theme change
            }
        }
    }
    

    Aplicar tema sin recrear objetos:
    Una de nuestras principales decisiones de diseño es actualizar el tema sin recrear ninguno de los objetos del panel. En lugar de derribar y construir nuevos componentes de UI, simplemente aplicamos el nuevo esquema de colores a los elementos existentes. Esto mantiene el sistema eficiente, reduciendo el retraso y manteniendo una experiencia de usuario fluida. También garantiza que el panel siga respondiendo a medida que aplicamos los nuevos colores dinámicamente.
    
    //Applying theme
    void ApplyTheme(color backgroundColor, color textColor)
    {
        // Update background and text colors of existing objects
        ObjectSetInteger(0, "AdminPanelBackground", OBJPROP_COLOR, backgroundColor); // Change background color
        ObjectSetInteger(0, "ClearButton", OBJPROP_COLOR, textColor);                // Change text color of clear button
        ObjectSetInteger(0, "SendButton", OBJPROP_COLOR, textColor);                 // Change text color of send button
        ObjectSetInteger(0, "InputBox", OBJPROP_COLOR, textColor);                   // Change text color of input box
        ChartRedraw(); // Redraw the chart to reflect the changes
    }
    

    Ajuste del nuevo diseño de los botones.

    Botón de cambio de fuente:

     El botón sobre cambiar de fuente lo posicionamos en el panel de administración con su esquina superior izquierda en (95, 95) y su esquina inferior derecha en (230, 115). Esto lo sitúa a la izquierda de los botones Enviar (Send) y Borrar (Clear). Sus dimensiones lo hacen lo suficientemente ancho para la etiqueta «Font<>» y la interacción con el usuario. El botón permite a los usuarios recorrer diferentes opciones de fuente para todos los elementos de texto dentro del panel.
    if (!changeFontButton.Create(chart_id, "ChangeFontButton", 0, 95, 95, 230, 115))

    Botón de cambio de tema:

    En cuanto al botón de cambio de tema, lo colocamos en las coordenadas (5, 95) para la parte superior izquierda y (90, 115) para la parte inferior derecha. Esto coloca el botón en el extremo izquierdo del panel, ligeramente por encima del botón de cambio de fuente, proporcionando una clara separación. El tamaño compacto y la proximidad a otros botones facilitan a los usuarios el cambio entre temas oscuros y claros sin saturar la interfaz.
    if (!toggleThemeButton.Create(chart_id, "ToggleThemeButton", 0, 5, 95, 90, 115))


    Aquí está nuestro programa completo con todas las nuevas funciones perfectamente integradas.
    //+------------------------------------------------------------------+
    //|                                             Admin Panel.mq5      |
    //|                     Copyright 2024, Clemence Benjamin            |
    //|     https://www.mql5.com/en/users/billionaire2024/seller         |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2024, Clemence Benjamin"
    #property link      "https://www.mql5.com/en/users/billionaire2024/seller"
    #property description "A responsive Admin Panel. Send messages to your telegram clients without leaving MT5"
    #property version   "1.11"
    
    #include <Trade\Trade.mqh>
    #include <Controls\Dialog.mqh>
    #include <Controls\Button.mqh>
    #include <Controls\Edit.mqh>
    #include <Controls\Label.mqh>
    
    // Input parameters
    input string QuickMessage1 = "Updates";
    input string QuickMessage2 = "Close all";
    input string QuickMessage3 = "In deep profits";
    input string QuickMessage4 = "Hold position";
    input string QuickMessage5 = "Swing Entry";
    input string QuickMessage6 = "Scalp Entry";
    input string QuickMessage7 = "Book profit";
    input string QuickMessage8 = "Invalid Signal";
    input string InputChatId = "Enter Chat ID from Telegram bot API";
    input string InputBotToken = "Enter BOT TOKEN from your Telegram bot";
    
    // Global variables
    CDialog adminPanel;
    CButton sendButton, clearButton, changeFontButton, toggleThemeButton;
    CButton quickMessageButtons[8], minimizeButton, maximizeButton, closeButton;
    CEdit inputBox;
    CLabel charCounter;
    #define BG_RECT_NAME "BackgroundRect"
    bool minimized = false;
    bool darkTheme = false;
    int MAX_MESSAGE_LENGTH = 4096;
    string availableFonts[] = { "Arial", "Courier New", "Verdana", "Times New Roman" };
    int currentFontIndex = 0;
    
    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
    {
        // Initialize the Dialog
        if (!adminPanel.Create(ChartID(), "Admin Panel", 0, 30, 30, 500, 500))
        {
            Print("Failed to create dialog");
            return INIT_FAILED;
        }
    
        // Create controls
        if (!CreateControls())
        {
            Print("Control creation failed");
            return INIT_FAILED;
        }
    
        adminPanel.Show();
        // Initialize with the default theme
        CreateOrUpdateBackground(ChartID(), darkTheme ? clrBlack : clrWhite);
    
        Print("Initialization complete");
        return INIT_SUCCEEDED;
    }
    
    //+------------------------------------------------------------------+
    //| Create necessary UI controls                                    |
    //+------------------------------------------------------------------+
    bool CreateControls()
    {
        long chart_id = ChartID();
    
        // Create the input box
        if (!inputBox.Create(chart_id, "InputBox", 0, 5, 25, 460, 95))
        {
            Print("Failed to create input box");
            return false;
        }
        adminPanel.Add(inputBox);
    
        // Character counter
        if (!charCounter.Create(chart_id, "CharCounter", 0, 380, 5, 460, 25))
        {
            Print("Failed to create character counter");
            return false;
        }
        charCounter.Text("0/" + IntegerToString(MAX_MESSAGE_LENGTH));
        adminPanel.Add(charCounter);
    
        // Clear button
        if (!clearButton.Create(chart_id, "ClearButton", 0, 235, 95, 345, 125))
        {
            Print("Failed to create clear button");
            return false;
        }
        clearButton.Text("Clear");
        adminPanel.Add(clearButton);
    
        // Send button
        if (!sendButton.Create(chart_id, "SendButton", 0, 350, 95, 460, 125))
        {
            Print("Failed to create send button");
            return false;
        }
        sendButton.Text("Send");
        adminPanel.Add(sendButton);
    
        // Change font button
        if (!changeFontButton.Create(chart_id, "ChangeFontButton", 0, 95, 95, 230, 115))
        {
            Print("Failed to create change font button");
            return false;
        }
        changeFontButton.Text("Font<>");
        adminPanel.Add(changeFontButton);
    
        // Toggle theme button
        if (!toggleThemeButton.Create(chart_id, "ToggleThemeButton", 0, 5, 95, 90, 115))
        {
            Print("Failed to create toggle theme button");
            return false;
        }
        toggleThemeButton.Text("Theme<>");
        adminPanel.Add(toggleThemeButton);
    
        // Minimize button
        if (!minimizeButton.Create(chart_id, "MinimizeButton", 0, 375, -22, 405, 0))
        {
            Print("Failed to create minimize button");
            return false;
        }
        minimizeButton.Text("_");
        adminPanel.Add(minimizeButton);
    
        // Maximize button
        if (!maximizeButton.Create(chart_id, "MaximizeButton", 0, 405, -22, 435, 0))
        {
            Print("Failed to create maximize button");
            return false;
        }
        maximizeButton.Text("[ ]");
        adminPanel.Add(maximizeButton);
    
        // Close button
        if (!closeButton.Create(chart_id, "CloseButton", 0, 435, -22, 465, 0))
        {
            Print("Failed to create close button");
            return false;
        }
        closeButton.Text("X");
        adminPanel.Add(closeButton);
    
        // Quick messages
        return CreateQuickMessageButtons();
    }
    
    //+------------------------------------------------------------------+
    //| Create quick message buttons                                     |
    //+------------------------------------------------------------------+
    bool CreateQuickMessageButtons()
    {
        string quickMessages[8] = { QuickMessage1, QuickMessage2, QuickMessage3, QuickMessage4, QuickMessage5, QuickMessage6, QuickMessage7, QuickMessage8 };
        int startX = 5, startY = 160, width = 222, height = 65, spacing = 5;
    
        for (int i = 0; i < 8; i++)
        {
            if (!quickMessageButtons[i].Create(ChartID(), "QuickMessageButton" + IntegerToString(i + 1), 0, startX + (i % 2) * (width + spacing), startY + (i / 2) * (height + spacing), startX + (i % 2) * (width + spacing) + width, startY + (i / 2) * (height + spacing) + height))
            {
                Print("Failed to create quick message button ", i + 1);
                return false;
            }
            quickMessageButtons[i].Text(quickMessages[i]);
            adminPanel.Add(quickMessageButtons[i]);
        }
        return true;
    }
    
    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
    {
        adminPanel.Destroy();
        ObjectDelete(ChartID(), BG_RECT_NAME);
        Print("Deinitialization complete");
    }
    
    //+------------------------------------------------------------------+
    //| Handle chart events                                              |
    //+------------------------------------------------------------------+
    void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
    {
        switch (id)
        {
            case CHARTEVENT_OBJECT_CLICK:
                if (sparam == "SendButton") OnSendButtonClick();
                else if (sparam == "ClearButton") OnClearButtonClick();
                else if (sparam == "ChangeFontButton") OnChangeFontButtonClick();
                else if (sparam == "ToggleThemeButton") OnToggleThemeButtonClick();
                else if (sparam == "MinimizeButton") OnMinimizeButtonClick();
                else if (sparam == "MaximizeButton") OnMaximizeButtonClick();
                else if (sparam == "CloseButton") OnCloseButtonClick();
                else if (StringFind(sparam, "QuickMessageButton") != -1)
                {
                    int index = StringToInteger(StringSubstr(sparam, 18));
                    OnQuickMessageButtonClick(index - 1);
                }
                break;
    
            case CHARTEVENT_OBJECT_ENDEDIT:
                if (sparam == "InputBox") OnInputChange();
                break;
        }
    }
    
    //+------------------------------------------------------------------+
    //| Handle custom message send button click                          |
    //+------------------------------------------------------------------+
    void OnSendButtonClick()
    {
        string message = inputBox.Text();
        if (message != "")
        {
            if (SendMessageToTelegram(message))
                Print("Custom message sent: ", message);
            else
                Print("Failed to send custom message.");
        }
        else
        {
            Print("No message entered.");
        }
    }
    
    //+------------------------------------------------------------------+
    //| Handle clear button click                                        |
    //+------------------------------------------------------------------+
    void OnClearButtonClick()
    {
        inputBox.Text(""); // Clear the text in the input box
        OnInputChange();   // Update the character counter
        Print("Input box cleared.");
    }
    
    //+------------------------------------------------------------------+
    //| Handle quick message button click                                |
    //+------------------------------------------------------------------+
    void OnQuickMessageButtonClick(int index)
    {
        string quickMessages[8] = { QuickMessage1, QuickMessage2, QuickMessage3, QuickMessage4, QuickMessage5, QuickMessage6, QuickMessage7, QuickMessage8 };
        string message = quickMessages[index];
    
        if (SendMessageToTelegram(message))
            Print("Quick message sent: ", message);
        else
            Print("Failed to send quick message.");
    }
    
    //+------------------------------------------------------------------+
    //| Update character counter                                         |
    //+------------------------------------------------------------------+
    void OnInputChange()
    {
        int currentLength = StringLen(inputBox.Text());
        charCounter.Text(IntegerToString(currentLength) + "/" + IntegerToString(MAX_MESSAGE_LENGTH));
        ChartRedraw();
    }
    
    //+------------------------------------------------------------------+
    //| Handle toggle theme button click                                 |
    //+------------------------------------------------------------------+
    void OnToggleThemeButtonClick()
    {
        darkTheme = !darkTheme;
        color bgColor = darkTheme ? clrBlack : clrWhite;
        color textColor = darkTheme ? clrWhite : clrBlack;
    
        // Set text color appropriate to the theme
        inputBox.Color(textColor);
        clearButton.Color(textColor);
        sendButton.Color(textColor);
        toggleThemeButton.Color(textColor);
        changeFontButton.Color(textColor);
    
        for(int i = 0; i < ArraySize(quickMessageButtons); i++)
        {
            quickMessageButtons[i].Color(textColor);
        }
    
        charCounter.Color(textColor);
    
        CreateOrUpdateBackground(ChartID(), bgColor);
    
        ChartRedraw();
    }
    
    //+------------------------------------------------------------------+
    //| Create and update background rectangle                           |
    //+------------------------------------------------------------------+
    void CreateOrUpdateBackground(long chart_id, color bgColor)
    {
        if (!ObjectFind(chart_id, BG_RECT_NAME))
        {
            if (!ObjectCreate(chart_id, BG_RECT_NAME, OBJ_RECTANGLE, 0, 0, 0))
                Print("Failed to create background rectangle");
        }
    
        ObjectSetInteger(chart_id, BG_RECT_NAME, OBJPROP_COLOR, bgColor);
        ObjectSetInteger(chart_id, BG_RECT_NAME, OBJPROP_BACK, true);
        ObjectSetInteger(chart_id, BG_RECT_NAME, OBJPROP_SELECTABLE, false);
        ObjectSetInteger(chart_id, BG_RECT_NAME, OBJPROP_SELECTED, false);
        ObjectSetInteger(chart_id, BG_RECT_NAME, OBJPROP_HIDDEN, false);
        ObjectSetInteger(chart_id, BG_RECT_NAME, OBJPROP_CORNER, CORNER_LEFT_UPPER);
        ObjectSetInteger(chart_id, BG_RECT_NAME, OBJPROP_XOFFSET, 25); 
        ObjectSetInteger(chart_id, BG_RECT_NAME, OBJPROP_YOFFSET, 25);
    }
    
    //+------------------------------------------------------------------+
    //| Handle change font button click                                  |
    //+------------------------------------------------------------------+
    void OnChangeFontButtonClick()
    {
        currentFontIndex = (currentFontIndex + 1) % ArraySize(availableFonts);
        
        inputBox.Font(availableFonts[currentFontIndex]);
        clearButton.Font(availableFonts[currentFontIndex]);
        sendButton.Font(availableFonts[currentFontIndex]);
        toggleThemeButton.Font(availableFonts[currentFontIndex]);
        changeFontButton.Font(availableFonts[currentFontIndex]);
        
        for(int i = 0; i < ArraySize(quickMessageButtons); i++)
        {
            quickMessageButtons[i].Font(availableFonts[currentFontIndex]);
        }
    
        Print("Font changed to: ", availableFonts[currentFontIndex]);
        ChartRedraw();
    }
    
    //+------------------------------------------------------------------+
    //| Handle minimize button click                                     |
    //+------------------------------------------------------------------+
    void OnMinimizeButtonClick()
    {
        minimized = true;
        adminPanel.Hide();
        minimizeButton.Hide();
        maximizeButton.Show();
        closeButton.Show();
        Print("Panel minimized.");
    }
    
    //+------------------------------------------------------------------+
    //| Handle maximize button click                                     |
    //+------------------------------------------------------------------+
    void OnMaximizeButtonClick()
    {
        if (minimized)
        {
            adminPanel.Show();
            minimizeButton.Show();
            maximizeButton.Hide();
            closeButton.Hide();
            Print("Panel maximized.");
        }
    }
    
    //+------------------------------------------------------------------+
    //| Handle close button click                                        |
    //+------------------------------------------------------------------+
    void OnCloseButtonClick()
    {
        ExpertRemove(); // Completely remove the EA
        Print("Admin Panel closed.");
    }
    
    //+------------------------------------------------------------------+
    //| Send the message to Telegram                                     |
    //+------------------------------------------------------------------+
    bool SendMessageToTelegram(string message)
    {
        string url = "https://api.telegram.org/bot" + InputBotToken + "/sendMessage";
        string jsonMessage = "{\"chat_id\":\"" + InputChatId + "\", \"text\":\"" + message + "\"}";
        char post_data[];
        ArrayResize(post_data, StringToCharArray(jsonMessage, post_data, 0, WHOLE_ARRAY) - 1);
    
        int timeout = 5000;
        char result[];
        string responseHeaders;
    
        int res = WebRequest("POST", url, "Content-Type: application/json\r\n", timeout, post_data, result, responseHeaders);
    
        if (res == 200) // HTTP 200 OK
        {
            Print("Message sent successfully: ", message);
            return true;
        }
        else
        {
            Print("Failed to send message. HTTP code: ", res, " Error code: ", GetLastError());
            Print("Response: ", CharArrayToString(result));
            return false;
        }
    }


    Pruebas de las nuevas funciones

    Nuevas funciones probadas en XAUUSD.

    Una vez aplicadas estas técnicas básicas de estilización, podemos explorar opciones de personalización más avanzadas que aporten mayor interactividad y atractivo visual a la interfaz gráfica de usuario. De la imagen anterior, podemos ver que nuestro tema sólo está trabajando en el texto en primer plano, sin embargo, queremos que también afectan el fondo del panel. En el próximo segmento, abordaremos las formas de resolver este problema.

    Mejora avanzada de la interfaz gráfica de usuario

    Ampliación de la clase de diálogo para la gestión de temas:

    Para ampliar Dialog para la gestión de temas, podemos personalizar la clase de diálogo existente para que admita cambios dinámicos de tema, de forma similar a como gestionamos los temas en el Panel de administración. Esto implicaría modificar o subclasificar la clase CDialog para incluir propiedades para los colores de fondo y texto, así como métodos para aplicar diferentes temas (claro u oscuro). Anulando el constructor o añadiendo métodos como "ApplyTheme", podemos asegurarnos de que los cuadros de diálogo creados con esta clase respondan a los cambios de tema sin tener que volver a crear los objetos de diálogo.

    Personalización de la clase Dialog

    Personalización de los colores en la clase Dialog.

    ¿Por qué es importante? 

    La ampliación de la clase Dialog para la gestión de temas permite una experiencia de usuario más fluida y cohesiva en todos los elementos de la interfaz de usuario, no solo en el Panel de administración. Asegura que todas las partes de la aplicación, incluidos los cuadros de diálogo, se adhieran al tema elegido, mejorando tanto la usabilidad como la consistencia visual. Esta característica se vuelve particularmente importante en aplicaciones comerciales donde los usuarios pueden pasar períodos prolongados interactuando con la interfaz, y los temas personalizables pueden reducir la fatiga visual y mejorar la satisfacción general del usuario.

    Tema de fondo después de modificar la clase Dialog

    Panel de administración: Tema de fondo después de modificar la clase Dialog.

    Otras opciones:

    Si bien ampliar la clase Dialog es un enfoque directo y flexible, otra opción es aplicar la gestión de temas a un nivel superior. Por ejemplo, podríamos crear un sistema de gestión de temas global que actualice automáticamente las propiedades de todos los elementos de la interfaz de usuario, incluidos los cuadros de diálogo, sin necesidad de realizar cambios en los componentes individuales. Además, aprovechar bibliotecas externas o diseñar un marco de diálogo personalizado podría ofrecer un control más granular sobre los elementos de la interfaz de usuario si surgen necesidades de estilo específicas.

    Clase CEdit

    Según la búsqueda en Google, la longitud máxima de un mensaje de Telegram es de 4096 caracteres y debe estar codificado en UTF-8. Al intentar implementar el valor en este proyecto, nos vimos limitados a un máximo de 63 caracteres y el problema debe estar dentro de la limitación de la clase CEdit que abordaremos en el próximo artículo.

    Al editar archivos de biblioteca MQL5, tenga cuidado, ya que las modificaciones inadecuadas pueden provocar errores de compilación o problemas en tiempo de ejecución debido a la estricta sintaxis y estructura del lenguaje, que está orientado a objetos. Haga siempre una copia de seguridad de los archivos originales antes de realizar cambios, y asegúrese de que entiende las relaciones de clase implicadas para evitar romper la funcionalidad. Tras la edición, compile el código para comprobar si hay errores y pruebe a fondo los cambios en un entorno de demostración para verificar la estabilidad. Si te encuentras con problemas importantes que no puedes resolver, considera la posibilidad de reinstalar la plataforma MetaTrader 5 para restaurar la configuración y los archivos por defecto.



    Conclusión

    En conclusión, nuestra implementación de la gestión de fuentes y temas en el programa Admin Panel ha demostrado resultados prometedores. Aunque el fondo estático de la clase de diálogo nos planteaba limitaciones, el primer plano de texto se adaptaba con éxito a los cambios de tema, lo que mejoraba la experiencia del usuario. La gestión dinámica de fuentes también funcionó bien, permitiendo a los usuarios cambiar entre diferentes fuentes con facilidad.

    De ahora en adelante, nuestro próximo objetivo será ampliar la clase de diálogo para que admita totalmente los cambios de tema, incluidas las actualizaciones dinámicas de fondo. Esta mejora tiene como objetivo superar las limitaciones actuales y proporcionar una interfaz más cohesiva y visualmente atractiva. ¡Permanezca atento mientras abordamos estos desafíos en nuestros próximos artículos!
    Pruebe estas técnicas de estilo en sus propios paneles comerciales y explore opciones de personalización adicionales en MQL5. Me encantaría conocer sus experiencias y conocimientos, así que no dude en compartirlos en los comentarios a continuación mientras profundizamos en desafíos de diseño de GUI más avanzados. Se adjunta el archivo fuente de este proyecto: no dudes en revisarlo.

    Contenido

    Traducción del inglés realizada por MetaQuotes Ltd.
    Artículo original: https://www.mql5.com/en/articles/15419

    Archivos adjuntos |
    Admin_Panel.mq5 (14.67 KB)
    SERGEI NAIDENOV
    SERGEI NAIDENOV | 23 may 2025 en 19:09
    al intentar compilar genera una advertencia:

    posible pérdida de datos debido a la conversión de tipo de 'long' a 'int' Admin_Panel.mq5 208 27

    línea de código:
    int index = StringToInteger(StringSubstr(sparam, 18));
    si lo haces de esta manera, todo es sin errores:
    int index = int(StringToInteger(StringSubstr(sparam, 18)));

    si se adjunta a un gráfico y se intenta minimizar, luego expandir... el botón para cerrar "X" (borrar EA) no funciona en el estado expandido del panel. Si lo minimizas, funciona.

    pregunta: ¿mover la ventana en el gráfico no está implementado?
    Soluciones sencillas para trabajar cómodamente con indicadores Soluciones sencillas para trabajar cómodamente con indicadores
    En este artículo le contaremos cómo crear un panel simple para cambiar la configuración del indicador directamente desde el gráfico, y qué cambios se deberán introducir en el indicador para conectar este panel. Este artículo está dirigido exclusivamente a aquellos que acaban de empezar a familiarizarse con MQL5.
    Cómo implementar la optimización automática en los asesores expertos de MQL5 Cómo implementar la optimización automática en los asesores expertos de MQL5
    Guía paso a paso para la optimización automática en MQL5 para Asesores Expertos. Cubriremos la lógica de optimización robusta, las mejores prácticas para la selección de parámetros y cómo reconstruir estrategias con pruebas retrospectivas. Además, se discutirán métodos de nivel superior, como la optimización del avance, para mejorar su enfoque comercial.
    Algoritmo de tiro con arco - Archery Algorithm (AA) Algoritmo de tiro con arco - Archery Algorithm (AA)
    Este artículo detalla un algoritmo de optimización inspirado en el tiro con arco, centrado en el uso del método de la ruleta como mecanismo de selección de zonas prometedoras para las "flechas". Este método nos permite evaluar la calidad de las soluciones y seleccionar las más prometedoras para seguir estudiándolas.
    Ejemplo de Análisis de Redes de Causalidad (CNA), Control Óptimo de Modelos Estocásticos (SMOC) y la Teoría de Juegos de Nash con Aprendizaje Profundo (Deep Learning) Ejemplo de Análisis de Redes de Causalidad (CNA), Control Óptimo de Modelos Estocásticos (SMOC) y la Teoría de Juegos de Nash con Aprendizaje Profundo (Deep Learning)
    Agregaremos Deep Learning a esos tres ejemplos que se publicaron en artículos anteriores y compararemos los resultados con los anteriores. El objetivo es aprender cómo agregar DL (Deep Learning) a otro EA.