Criando um Painel de Administração de Trading em MQL5 (Parte IX): Organização de Código (III): Módulo de Comunicação
Conteúdo
- Introdução
- Desenvolvendo a classe CommunicationsDialog.
- Integração de CommunicationsDialog com outros arquivos de cabeçalho e o programa principal
- Testes e Resultados
- Conclusão
Introdução
Hoje, nosso objetivo é expandir nosso Novo Painel de Administração a partir de onde paramos no artigo anterior, onde introduzimos a modularização como um aspecto-chave de uma organização de código mais ampla. Introduzimos a classe AdminHomeDialog, responsável por criar a interface inicial do Admin. Este painel inicial serve como o hub central para acessar várias funcionalidades e consiste em botões de controle de acesso que levam a três painéis de componentes principais:
- Painel de Gerenciamento de Trades
- Painel de Comunicações
- Painel de Análises
Esses não são os limites finais do sistema, pois novos recursos continuam viáveis à medida que seguimos refinando e expandindo a base existente. Nesta discussão, focamos especificamente no Painel de Comunicações como um módulo, aprimorando-o ainda mais a partir de sua versão anterior dentro do Painel de Administração monolítico.
Principais pontos desta discussão:
- Compreendendo classes em MQL5
- Desenvolvendo arquivos de cabeçalho
- Herdando de classes internas
- Utilizando o arquivo de cabeçalho ListView
- Aplicando cores para um melhor design de interface

Fluxo modular do Novo Painel de Administração
1. Programa Principal (New_Admin_Panel.mq5)
- Este é o ponto de entrada da aplicação.
- Ele inicializa o sistema e cria uma instância de AdminHomeDialog, que serve como a interface principal do usuário.
2. AdminHomeDialog
- Atua como o hub central para a interação do usuário.
- Responde a eventos, como cliques de botão, para criar instâncias de outros diálogos como CommunicationsDialog.
- Projetado para ser extensível, permitindo gerar diálogos adicionais (por exemplo, FutureDialog) conforme necessário.
3. CommunicationsDialog
- Um diálogo especializado responsável por funcionalidades específicas, como enviar mensagens via API do Telegram.
- Criado dinamicamente por AdminHomeDialog quando acionado por uma ação do usuário (por exemplo, clicar em um botão "Enviar Mensagem").
4. Interações Orientadas a Eventos
- O sistema utiliza uma abordagem orientada a eventos, comum em aplicações MQL5. Por exemplo, um clique de botão em AdminHomeDialog aciona a criação e exibição de CommunicationsDialog, que então executa sua tarefa (por exemplo, interagir com o Telegram).
5. Design Modular
- Cada componente tem um papel distinto: o programa principal inicializa, AdminHomeDialog gerencia a interface, e CommunicationsDialog lida com tarefas de comunicação. Essa modularidade permite fácil expansão ou modificação.
Com a introdução e visão geral acima do nosso novo programa, agora podemos mergulhar nos detalhes do desenvolvimento do nosso módulo pioneiro, CommunicationsDialog. Na Parte (I), esta era apenas uma interface básica de comunicação, mas agora expandimos sua funcionalidade incorporando novos recursos. Com esse conhecimento fundamental, podemos visualizar melhor nossa direção. Nas próximas subseções, exploraremos os blocos de construção essenciais que tornam possíveis nossas classes personalizadas.
Desenvolvendo a classe CommunicationsDialog
Para melhorar a compreensão, incluí abaixo uma imagem que ilustra a hierarquia de fluxo da classe base até nossas classes personalizadas. Essa abordagem é uma forma poderosa de promover a reutilização de código. Ao longo do projeto, há muitos componentes de painel, cada um servindo a um propósito distinto. Essa singularidade torna cada componente de código adaptável para integração em outros projetos também.
Em MQL5, a classe Dialog normalmente se refere a CAppDialog ou CDialog do arquivo de inclusão (Dialog.mqh), pois estas servem como as classes de diálogo fundamentais na biblioteca padrão.
Tanto CommunicationsDialog quanto CAdminHomeDialog herdam de CAppDialog, criando uma hierarquia estruturada onde múltiplas classes de diálogo compartilham uma base comum para funcionalidade de diálogo. Essa estrutura será representada no fluxograma de hierarquia abaixo.

Relação da classe base e das classes personalizadas
Para começar, abra o MetaEditor 5 a partir da sua área de trabalho ou inicie-o pelo terminal pressionando F4.
No Navegador, localize Dialog.mqh na pasta Includes e abra-o para referência.
Siga a imagem abaixo para orientação. 
Localizando a classe Dialog no MetaEditor 5
Em seguida, crie um novo arquivo para desenvolver a nova classe.
Geralmente, nosso programa é composto por seções como configuração do cabeçalho, definições de layout e cores, declaração de classes e implementações de métodos. Começaremos com o básico do nosso arquivo. Os comentários de cabeçalho no topo nos informam o que é este arquivo (CommunicationsDialog.mqh), quem é o proprietário (MetaQuotes Ltd., embora você possa alterar para o seu nome) e de onde ele vem (comunidade MQL5). Esses comentários funcionam como uma página de título para o seu código, ajudando outras pessoas a identificar seu propósito.
Em seguida, usamos #ifndef e #define para criar uma "proteção" chamada COMMUNICATIONS_DIALOG_MQH. Isso impede que o arquivo seja incluído múltiplas vezes em um projeto, o que poderia causar erros. Pense nisso como um bloqueio que diz: "Se eu já fui aberto, não me abra novamente."
As linhas #include trazem ferramentas que precisamos da biblioteca MQL5. O (Dialog.mqh) nos fornece a classe base de diálogo (CAppDialog), enquanto (Button.mqh) e (Edit.mqh) fornecem classes de botão e caixa de texto. (Label.mqh) e (ListView.mqh) adicionam um rótulo e uma lista para mensagens rápidas. Por fim, (Telegram.mqh) é um arquivo personalizado (presume-se que exista) que lida com mensagens do Telegram. Esses são como ferramentas emprestadas de uma caixa de ferramentas para construir nosso diálogo. Abaixo está o trecho de código para esta seção.
Seção 1: Cabeçalho do Arquivo e Includes
//+------------------------------------------------------------------+ //| 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"
Seção 2: Definições de Layout e Cores
Antes de construir o diálogo, precisamos planejar seu tamanho e aparência. As instruções #define funcionam como a criação de um projeto com medidas e cores. Para o layout, COMMS_PANEL_WIDTH (300 pixels) e COMMS_PANEL_HEIGHT (350 pixels) definem o tamanho do nosso diálogo na tela. As margens (COMMS_MARGIN_LEFT, COMMS_MARGIN_TOP, COMMS_MARGIN_RIGHT) fornecem espaçamento nas bordas, enquanto COMMS_GAP_VERTICAL adiciona espaço entre os elementos verticalmente. Cada controle possui seu próprio tamanho: a caixa de entrada é alta (COMMS_INPUT_HEIGHT), os botões são pequenos retângulos (COMMS_BUTTON_WIDTH e HEIGHT), e a list view e o rótulo possuem suas próprias dimensões.
As cores tornam o diálogo mais agradável e legível. Usamos números hexadecimais (como 0x808080 para cinza escuro) porque é assim que os computadores entendem cores em MQL5. CLR_PANEL_BG define o fundo principal do diálogo, CLR_CLIENT_BG colore a área onde os controles ficam, e CLR_CAPTION_BG e CLR_CAPTION_TEXT estilizam a barra de título. As bordas recebem CLR_BORDER_BG e CLR_BORDER, enquanto controles como a caixa de entrada (CLR_INPUT_BG, CLR_INPUT_TEXT) e botões (CLR_SEND_BG, CLR_CLEAR_BG) recebem suas próprias cores. Comentários ao lado de cada define explicam o que fazem, facilitando ajustes posteriores.
// **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)
Seção 3: Declaração da Classe
Agora estamos definindo o núcleo do nosso diálogo: a classe CCommunicationDialog. Pense em uma classe como uma receita para criar um objeto de diálogo. Dizemos: public CAppDialog porque nosso diálogo é baseado em CAppDialog, uma classe de diálogo pronta do MQL5 que nos fornece recursos básicos de diálogo, como uma barra de título e bordas.
A seção private lista os componentes que usaremos dentro do diálogo. O m_inputBox é um campo de texto onde os usuários digitam mensagens, m_sendButton e m_clearButton são botões para enviar ou limpar a mensagem, m_quickMsgLabel é um rótulo de texto, e m_quickMessageList é uma lista de mensagens pré-definidas. Também armazenamos m_chatId e m_botToken como strings para o Telegram, e m_quickMessages como um array para armazenar oito opções de mensagens rápidas.
Na seção public, listamos as funções que qualquer pessoa pode usar para interagir com nosso diálogo. O construtor (CCommunicationDialog) configura o diálogo com um chat ID e bot token, e o destrutor (~CCommunicationDialog) faz a limpeza quando terminamos. O método `Create` constrói a caixa de diálogo no gráfico, o método `OnEvent` lida com cliques e ações, e o método `Toggle` mostra ou oculta a caixa de diálogo.
De volta à seção private, temos funções auxiliares para criar cada controle (CreateInputBox, etc.) e manipuladores de eventos (OnClickSend, OnClickClear) para definir o que acontece quando os botões são clicados. Essas são privadas porque são detalhes internos de funcionamento do diálogo.
//+------------------------------------------------------------------+ //| 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 };
Seção 4: Construtor e Destrutor
O construtor é como preparar um novo brinquedo antes de usá-lo. Quando alguém cria um CCommunicationDialog, deve fornecer um chatId e um botToken (strings para o Telegram). A parte m_chatId(chatId) e m_botToken(botToken) copia esses valores para nossas variáveis privadas para que o diálogo saiba para onde enviar mensagens. Dentro das chaves, preenchemos o array m_quickMessages com oito frases úteis que os usuários podem selecionar. Isso acontece quando o diálogo é criado, então ele já fica pronto para uso.
O destrutor é responsável pela limpeza. Ele é executado quando o diálogo é excluído (como ao fechar o programa). Atualmente, está vazio porque CAppDialog cuida da maior parte da limpeza, e não temos nada adicional para tratar. Ele está aqui como um espaço reservado caso adicionemos limpeza especial no futuro, como liberação de memória.
//+------------------------------------------------------------------+ //| 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() { }
Seção 5: Método Create
O método Create é onde construímos o diálogo na tela, como montar um objeto a partir de peças. Ele recebe parâmetros como chart (onde aparece), name (um identificador único), subwin (qual janela do gráfico), e coordenadas (x1, y1, x2, y2) para posição e tamanho. Chamamos CAppDialog::Create primeiro para configurar a estrutura básica do diálogo. Se isso falhar, retornamos false para indicar erro.
Em seguida, Caption("Communications Panel") define o título no topo do diálogo. Depois, aplicamos as cores! ObjectSetInteger altera cores interagindo diretamente com partes do diálogo (como “Back” para o fundo, “Client” para a área de controles). Usamos nossas definições de cores (por exemplo, CLR_PANEL_BG) para melhorar a aparência. A verificação if(!m_panel_flag) adiciona bordas apenas se o diálogo não for um tipo especial (controlado por m_panel_flag, uma variável de CAppDialog).
Depois disso, chamamos funções auxiliares para criar cada controle (caixa de entrada, botões, etc.). Se alguma falhar, interrompemos e retornamos false. Por fim, colocamos a primeira mensagem rápida na caixa de entrada com m_inputBox.Text e chamamos ChartRedraw() para exibir tudo no gráfico. Retornar true significa sucesso.
//+------------------------------------------------------------------+ //| 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); }
Seção 6: Métodos de Criação de Controles
Esses métodos funcionam como a montagem das peças do nosso diálogo. Cada um cria um controle (caixa de entrada, botões, rótulo, lista) e o posiciona no diálogo. Todos retornam true em caso de sucesso ou false se algo falhar, permitindo interromper em caso de erro.
Para CreateInputBox, calculamos as posições usando nossas definições de layout (por exemplo, COMMS_MARGIN_LEFT). A caixa de entrada é larga (usando ClientAreaWidth()) e alta (três vezes COMMS_INPUT_HEIGHT). Chamamos m_inputBox.Create com o ID do gráfico, um nome único (m_name + "_InputBox") e as coordenadas. Add a insere no diálogo, e ObjectSetInteger define suas cores.
CreateClearButton e CreateSendButton criam botões abaixo da caixa de entrada. Nós os empilhamos verticalmente com COMMS_GAP_VERTICAL e posicionamos o botão Send ao lado do Clear usando clear_button_x2. Cada um recebe um nome, texto ("Clear" ou "Send") e cores definidas.
CreateQuickMsgLabel adiciona um rótulo abaixo dos botões, usando mais cálculos de espaçamento. É apenas texto, então precisa apenas da cor do texto. CreateQuickMessageList cria uma list view ainda mais abaixo, preenchendo-a com nossas mensagens rápidas do construtor. Cada controle segue o mesmo padrão: criar, adicionar, colorir e verificar erros com mensagens Print para ajudar na depuração.
//+------------------------------------------------------------------+ //| 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); }
Seção 7: Alternância e tratamento de eventos
Toggle é um interruptor simples para mostrar ou ocultar o diálogo. IsVisible() verifica se ele está na tela. Se estiver, Hide() o oculta; caso contrário, Show() o exibe. ChartRedraw() atualiza o gráfico para que a mudança seja visível imediatamente. É como acionar um interruptor de luz para o diálogo.
OnEvent é o cérebro que escuta as ações do usuário, como cliques. Ele recebe um id (o que aconteceu), lparam (detalhes), dparam (mais detalhes) e sparam (qual objeto). Se id for CHARTEVENT_OBJECT_CLICK, significa que algo foi clicado. Verificamos se sparam (o nome do objeto clicado) corresponde a m_sendButton.Name() ou m_clearButton.Name(), então chamamos OnClickSend ou OnClickClear. Retornar true indica que tratamos o evento.
Se id for ON_CHANGE e sparam for o nome da lista, o usuário selecionou uma mensagem rápida. lparam nos informa qual (como um número), e colocamos essa mensagem na caixa de entrada com m_inputBox.Text. Se nada corresponder, deixamos CAppDialog::OnEvent tratar, passando o evento adiante na cadeia.
//+------------------------------------------------------------------+ //| 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); }
Seção 8: Manipuladores de Evento dos Botões
OnClickSend é executado quando o botão "Send" é clicado. Ele obtém o texto de m_inputBox.Text() e verifica se não está vazio (""). Se houver uma mensagem, tenta enviá-la usando SendMessageToTelegram (uma função de Telegram.mqh) com nossos m_chatId e m_botToken. Se funcionar, exibimos uma mensagem de sucesso; caso contrário, uma de falha. Se a caixa estiver vazia, apenas exibimos uma observação. Essa é a principal função do diálogo—enviar mensagens!
OnClickClear é mais simples. Quando "Clear" é clicado, ele define o texto da caixa de entrada como vazio ("") com m_inputBox.Text(""), redesenha o gráfico para mostrar a mudança e imprime uma confirmação. É como pressionar reset em um formulário.
//+------------------------------------------------------------------+ //| 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."); }
No final do arquivo, adicione #endif para fechar a proteção de cabeçalho:
#endif // COMMUNICATIONS_DIALOG_MQH
Isso corresponde ao #ifndef no início, organizando tudo corretamente.
Integração de CommunicationsDialog com outros arquivos de cabeçalho e o programa principal
O CommunicationsDialog é tratado dentro do AdminHomeDialog. Nesta seção, explicarei como isso funciona. Não abordarei o código completo de AdminHomeDialog, pois já o discutimos detalhadamente no artigo anterior.
Etapa 1
Para conectar CommunicationsDialog ao AdminHomeDialog, precisamos incluí-lo no nosso arquivo. A linha #include (CommunicationsDialog.mqh) funciona como abrir uma porta entre os dois. Ela informa ao MQL5 para carregar o arquivo CommunicationsDialog.mqh, tornando sua classe CCommunicationDialog disponível para uso. Coloque isso próximo ao topo de (AdminHomeDialog.mqh), após outros includes como (Dialog.mqh) e (Button.mqh). Sem isso, AdminHomeDialog não conheceria o painel de comunicação, então este é o primeiro passo para conectá-los.
Etapa 2
Dentro da classe CAdminHomeDialog, precisamos de uma forma de armazenar nosso painel de comunicação. Adicionamos CCommunicationDialog *m_commPanel na seção private, usando um ponteiro (o * indica que é uma referência para um objeto que criaremos depois). Isso é como reservar um espaço para algo que será criado quando necessário. Também adicionamos m_chatId e m_botToken como variáveis string para armazenar dados do Telegram, que serão passados para CCommunicationDialog. Esses são privados porque apenas essa classe precisa gerenciá-los, preparando a integração.
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 };
Etapa 3
O construtor configura CAdminHomeDialog quando ele é criado. Nós o atualizamos para receber chatId e botToken como parâmetros, que copiamos para m_chatId e m_botToken usando a parte : m_chatId(chatId), m_botToken(botToken). Também definimos m_commPanel como NULL (ainda não criado) com : m_commPanel(NULL). Isso significa que não criaremos o painel de comunicação imediatamente—esperamos até que o usuário solicite. É como manter algo guardado até o momento de uso.
O destrutor faz a limpeza quando o diálogo é finalizado. Verificamos if(m_commPanel) para saber se o painel de comunicação foi criado. Se sim, delete m_commPanel libera a memória, e m_commPanel = NULL garante que não será reutilizado por engano. Isso mantém o programa organizado e evita falhas ao trabalhar com 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; } }
Etapa 4
Aqui acontece a ação—OnClickCommunications inicia o painel de comunicação quando o botão "Communications" é clicado. Primeiro, verificamos if(m_commPanel == NULL) para saber se ainda não foi criado. Se for NULL, usamos new CCommunicationDialog(m_chatId, m_botToken) para criar um novo, passando os dados do Telegram. Se new falhar (por exemplo, falta de memória), m_commPanel continua NULL, imprimimos um erro e interrompemos.
Se a nova operação falhar (talvez o computador tenha ficado sem espaço), m_commPanel permanece NULL, e imprimimos um erro e paramos. Caso contrário, chamamos m_commPanel.Create para criá-lo no gráfico. Usamos m_chart_id (gráfico atual), "CommPanel" como nome, m_subwin (janela), e coordenadas (20, 435 para o canto superior esquerdo, 300 de largura e 350 de altura). Se Create falhar, exibimos um erro, deletamos o objeto e redefinimos m_commPanel para NULL.
Se já existir ou for criado com sucesso, m_commPanel.Toggle() alterna sua visibilidade—mostrando se estiver oculto, ocultando se estiver visível. Essa “criação sob demanda” significa que só o construímos quando necessário, economizando recursos.
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(); }
Etapa 5
OnEvent escuta ações do usuário como cliques e conecta CommunicationsDialog ao encaminhar eventos para ele. Quando id é CHARTEVENT_OBJECT_CLICK, verificamos if sparam (nome do objeto clicado) corresponde a m_commButton.Name(). Se corresponder, exibimos uma mensagem e chamamos OnClickCommunications para abrir o painel, retornando true para indicar que tratamos o evento.
A parte principal da integração é o bloco else if(m_commPanel != NULL && m_commPanel.IsVisible()). Se o painel de comunicação existir (!= NULL) e estiver visível (IsVisible()), enviamos o evento (id, lparam, etc.) para m_commPanel.OnEvent. Isso permite que CommunicationsDialog trate seus próprios eventos, como "Send" ou "Clear". Retornamos o valor de m_commPanel.OnEvent, conectando os sistemas de eventos dos dois diálogos. Se não houver correspondência, CAppDialog::OnEvent assume. Essa cooperação permite que ambos os diálogos respondam corretamente ao usuário.
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); }
Testes e Resultados
Após compilar com sucesso o módulo de comunicações e integrá-lo ao programa principal, executamos o programa no gráfico do terminal sem problemas. Todos os painéis respondem aos cliques, conforme confirmado pelos registros no log Experts. Ao clicar no botão 'Communications Panel' em AdminHomeDialog, a criação de CommunicationsDialog é iniciada, mas a lógica inicial o mantém oculto, exigindo um segundo clique para torná-lo visível. Quando o CommunicationsDialog está visível no gráfico, clicar no botão novamente o oculta, alternando efetivamente entre mostrar e esconder.
No entanto, notei um desafio significativo com a list view: os eventos de clique e rolagem para selecionar mensagens rápidas para envio não estão funcionando como esperado, e precisamos identificar onde erramos. Acredito que seja um problema pequeno que podemos corrigir. Por enquanto, o conceito principal está funcional e visível, e nossos próximos passos são refiná-lo e adicionar mais funcionalidades.

Testando o Módulo de Comunicações
Conclusão
O desenvolvimento do módulo CommunicationsDialog, integrado ao AdminHomeDialog.mqh, resultou em um sistema funcional e modular para envio de mensagens via Telegram dentro do nosso Novo Painel de Administração, aplicação de trading em MQL5. Alcançamos uma interface administrativa responsiva que inicia e alterna corretamente o painel de comunicação sob demanda, conforme evidenciado pelo log Experts mostrando eventos de clique como "Clicked object: m_SendButton." A abordagem de criação sob demanda e alternância otimiza o uso de recursos, enquanto o design orientado a eventos garante escalabilidade, comprovando que o conceito principal está visível e operacional no gráfico. Essa estrutura modular, com CAdminHomeDialog como hub e CCommunicationDialog como ferramenta especializada, estabelece uma base sólida para expansões futuras.
No entanto, ainda existem desafios menores, como os eventos de clique e rolagem da list view que não funcionam para a seleção de mensagens rápidas, e os eventos dos botões do painel de comunicações que precisam de mais refinamento. Apesar disso, os pontos fortes do sistema — eficiência, responsividade e integração clara — superam as limitações. Com correções direcionadas na propagação de eventos e planos para refinamento e adição de novos recursos, estamos bem posicionados para transformar este protótipo em uma ferramenta robusta e rica em funcionalidades para traders. Boas notícias! O arquivo Telegram.mqh agora está disponível na Codebase.
Tabela de anexos de arquivos
| Nome do Arquivo | Descrição |
|---|---|
| CommunicationsDialog.mqh | Define um diálogo para envio de mensagens via Telegram, com campo de entrada de texto e uma lista de opções de mensagens rápidas. |
| AdminHomeDialog.mqh | Para criação do diálogo principal do admin. Contém todas as declarações de coordenadas. |
| New_Admin_Panel.mqh | O painel de administração mais recente incorpora o conceito de modularidade. |
| Telegram.mqh | Para transmissão de mensagens e notificações via Telegram. |
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/17044
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.
Ciência de Dados e ML (Parte 34): Decomposição de séries temporais, desmembrando o mercado de ações até o núcleo
Do básico ao intermediário: Objetos e sub janelas (II)
Está chegando o novo MetaTrader 5 e MQL5
Rede neural na prática: Perceptron
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso