English 中文 Español Deutsch 日本語
preview
Создание торговой панели администратора на MQL5 (Часть III): Улучшение графического интерфейса пользователя (GUI) с помощью визуального оформления (I)

Создание торговой панели администратора на MQL5 (Часть III): Улучшение графического интерфейса пользователя (GUI) с помощью визуального оформления (I)

MetaTrader 5Примеры | 17 апреля 2025, 07:54
395 1
Clemence Benjamin
Clemence Benjamin

Содержание:

  1. Введение
  2. Важность визуально привлекательного графического интерфейса пользователя (GUI)
  3. Применение функций стилизации графического интерфейса пользователя (GUI) на MQL5
  4. Настройка цветов и шрифтов
  5. Логика управления темами
  6. Настройка нового расположения кнопок
  7. Расширенное усовершенствование графического интерфейса пользователя (GUI)
  8. Заключение

Введение

Глядя на цели, изложенные в нашей предыдущей статье, можем ли мы с уверенностью сказать, что сделали достаточно? На мой взгляд, то, что я вижу, вдохновляет на стремление выйти за рамки наших текущих предложений. Представьте, как было бы полезно реализовать переключение между темной и светлой темами в нашей панели администратора. Кроме того, мы могли бы улучшить впечатления пользователя, добавив стильные кнопки, предложив широкий выбор шрифтов и включив возможность переключения между основными языками. Это сделало бы нашу панель более удобной для всех пользователей.

Наша цель - предоставить торговым администраторам комплексное коммуникационное решение, интегрированное в торговую платформу. Концепции, которые мы стремимся внедрить, вдохновлены влиянием исследований и разработок в области графических пользовательских интерфейсов (GUI), проводившихся с 1970-х годов. Перечень известных авторов включает в себя: Alan Kay, Xerox PARC, Apple (macOS), Microsoft (Windows), CSS (Cascading Style Sheets), а также Material Design от Google. Используя эти данные, мы можем создать панель администратора,  отвечающую потребностям пользователей и улучшающую их общее впечатление.



Simple Admin with Quick Message buttons

Базовая панель администратора, разработанная нами на данный момент.

Подведем итоги того, чего мы достигли на данный момент:

К концу настоящей статьи у нас будет полностью настроенная и визуально оформленная торговая панель администратора на MQL5. Вы узнаете, как использовать различные методы стилизации, которые улучшают как внешний вид, так и функциональность интерфейса, создавая профессиональную и удобную среду для трейдеров.

Вот основные цели данной статьи:
  • Применение основных приемов стилизации с использованием MQL5
  • Настройка шрифтов, цветов и расположения
  • Улучшение взаимодействия пользователя с визуальными элементами
  • Внедрение возможности настройки между светлыми и темными темами.
  • Добавление динамических функций, таких как анимация и переходы

MQL5 предоставляет различные функции и опции для оформления GUI вашего торгового приложения. Они включают в себя опции настройки цветов, шрифтов и расположения в соответствии с потребностями ваших пользователей и общей эстетикой дизайна, которую вы хотите достичь.

