English 中文 Español Deutsch 日本語 Português
preview
Создание торговой панели администратора на MQL5 (Часть III): Расширение встроенных классов для управления темами (II)

Создание торговой панели администратора на MQL5 (Часть III): Расширение встроенных классов для управления темами (II)

MetaTrader 5Примеры |
505 0
Clemence Benjamin
Clemence Benjamin

Содержание:


Введение

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

Благодаря исследованиям мы наконец определили методы безопасной интеграции функций изменения темы в доступные классы. После успешного внедрения этих изменений мы скорректировали алгоритм панели администратора для соответствия новым интегрированным функциям.

Новая тема панели

Изменение темы прошло успешно

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

Я перечислю их ниже:

  • Цвет текста
  • Цвет кнопки
  • Границы
  • Цвет фона

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

Классы в MQL5.

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

Классы: 

Классы являются основой объектно-ориентированного программирования (ООП) в MQL5, позволяя разработчикам группировать связанные переменные (атрибуты) и функции (методы) в единый блок для представления сложных концепций и поведения в программе.

Выделяют:

  1. Атрибуты: переменные, хранящие состояние или данные объектов класса.
  2. Методы: функции, определяющие поведение или действия объектов класса.

Краткое описание основных характеристик класса:

  • Инкапсуляция в классе подразумевает объединение данных (переменных) и методов (функций), которые работают с этими данными, обеспечивая их защиту от внешнего доступа и неправомерного использования.
  • Наследование позволяет классу наследовать свойства и методы другого класса, способствуя повторному использованию кода и созданию иерархической структуры.
  • Полиморфизм позволяет переопределять методы, позволяя подклассам предоставлять конкретные реализации для методов, уже определенных в их родительских классах.
  • Абстракция упрощает моделирование сложных систем, сосредотачиваясь только на релевантных данных и методах и скрывая ненужные детали от пользователя.
Чтобы получить доступ к заголовочным файлам MetaQuotes, содержащим классы графического интерфейса, полезные для нашего проекта, обратитесь к следующему изображению, на котором показано, как можно найти эти файлы.

Расположение заголовочных файлов MQ

Расположение заголовочных файлов MQL5


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

//Basic parts of a class.

class CDialog : public CWndContainer
{
public:
   // Constructor and Destructor (Methods)
   CDialog(void);   // Constructor
   ~CDialog(void);  // Destructor

   // Public Methods (Functions)
   virtual bool Create(const long chart, const string name, const int subwin, const int x1, const int y1, const int x2, const int y2);
   virtual bool OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam);
   string Caption(void) const;
   bool Caption(const string text);
   bool Add(CWnd *control);   // Add control by pointer
   bool Add(CWnd &control);   // Add control by reference
   virtual bool Save(const int file_handle);
   virtual bool Load(const int file_handle);
   void UpdateThemeColors(bool darkTheme);

protected:
   // Attributes (Variables)
   bool m_panel_flag;        // Panel visibility flag
   bool m_minimized;         // Minimized state flag
   CWnd m_caption;           // Caption control
   CWnd m_client_area;       // Client area control
   CRect m_norm_rect;        // Normal (non-minimized) rectangle
   CRect m_min_rect;         // Minimized rectangle
   CWnd m_white_border;      // White border control

   // Protected Methods (Internal functions)
   virtual bool CreateWhiteBorder(void);
   virtual bool CreateBackground(void);
   virtual bool CreateCaption(void);
   virtual bool CreateButtonClose(void);
   virtual bool CreateClientArea(void);
   virtual void OnClickCaption(void);
   virtual void OnClickButtonClose(void);
   virtual bool OnDialogDragStart(void);
   virtual bool OnDialogDragProcess(void);
   virtual bool OnDialogDragEnd(void);
};

В этой таблице представлен обзор атрибутов, доступных в приведенных выше фрагментах кода, и их описание.

Атрибуты (свойства) Описание
bool m_panel_flag;

 Флаг, указывающий, видна ли панель.
bool m_minimized;

 Флаг, указывающий, свернуто ли диалоговое окно
CWnd m_caption;

Управление текстом подписи.
CWnd m_client_area;

Управление клиентской областью, где находятся другие элементы.
CRect m_norm_rect;

Координаты для нормального (не минимизированного) состояния.
CRect m_min_rect;

Координаты для свернутого состояния.
CWnd m_white_border;

Управление белой рамкой вокруг диалогового окна.

В этой таблице обобщены методы, используемые в примере кода класса.

 Методы Описание
CDialog(void)
Конструктор, инициализирующий диалог.
~CDialog(void)
Деструктор для очистки ресурсов.
Create(...)

Создает диалоговое окно и его элементы управления.
 
OnEvent(...)

  Обрабатывает события графика для диалога.
Caption(void)

Возвращает текущий текст подписи.
Caption(const string text)

Устанавливает текст подписи.
Add(CWnd *control)

Добавляет элемент управления в клиентскую область с помощью указателя.
Add(CWnd &control)

Добавляет элемент управления в клиентскую область по ссылке.
Save(const int file_handle)

Сохраняет состояние диалога в файл.
Load(const int file_handle)

Загружает состояние диалога из файла.
UpdateThemeColors(bool darkTheme)

Обновляет цвета темы (темные или светлые).
CreateWhiteBorder(void)
Создает белую рамку для диалогового окна.
 
CreateBackground(void)
   Создает фон диалогового окна.
 
