English Русский 中文 Español Deutsch 日本語
Interfaces gráficas I: Funções para os Botões do Formulário e Remoção dos Elementos da Interface (Capítulo 4)

Interfaces gráficas I: Funções para os Botões do Formulário e Remoção dos Elementos da Interface (Capítulo 4)

MetaTrader 5Exemplos | 25 julho 2016, 10:25
2 039 0
Anatoli Kazharski
Anatoli Kazharski

Conteúdo

 

Introdução

Este artigo é a continuação da primeira parte da série sobre as interfaces gráficas. O primeiro artigo Interfaces gráficas I: Preparação da Estrutura da Biblioteca (Capítulo 1) explica em detalhes a finalidade desta biblioteca. A lista completa dos links para os artigos da primeira parte se encontram no final de cada capítulo. Lá, você também pode encontrar e baixar a versão completa da biblioteca, no estágio de desenvolvimento atual. Os arquivos devem estar localizados nas mesmas pastas que o arquivo baixado.

No artigo anterior, a classe CWindow foi estendida com adições que permitiram o deslocamento do formulário sobre o gráfico. Agora, os controles do formulário reagem aos movimentos do cursor do mouse. Neste artigo, nós vamos continuar desenvolvendo a classe CWindow e enriquecê-la com os métodos que nos permitem gerir o formulário mediante os cliques em seus controles. Nós habilitaremos o fechamento do programa por um botão do formulário, bem como implementar a funcionalidade de minimização e maximização do mesmo.

 

Funções para os Botões do Formulário

O formulário na biblioteca em desenvolvimento contém dois botões principais, que são obrigatórios para um EA e um indicador, e um adicional que poderá ser omitido.

  1. O primeiro botão à direita deve conter a função para fechar a janela. Ela deve ser especificada aqui como isso será exatamente implementado. Se o botão que foi pressionado está em uma janela de diálogo que foi chamada da janela principal ou outra janela de diálogo, então, esta janela de diálogo será fechada. Se o botão que foi pressionado estiver na janela principal, então, o programa será excluído do gráfico.

  2. O segundo botão consiste de duas partes. Para ser mais preciso, de dois botões. Cada um deles será exibido dependendo do modo atual. Se a janela estiver maximizada, então, será mostrado o botão para minimizar a janela. Uma janela minimizada terá um botão para maximizar a janela.

  3. O terceiro botão serve para ativar o modo de dica personalizada. É necessário haver algum esclarecimento quanto ao significado de "dicas personalizadas". A linguagem MQL permite exibir dicas de texto para cada objeto gráfico no foco do cursor do mouse.

    Você deve ter notado que no código da classe CWindow o método CChartObject::Tooltip() foi utilizado para a criação de cada objeto do formulário quando as propriedades dele foram especificadas. Se for necessário o objeto ter uma dica, então, o texto que será mostrado quando o cursor é passado sobre o objeto deve ser passado para o método Tooltip(). Se a dica não for necessária, então deve ser passado o texto "\n".

    Esta propriedade possui algumas limitações: (1) se o texto contiver mais de 128 símbolos, ele não poderá ser exibido completamente e as (2) propriedades do texto, como a espessura do traço, cor, fonte da letra não poderão ser gerenciados. Desse modo, elas não serão dicas personalizadas. Nós vamos classificá-las como dicas padrões em MQL. Essas dicas são boas para pequenos comentários. Eles não se adequam, no entanto, quando o texto for significativamente maior e tem que ser exibido em várias linhas com algumas palavras em destaque.

    Eu tenho certeza de que todo mundo está familiarizado com o Excel. Como um exemplo, você pode visualizar as dicas para todas as opções neste programa (veja a imagem abaixo). Será criado na biblioteca em desenvolvimento uma classe que permitirá a exibição de dicas deste tipo. Essas dicas serão chamadas de personalizadas já que somos nós que criamos e estamos implementando a funcionalidade. Voltaremos a este assunto somente quando a classe de formulário para os controles estiver totalmente implementada e que há pelo menos um controle que possa ser anexado ao formulário.

Fig. 1. As dicas em Excel

Fig. 1. As dicas em Excel

Vamos começar com o botão para fechar a janela. Crie o método CloseWindow() na classe CWindow. Ela é para ser chamada dentro do manipulador de eventos do gráfico CWindow::OnEvent() ao manipular um evento com o identificador CHARTEVENT_OBJECT_CLICK causado por um clique em um objeto gráfico. Um evento com este identificador é gerado ao clicar em qualquer objeto gráfico. Naquele momento, o parâmetro de string dos eventos do gráfico (sparam) continha o nome do objeto que fosse clicado. Por essa razão, é realizado uma verificação com o nome do objeto para se certificar de que este é o objeto em questão.

O método CWindow::CloseWindow() aceita um parâmetro - o nome do objeto que foi clicado. No início do código há uma verificação se o clique foi no objeto, que por nossa concepção é um botão para fechar a janela. Em seguida, o código do método é dividido em dois ramos - para o principal e a janela de diálogo. Como o modo multi-janela não está completo ainda, o ramo para a janela de diálogo será deixado vazio. Mais tarde, nós vamos voltar para a sua implementação. Tudo está pronto para se trabalhar com a janela principal. É por isso que é exigido as verificações para o tipo do programa (seja este um EA ou um indicador) no corpo desta condição. Isto é porque a remoção dos diferentes tipos de programas requer diferentes funções da linguagem MQL.

O MQL tem sua própria função para chamar uma janela de diálogo para a confirmação de várias ações do usuário. Está é a função MessageBox(). Nós vamos usá-la temporariamente para a confirmação da exclusão de um EA do gráfico. Esta janela não pode ser usada para apagar um indicador já que isso pertence ao tipo modal. A razão é que os indicadores são executados no fluxo da interface e nós não devemos suspendê-lo. Quando o modo multi-janela e o controle "Button" são implementados, nós poderemos usar a nossa janela de diálogo, que é a janela criada com os meios de nossa biblioteca.

Antes do programa ser removido do gráfico, será impresso no registro da aba Experts do terminal uma mensagem dizendo que o programa foi removido devido a decisão do usuário.

Vamos apresentar as adições para a classe CWindow, como mostrado no código abaixo.

Declaração e implementação do método CWindow::CloseWindow() para fechar a janela:

class CWindow: public CElement
  {
public:
   //--- Fechando a janela
   bool              CloseWindow(const string pressed_object);
  };
//+------------------------------------------------------------------+
//| Fechando a janela de diálogo ou o programa                       |
//+------------------------------------------------------------------+
bool CWindow::CloseWindow(const string pressed_object)
  {
//--- Se o clique não foi no botão para fechar a janela
   if(pressed_object!=m_button_close.Name())
      return(false);
//--- Se está é a janela principal
   if(m_window_type==W_MAIN)
     {
      //--- Se o programa é do tipo "Expert Advisor"
      if(CElement::ProgramType()==PROGRAM_EXPERT)
        {
         string text="Do you want the program to be deleted from the chart?";
         //--- Abre uma janela de diálogo
         int mb_res=::MessageBox(text,NULL,MB_YESNO|MB_ICONQUESTION);
         //--- Se o botão "Yes" é pressionado, remove o programa a partir do gráfico
         if(mb_res==IDYES)
           {
            ::Print(__FUNCTION__," > O programa foi removido do gráfico devido à sua decisão!");
            //--- Removendo o Expert Advisor do gráfico
            ::ExpertRemove();
            return(true);
           }
        }
      //--- Se o programa é do tipo "Indicador"
      else if(CElement::ProgramType()==PROGRAM_INDICATOR)
        {
         //--- Removendo o indicador do gráfico
         if(::ChartIndicatorDelete(m_chart_id,m_subwin,CElement::ProgramName()))
           {
            ::Print(__FUNCTION__," > O programa foi removido do gráfico devido à sua decisão!");
            return(true);
           }
        }
     }
   //--- Se isto é uma janela de diálogo
   else if(m_window_type==W_DIALOG)
     {
     }
//---
   return(false);
  }

Chamando o método CWindow::CloseWindow() no manipulador de eventos do gráfico:

//+------------------------------------------------------------------+
//| Manipulador de eventos do gráfico                                |
//+------------------------------------------------------------------+
void CWindow::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Manipulando o evento de clique em um objeto
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- Fechando a janela
      CloseWindow(sparam);
      return;
     }
  }

Compile os arquivos da biblioteca e o EA que foi usado antes dos testes. Carregue o EA para o gráfico. Pressionando o botão para fechar a janela no formulário irá abrir uma janela como é mostrado na imagem:

Fig. 2. Teste do fechamento do programa pressionando um botão no formulário

Fig. 2. Teste do fechamento do programa pressionando um botão no formulário

Agora, nós vamos criar métodos para o segundo botão, o que permitirá maximizar e minimizar o formulário:

  • O método CWindow::RollUp() para minimizar o formulário.
  • O método CWindow::Unroll() para maximizar o formulário.