Оформление GUI в MQL5 предполагает использование нескольких ключевых функций и приемов.  Мы подробно обсудим функции, позволяющие нам изменять свойства графических объектов, таких как кнопки, метки и панели. С их помощью мы можем настроить цвет фона, стиль рамки, размер шрифта и другие визуальные аспекты для создания единого внешнего вида.

  1. Настройка цветов и шрифтов
  2. Логика управления темами
  3. Настройка нового расположения кнопок

    Настройка цветов и шрифтов:

    Массив шрифтов и индекс:

    Начинаем с определения массива availableFonts и currentFontIndex для управления выбором шрифтов в панели администратора. Массив availableFonts включает в себя такие названия шрифтов, как "Arial", "Courier New", "Verdana" и "Times New Roman", что предоставляет пользователям широкий спектр возможностей для настройки внешнего вида панели. currentFontIndex отслеживает выбранный шрифт путем индексирования в этот массив. Данная настройка позволяет нам легко переключаться между шрифтами и применять их к компонентам пользовательского интерфейса всякий раз, когда пользователь меняет шрифт, гарантируя, что пользовательский опыт остается динамичным и целостным.


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


    Создание кнопки изменения шрифта:

    Создадим кнопку с меткой "Font<>," которую мы стратегически размещаем на панели администратора. Эта кнопка - не просто одна из кнопок; это ключевая функция для изменения шрифтов. Мы проследим за тем, чтобы она хорошо вписалась в макет панели, и решим любые вопросы, связанные с её созданием. Добавляя эту кнопку, мы предоставляем пользователям интуитивно понятный способ переключения между различными шрифтами, повышая удобство использования панели и эстетическую гибкость. Если при создании кнопки возникает загвоздка, выводим сообщение об ошибке, чтобы отслеживать любые неполадки.
    // 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.");
    }
    

    Обработка нажатия кнопки изменения шрифта:

    При реализации функции OnChangeFontButtonClick, наша цель - обеспечить плавное управление процессом смены шрифта. Данная функция обновляет currentFontIndex, чтобы выбрать следующий шрифт в массиве availableFonts, при необходимости возвращаясь к началу. После обновления индекса мы применяем новый шрифт ко всем соответствующим компонентам пользовательского интерфейса, таким как поле ввода, кнопка очистки и кнопка отправки, обеспечивая единообразный вид всей панели. Чтобы завершить внесение изменений, мы используем ChartRedraw для обновления дисплея и вывода сообщения с подтверждением, сообщающего пользователям, что изменение шрифта прошло успешно.
    // 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 для обработки нажатия кнопок:

    В функции OnChartEvent мы обрабатываем взаимодействие пользователя с различными объектами диаграммы, в т.ч. с нашей кнопкой изменения шрифта. Данная функция отслеживает события нажатия кнопки и проверяет, какая кнопка была нажата, проверяя строку sparam. При нажатии кнопки "ChangeFontButton", мы вызываем функцию OnChangeFontButtonClick для управления заменой шрифта. Такой основанный на событиях подход делает наш пользовательский интерфейс быстро реагирующим и интерактивным, гарантируя, что действия пользователя вызывают правильную реакцию и поддерживают привлекательный интерфейс.
    // 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();
            }
        }
    }
    

    Переключение шрифтов работает отлично

    Переключение шрифтов работает

    Логика управления темами:


    Логика управления темами:
    Мы начинаем с настройки системы управления темами с двумя различными темами: светлой и темной. Для управления переключением мы используем логическую переменную isDarkMode, отслеживающую, какая тема активна в данный момент. Переключение происходит просто: когда пользователь нажимает кнопку темы, значение isDarkMode меняется, полностью меняя внешний вид и ощущение от панели администратора. Определяя цвета для каждой темы отдельно, мы ускоряем процесс, упрощая поддержку и применение новых стилей при необходимости.
    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
    

    Создание кнопки переключения темы:
    Перейдем к созданию кнопки с меткой "Theme<>." Эта кнопка расположена внутри панели администратора и предоставляет пользователям простой способ переключения между светлым и темным режимами. Если что-то пойдет не так во время его создания, мы обязательно выведем сообщение об ошибке. Это упрощает поиск и устранение неполадок и гарантирует, что интерфейс остается интуитивно понятным и быстро реагирующим. 
    //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
    }
    

    Обработка нажатия кнопки переключения темы:
    Далее обрабатываем изменение фактической темы, реализуя функцию OnToggleModeButtonClick. Данная функция изменяет значение переменной isDarkMode, переключаясь между светлой и темной темами. Как только узнаем, какая тема активна, применим соответствующие цвета фона и текста ко всем элементам пользовательского интерфейса, таким как панель, кнопки и текст. Смена темы происходит в режиме реального времени благодаря быстрому обновлению, что делает интерфейс более плавным и быстро реагирующим. Также выводим сообщение с подтверждением для пользователей, чтобы они знали, когда режим изменился.
    //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 для обработки кнопки нажатия переключения темы:
    В функции OnChartEvent определяем, когда пользователь нажимает кнопку «Toggle Theme» ("Переключить тему»), и запускаем функцию OnToggleModeButtonClick. Такой подход, основанный на управлении событиями, гарантирует, что панель мгновенно реагирует на действия пользователя. Отслеживая события нажатия кнопок, мы следим за тем, чтобы панель администратора оставалась интерактивной и привлекательной, позволяя пользователям легко переключаться между светлой и темной темами по мере необходимости.
    //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
            }
        }
    }
    

    Применение темы без повторного создания объектов:
    Одним из наших ключевых дизайнерских решений является обновление темы без воссоздания каких-либо объектов панели. Вместо того чтобы разрушать и создавать новые компоненты пользовательского интерфейса, мы просто применяем новую цветовую схему к существующим элементам. Это поддерживает эффективность работы системы, сокращает лаги и обеспечивает бесперебойную работу пользователей. Это также гарантирует, что панель остается реагирующей при динамическом применении новых цветов.
    
    //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
    }
    

    Настройка нового расположения кнопок.

    Кнопка изменения шрифта:

     Кнопку изменения шрифта мы расположили на панели администратора так, чтобы ее верхний левый угол находился по координатам (95, 95), а нижний правый угол - (230, 115). Таким образом, она помещается слева от кнопок Send (Отправить) и Clear (Очистить). Её размеры делают её достаточно широкой для размещения метки "Font<>" и удобного взаимодействия с пользователем. Кнопка позволяет пользователям переключаться между различными вариантами шрифта для всех текстовых элементов на панели.
    if (!changeFontButton.Create(chart_id, "ChangeFontButton", 0, 95, 95, 230, 115))

    Кнопка переключения темы:

    Что касается кнопки переключения тем, мы разместили ее в координатах (5, 95) относительно верхнего левого угла и (90, 115) относительно правого нижнего угла. Таким образом, кнопка расположена в крайнем левом углу панели, немного выше кнопки изменения шрифта, что обеспечивает четкое разделение. Компактный размер и близость к другим кнопкам позволяют пользователям легко переключаться между темной и светлой темами, не загромождая интерфейс.
    if (!toggleThemeButton.Create(chart_id, "ToggleThemeButton", 0, 5, 95, 90, 115))


    Вот наша полная программа, в которую идеально интегрированы все новые функции.
    //+------------------------------------------------------------------+
    //|                                             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;
        }
    }


    New Features Testing

    Новые функции, тестируемые на XAUUSD

    Благодаря этим основополагающим методам стилизации мы теперь можем использовать более продвинутые возможности настройки, которые могут обеспечить еще большую интерактивность и визуальную привлекательность графического интерфейса. Из приведенного выше изображения мы видим, что наша тема работает только с текстом на переднем плане, но нам надо, чтобы она также влияла на фон панели. В следующем разделе мы рассмотрим способы решения этой проблемы.

    Расширенное усовершенствование GUI

    Расширение класса Dialog для управления темами:

    Чтобы расширить  Dialog для управления темами, мы можем настроить существующий класс dialog для поддержки динамических изменений тем, аналогично тому, как мы управляем темами в панели администратора. Это потребовало бы изменения или создания подкласса класса CDialog, чтобы включить свойства для цветов фона и текста, а также методы применения различных тем (светлой или темной). Переопределяя конструктор или добавляя методы, такие как ApplyTheme, мы можем гарантировать, что созданные с использованием этого класса диалоговые окна будут реагировать на изменения темы без повторного создания диалоговых объектов.

    Customizing the Dialog class

    Настройка цветов в классе Dialog

    Почему это важно? 

    Расширение класса Dialog для управления темами позволяет обеспечить более плавный и согласованный опыт для пользователя в отношении всех элементов пользовательского интерфейса, а не только в отношении панели администратора. Оно гарантирует, что все части приложения, включая диалоговые окна, соответствуют выбранной теме, повышая как удобство использования, так и визуальную согласованность. Данная функция становится особенно важной в торговых приложениях, где пользователи могут проводить длительное время, взаимодействуя с интерфейсом, а настраиваемые темы оформления могут снизить нагрузку на зрение и повысить общую удовлетворенность пользователей.

    Background theme after modifying Dialog class

    Панель администратора: фоновая тема после изменения класса Dialog

    Прочие опции:

    Хотя расширение класса Dialog является прямым и гибким подходом, другим вариантом является применение управления темами на более высоком уровне. Например, мы могли бы создать глобальную систему управления темами, которая автоматически обновляет свойства всех элементов пользовательского интерфейса, включая диалоговые окна, не требуя внесения изменений в отдельные компоненты. Кроме того, использование внешних библиотек или разработка пользовательской диалоговой платформы могут обеспечить более детальный контроль над элементами пользовательского интерфейса, если возникнут особые потребности в стилизации.

    Класс CEdit

    Согласно данным поиска в Google, максимальная длина сообщения в Telegram составляет  4096 символов  и оно должно быть закодировано в UTF-8. При попытке реализовать значение в этом проекте мы были ограничены максимум 63 символами, и проблема должна быть в пределах ограничения класса CEdit, которое мы рассмотрим в следующей статье.

    При редактировании файлов библиотеки MQL5 соблюдайте осторожность, так как неправильные изменения могут привести к ошибкам компиляции или проблемам во время выполнения из-за строгого синтаксиса и структуры языка, который является объектно-ориентированным. Перед внесением изменений всегда делайте резервные копии исходных файлов и убедитесь, что понимаете взаимосвязи классов, применяемых, чтобы избежать нарушения функциональности. После редактирования скомпилируйте свой код, чтобы проверить его на наличие ошибок, и тщательно протестируйте изменения в демонстрационной среде для проверки стабильности. Если вы столкнетесь со значительными проблемами, которые не сможете устранить, рассмотрите возможность переустановки платформы MetaTrader 5, чтобы восстановить настройки и файлы по умолчанию.



    Заключение

    В заключение, реализация нами управления шрифтами и темами в программе панель администратора, продемонстрировала многообещающие результаты. В то время как мы столкнулись с ограничениями, связанными со статичным фоном класса dialog, текстовый передний план успешно адаптировался к изменениям темы, обеспечивая улучшенный пользовательский опыт. Динамическое управление шрифтами также работало хорошо, позволяя пользователям с легкостью переключаться между различными шрифтами.

    В дальнейшем нашей следующей попыткой будет расширение класса dialog для полной поддержки изменений тем, включая динамические фоновые обновления. Данное усовершенствование направлено на преодоление существующих ограничений и создание более целостного и визуально привлекательного интерфейса. Следите за новостями! Мы расскажем об этих проблемах в наших следующих статьях!
    Опробуйте эти методы оформления на собственных торговых панелях и изучите дополнительные возможности настройки в MQL5. Буду рад услышать о вашем опыте и наблюдениях, поэтому делитесь ими в комментариях ниже, по мере перехода к более сложным задачам проектирования GUI. Исходный файл для этого проекта прилагается — не стесняйтесь ознакомиться с ним.

    Содержание

    Перевод с английского произведен MetaQuotes Ltd.
    Оригинальная статья: https://www.mql5.com/en/articles/15419

    Прикрепленные файлы |
    Admin_Panel.mq5 (14.67 KB)
    Последние комментарии | Перейти к обсуждению на форуме трейдеров (1)
    SERGEI NAIDENOV
    SERGEI NAIDENOV | 23 мая 2025 в 19:09
    при попытке компилировать выдает предупреждение:

    possible loss of data due to type conversion from 'long' to 'int' Admin_Panel.mq5 208 27

    строка кода:
    int index = StringToInteger(StringSubstr(sparam, 18));
    если сделать так, то все без ошибок:
    int index = int(StringToInteger(StringSubstr(sparam, 18)));

    если прикрепить к графику и попытаться свернуть, затем развернуть... кнопка закрытия "Х" (удаления эксперта) не работает в развернутом состоянии панели. Если свернуть то работает.

    вопрос: перемещение окна по графику не реализовано? планируется?
    Угловой анализ ценовых движений: гибридная модель прогнозирования финансовых рынков Угловой анализ ценовых движений: гибридная модель прогнозирования финансовых рынков
    Что такое угловой анализ финансовых рынков? Как использовать углы движения цен и машинное обучение для точного прогнозирования с точностью 67? Как совместить регрессионную и классификационную модель с угловыми признаками и получить работающий алгоритм? Причем тут Ганн? Почему углы движения цен являются хорошим признаком для машинного обучения?
    Как опередить любой рынок (Часть IV): Индексы волатильности евро и золота CBOE Как опередить любой рынок (Часть IV): Индексы волатильности евро и золота CBOE
    Мы проанализируем альтернативные данные, собранные Чикагской опционной биржей (Chicago Board of Options Exchange, CBOE), чтобы повысить точность наших глубоких нейронных сетей при прогнозировании символа XAUEUR.
    Самооптимизирующийся советник на языках MQL5 и Python (Часть IV): Стекинг моделей Самооптимизирующийся советник на языках MQL5 и Python (Часть IV): Стекинг моделей
    В статье мы продемонстрируем, как можно создавать торговые приложения на базе ИИ, способные учиться на собственных ошибках. Мы рассмотрим технику, известную как стекинг (stacking), при которой мы используем 2 модели для создания 1 прогноза. Первая модель, как правило, является более слабым обучающимся алгоритмом, а вторая - более мощной моделью, которая обучается на результатах более слабого алгоритма. Наша цель — создать ансамбль моделей, чтобы достичь более высокой точности.
    Оптимизация коралловых рифов — Coral Reefs Optimization (CRO) Оптимизация коралловых рифов — Coral Reefs Optimization (CRO)
    В данной статье представлен комплексный анализ алгоритма оптимизации коралловых рифов (CRO) — метаэвристического метода, вдохновленного биологическими процессами формирования и развития коралловых рифов. Алгоритм моделирует ключевые аспекты эволюции кораллов: внешнее и внутреннее размножение, оседание личинок, бесполое размножение и конкуренцию за ограниченное пространство в рифе. Особое внимание в работе уделяется усовершенствованной версии алгоритма.