Создание торговой панели администратора на MQL5 (Часть IX): Организация кода (III): Модуль коммуникации
Содержание
- Введение
- Разработка класса CommunicationsDialog
- Интеграция CommunicationsDialog с другими заголовочными файлами и основной программой
- Тестирование и результаты
- Заключение
Введение
Мы продолжим начатое в предыдущей статье и расширим нашу новую панель администратора, в которой мы реализовали модуляризацию как ключевой аспект организации кода. Мы ввели класс AdminHomeDialog, отвечающий за создание интерфейса главной страницы панели администратора. Эта домашняя панель служит центральным узлом для доступа к различным функциям и состоит из кнопок управления доступом, ведущих к трем основным панелям:
- Панель управления торговлей
- Панель коммуникации
- Панель аналитики
Это не окончательные пределы системы, поскольку по мере дальнейшего совершенствования и расширения существующей основы остаются возможными новые функции. В данном обсуждении мы сосредоточимся на панели коммуникации, расширив его возможности по сравнению с предыдущей версией в рамках единой административной панели.
Основные моменты:
- Классы в MQL5
- Разработка заголовочных файлов
- Наследование от встроенных классов
- Использование заголовочного файла ListView
- Применение цветов для улучшения дизайна пользовательского интерфейса

Модульный процесс новой панели администратора
1. Главная программа (New_Admin_Panel.mq5)
- Это точка входа в приложение.
- Она инициализирует систему и создает экземпляр AdminHomeDialog, который служит основным пользовательским интерфейсом.
2. AdminHomeDialog
- Выступает в качестве центрального узла для взаимодействия с пользователями.
- Реагирует на события, такие как нажатия кнопок, для создания экземпляров других диалоговых окон, например, CommunicationsDialog.
- Разработан с учетом возможности расширения, что позволяет создавать дополнительные диалоговые окна (например, FutureDialog) по мере необходимости.
3. CommunicationsDialog
- Специализированный диалог, отвечающий за определенные функции, такие как отправка сообщений через API Telegram.
- Создается динамически AdminHomeDialog при определенных действиях пользователя (например, при нажатии кнопки Send Message (отправить сообщение)).
4. Взаимодействия на основе событий
- Система использует событийно-ориентированный подход, распространенный в MQL5-приложениях. Например, нажатие кнопки в AdminHomeDialog запускает создание и отображение CommunicationsDialog, которая затем выполняет свою задачу (например, взаимодействует с Telegram).
5. Модульная конструкция
- Каждый компонент выполняет свою роль: основная программа инициализирует, AdminHomeDialog управляет интерфейсом, а CommunicationsDialog отвечает за коммуникацию. Такая модульность позволяет легко расширять или модифицировать систему.
Рассмотрев нашу новую программу, мы можем перейти к детальному изучению разработки нашего нового модуля - CommunicationsDialog. В первой части это был всего лишь базовый интерфейс для коммуникации, но теперь мы расширили его функциональность, включив в него новые функции. Обладая этими базовыми знаниями, мы можем лучше представить себе направление нашего движения. В следующих подразделах мы рассмотрим основные составляющие, которые делают возможным создание наших пользовательских классов.
Разработка класса CommunicationsDialog
Для лучшего понимания ниже приведено изображение, иллюстрирующее иерархию процесса от базового класса к нашим пользовательским классам. Этот подход повышает важность повторного использования кода. В рамках проекта используется множество компонентов панелей, каждый из которых выполняет свою особую функцию. Эта уникальность позволяет адаптировать каждый компонент кода для интеграции и в другие проекты.
В MQL5 класс Dialog обычно относится к CAppDialog или CDialog из включаемого файла Dialog.mqh, поскольку они служат базовыми классами диалогов в стандартной библиотеке.
Как CommunicationsDialog, так и CAdminHomeDialog наследуют от CAppDialog, создавая структурированную иерархию, в которой несколько классов диалогов используют общую основу для функциональности. Данная структура будет представлена в виде иерархической блок-схемы ниже.

Взаимосвязь базового и пользовательских классов
Для начала откройте MetaEditor 5 на рабочем столе или запустите его из терминала, нажав клавишу F4.
В меню "Навигатор" найдите файл Dialog.mqh в папке Includes и откройте его.