CreateCaption(void)

   Создает область подписи.
 
CreateButtonClose(void)
 Создает кнопку закрытия.   
 
CreateClientArea(void)

  Создает клиентскую зону.
 
OnClickCaption(void)
   Обрабатывает событие щелчка по заголовку.
 
OnClickButtonClose(void)

   Обрабатывает событие нажатия кнопки закрытия.
 
OnDialogDragStart(void)

   Обрабатывает начало события перетаскивания диалогового окна.
 
OnDialogDragProcess(void)

  Управляет процессом перетаскивания диалогового окна.
 
OnDialogDragEnd(void)
   Обрабатывает событие перетаскивания конца диалогового окна.

Давайте кратко рассмотрим один из основных классов, который мы используем в нашей программе ниже. 


Добавление методов управления темами в (CDialog, CEdit и CButton)

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


Методы управления темами CDialog:


CDialog:

Класс CDialog в MQL5 отвечает за создание и управление пользовательскими графическими диалоговыми окнами или панелями в платформе MetaTrader 5. Он позволяет разработчикам создавать диалоговые окна, содержащие компоненты пользовательского интерфейса, такие как заголовки, клиентские области, границы и кнопки закрытия. Класс обрабатывает такие взаимодействия с пользователем, как нажатие и перетаскивание диалогового окна, а также динамическое обновление его темы (например, переключение между светлой и темной темами). Кроме того, он предоставляет методы для сохранения и загрузки состояния диалогового окна, гарантируя сохранение его размера, положения и статуса минимизации. В диалоговое окно можно добавлять элементы управления, такие как кнопки и текстовые поля, что делает его универсальным инструментом для создания интерактивных и визуально привлекательных интерфейсов в торговых приложениях.

В классе CDialog мы ввели метод обработки динамических обновлений темы. Этот метод отвечает за обновление визуального вида диалога в зависимости от того, активна ли темная тема (darkTheme) или нет. Вот как этот метод реализован и как он связан с другими компонентами класса CDialog. Я объясню в два этапа. Однако вы можете пропустить первый этап, если не собираетесь определять цвета.

Этап 1: Определение цветов темы

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

// Theme colors that can be defined elsewhere in our program
const color DARK_THEME_BG = clrBlack;
const color DARK_THEME_BORDER = clrGray;
const color LIGHT_THEME_BG = clrWhite;
const color LIGHT_THEME_BORDER = clrSilver;


Шаг 2: Метод обновления цветов темы

Эта функция проверяет, активна ли darkTheme (true или false), и применяет соответствующие цвета к ключевым компонентам: белая рамка (m_white_border) обновляется цветами фона и рамки; фон (m_background) изменяет цвета фона и рамки; заголовок (m_caption) изменяет цвета текста и фона строки заголовка; а клиентская область (m_client_area) применяет изменения цвета к клиентской области. Наконец, функция вызывает Redraw(), чтобы гарантировать визуальное применение новой темы без повторного создания объектов. Если вы перешли ко второму шагу, то выделенные определения цветов не будут работать, и цвет необходимо будет указать, например, ClrBlack или ClrBlue и так далее


//+------------------------------------------------------------------+
//| Method for dynamic theme updates                                 |
//+------------------------------------------------------------------+


void CDialog::UpdateThemeColors(bool darkTheme)
{
   color backgroundColor = darkTheme ? DARK_THEME_BG : LIGHT_THEME_BG;
   color borderColor = darkTheme ? DARK_THEME_BORDER : LIGHT_THEME_BORDER;

   // Update White Border colors
   m_white_border.ColorBackground(backgroundColor);
   m_white_border.ColorBorder(borderColor);

   // Update Background colors
   m_background.ColorBackground(backgroundColor);
   m_background.ColorBorder(borderColor);

   // Update Caption colors (optional for text-based themes)
   m_caption.Color(darkTheme ? clrWhite : clrBlack);
   m_caption.ColorBackground(backgroundColor);

   // Update Client Area colors
   m_client_area.ColorBackground(backgroundColor);
   m_client_area.ColorBorder(borderColor);

   // Redraw the controls to reflect the theme changes
   Redraw();
}


Управление темами класса CButton

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

 //--- theme methods
   void              SetTextColor(color clr)       { m_button.Color(clr);                           }
   void              SetBackgroundColor(color clr) { m_button.BackColor(clr);                       }
   void              SetBorderColor(color clr)     { m_button.BorderColor(clr);                     }

Программа CButton по умолчанию из MQL5

//+------------------------------------------------------------------+
//|                                                       Button.mqh |
//|                             Copyright 2000-2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include "WndObj.mqh"
#include <ChartObjects\ChartObjectsTxtControls.mqh>
//+------------------------------------------------------------------+
//| Class CButton                                                    |
//| Usage: control that is displayed by                              |
//|             the CChartObjectButton object                        |
//+------------------------------------------------------------------+
class CButton : public CWndObj
  {
private:
   CChartObjectButton m_button;             // chart object

public:
                     CButton(void);
                    ~CButton(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- state
   bool              Pressed(void)          const { return(m_button.State());                       }
   bool              Pressed(const bool pressed)  { return(m_button.State(pressed));                }
   //--- properties
   bool              Locking(void)          const { return(IS_CAN_LOCK);                            }
   void              Locking(const bool flag);

protected:
   //--- handlers of object settings
   virtual bool      OnSetText(void)              { return(m_button.Description(m_text));           }
   virtual bool      OnSetColor(void)             { return(m_button.Color(m_color));                }
   virtual bool      OnSetColorBackground(void)   { return(m_button.BackColor(m_color_background)); }
   virtual bool      OnSetColorBorder(void)       { return(m_button.BorderColor(m_color_border));   }
   virtual bool      OnSetFont(void)              { return(m_button.Font(m_font));                  }
   virtual bool      OnSetFontSize(void)          { return(m_button.FontSize(m_font_size));         }
   //--- internal event handlers
   virtual bool      OnCreate(void);
   virtual bool      OnShow(void);
   virtual bool      OnHide(void);
   virtual bool      OnMove(void);
   virtual bool      OnResize(void);
   //--- íîâûå îáðàáîò÷èêè
   virtual bool      OnMouseDown(void);
   virtual bool      OnMouseUp(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CButton::CButton(void)
  {
   m_color           =CONTROLS_BUTTON_COLOR;
   m_color_background=CONTROLS_BUTTON_COLOR_BG;
   m_color_border    =CONTROLS_BUTTON_COLOR_BORDER;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CButton::~CButton(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CButton::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- call method of the parent class
   if(!CWndObj::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create the chart object
   if(!m_button.Create(chart,name,subwin,x1,y1,Width(),Height()))
      return(false);
//--- call the settings handler
   return(OnChange());
  }
//+------------------------------------------------------------------+
//| Locking flag                                                     |
//+------------------------------------------------------------------+
void CButton::Locking(const bool flag)
  {
   if(flag)
      PropFlagsSet(WND_PROP_FLAG_CAN_LOCK);
   else
      PropFlagsReset(WND_PROP_FLAG_CAN_LOCK);
  }
//+------------------------------------------------------------------+
//| Create object on chart                                           |
//+------------------------------------------------------------------+
bool CButton::OnCreate(void)
  {
//--- create the chart object by previously set parameters
   return(m_button.Create(m_chart_id,m_name,m_subwin,m_rect.left,m_rect.top,m_rect.Width(),m_rect.Height()));
  }
//+------------------------------------------------------------------+
//| Display object on chart                                          |
//+------------------------------------------------------------------+
bool CButton::OnShow(void)
  {
   return(m_button.Timeframes(OBJ_ALL_PERIODS));
  }
//+------------------------------------------------------------------+
//| Hide object from chart                                           |
//+------------------------------------------------------------------+
bool CButton::OnHide(void)
  {
   return(m_button.Timeframes(OBJ_NO_PERIODS));
  }
//+------------------------------------------------------------------+
//| Absolute movement of the chart object                            |
//+------------------------------------------------------------------+
bool CButton::OnMove(void)
  {
//--- position the chart object
   return(m_button.X_Distance(m_rect.left) && m_button.Y_Distance(m_rect.top));
  }
//+------------------------------------------------------------------+
//| Resize the chart object                                          |
//+------------------------------------------------------------------+
bool CButton::OnResize(void)
  {
//--- resize the chart object
   return(m_button.X_Size(m_rect.Width()) && m_button.Y_Size(m_rect.Height()));
  }
//+------------------------------------------------------------------+
//| Handler of click on the left mouse button                        |
//+------------------------------------------------------------------+
bool CButton::OnMouseDown(void)
  {
   if(!IS_CAN_LOCK)
      Pressed(!Pressed());
//--- call of the method of the parent class
   return(CWnd::OnMouseDown());
  }
//+------------------------------------------------------------------+
//| Handler of click on the left mouse button                        |
//+------------------------------------------------------------------+
bool CButton::OnMouseUp(void)
  {
//--- depress the button if it is not fixed
   if(m_button.State() && !IS_CAN_LOCK)
      m_button.State(false);
//--- call of the method of the parent class
   return(CWnd::OnMouseUp());
  }
//+------------------------------------------------------------------+


CButton со встроенным методом управления темами:

Обратите внимание на выделенный раздел.

//+------------------------------------------------------------------+
//|                                                       Button.mqh |
//|                             Copyright 2000-2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include "WndObj.mqh"
#include <ChartObjects\ChartObjectsTxtControls.mqh>
//+------------------------------------------------------------------+
//| Class CButton                                                    |
//| Usage: control that is displayed by                              |
//|             the CChartObjectButton object                        |
//+------------------------------------------------------------------+
class CButton : public CWndObj
  {
private:
   CChartObjectButton m_button;             // chart object

public:
                     CButton(void);
                    ~CButton(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- state
   bool              Pressed(void)          const { return(m_button.State());                       }
   bool              Pressed(const bool pressed)  { return(m_button.State(pressed));                }
   //--- properties
   bool              Locking(void)          const { return(IS_CAN_LOCK);                            }
   void              Locking(const bool flag);
   
   //--- theme methods
   void              SetTextColor(color clr)       { m_button.Color(clr);                           }
   void              SetBackgroundColor(color clr) { m_button.BackColor(clr);                       }
   void              SetBorderColor(color clr)     { m_button.BorderColor(clr);                     }

protected:
   //--- handlers of object settings
   virtual bool      OnSetText(void)              { return(m_button.Description(m_text));           }
   virtual bool      OnSetColor(void)             { return(m_button.Color(m_color));                }
   virtual bool      OnSetColorBackground(void)   { return(m_button.BackColor(m_color_background)); }
   virtual bool      OnSetColorBorder(void)       { return(m_button.BorderColor(m_color_border));   }
   virtual bool      OnSetFont(void)              { return(m_button.Font(m_font));                  }
   virtual bool      OnSetFontSize(void)          { return(m_button.FontSize(m_font_size));         }
   //--- internal event handlers
   virtual bool      OnCreate(void);
   virtual bool      OnShow(void);
   virtual bool      OnHide(void);
   virtual bool      OnMove(void);
   virtual bool      OnResize(void);
   virtual bool      OnMouseDown(void);
   virtual bool      OnMouseUp(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CButton::CButton(void)
  {
   m_color           =CONTROLS_BUTTON_COLOR;
   m_color_background=CONTROLS_BUTTON_COLOR_BG;
   m_color_border    =CONTROLS_BUTTON_COLOR_BORDER;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CButton::~CButton(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CButton::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- call method of the parent class
   if(!CWndObj::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create the chart object
   if(!m_button.Create(chart,name,subwin,x1,y1,Width(),Height()))
      return(false);
//--- call the settings handler
   return(OnChange());
  }
//+------------------------------------------------------------------+
//| Locking flag                                                     |
//+------------------------------------------------------------------+
void CButton::Locking(const bool flag)
  {
   if(flag)
      PropFlagsSet(WND_PROP_FLAG_CAN_LOCK);
   else
      PropFlagsReset(WND_PROP_FLAG_CAN_LOCK);
  }
//+------------------------------------------------------------------+
//| Create object on chart                                           |
//+------------------------------------------------------------------+
bool CButton::OnCreate(void)
  {
//--- create the chart object by previously set parameters
   return(m_button.Create(m_chart_id,m_name,m_subwin,m_rect.left,m_rect.top,m_rect.Width(),m_rect.Height()));
  }
//+------------------------------------------------------------------+
//| Display object on chart                                          |
//+------------------------------------------------------------------+
bool CButton::OnShow(void)
  {
   return(m_button.Timeframes(OBJ_ALL_PERIODS));
  }
//+------------------------------------------------------------------+
//| Hide object from chart                                           |
//+------------------------------------------------------------------+
bool CButton::OnHide(void)
  {
   return(m_button.Timeframes(OBJ_NO_PERIODS));
  }
//+------------------------------------------------------------------+
//| Absolute movement of the chart object                            |
//+------------------------------------------------------------------+
bool CButton::OnMove(void)
  {
//--- position the chart object
   return(m_button.X_Distance(m_rect.left) && m_button.Y_Distance(m_rect.top));
  }
//+------------------------------------------------------------------+
//| Resize the chart object                                          |
//+------------------------------------------------------------------+
bool CButton::OnResize(void)
  {
//--- resize the chart object
   return(m_button.X_Size(m_rect.Width()) && m_button.Y_Size(m_rect.Height()));
  }
//+------------------------------------------------------------------+
//| Handler of click on the left mouse button                        |
//+------------------------------------------------------------------+
bool CButton::OnMouseDown(void)
  {
   if(!IS_CAN_LOCK)
      Pressed(!Pressed());
//--- call of the method of the parent class
   return(CWnd::OnMouseDown());
  }
//+------------------------------------------------------------------+
//| Handler of click on the left mouse button                        |
//+------------------------------------------------------------------+
bool CButton::OnMouseUp(void)
  {
//--- depress the button if it is not fixed
   if(m_button.State() && !IS_CAN_LOCK)
      m_button.State(false);
//--- call of the method of the parent class
   return(CWnd::OnMouseUp());
  }
//+------------------------------------------------------------------+


Управление темами класса CEdit

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

В классе CEdit по умолчанию уже есть методы для установки цветов (OnSetColor, OnSetColorBackground и OnSetColorBorder). Мы можем использовать эти методы для обновления внешнего вида объекта CEdit при изменении темы. Мы применяем новые методы для переключения тем, например, добавляя методы SetTextColor, SetBackgroundColor и SetBorderColor в класс CEdit. Эти методы обновляют соответствующие цвета и вызывают существующие методы (OnSetColor, OnSetColorBackground, OnSetColorBorder) для применения изменений к объекту графика.

//+------------------------------------------------------------------+
//| Set text color                                                   |
//+------------------------------------------------------------------+
bool CEdit::SetTextColor(const color clr)
  {
   m_color = clr;
   return(OnSetColor());
  }

//+------------------------------------------------------------------+
//| Set background color                                             |
//+------------------------------------------------------------------+
bool CEdit::SetBackgroundColor(const color clr)
  {
   m_color_background = clr;
   return(OnSetColorBackground());
  }

//+------------------------------------------------------------------+
//| Set border color                                                 |
//+------------------------------------------------------------------+
bool CEdit::SetBorderColor(const color clr)
  {
   m_color_border = clr;
   return(OnSetColorBorder());
  }

Ниже мы рассмотрим неотредактированный исходный код класса CEdit и перейдем к представлению встроенной программы, расположенной сразу под ним.

CEdit по умолчанию из MQL5:

//+------------------------------------------------------------------+
//|                                                         Edit.mqh |
//|                             Copyright 2000-2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include "WndObj.mqh"
#include <ChartObjects\ChartObjectsTxtControls.mqh>
//+------------------------------------------------------------------+
//| Class CEdit                                                      |
//| Usage: control that is displayed by                              |
//|             the CChartObjectEdit object                          |
//+------------------------------------------------------------------+
class CEdit : public CWndObj
  {
private:
   CChartObjectEdit  m_edit;                // chart object
   //--- parameters of the chart object
   bool              m_read_only;           // "read-only" mode flag
   ENUM_ALIGN_MODE   m_align_mode;          // align mode

public:
                     CEdit(void);
                    ~CEdit(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- parameters of the chart object
   bool              ReadOnly(void)         const { return(m_read_only);                          }
   bool              ReadOnly(const bool flag);
   ENUM_ALIGN_MODE   TextAlign(void)        const { return(m_align_mode);                         }
   bool              TextAlign(const ENUM_ALIGN_MODE align);
   //--- data access
   string            Text(void)             const { return(m_edit.Description());                 }
   bool              Text(const string value)     { return(CWndObj::Text(value));                 }

protected:
   //--- handlers of object events
   virtual bool      OnObjectEndEdit(void);
   //--- handlers of object settings
   virtual bool      OnSetText(void)              { return(m_edit.Description(m_text));           }
   virtual bool      OnSetColor(void)             { return(m_edit.Color(m_color));                }
   virtual bool      OnSetColorBackground(void)   { return(m_edit.BackColor(m_color_background)); }
   virtual bool      OnSetColorBorder(void)       { return(m_edit.BorderColor(m_color_border));   }
   virtual bool      OnSetFont(void)              { return(m_edit.Font(m_font));                  }
   virtual bool      OnSetFontSize(void)          { return(m_edit.FontSize(m_font_size));         }
   virtual bool      OnSetZOrder(void)            { return(m_edit.Z_Order(m_zorder));             }
   //--- internal event handlers
   virtual bool      OnCreate(void);
   virtual bool      OnShow(void);
   virtual bool      OnHide(void);
   virtual bool      OnMove(void);
   virtual bool      OnResize(void);
   virtual bool      OnChange(void);
   virtual bool      OnClick(void);
  };
//+------------------------------------------------------------------+
//| Common handler of chart events                                   |
//+------------------------------------------------------------------+
bool CEdit::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
   if(m_name==sparam && id==CHARTEVENT_OBJECT_ENDEDIT)
      return(OnObjectEndEdit());
//--- event was not handled
   return(CWndObj::OnEvent(id,lparam,dparam,sparam));
  }
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CEdit::CEdit(void) : m_read_only(false),
                     m_align_mode(ALIGN_LEFT)
  {
   m_color           =CONTROLS_EDIT_COLOR;
   m_color_background=CONTROLS_EDIT_COLOR_BG;
   m_color_border    =CONTROLS_EDIT_COLOR_BORDER;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CEdit::~CEdit(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CEdit::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- call method of the parent class
   if(!CWndObj::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create the chart object
   if(!m_edit.Create(chart,name,subwin,x1,y1,Width(),Height()))
      return(false);
//--- call the settings handler
   return(OnChange());
  }
//+------------------------------------------------------------------+
//| Set parameter                                                    |
//+------------------------------------------------------------------+
bool CEdit::ReadOnly(const bool flag)
  {
//--- save new value of parameter
   m_read_only=flag;
//--- set up the chart object
   return(m_edit.ReadOnly(flag));
  }
//+------------------------------------------------------------------+
//| Set parameter                                                    |
//+------------------------------------------------------------------+
bool CEdit::TextAlign(const ENUM_ALIGN_MODE align)
  {
//--- save new value of parameter
   m_align_mode=align;
//--- set up the chart object
   return(m_edit.TextAlign(align));
  }
//+------------------------------------------------------------------+
//| Create object on chart                                           |
//+------------------------------------------------------------------+
bool CEdit::OnCreate(void)
  {
//--- create the chart object by previously set parameters
   return(m_edit.Create(m_chart_id,m_name,m_subwin,m_rect.left,m_rect.top,m_rect.Width(),m_rect.Height()));
  }
//+------------------------------------------------------------------+
//| Display object on chart                                          |
//+------------------------------------------------------------------+
bool CEdit::OnShow(void)
  {
   return(m_edit.Timeframes(OBJ_ALL_PERIODS));
  }
//+------------------------------------------------------------------+
//| Hide object from chart                                           |
//+------------------------------------------------------------------+
bool CEdit::OnHide(void)
  {
   return(m_edit.Timeframes(OBJ_NO_PERIODS));
  }
//+------------------------------------------------------------------+
//| Absolute movement of the chart object                            |
//+------------------------------------------------------------------+
bool CEdit::OnMove(void)
  {
//--- position the chart object
   return(m_edit.X_Distance(m_rect.left) && m_edit.Y_Distance(m_rect.top));
  }
//+------------------------------------------------------------------+
//| Resize the chart object                                          |
//+------------------------------------------------------------------+
bool CEdit::OnResize(void)
  {
//--- resize the chart object
   return(m_edit.X_Size(m_rect.Width()) && m_edit.Y_Size(m_rect.Height()));
  }
//+------------------------------------------------------------------+
//| Set up the chart object                                          |
//+------------------------------------------------------------------+
bool CEdit::OnChange(void)
  {
//--- set up the chart object
   return(CWndObj::OnChange() && ReadOnly(m_read_only) && TextAlign(m_align_mode));
  }
//+------------------------------------------------------------------+
//| Handler of the "End of editing" event                            |
//+------------------------------------------------------------------+
bool CEdit::OnObjectEndEdit(void)
  {
//--- send the ON_END_EDIT notification
   EventChartCustom(CONTROLS_SELF_MESSAGE,ON_END_EDIT,m_id,0.0,m_name);
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of the "click" event                                     |
//+------------------------------------------------------------------+
bool CEdit::OnClick(void)
  {
//--- if editing is enabled, send the ON_START_EDIT notification
   if(!m_read_only)
     {
      EventChartCustom(CONTROLS_SELF_MESSAGE,ON_START_EDIT,m_id,0.0,m_name);
      //--- handled
      return(true);
     }
//--- else send the ON_CLICK notification
   return(CWnd::OnClick());
  }
//+------------------------------------------------------------------+


CEdit с использованием метода управления темами: 

Обратите внимание на выделенные разделы.

//+------------------------------------------------------------------+
//|                                                         Edit.mqh |
//|                             Copyright 2000-2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include "WndObj.mqh"
#include <ChartObjects\ChartObjectsTxtControls.mqh>

//+------------------------------------------------------------------+
//| Class CEdit                                                      |
//| Usage: control that is displayed by                              |
//|             the CChartObjectEdit object                          |
//+------------------------------------------------------------------+
class CEdit : public CWndObj
  {
private:
   CChartObjectEdit  m_edit;                // chart object
   //--- parameters of the chart object
   bool              m_read_only;           // "read-only" mode flag
   ENUM_ALIGN_MODE   m_align_mode;          // align mode

public:
                     CEdit(void);
                    ~CEdit(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- parameters of the chart object
   bool              ReadOnly(void)         const { return(m_read_only);                          }
   bool              ReadOnly(const bool flag);
   ENUM_ALIGN_MODE   TextAlign(void)        const { return(m_align_mode);                         }
   bool              TextAlign(const ENUM_ALIGN_MODE align);
   //--- data access
   string            Text(void)             const { return(m_edit.Description());                 }
   bool              Text(const string value)     { return(CWndObj::Text(value));                 }
   //--- theme handling
   bool              SetTextColor(const color clr);
   bool              SetBackgroundColor(const color clr);
   bool              SetBorderColor(const color clr);

protected:
   //--- handlers of object events
   virtual bool      OnObjectEndEdit(void);
   //--- handlers of object settings
   virtual bool      OnSetText(void)              { return(m_edit.Description(m_text));           }
   virtual bool      OnSetColor(void)             { return(m_edit.Color(m_color));                }
   virtual bool      OnSetColorBackground(void)   { return(m_edit.BackColor(m_color_background)); }
   virtual bool      OnSetColorBorder(void)       { return(m_edit.BorderColor(m_color_border));   }
   virtual bool      OnSetFont(void)              { return(m_edit.Font(m_font));                  }
   virtual bool      OnSetFontSize(void)          { return(m_edit.FontSize(m_font_size));         }
   virtual bool      OnSetZOrder(void)            { return(m_edit.Z_Order(m_zorder));             }
   //--- internal event handlers
   virtual bool      OnCreate(void);
   virtual bool      OnShow(void);
   virtual bool      OnHide(void);
   virtual bool      OnMove(void);
   virtual bool      OnResize(void);
   virtual bool      OnChange(void);
   virtual bool      OnClick(void);
  };

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CEdit::CEdit(void) : m_read_only(false),
                     m_align_mode(ALIGN_LEFT)
  {
   m_color           =CONTROLS_EDIT_COLOR;
   m_color_background=CONTROLS_EDIT_COLOR_BG;
   m_color_border    =CONTROLS_EDIT_COLOR_BORDER;
  }

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CEdit::~CEdit(void)
  {
  }

//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CEdit::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
   //--- call method of the parent class
   if(!CWndObj::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
   //--- create the chart object
   if(!m_edit.Create(chart,name,subwin,x1,y1,Width(),Height()))
      return(false);
   //--- call the settings handler
   return(OnChange());
  }

//+------------------------------------------------------------------+
//| Set parameter                                                    |
//+------------------------------------------------------------------+
bool CEdit::ReadOnly(const bool flag)
  {
   //--- save new value of parameter
   m_read_only=flag;
   //--- set up the chart object
   return(m_edit.ReadOnly(flag));
  }

//+------------------------------------------------------------------+
//| Set parameter                                                    |
//+------------------------------------------------------------------+
bool CEdit::TextAlign(const ENUM_ALIGN_MODE align)
  {
   //--- save new value of parameter
   m_align_mode=align;
   //--- set up the chart object
   return(m_edit.TextAlign(align));
  }

//+------------------------------------------------------------------+
//| Set text color                                                   |
//+------------------------------------------------------------------+
bool CEdit::SetTextColor(const color clr)
  {
   m_color = clr;
   return(OnSetColor());
  }

//+------------------------------------------------------------------+
//| Set background color                                             |
//+------------------------------------------------------------------+
bool CEdit::SetBackgroundColor(const color clr)
  {
   m_color_background = clr;
   return(OnSetColorBackground());
  }

//+------------------------------------------------------------------+
//| Set border color                                                 |
//+------------------------------------------------------------------+
bool CEdit::SetBorderColor(const color clr)
  {
   m_color_border = clr;
   return(OnSetColorBorder());
  }

//+------------------------------------------------------------------+
//| Create object on chart                                           |
//+------------------------------------------------------------------+
bool CEdit::OnCreate(void)
  {
   //--- create the chart object by previously set parameters
   return(m_edit.Create(m_chart_id,m_name,m_subwin,m_rect.left,m_rect.top,m_rect.Width(),m_rect.Height()));
  }

//+------------------------------------------------------------------+
//| Display object on chart                                          |
//+------------------------------------------------------------------+
bool CEdit::OnShow(void)
  {
   return(m_edit.Timeframes(OBJ_ALL_PERIODS));
  }

//+------------------------------------------------------------------+
//| Hide object from chart                                           |
//+------------------------------------------------------------------+
bool CEdit::OnHide(void)
  {
   return(m_edit.Timeframes(OBJ_NO_PERIODS));
  }

//+------------------------------------------------------------------+
//| Absolute movement of the chart object                            |
//+------------------------------------------------------------------+
bool CEdit::OnMove(void)
  {
   //--- position the chart object
   return(m_edit.X_Distance(m_rect.left) && m_edit.Y_Distance(m_rect.top));
  }

//+------------------------------------------------------------------+
//| Resize the chart object                                          |
//+------------------------------------------------------------------+
bool CEdit::OnResize(void)
  {
   //--- resize the chart object
   return(m_edit.X_Size(m_rect.Width()) && m_edit.Y_Size(m_rect.Height()));
  }

//+------------------------------------------------------------------+
//| Set up the chart object                                          |
//+------------------------------------------------------------------+
bool CEdit::OnChange(void)
  {
   //--- set up the chart object
   return(CWndObj::OnChange() && ReadOnly(m_read_only) && TextAlign(m_align_mode));
  }

//+------------------------------------------------------------------+
//| Handler of the "End of editing" event                            |
//+------------------------------------------------------------------+
bool CEdit::OnObjectEndEdit(void)
  {
   //--- send the ON_END_EDIT notification
   EventChartCustom(CONTROLS_SELF_MESSAGE,ON_END_EDIT,m_id,0.0,m_name);
   //--- handled
   return(true);
  }

//+------------------------------------------------------------------+
//| Handler of the "click" event                                     |
//+------------------------------------------------------------------+
bool CEdit::OnClick(void)
  {
   //--- if editing is enabled, send the ON_START_EDIT notification
   if(!m_read_only)
     {
      EventChartCustom(CONTROLS_SELF_MESSAGE,ON_START_EDIT,m_id,0.0,m_name);
      //--- handled
      return(true);
     }
   //--- else send the ON_CLICK notification
   return(CWnd::OnClick());
  }

//+------------------------------------------------------------------+
Мы успешно подготовили файлы управления для панели администратора и сейчас находимся ближе к завершению нашего проекта, чем когда-либо прежде. В следующем сегменте мы поправим код панели администратора для поддержки переключения тем в соответствии с последними разработками.

Настройка панели администратора для переключения тем.

 Логически в нашем управлении темами можно выделить четыре ключевых направления. 

  • Функциональность переключения тем в нашей панели администратора должна быть сосредоточена вокруг булевой переменной darkTheme и функции UpdateThemeColors(). Вот как это работает:

bool darkTheme = false;

  • Приведенный выше флаг определяет, является ли текущая тема темной или светлой. Переключается при нажатии toggleThemeButton, как показано ниже.

void OnToggleThemeButtonClick()
{
    darkTheme = !darkTheme;
    UpdateThemeColors();
    Print("Theme toggled: ", darkTheme ? "Dark" : "Light");
}

  • Нажатие кнопки переключения темы вызывает эту функцию, которая переключает флаг darkTheme  и впоследствии обновляет тему пользовательского интерфейса через UpdateThemeColors().

void UpdateThemeColors()
{
    // Determine colors based on the current theme
    color textColor = darkTheme ? clrWhite : clrBlack;
    color buttonBgColor = darkTheme ? clrDarkSlateGray : clrGainsboro;
    color borderColor = darkTheme ? clrSlateGray : clrGray;
    color bgColor     = darkTheme ? clrDarkBlue : clrWhite;

    // Set text box colors
    inputBox.SetTextColor(textColor);
    inputBox.SetBackgroundColor(bgColor);
    inputBox.SetBorderColor(borderColor);

    // Update button colors
    UpdateButtonTheme(clearButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(sendButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(toggleThemeButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(changeFontButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(minimizeButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(maximizeButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(closeButton, textColor, buttonBgColor, borderColor);

    // Update quick message buttons
    for (int i = 0; i < ArraySize(quickMessageButtons); i++)
    {
        UpdateButtonTheme(quickMessageButtons[i], textColor, buttonBgColor, borderColor);
    }

    // Update character counter color
    charCounter.Color(textColor);

    // Redraw chart to apply changes
    ChartRedraw();
}

 Пользуясь флагом darkTheme, мы выбрали разные цвета для текста, фона кнопок, границ и фона. Цвета применяются к различным компонентам пользовательского интерфейса следующим образом:

  • Текстовое поле (inputBox): Функции SetTextColor, SetBackgroundColor, и SetBorderColor используются для применения темы.
  • Кнопки: Функция UpdateButtonTheme() вызывается для каждой кнопки, устанавливая цвет текста, цвет фона и цвет границы в соответствии с заданными параметрами.
  • Счетчик символов: напрямую устанавливает свой цвет при нажатии кнопки темы.

//Theme button application
void UpdateButtonTheme(CButton &button, color textColor, color bgColor, color borderColor)
{
    button.SetTextColor(textColor);
    button.SetBackgroundColor(bgColor);
    button.SetBorderColor(borderColor);
}

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


Окончательный код и результаты

Вот окончательный вариант нашей программы с новыми функциями.


//+------------------------------------------------------------------+
//|                                             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 version   "1.12"

#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;
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();
    UpdateThemeColors();

    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();
    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)
            {
                long 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("");
    OnInputChange();
    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;
    UpdateThemeColors();
    Print("Theme toggled: ", darkTheme ? "Dark" : "Light");
}

//+------------------------------------------------------------------+
//| Update theme colors for the panel                                |
//+------------------------------------------------------------------+
void UpdateThemeColors()
{
    // Use the dialog's theme update method as a placeholder.
    adminPanel.UpdateThemeColors(darkTheme);

    color textColor = darkTheme ? clrWhite : clrBlack;
    color buttonBgColor = darkTheme ? clrDarkSlateGray : clrGainsboro;
    color borderColor = darkTheme ? clrSlateGray : clrGray;
    color bgColor     = darkTheme?  clrDarkBlue : clrWhite;

          inputBox.SetTextColor(textColor);
          inputBox.SetBackgroundColor(bgColor);
          inputBox.SetBorderColor(borderColor);

    UpdateButtonTheme(clearButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(sendButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(toggleThemeButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(changeFontButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(minimizeButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(maximizeButton, textColor, buttonBgColor,borderColor);
    UpdateButtonTheme(closeButton, textColor, buttonBgColor, borderColor);
    

    for (int i = 0; i < ArraySize(quickMessageButtons); i++)
    {
        UpdateButtonTheme(quickMessageButtons[i], textColor, buttonBgColor, borderColor);
    }

    charCounter.Color(textColor);

    ChartRedraw();
}

//+------------------------------------------------------------------+
//| Apply theme settings to a button                                 |
//+------------------------------------------------------------------+
void UpdateButtonTheme(CButton &button, color textColor, color bgColor, color borderColor)
{
    button.SetTextColor(textColor);
    button.SetBackgroundColor(bgColor);
    button.SetBorderColor(borderColor);
}

//+------------------------------------------------------------------+
//| 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();
    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)
    {
        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;
    }
}


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


Расширенная панель администратора

Новая тема панели администратора

На рисунке ниже показаны все операции, выполняемые на панели, включая обработку ошибок. Невозможность отправить пользовательское сообщение может быть связана с отсутствующим или неправильным токеном Telegram-бота и идентификатором чата. Как видно на изображении, эти поля остались пустыми. Важно убедиться, что эти учетные данные введены правильно, поскольку они имеют решающее значение для работы. Сохраните эти учетные данные в надежном месте, чтобы предотвратить несанкционированный доступ. 

Логи на вкладке "Эксперты"

Логи на вкладке "Эксперты"

Заключение

Статья знаменует собой еще одну веху в развитии нашей торговой панели администратора. Мы успешно интегрировали алгоритмы управления темами в существующие классы, не заметив при этом никаких проблем с производительностью, влияющих на другие функции платформы, которые используют те же библиотеки. Эти достижения предназначены в первую очередь для учебных и исследовательских целей. Тем не менее, изменение классов и интеграция новых методов могут иметь положительный эффект, но они также несут в себе риск нежелательных результатов, если не реализованы правильно. Наш проект стал более сложным, интегрируя функционал Telegram и расширенные возможности визуализации.

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

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


К содержанию


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

Прикрепленные файлы |
Admin_Panel.mq5 (14.69 KB)
От начального до среднего уровня: Массив (III) От начального до среднего уровня: Массив (III)
В этой статье мы рассмотрим, как работать с массивами в MQL5, в том числе, как передавать информацию между функциями и процедурами с помощью массивов. Цель — подготовить вас к тому, что будет демонстрироваться и разъясняться в будущих материалах серии. Поэтому настоятельно рекомендую внимательно изучить то, что будет показано в этой статье.
Количественный анализ трендов: Собираем статистику на Python Количественный анализ трендов: Собираем статистику на Python
Что такое количественный анализ трендов на рынке Форекс. Собираем статистику по трендам, их величине и распределению по валютной паре EURUSD. Как количественный анализ трендов поможет создать прибыльный торговый советник.
Критерий независимости Гильберта-Шмидта (HSIC) Критерий независимости Гильберта-Шмидта (HSIC)
В статье рассматривается непараметрический статистический тест HSIC (Hilbert-Schmidt Independence Criterion) предназначенный для выявления линейных и нелинейных зависимостей в данных. Предложены реализации двух алгоритмов вычисления HSIC на языке MQL5: точного перестановочного теста и гамма-аппроксимации. Эффективность метода демонстрируется на синтетических данных, моделирующих нелинейную связь признаков и целевой переменной.
Самооптимизирующийся советник на языках MQL5 и Python (Часть V): Глубокие марковские модели Самооптимизирующийся советник на языках MQL5 и Python (Часть V): Глубокие марковские модели
Мы применим простую цепь Маркова к индикатору RSI, чтобы наблюдать за поведением цены после того, как индикатор проходит через ключевые уровни. Мы пришли к выводу, что самые сильные сигналы на покупку и продажу по паре NZDJPY генерируются, когда RSI находится в диапазоне 11–20 и 71–80 соответственно. Мы покажем, как можно манипулировать данными, чтобы создавать оптимальные торговые стратегии, основанные непосредственно на имеющихся данных. Кроме того, мы продемонстрируем, как обучить глубокую нейронную сеть оптимальному использованию матрицы перехода.