Estes métodos são muito simples e semelhantes em seu conteúdo. No início de cada método, o ícone é alterado, então, o tamanho (altura) do fundo é definido e armazenado, em seguida, o foco é zerado e é definido o estado correspondente a escolha do usuário. Quando o programa é um indicador localizado em qualquer janela do gráfico diferente da principal, o modo de altura fixo da sub-janela do indicador é definido na criação do formulário e o modo permitindo a alteração do tamanho da sub-janela do indicador é definido, então o método CWindow::ChangeSubwindowHeight() é chamado. Isto foi criado anteriormente, devendo estar na classe CWindow.

O código dos métodos CWindow::RollUp() e CWindow::Unroll() é apresentado abaixo em detalhes:

//+------------------------------------------------------------------+
//| Minimiza a janela                                                |
//+------------------------------------------------------------------+
void CWindow::RollUp(void)
  {
//--- Altera o botão
   m_button_rollup.Timeframes(OBJ_NO_PERIODS);
   m_button_unroll.Timeframes(OBJ_ALL_PERIODS);
//--- Define e armazena o tamanho
   m_bg.Y_Size(m_caption_height);
   CElement::YSize(m_caption_height);
//--- Desabilita o botão
   m_button_unroll.MouseFocus(false);
   m_button_unroll.State(false);
//--- Estado do formulário "Minimizado"
   m_is_minimized=true;
//--- Se este é um indicador com uma altura definida e com a sub-janela no modo minimizado,
//    define o tamanho do indicador da sub-janela
   if(m_height_subwindow_mode)
      if(m_rollup_subwindow_mode)
         ChangeSubwindowHeight(m_caption_height+3);
  }
//+------------------------------------------------------------------+
//| Maximiza a janela                                                |
//+------------------------------------------------------------------+
void CWindow::Unroll(void)
  {
//--- Altera o botão
   m_button_unroll.Timeframes(OBJ_NO_PERIODS);
   m_button_rollup.Timeframes(OBJ_ALL_PERIODS);
//--- Define e armazena o tamanho
   m_bg.Y_Size(m_bg_full_height);
   CElement::YSize(m_bg_full_height);
//--- Desabilita o botão
   m_button_rollup.MouseFocus(false);
   m_button_rollup.State(false);
//--- Estado do formulário "Maximizado"
   m_is_minimized=false;
//--- Se este é um indicador com uma altura definida e com a sub-janela no modo minimizado,
//    define o tamanho do indicador da sub-janela
   if(m_height_subwindow_mode)
      if(m_rollup_subwindow_mode)
         ChangeSubwindowHeight(m_subwindow_height);
  }

Agora, nós criamos um ou mais métodos aceitando o parâmetro de string nos eventos do gráfico. Será realizado a verificação do nome do objeto clicado parâmetro de string semelhante a maneira que foi implementado no método CWindow::CloseWindow(). Dependendo de qual botão foi pressionado, o método correspondente considerado no código acima será chamado. Vamos nomear este método de CWindow::ChangeWindowState(). Adicione esta declaração, implementação e a chamada para o manipulador de evento do gráfico na classe CWindow, como mostrado no código abaixo:

class CWindow: public CElement
  {
public:
   //--- Alterando o estado da janela
   bool              ChangeWindowState(const string pressed_object);
  };
//+------------------------------------------------------------------+
//| Manipulador de eventos do gráfico                                |
//+------------------------------------------------------------------+
void CWindow::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Manipulando o evento de clique em um objeto
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- Minimiza/Maximiza a janela.
      ChangeWindowState(sparam);
      return;
     }
  }
//+------------------------------------------------------------------+
//| Verifica o evento para a minimização/maximização da janela       |
//+------------------------------------------------------------------+
bool CWindow::ChangeWindowState(const string pressed_object)
  {
//--- Se o botão "Minimize the window" foi pressionado
   if(pressed_object==m_button_rollup.Name())
     {
      RollUp();
      return(true);
     }
//--- Se o botão "Maximize the window" foi pressionado
   if(pressed_object==m_button_unroll.Name())
     {
      Unroll();
      return(true);
     }
//---
   return(false);
  }

Compile os arquivos da biblioteca onde você introduziu as alterações e o arquivo de programa destinado aos testes. O resultado esperado do nosso trabalho é a possibilidade de minimizar/maximizar a formulário para os controles. A imagem abaixo mostra o formulário no modo minimizado:

Fig. 3. Teste da funcionalidade do formulário.

Fig. 3. Teste da funcionalidade do formulário

Isso funciona. O formulário para os controles pode ser deslocado sobre o gráfico, cada objeto reage ao cursor do mouse e os botões funcionam de acordo com a sua funcionalidade projetada.

 

Remoção dos Elementos da Interface

Se você seguiu a sequência de ações sugeridas no artigo e fazendo isso até este ponto, você pode ver que quando o EA é removido do gráfico, todos os objetos da interface gráfica são removidos. Nós ainda não discutimos os métodos para apagar os objetos gráficos a partir do gráfico. Por que os objetos são apagados quando o EA é removido? Isto é fornecido na biblioteca padrão das classes, para ser mais preciso, no destrutor da classe CChartObject, a classe derivada que é utilizada em nossa biblioteca. Quando o programa é removido do gráfico, os destrutores de classes são chamados, incluindo este. Se um objeto é anexado neste gráfico, ele é removido:

//+------------------------------------------------------------------+
//| Destrutor                                                        |
//+------------------------------------------------------------------+
CChartObject::~CChartObject(void)
  {
   if(m_chart_id!=-1)
      ::ObjectDelete(m_chart_id,m_name);
  }

Se o símbolo gráfico ou o seu tempo gráfico é alterado quando o EA está neste gráfico, então, os destrutores não são chamados e objetos gráficos não são removidos. Como a interface gráfica é criada na função de inicialização OnInit() no arquivo de programa principal, e é realizado a desinicialização e, em seguida, a inicialização na mudança do símbolo ou no tempo gráfico do EA, a interface gráfica é criada no topo da existente. Como resultado, a primeira instância de tal mudança lhe dará duas cópias dos objetos. Se você continuar a alterar o símbolo gráfico ou o tempo gráfico, você terá muitas cópias de objetos de interface.

Fig. 4. Teste do formulário quando se muda o símbolo gráfico e o período gráfico.

Fig. 4. Teste do formulário quando se muda o símbolo gráfico e o período gráfico

Nós devemos levar isso na biblioteca que estamos desenvolvendo, ou seja, nós temos de assegurar que quando o programa é desinicializado, todos os objetos gráficos são removidos. Além disso, todos os arrays contendo ponteiros para estes objetos devem ser esvaziados e aceitar o tamanho de suas dimensões como zero. Depois que foi feito, a configuração dos objetos ficará correto no momento da inicialização, ou seja, sem a criação de clones. Agora, nós vamos implementar este mecanismo.

O método virtual Delete() é declarado na classe CElement. Cada classe derivada da classe CElement terá sua própria implementação deste método. O método Delete() foi declarado anteriormente na classe CWindow. Agora, nós só temos de implementar este método (ver o código abaixo). Neste método algumas variáveis ​​são zeradas, todos os objetos do controle são excluídos e o array de ponteiros para os objetos da classe base também são esvaziados.

//+------------------------------------------------------------------+
//| Remoção                                                          |
//+------------------------------------------------------------------+
void CWindow::Delete(void)
  {
//--- Zerando as variáveis
   m_right_limit=0;
//--- Removendo objetos
   m_bg.Delete();
   m_caption_bg.Delete();
   m_icon.Delete();
   m_label.Delete();
   m_button_close.Delete();
   m_button_rollup.Delete();
   m_button_unroll.Delete();
   m_button_tooltip.Delete();
//--- Esvaziando o array de objetos
   CElement::FreeObjectsArray();
//--- Zerando o foco do controle
   CElement::MouseFocus(false);
  }

O acesso aos métodos Delete() de todos os controles de interface podem ser obtidos a partir da classe CWndEvents. Portanto, nós vamos criar o método CWndEvents::Destroy() para apagar a interface gráfica. Neste método é iterado sobre os controles de todas os formulários, chamando o método Delete() de cada controle. Antes de chamar o método Delete(), verifique a validade do ponteiro. Após a remoção dos objetos, os arrays de controle devem ser esvaziados. Após a saída do loop do formulário, seus arrays devem ser esvaziados também.

O código a seguir mostra a declaração e implementação do método CWndEvents::Destroy():

class CWndEvents : public CWndContainer
  {
protected:
   //--- Removendo a interface
   void              Destroy(void);
  };
//+------------------------------------------------------------------+
//| Removendo todos os objetos                                       |
//+------------------------------------------------------------------+
void CWndEvents::Destroy(void)
  {
   int window_total=CWndContainer::WindowsTotal();
   for(int w=0; w<window_total; w++)
     {
      int elements_total=CWndContainer::ElementsTotal(w);
      for(int e=0; e<elements_total; e++)
        {
         //--- Se o ponteiro é inválido, passe para o próximo
         if(::CheckPointer(m_wnd[w].m_elements[e])==POINTER_INVALID)
            continue;
         //--- Remove os objetos do controle
         m_wnd[w].m_elements[e].Delete();
        }
      //--- Esvazia os arrays de controle
      ::ArrayFree(m_wnd[w].m_objects);
      ::ArrayFree(m_wnd[w].m_elements);
     }
//--- Esvazia os arrays de formulário
   ::ArrayFree(m_wnd);
   ::ArrayFree(m_windows);
  }

Agora, chame o método Destroy() dentro do método CProgram::OnDeinitEvent() ligado à função OnDeinit() no arquivo principal do programa. Adicione-o lá, como é mostrado abaixo:

//+------------------------------------------------------------------+
//| Desinicialização                                                 |
//+------------------------------------------------------------------+
void CProgram::OnDeinitEvent(const int reason)
  {
   //--- Removendo a interface
   CWndEvents::Destroy();  
  }

Compile todos os arquivos da biblioteca, onde foram feitos as últimas alterações e o arquivo de programa principal. Carregue o EA para o gráfico e altere algumas vezes o símbolo e o período gráfico. Agora, tudo deve funcionar corretamente. Os clones de objetos não aparecem mais. Problema resolvido.

 

Conclusão

No capítulo seguinte, nós iremos realizar os testes para ver como o formulário funciona em outros tipos de programa, como indicadores e scripts. Nós também iremos realizar testes no terminal MetaTrader 4 já que o objetivo inicial era fazer uma biblioteca para se criar interfaces gráficas multi-plataforma.

Você pode encontrar o material da Parte I e baixá-la para testar como é seu funcionamento. Se você tiver dúvidas sobre a utilização do material a partir desses arquivos, você poderá consultar a descrição detalhada do desenvolvimento da biblioteca em um dos artigos da lista abaixo ou fazer sua pergunta nos comentários deste artigo.

Lista de artigos (capítulos) da primeira parte:

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/2128

Arquivos anexados |
Interfaces gráficas I: Biblioteca de Testes em Programas de Diferentes Tipos e no Terminal MetaTrader 4 (Capítulo 5) Interfaces gráficas I: Biblioteca de Testes em Programas de Diferentes Tipos e no Terminal MetaTrader 4 (Capítulo 5)
No capítulo anterior da primeira parte da série sobre interfaces gráficas, a classe de formulário foi enriquecida por métodos que permitiram gerir o formulário através dos cliques em seus controles. Neste artigo, nós vamos testar nosso trabalho em diferentes tipos de programa MQL, como indicadores e scripts. Já que a biblioteca foi concebida para ser multi-plataforma para que ela pudesse ser utilizada em todas as plataformas MetaTrader, nós também vamos testá-la no MetaTrader 4.
Expert Advisor Universal: Ordens Pendentes e Suporte para Cobertura de Risco (Parte 5) Expert Advisor Universal: Ordens Pendentes e Suporte para Cobertura de Risco (Parte 5)
Este artigo fornece uma descrição mais detalhada do mecanismo de negociação CStrategy. Por demanda popular dos usuários, nós adicionamos funções de apoio as ordem pendente no mecanismo de negociação. Além disso, a versão mais recente do MetaTrader 5 agora oferece suporte a contas com a opção de cobertura (hedge). O mesmo suporte foi adicionado ao CStrategy. O artigo fornece uma descrição detalhada de algoritmos para o uso de ordens pendentes, bem como dos princípios de funcionamento da classe CStrategy nas contas com a opção de cobertura (hedge) habilitada.
Expert Advisor Universal: Um Trailing Stop Customizado (Parte 6) Expert Advisor Universal: Um Trailing Stop Customizado (Parte 6)
A sexta parte do artigo sobre o Expert Advisor universal descreve o uso do recurso "Trailing Stop". O artigo irá guiá-lo através de como criar um módulo "Trailing Stop" personalizado com regras unificadas, bem como adicioná-lo ao motor de negociação para gerir automaticamente as posições.
Expert Advisor Universal: Negociação em Grupo e Gestão de uma Carteira de Estratégias (Parte 4) Expert Advisor Universal: Negociação em Grupo e Gestão de uma Carteira de Estratégias (Parte 4)
Na última parte da série de artigos sobre o mecanismo de negociação CStrategy, vamos considerar a operação simultânea de vários algoritmos de negociação, aprenderemos a carregar estratégias de arquivos XML, e apresentaremos um painel simples para selecionar Expert Advisors partir de um único módulo executável e gerenciar os seus modos de negociação.