Находим класс Dialog в MetaEditor 5
Далее создайте новый файл для разработки нового класса.
В целом, наша программа состоит из таких разделов, как настройка заголовка, определение макета и цветов, объявление классов и реализация методов. Начнём с основных сведений о нашем файле. В заголовке вверху указано, что это за файл (CommunicationsDialog.mqh), кто им владеет (MetaQuotes Ltd., хотя вы можете указать свое имя) и откуда он (MQL5 community). Эти комментарии подобны титульной странице вашего кода и помогают другим понять его назначение.
Далее мы используем #ifndef и #define, чтобы создать "защиту" под названием COMMUNICATIONS_DIALOG_MQH. Это предотвращает многократное включение файла в проект, что может привести к ошибкам. Представьте, что это замок, который говорит: "Если меня уже открыли, не открывайте меня снова".
Строки #include подключают необходимые нам инструменты из библиотеки MQL5. Файл Dialog.mqh предоставляет нам базовый класс диалогового окна CAppDialog, а файлы Button.mqh и Edit.mqh — классы кнопок и текстовых полей. Label.mqh и ListView.mqh добавляют метку и список для быстрых сообщений. Наконец, файл Telegram.mqh — это пользовательский файл (предполагается, что он существует), который обрабатывает сообщения в Telegram. Это как заимствовать инструменты из ящика с инструментами для построения нашего диалога. Ниже приведен фрагмент кода для этого раздела.
Раздел 1. Заголовок файла и включаемые файлы
//+------------------------------------------------------------------+ //| CommunicationsDialog.mqh | //| Copyright 2000-2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #ifndef COMMUNICATIONS_DIALOG_MQH #define COMMUNICATIONS_DIALOG_MQH #include <Controls\Dialog.mqh> #include <Controls\Button.mqh> #include <Controls\Edit.mqh> #include <Controls\Label.mqh> #include <Controls\ListView.mqh> #include "Telegram.mqh"
Раздел 2. Макет и определение цветов
Прежде чем начать диалог, нам необходимо спланировать его размер и внешний вид. Операторы #define похожи на составление чертежа с указанием размеров и цветов. Применительно к макету COMMS_PANEL_WIDTH (300 пикселей) и COMMS_PANEL_HEIGHT (350 пикселей) определяют размер диалогового окна на экране. Параметры полей (COMMS_MARGIN_LEFT, COMMS_MARGIN_TOP, COMMS_MARGIN_RIGHT) задают отступы по краям, а COMMS_GAP_VERTICAL добавляет вертикальное пространство между элементами. Каждый элемент управления имеет свои размеры: поле ввода имеет высоту (COMMS_INPUT_HEIGHT), кнопки представляют собой небольшие прямоугольники (COMMS_BUTTON_WIDTH и HEIGHT), а список и метка имеют свои собственные размеры.
Цвета делают диалоги красивыми и читаемыми. Мы используем шестнадцатеричные числа (например, 0x808080 для темно-серого цвета), потому что именно так компьютеры понимают цвета в MQL5. CLR_PANEL_BG задает основной фон диалогового окна, CLR_CLIENT_BG окрашивает область, где расположены элементы управления, а CLR_CAPTION_BG и CLR_CAPTION_TEXT стилизуют заголовок окна. Границы получают значения CLR_BORDER_BG и CLR_BORDER, в то время как элементы управления, такие как поле ввода (CLR_INPUT_BG, CLR_INPUT_TEXT) и кнопки (CLR_SEND_BG, CLR_CLEAR_BG), получают свои собственные цвета. Комментарии рядом с каждым параметром поясняют его назначение, что позволяет легко вносить корректировки в дальнейшем.
// **Layout Defines** #define COMMS_PANEL_WIDTH 300 #define COMMS_PANEL_HEIGHT 350 #define COMMS_MARGIN_LEFT 10 #define COMMS_MARGIN_TOP 10 #define COMMS_MARGIN_RIGHT 10 #define COMMS_GAP_VERTICAL 10 #define COMMS_INPUT_HEIGHT 30 #define COMMS_BUTTON_WIDTH 80 #define COMMS_BUTTON_HEIGHT 30 #define COMMS_LISTVIEW_WIDTH 280 #define COMMS_LISTVIEW_HEIGHT 80 #define COMMS_LABEL_HEIGHT 20 // **Color Defines (Hexadecimal Values)** #define CLR_PANEL_BG 0x808080 // Dark Gray (Dialog background) #define CLR_CLIENT_BG 0xD3D3D3 // Light Gray (Client area background) #define CLR_CAPTION_BG 0x404040 // Darker Gray (Caption background) #define CLR_CAPTION_TEXT 0xFFFFFF // White (Caption text) #define CLR_BORDER_BG 0xFFFFFF // White (Border background) #define CLR_BORDER 0xA9A9A9 // Gray (Border color) #define CLR_INPUT_BG 0xFFFFFF // White (Input box background) #define CLR_INPUT_TEXT 0x000000 // Black (Input box text) #define CLR_SEND_BG 0x00FF00 // Lime Green (Send button background) #define CLR_CLEAR_BG 0xF08080 // Light Coral (Clear button background) #define CLR_BUTTON_TEXT 0x000000 // Black (Button text) #define CLR_LABEL_TEXT 0xFFFFFF // White (Label text) #define CLR_LIST_BG 0xFFFFFF // White (List view background) #define CLR_LIST_TEXT 0x000000 // Black (List view text)
Раздел 3. Объявление класса
Теперь мы определяем ядро нашего диалога: класс CCommunicationDialog. Представьте себе класс как рецепт создания диалогового объекта. Публичный CAppDialog, так как наш диалог основан на CAppDialog. Это готовый класс диалогового окна из MQL5, который предоставляет нам основные функции диалогового окна, такие как заголовок и границы.
В приватном разделе перечислены ингредиенты, которые мы будем использовать в диалоговом окне. m_inputBox - текстовое поле, в котором пользователи вводят сообщения, m_sendButton и m_clearButton - кнопки для отправки или удаления сообщения, m_quickMsgLabel - текстовая метка, а m_quickMessageList - список предустановленных сообщений. Мы также храним m_chatId и m_botToken в виде строк для Telegram, а m_quickMessages - в виде массива для хранения восьми вариантов быстрых сообщений.
В общедоступном разделе мы перечисляем функции, которые любой желающий может использовать для взаимодействия с нашим классом диалога. Конструктор CCommunicationDialog настраивает диалог с идентификатором чата и токеном бота, а деструктор ~CCommunicationDialog производит очистку по завершении. Create формирует диалоговое окно на графике, OnEvent обрабатывает клики и действия, а Toggle отображает или скрывает окно.
В приватном режиме у нас есть вспомогательные функции для создания каждого элемента управления (CreateInputBox и т. д.) и обработчиков событий (OnClickSend, OnClickClear), определяющих, что происходит при нажатии кнопок. Эти данные являются приватными, поскольку представляют собой внутреннюю информацию о работе диалогового окна.
//+------------------------------------------------------------------+ //| Class CCommunicationDialog | //| Purpose: A dialog for sending Telegram messages with controls | //+------------------------------------------------------------------+ class CCommunicationDialog : public CAppDialog { private: CEdit m_inputBox; // Field to edit/send message CButton m_sendButton; // Send message button CButton m_clearButton; // Clear edit box button CLabel m_quickMsgLabel; // Label for "QuickMessages" CListView m_quickMessageList; // ListView for quick messages string m_chatId; // Telegram chat ID string m_botToken; // Telegram bot token string m_quickMessages[8]; // Array of quick messages public: CCommunicationDialog(const string chatId, const string botToken); ~CCommunicationDialog(); 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); void Toggle(); // Toggle dialog visibility private: //--- Create dependent controls bool CreateInputBox(void); bool CreateClearButton(void); bool CreateSendButton(void); bool CreateQuickMsgLabel(void); bool CreateQuickMessageList(void); //--- Handlers of dependent controls events void OnClickSend(void); // Handler for Send button void OnClickClear(void); // Handler for Clear button };
Раздел 4. Конструктор и деструктор
Конструктор подобен подготовке новой игрушки перед началом игры. При создании CCommunicationDialog необходимо назначить его chatId и botToken (строки для Telegram). Части m_chatId(chatId) и m_botToken(botToken) копируют эти данные в наши приватные переменные, чтобы знать, куда отправлять сообщения. Внутри фигурных скобок заполняем массив m_quickMessages с восемью фразами, из которых пользователь может выбирать. Это происходит при создании диалога.
Деструктор занимается уборкой. Он запускается при удалении диалогового окна (например, при закрытии программы). В данный момент он пуст, потому что CAppDialog выполняет большую часть очистки за нас, и нам нечего дополнительно убирать. Здесь он является заполнителем на случай, если мы позже добавим специальные функции очистки, например, освобождение памяти.
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CCommunicationDialog::CCommunicationDialog(const string chatId, const string botToken) : m_chatId(chatId), m_botToken(botToken) { // Initialize quick messages m_quickMessages[0] = "Updates"; m_quickMessages[1] = "Close all"; m_quickMessages[2] = "In deep profits"; m_quickMessages[3] = "Hold position"; m_quickMessages[4] = "Swing Entry"; m_quickMessages[5] = "Scalp Entry"; m_quickMessages[6] = "Book profit"; m_quickMessages[7] = "Invalid Signal"; } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CCommunicationDialog::~CCommunicationDialog() { }
Раздел 5. Создание метода
Метод Create используется для создания диалогового окна на экране, подобно сборке игрушки из деталей. Для ввода данных требуются следующие параметры: график (где отображается), имя (уникальный идентификатор), подокно (на каком из окон графика) и координаты (x1, y1, x2, y2) для позиции и размера. Сначала вызываем метод CAppDialog::Create, чтобы настроить базовую структуру диалога. Если это не сработает, возвращаем false, чтобы сообщить, что что-то пошло не так.
Далее Caption("Communications Panel") устанавливает заголовок в верхней части диалогового окна. Давайте его раскрасим! ObjectSetInteger изменяет цвета, взаимодействуя напрямую с элементами диалогового окна (например, Back - фон, а Client - область управления). Мы используем наши цветовые определения (например, CLR_PANEL_BG) для большей визуальной привлекательности. Проверка if(!m_panel_flag) добавляет границы только в том случае, если диалоговое окно не относится к специальному типу (управляемому переменной m_panel_flag из класса CAppDialog).
После этого вызываем вспомогательные функции для создания каждого элемента управления (поля ввода, кнопок и т. д.). Если какой-либо из них не сработает, остановимся и вернем false. Наконец, помещаем первое краткое сообщение в поле ввода с помощью m_inputBox.Text и вызываем ChartRedraw(), чтобы отобразить все на графике. true означает успех.
//+------------------------------------------------------------------+ //| Create Method | //| Initializes the dialog and its controls with full color styling | //+------------------------------------------------------------------+ bool CCommunicationDialog::Create(const long chart, const string name, const int subwin, const int x1, const int y1, const int x2, const int y2) { // Create the base dialog if(!CAppDialog::Create(chart, name, subwin, x1, y1, x2, y2)) return(false); Caption("Communications Panel"); // Set the title // Set dialog background color ObjectSetInteger(m_chart_id, m_name + "Back", OBJPROP_BGCOLOR, CLR_PANEL_BG); // Set client area background color ObjectSetInteger(m_chart_id, m_name + "Client", OBJPROP_BGCOLOR, CLR_CLIENT_BG); // Set caption colors ObjectSetInteger(m_chart_id, m_name + "Caption", OBJPROP_BGCOLOR, CLR_CAPTION_BG); ObjectSetInteger(m_chart_id, m_name + "Caption", OBJPROP_COLOR, CLR_CAPTION_TEXT); // Set border colors (if border exists, i.e., m_panel_flag is false) if(!m_panel_flag) { ObjectSetInteger(m_chart_id, m_name + "Border", OBJPROP_BGCOLOR, CLR_BORDER_BG); ObjectSetInteger(m_chart_id, m_name + "Border", OBJPROP_BORDER_COLOR, CLR_BORDER); } // Create all controls if(!CreateInputBox()) return(false); if(!CreateClearButton()) return(false); if(!CreateSendButton()) return(false); if(!CreateQuickMsgLabel()) return(false); if(!CreateQuickMessageList()) return(false); // Set initial text in input box m_inputBox.Text(m_quickMessages[0]); ChartRedraw(); return(true); }
Раздел 6. Методы создания элементов управления
Эти методы подобны сборке фрагментов диалогового окна. Каждый из них создает и добавляет элемент управления (поле ввода, кнопки, метку, список). Все они возвращают true в случае успеха или false в случае неудачи, поэтому мы можем остановиться, если возникнет проблема.
Для функции CreateInputBox мы вычисляем позиции, используя наши данные макета (например, COMMS_MARGIN_LEFT). Поле ввода имеет ширину (ClientAreaWidth()) и высоту (в три раза больше COMMS_INPUT_HEIGHT). Вызываем метод m_inputBox.Create, передавая идентификатор графика, уникальное имя (m_name + "_InputBox") и координаты. Add добавляет объект в диалоговое окно, а ObjectSetInteger устанавливает его цвета.
CreateClearButton и CreateSendButton создают кнопки под полем ввода. Мы располагаем их вертикально с помощью COMMS_GAP_VERTICAL и размещаем кнопку Send (отправить) рядом с кнопкой Clear (очистить), используя clear_button_x2. Каждому элементу присваивается имя, текст (Clear или Send) и цвет из наших настроек.
CreateQuickMsgLabel добавляет подпись под кнопками, используя дополнительные вычисления интервала. Это просто текст, поэтому нужен только цвет текста. CreateQuickMessageList еще больше уменьшает размер списка, заполняя его нашими быстрыми сообщениями из конструктора. Для каждого элемента управления используется один и тот же шаблон: создание, добавление, раскрашивание и проверка на ошибки с выводом сообщений для отладки.
//+------------------------------------------------------------------+ //| CreateInputBox | //+------------------------------------------------------------------+ bool CCommunicationDialog::CreateInputBox(void) { int x1 = COMMS_MARGIN_LEFT; int y1 = COMMS_MARGIN_TOP; int x2 = ClientAreaWidth() - COMMS_MARGIN_RIGHT; int y2 = y1 + 3 * COMMS_INPUT_HEIGHT; if(!m_inputBox.Create(m_chart_id, m_name + "_InputBox", m_subwin, x1, y1, x2, y2)) { Print("Failed to create InputBox"); return(false); } if(!Add(m_inputBox)) return(false); ObjectSetInteger(m_chart_id, m_name + "_InputBox", OBJPROP_BGCOLOR, CLR_INPUT_BG); ObjectSetInteger(m_chart_id, m_name + "_InputBox", OBJPROP_COLOR, CLR_INPUT_TEXT); return(true); } //+------------------------------------------------------------------+ //| CreateClearButton | //+------------------------------------------------------------------+ bool CCommunicationDialog::CreateClearButton(void) { int button_y1 = COMMS_MARGIN_TOP + 3 * COMMS_INPUT_HEIGHT + COMMS_GAP_VERTICAL; int x1 = COMMS_MARGIN_LEFT; int x2 = x1 + COMMS_BUTTON_WIDTH; int y2 = button_y1 + COMMS_BUTTON_HEIGHT; if(!m_clearButton.Create(m_chart_id, m_name + "_ClearButton", m_subwin, x1, button_y1, x2, y2)) { Print("Failed to create ClearButton"); return(false); } m_clearButton.Text("Clear"); if(!Add(m_clearButton)) return(false); ObjectSetInteger(m_chart_id, m_name + "_ClearButton", OBJPROP_BGCOLOR, CLR_CLEAR_BG); ObjectSetInteger(m_chart_id, m_name + "_ClearButton", OBJPROP_COLOR, CLR_BUTTON_TEXT); return(true); } //+------------------------------------------------------------------+ //| CreateSendButton | //+------------------------------------------------------------------+ bool CCommunicationDialog::CreateSendButton(void) { int button_y1 = COMMS_MARGIN_TOP + 3 * COMMS_INPUT_HEIGHT + COMMS_GAP_VERTICAL; int clear_button_x2 = COMMS_MARGIN_LEFT + COMMS_BUTTON_WIDTH; int x1 = clear_button_x2 + COMMS_GAP_VERTICAL; int x2 = x1 + COMMS_BUTTON_WIDTH; int y2 = button_y1 + COMMS_BUTTON_HEIGHT; if(!m_sendButton.Create(m_chart_id, m_name + "_SendButton", m_subwin, x1, button_y1, x2, y2)) { Print("Failed to create SendButton"); return(false); } m_sendButton.Text("Send"); if(!Add(m_sendButton)) return(false); ObjectSetInteger(m_chart_id, m_name + "_SendButton", OBJPROP_BGCOLOR, CLR_SEND_BG); ObjectSetInteger(m_chart_id, m_name + "_SendButton", OBJPROP_COLOR, CLR_BUTTON_TEXT); return(true); } //+------------------------------------------------------------------+ //| CreateQuickMsgLabel | //+------------------------------------------------------------------+ bool CCommunicationDialog::CreateQuickMsgLabel(void) { int label_y1 = COMMS_MARGIN_TOP + 3 * COMMS_INPUT_HEIGHT + COMMS_GAP_VERTICAL + COMMS_BUTTON_HEIGHT + COMMS_GAP_VERTICAL; int x1 = COMMS_MARGIN_LEFT; int x2 = x1 + COMMS_LISTVIEW_WIDTH; int y2 = label_y1 + COMMS_LABEL_HEIGHT; if(!m_quickMsgLabel.Create(m_chart_id, m_name + "_QuickMsgLabel", m_subwin, x1, label_y1, x2, y2)) { Print("Failed to create QuickMessages Label"); return(false); } m_quickMsgLabel.Text("QuickMessages"); if(!Add(m_quickMsgLabel)) return(false); ObjectSetInteger(m_chart_id, m_name + "_QuickMsgLabel", OBJPROP_COLOR, CLR_LABEL_TEXT); return(true); } //+------------------------------------------------------------------+ //| CreateQuickMessageList | //+------------------------------------------------------------------+ bool CCommunicationDialog::CreateQuickMessageList(void) { int list_y1 = COMMS_MARGIN_TOP + 3 * COMMS_INPUT_HEIGHT + COMMS_GAP_VERTICAL + COMMS_BUTTON_HEIGHT + COMMS_GAP_VERTICAL + COMMS_LABEL_HEIGHT + COMMS_GAP_VERTICAL; int x1 = COMMS_MARGIN_LEFT; int x2 = x1 + COMMS_LISTVIEW_WIDTH; int y2 = list_y1 + COMMS_LISTVIEW_HEIGHT; if(!m_quickMessageList.Create(m_chart_id, m_name + "_QuickMsgList", m_subwin, x1, list_y1, x2, y2)) { Print("Failed to create ListView"); return(false); } if(!Add(m_quickMessageList)) return(false); ObjectSetInteger(m_chart_id, m_name + "_QuickMsgList", OBJPROP_BGCOLOR, CLR_LIST_BG); ObjectSetInteger(m_chart_id, m_name + "_QuickMsgList", OBJPROP_COLOR, CLR_LIST_TEXT); for(int i = 0; i < ArraySize(m_quickMessages); i++) { if(!m_quickMessageList.AddItem("Message: " + m_quickMessages[i])) return(false); } return(true); }
Раздел 7. Переключение и обработка событий
Toggle — это простой переключатель для отображения или скрытия диалогового окна. IsVisible() проверяет, находится ли объект на экране. Если да, Hide() удаляет его; если нет, Show() возвращает его. ChartRedraw() обновляет график, поэтому вы сразу видите изменения. Это как щелкнуть выключателем, чтобы включить диалоговое окно.
OnEvent — это "мозг", который отслеживает действия пользователя, например, клики. Он получает id (что произошло), lparam (подробности), dparam (дополнительные сведения) и sparam (какой объект). Если id равен CHARTEVENT_OBJECT_CLICK, это означает, что был совершен клик. Мы проверяем, совпадает ли sparam (имя кликнутого объекта) с m_sendButton.Name() или m_clearButton.Name(), а затем вызываем OnClickSend или OnClickClear. Возвращаемое значение true означает завершение обработки.
Если id равен ON_CHANGE, а sparam — имя списка, значит пользователь выбрал быстрое сообщение. lparam указывает, какое именно (в виде числа), и мы вводим это сообщение в поле ввода с помощью m_inputBox.Text. Если ничего не совпадает, мы позволяем CAppDialog::OnEvent обработать это событие, передав его дальше по цепочке.
//+------------------------------------------------------------------+ //| Toggle | //+------------------------------------------------------------------+ void CCommunicationDialog::Toggle() { if(IsVisible()) Hide(); else Show(); ChartRedraw(); } //+------------------------------------------------------------------+ //| OnEvent | //+------------------------------------------------------------------+ bool CCommunicationDialog::OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if(id == CHARTEVENT_OBJECT_CLICK) { if(sparam == m_sendButton.Name()) { OnClickSend(); return true; } else if(sparam == m_clearButton.Name()) { OnClickClear(); return true; } } else if(id == ON_CHANGE && sparam == m_quickMessageList.Name()) { int selectedIndex = (int)lparam; if(selectedIndex >= 0 && selectedIndex < ArraySize(m_quickMessages)) { m_inputBox.Text(m_quickMessages[selectedIndex]); } return true; } return CAppDialog::OnEvent(id, lparam, dparam, sparam); }
Раздел 8. Обработчики событий кнопок
OnClickSend запускается при нажатии кнопки Send (отправить). Функция извлекает текст из m_inputBox.Text() и проверяет содержимое ("). Если сообщение есть, программа пытается отправить его с помощью функции SendMessageToTelegram (из Telegram.mqh), используя m_chatId и m_botToken. Если всё работает, мы выводим сообщение об успехе; если нет — сообщение о неудаче. Если поле пустое, мы просто распечатываем уведомление. В конце концов, главная задача диалога — отправка сообщений!
OnClickClear устроена проще. При нажатии Clear (очистить) текст в поле ввода становится пустым ("") с помощью m_inputBox.Text(""), график перерисовывается, чтобы показать изменение, и выводится подтверждение. Чем-то напоминает кнопку "Очистить форму" при заполнении какой-нибудь анкеты.
//+------------------------------------------------------------------+ //| OnClickSend | //+------------------------------------------------------------------+ void CCommunicationDialog::OnClickSend() { string message = m_inputBox.Text(); if(message != "") { if(SendMessageToTelegram(message, m_chatId, m_botToken)) Print("Message sent to Telegram: ", message); else Print("Failed to send message to Telegram"); } else { Print("No message to send - input box is empty"); } } //+------------------------------------------------------------------+ //| OnClickClear | //+------------------------------------------------------------------+ void CCommunicationDialog::OnClickClear() { m_inputBox.Text(""); // Clear the input box ChartRedraw(); Print("Input box cleared."); }
В конце файла добавьте #endif, чтобы закрыть "защиту" заголовка:
#endif // COMMUNICATIONS_DIALOG_MQH
Это соответствует тегу #ifndef в начале, обеспечивая аккуратное завершение.
Интеграция CommunicationsDialog с другими заголовочными файлами и основной программой
CommunicationsDialog обрабатывается в рамках AdminHomeDialog. В этом разделе я объясню, как это работает. Я не буду описывать здесь весь код AdminHomeDialog, так как мы подробно рассмотрели его в предыдущей статье.
Шаг 1
Для объединения CommunicationsDialog с AdminHomeDialog, нам необходимо включить его в наш файл. Строка #include (CommunicationsDialog.mqh) позволяет обеспечить взаимосвязь. Это указывает MQL5 загрузить файл CommunicationsDialog.mqh, что делает его класс CCommunicationDialog доступным для использования. Разместим его в верхней части AdminHomeDialog.mqh, после других включаемых файлов, таких как Dialog.mqh и Button.mqh. Без этого AdminHomeDialog не будет знать о панели коммуникации, поэтому это первый шаг к установлению связи между ними.
#include <CommunicationsDialog.mqh> // Use the enhanced Communications dialog
Шаг 2
Внутри класса CAdminHomeDialog нам нужен способ разместить нашу панель коммуникации. Добавим CCommunicationDialog *m_commPanel в приватный раздел, используя указатель (звездочка * означает ссылку на объект, который мы создадим позже). Это как зарезервировать место для игрушки, которую мы распакуем, когда она понадобится. Мы также добавляем переменные m_chatId и m_botToken в виде строк для хранения данных Telegram, которые мы передадим в CCommunicationDialog. Эти данные являются приватными, поскольку управлять ими должен только этот класс, что создает предпосылки для интеграции.
class CAdminHomeDialog : public CAppDialog { private: CCommunicationDialog *m_commPanel; // Pointer to the Communications panel string m_chatId; // Telegram Chat ID string m_botToken; // Telegram Bot Token ///.................Space for other members e.g. buttons };
Шаг 3
Конструктор настраивает CAdminHomeDialog при его создании. Мы обновляем его, чтобы он принимал chatId и botToken в качестве входных данных, которые мы копируем в m_chatId и m_botToken, используя : m_chatId(chatId), m_botToken(botToken). Мы также устанавливаем значение m_commPanel в NULL (пока ничего) с помощью команды : m_commPanel(NULL). Это значит, что мы не будем создавать коммуникационную панель сразу — мы подождем, пока пользователь сам об этом не попросит. Это как держать коробку закрытой, пока не будешь готов поиграть с тем, что внутри.
Деструктор завершает работу программы после окончания диалога. Проверяем условие if(m_commPanel), чтобы узнать, создали ли мы панель коммуникации. Если это так, delete m_commPanel освобождает ее память (подобно выбрасыванию использованной игрушки), а m_commPanel = NULL гарантирует, что мы случайно не попытаемся использовать ее снова. Это позволяет поддерживать программу в рабочем состоянии и предотвращает сбои при подключении к CommunicationsDialog.
CAdminHomeDialog::CAdminHomeDialog(string chatId, string botToken) : m_commPanel(NULL), m_chatId(chatId), m_botToken(botToken) { } CAdminHomeDialog::~CAdminHomeDialog(void) { if(m_commPanel) { delete m_commPanel; m_commPanel = NULL; } }
Шаг 4
Здесь происходит магия — OnClickCommunications запускает панель коммуникации при клике на кнопке Communications (коммуникации). Сначала убеждаемся в том, что панель еще не создана: if(m_commPanel == NULL). При NULL используем new CCommunicationDialog(m_chatId, m_botToken), чтобы создать новый диалог, передав сохраненные данные Telegram. Это как открыть коробку с игрушкой и собрать ее, следуя инструкции.
Если новая операция завершается неудачей (возможно, закончилось место на компьютере), m_commPanel остается равной NULL. Выводим сообщение об ошибке и останавливаемся. В противном случае мы вызываем метод m_commPanel.Create для построения графика. Мы используем m_chart_id (наш рабочий график), "CommPanel" в качестве имени, m_subwin (рабочее окно) и координаты (20, 435 - верхний левый угол, 300 - ширина и 350 - размер). Если построение не удалось, выводим сообщение об ошибке, удаляем объект и устанавливаем значение m_commPanel в NULL.
Если панель уже успешно создана, функция m_commPanel.Toggle() включает или выключает ее — отображает, если панель скрыта, и скрывает, если она отображена. Такое "ленивое построение" позволяет создавать объект только при клике пользователя, экономя ресурсы.
void CAdminHomeDialog::OnClickCommunications() { if(m_commPanel == NULL) { m_commPanel = new CCommunicationDialog(m_chatId, m_botToken); // Pass chatId and botToken if(m_commPanel == NULL) { Print("Error: Failed to allocate Communications panel"); return; } if(!m_commPanel.Create(m_chart_id, "CommPanel", m_subwin, 20, 435, 20 + 300, 435 + 350)) { Print("Error: Failed to create Communications panel"); delete m_commPanel; m_commPanel = NULL; return; } } m_commPanel.Toggle(); }
Шаг 5
OnEvent отслеживает действия пользователя, такие как клики, и подключает CommunicationsDialog, передавая ему события. Если id равен CHARTEVENT_OBJECT_CLICK, проверяем, соответствует ли sparam (имя выбранного объекта) m_commButton.Name(). Если это произойдёт, мы распечатаем сообщение и вызовем OnClickCommunications для открытия панели, вернув true, чтобы сообщить об окончании обработки.
Ключевой частью интеграции является блок else if(m_commPanel != NULL && m_commPanel.IsVisible()). Если панель коммуникации существует (!= NULL) и находится на экране (IsVisible()), отправляем событие (id, lparam и т.д.) в m_commPanel.OnEvent. Это позволяет CommunicationsDialog обрабатывать собственные клики, такие как Send или Clear. Мы возвращаем всё, что возвращает m_commPanel.OnEvent, связывая системы событий двух диалогов. Если совпадения нет, управление передается CAppDialog::OnEvent. Благодаря такому взаимодействию оба диалоговых окна бесперебойно реагируют на действия пользователя.
bool CAdminHomeDialog::OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if(id == CHARTEVENT_OBJECT_CLICK) { Print("Clicked object: ", sparam); if(sparam == m_commButton.Name()) { Print("Communications button detected"); OnClickCommunications(); return true; } // ... other button checks ... } // Forward remaining events to CommPanel if visible else if(m_commPanel != NULL && m_commPanel.IsVisible()) { return m_commPanel.OnEvent(id, lparam, dparam, sparam); } return CAppDialog::OnEvent(id, lparam, dparam, sparam); }
Тестирование и результаты
После успешной компиляции модуля связи и его интеграции для работы с основной программой, мы без проблем запустили программу в терминале. Все панели реагируют на клики, что подтверждается записями на вкладке "Эксперты". Клик по кнопке Communications Panel в AdminHomeDialog запускает создание CommunicationsDialog, но первоначальная логика скрывает его, требуя второго клика. После того как CommunicationsDialog появляется на графике, повторное нажатие скрывает его.
Однако я заметил существенную проблему с представлением в виде списка: события клика и прокрутки для быстрого выбора сообщений для отправки работают не так, как ожидалось. Я считаю, что это незначительная проблема, которую я смогу исправить. На данный момент основная концепция функциональна и наглядна. Следующие шаги — доработка и добавление новых возможностей.

Тестирование модуля коммуникации
Заключение
Разработка модуля CommunicationsDialog, интегрированного в AdminHomeDialog.mqh, привела к созданию функциональной и модульной системы для обмена сообщениями в Telegram в рамках нашей новой панели администратора. Нам удалось создать адаптивный администраторский интерфейс, который успешно запускает и переключает панель коммуникации по запросу, о чем свидетельствует вкладка "Эксперты", отображающая события кликов, такие как "Clicked object: m_SendButton". "Ленивое" создание и переключение оптимизирует использование ресурсов, а событийно-ориентированный дизайн обеспечивает масштабируемость, демонстрируя наглядность и работоспособность основной концепции на графике. Модульная структура (CAdminHomeDialog в качестве хаба и CCommunicationDialog в качестве специализированного инструмента) закладывает прочную основу для будущих расширений.
Однако остаются незначительные проблемы, такие как неработающие события клика и прокрутки в режиме списка для быстрого выбора сообщений, а также необходимость доработки событий кнопок на панели коммуникации. Несмотря на это, сильные стороны системы — эффективность, быстродействие и понятная интеграция — перевешивают недостатки. Благодаря целенаправленным исправлениям в механизме распространения событий и планам по его усовершенствованию и добавлению новых функций, мы имеем все возможности для превращения прототипа в отточенный, многофункциональный инструмент для трейдеров. Хорошие новости! Файл Telegram.mqh теперь доступен в CodeBase.
Таблица приложенных файлов
| Имя файла | Описание |
|---|---|
| CommunicationsDialog.mqh | Диалоговое окно для отправки сообщений в Telegram, включающее текстовое поле и список быстрых сообщений. |
| AdminHomeDialog.mqh | Для создания диалогового окна администратора. Содержит все объявления координат. |
| New_Admin_Panel.mqh | В последней версии панели администратора реализована концепция модульности. |
| Telegram.mqh | Для передачи сообщений и уведомлений через Telegram. |
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/17044
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Нейросети в трейдинге: Пространственно-управляемая агрегация рыночных событий (Энкодеры)
Нейросети в трейдинге: Пространственно-управляемая агрегация рыночных событий (Основные модули)
От новичка до эксперта: Торговля с временной фильтрацией
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования