English Русский 中文 Español Deutsch 日本語
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)

MetaTrader 5Exemplos | 26 julho 2016, 10:26
1 111 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) considera 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 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. Os testes foram realizados previamente para o programa do tipo "Expert Advisor" e apenas no terminal MetaTrader 5. 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.

 

Usando o Formulário em Indicadores

Crie uma pasta para um novo indicador na pasta de indicadores do terminal MetaTrader 5 <data_directory>\MQL5\Indicators). Como o teste que nós realizamos anteriormente para o EA, crie nessa pasta o arquivo do programa principal e o arquivo Program.mqh que irá conter a classe CProgram. Você pode apenas copiar o arquivo Program.mqh da pasta criada para o EA para a pasta com o indicador. Nesta fase, este código já é suficiente para testar o formulário de controles sobre o indicador. Digite o código no arquivo principal do indicador como é mostrado abaixo.

A linha destacada em amarelo significa que o indicador será localizado na janela principal do gráfico. O número de buffers do indicador pode ser definido como zero já agora nós estamos apenas indo testar os elementos da interface gráfica do indicador e ignorar os cálculos com os arrays de preços.

//+------------------------------------------------------------------+
//|                                                  ChartWindow.mq5 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2015, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots   0
//--- Incluindo a classe do painel de negociação
#include "Program.mqh"
CProgram program;
//+------------------------------------------------------------------+
//| Função de inicialização do indicador personalizado               |
//+------------------------------------------------------------------+
int OnInit(void)
  {
   program.OnInitEvent();  
//--- Configura o painel de negociação
   if(!program.CreateTradePanel())
     {
      ::Print(__FUNCTION__," > Falha na criação de uma interface gráfica!");
      return(INIT_FAILED);
     }
//--- Inicialização bem sucedida
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Função de desinicialização do Expert                             |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   program.OnDeinitEvent(reason);
  }
//+------------------------------------------------------------------+
//| Função de iteração do indicador personalizado                    |
//+------------------------------------------------------------------+
int OnCalculate (const int    rates_total,     // tamanho do array price[]
                 const int    prev_calculated, // barras processadas na chamada anterior
                 const int    begin,           // início dos dados significativos
                 const double &price[])        // array para cálculo
  {
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Funçao timer                                                     |
//+------------------------------------------------------------------+
void OnTimer(void)
  {
   program.OnTimerEvent();
  }
//+------------------------------------------------------------------+
//| Função ChartEvent                                                |
//+------------------------------------------------------------------+
void OnChartEvent(const int    id,
                  const long   &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   program.ChartEvent(id,lparam,dparam,sparam);
  }
//+------------------------------------------------------------------+

Compile o indicador e carregue-o para o gráfico. O resultado deve ser como na imagem abaixo. Semelhante ao EA, o formulário pode ser minimizado, maximizado e deslocado sobre o gráfico. Seus controles reagem ao movimento do cursor e se o botão "Close" for pressionado (a cruz no canto superior direito), o indicador será removido do gráfico.

Fig. 1. Teste do formulário no indicador na janela principal do gráfico.

Fig. 1. Teste do formulário no indicador na janela principal do gráfico

Por favor, observe o ícone do formulário em seu canto superior esquerdo. O programa automaticamente identifica o tipo do programa e define o ícone padrão. Como você pode lembrar, nesta fase do desenvolvimento da classe CWindow, há a possibilidade de alterar este ícone.

O formulário para os controles funciona em um indicador também sem qualquer alteração significativa do código. No entanto, se nós tentarmos criar um indicador com o mesmo formulário em uma janela que não seja a principal, nem todos os recursos funcionarão como esperado. Ou seja, (1) o formulário será criado na janela do gráfico principal em vez da sub-janela do indicador, conforme é exigido, (2) pressionando o botão "Close", ele não irá remover o indicador do gráfico. Por que é assim? Nós vamos ver como isso pode ser alterado.

Na verdade, nada precisará ser mudado do que nós fizemos anteriormente. Tudo o que precisamos fazer é criar na classe CWndEvents um método que irá identificar automaticamente o número da janela no programa dependendo do tipo e do número dos indicadores localizados na sub-janela no gráfico. Vamos chamar este método de DetermineSubwindow(). No início deste método deve haver uma verificação se o programa é um indicador. Se ele não for um indicador, não há nenhum ponto em prosseguir pois, de todos os tipos de programas em MQL, apenas os indicadores que são localizados nas sub-janelas.

Em seguida, obtenha o número da janela do indicador usando a função ChartWindowFind(). Se isso não for bem sucedido e a função retornar -1, então, uma mensagem correspondente é impressa na aba Experts e o método termina o seu trabalho. Se o número da janela for maior do que zero, significa que esta não é a janela principal do gráfico. É necessário uma verificação adicional se esta sub-janela conter quaisquer outros indicadores. Se houver outros indicadores desta sub-janela, então o nosso indicador será removido do gráfico, relatando sobre o motivo da remoção no registro. Para isso, obtenha o número total de indicadores na sub-janela especificada utilizando a função ChartIndicatorsTotal(). Em seguida, obtenha o nome curto do último indicador na lista e se o número de indicadores não for igual a 1, remova o programa do gráfico. É obrigatório obter o nome curto do indicador já que a remoção do indicador no gráfico só é possível se o seu nome curto é especificado.

Adicione a declaração e implementação do método DetermineSubwindow() na classe CWndEvents, como é mostrado no código abaixo:

class CWndEvents : public CWndContainer
  {
private:
   //--- Identificando o número da sub-janela
   void              DetermineSubwindow(void);
  };
//+------------------------------------------------------------------+
//| Identificando o número da sub-janela                             |
//+------------------------------------------------------------------+
void CWndEvents::DetermineSubwindow(void)
  {
//--- Se o tipo do programa não é um indicador, retorna
   if(PROGRAM_TYPE!=PROGRAM_INDICATOR)
      return;
//--- Reseta o último erro
   ::ResetLastError();
//--- Identificando o número da janela do indicador
   m_subwin=::ChartWindowFind();
//--- Se a identificação do número falhou, retorna
   if(m_subwin<0)
     {
      ::Print(__FUNCTION__," > Erro ao identificar o número da sub-janela: ",::GetLastError());
      return;
     }
//--- Se esta não é a janela principal do gráfico
   if(m_subwin>0)
     {
      //--- Obtém o número total de indicadores na sub-janela especificada
      int total=::ChartIndicatorsTotal(m_chart_id,m_subwin);
      //--- Obtém o nome abreviado do último indicador na lista
      string indicator_name=::ChartIndicatorName(m_chart_id,m_subwin,total-1);
      //--- Se a sub-janela já contém um indicador, remove o programa a partir do gráfico
      if(total!=1)
        {
         ::Print(__FUNCTION__," > Esta sub-janela já contém um indicador.");
         ::ChartIndicatorDelete(m_chart_id,m_subwin,indicator_name);
         return;
        }
     }
  }

Este método deve ser chamado no construtor da classe CWndEvents:

//+------------------------------------------------------------------+
//| Construtor                                                       |
//+------------------------------------------------------------------+
CWndEvents::CWndEvents(void)
  {
//--- Identificando o número da sub-janela
   DetermineSubwindow();
  }

Agora, a biblioteca estará pronta para os indicadores, que não estão na janela principal do gráfico. Nós vamos testar como isso funciona e corrigir os erros e falhas, caso sejam descobertos.

A biblioteca terá três modos principais para as interfaces gráficas, que serão localizadas na sub-janela:

  1. Modo livre. Neste modo, a altura da sub-janela do indicador não é fixa, podendo ser alterada sem limitações por parte do usuário. Quando o formulário dos controlos for minimizado, a altura da sub-janela do indicador não é alterada e se mantém livre para ser modificada.
  2. Modo fixo. A altura da sub-janela do indicador poderá ser fixa para o tamanho da altura definida no formulário de controles. Quando o formulário for minimizado, a altura da sub-janela permanece a mesma neste modo também.
  3. O modo fixo com a possibilidade de minimização da sub-janela. Isto significa que, quando o indicador é carregado para o gráfico, o tamanho da sub-janela é fixado para o tamanho da altura definida no formulário de controle, mas quando a sub-janela é minimizada, leva-se em conta o tamanho do cabeçalho do formulário.

Para uma definição conveniente de qualquer um dos modos descritos acima, o método RollUpSubwindowMode() foi criado anteriormente na classe CWindow. A maneira de usá-lo será mostrada mais adiante.

Vamos criar três indicadores para demonstrar cada modo. Para tornar as coisas mais interessantes, a enumeração (a lista suspensa) irá desempenhar um papel do parâmetro externo para esses indicadores. Esta lista suspensa dá uma escolha de três opções para a criação de um formulário:

  1. no lado esquerdo da janela do gráfico;
  2. no lado direito da janela do gráfico;
  3. ocupando toda a largura do gráfico.

Se a terceira opção é selecionada, a largura do formulário deve mudar quando a largura do gráfico for alterada. Atualmente, a classe CWindow não contém um método que nos permite fazer isso. Vamos criar tal método e chamá-lo de ChangeWindowWidth(). No início do método, a largura atual será comparada com o valor que foi passado para o método. Se o valor transmitido for diferente, então, a largura dos objetos do formulário deve ser alterada e as coordenadas de botão devem ser atualizadas.

Declaração e implementação do método CWindow::ChangeWindowWidth():

class CWindow : public CElement
  {
public:
   //--- Altera a largura da janela
   void              ChangeWindowWidth(const int width);
  };
//+------------------------------------------------------------------+
//| Altera a largura da janela                                       |
//+------------------------------------------------------------------+
void CWindow::ChangeWindowWidth(const int width)
  {
//--- Se a largura não foi alterada, retorna
   if(width==m_bg.XSize())
      return;
//--- Atualiza a largura para o fundo e o cabeçalho
   CElement::XSize(width);
   m_bg.XSize(width);
   m_bg.X_Size(width);
   m_caption_bg.XSize(width);
   m_caption_bg.X_Size(width);
//--- Atualiza as coordenadas e as margens para todos os botões:
//--- Botão de fechamento
   int x=CElement::X2()-CLOSE_BUTTON_OFFSET;
   m_button_close.X(x);
   m_button_close.XGap(x-m_x);
   m_button_close.X_Distance(x);
//--- Botão de Maximização
   x=CElement::X2()-ROLL_BUTTON_OFFSET;
   m_button_unroll.X(x);
   m_button_unroll.XGap(x-m_x);
   m_button_unroll.X_Distance(x);
//--- Botão de Minimização
   m_button_rollup.X(x);
   m_button_rollup.XGap(x-m_x);
   m_button_rollup.X_Distance(x);
//--- Botão de dica (se habilitado)
   if(m_tooltips_button)
     {
      x=CElement::X2()-TOOLTIP_BUTTON_OFFSET;
      m_button_tooltip.X(x);
      m_button_tooltip.XGap(x-m_x);
      m_button_tooltip.X_Distance(x);
     }
  }

Criar três cópias do indicador, que foi criado anteriormente para os testes na janela principal do gráfico. Cada cópia será de um dos modos acima listados. Dado a cada um deles um nome único.

Por exemplo:

  • FreeHeight — para o modo livre.
  • RollUp — para o modo fixo.
  • DownFall — para o modo fixo com a possibilidade de minimização da sub-janela.

Deve-se alterar uma linha no código do arquivo principal de todos estes indicadores. Em vez de colocar o indicador na janela principal, deve-se especificar que o indicador será criado na sub-janela:

//+------------------------------------------------------------------+
//|                                                   FreeHeight.mq5 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2015, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property indicator_separate_window
#property indicator_buffers 0
#property indicator_plots   0

Então, no início de todo Program.mqh inclua o arquivo e adicione a enumeração (enum) e o parâmetro externo (input) para selecionar o modo de definição do formulário:

//--- Enumeração dos modos de ajuste da janela
enum ENUM_WINDOW_MODE
  {
   LEFT  =0,
   RIGHT =1,
   FULL  =2
  };
//--- Parâmetros externos
input ENUM_WINDOW_MODE WindowMode=LEFT;

Se a opção RIGHT ou a FULL for selecionada nas configurações externas, quando o tamanho da janela do gráfico mudar, o programa irá ajustar tanto as coordenadas do formulário (no modo RIGHT) quanto o seu tamanho (no modo FULL) para que a sua borda direita não ultrapasse o limite do gráfico.

Eu acho que não há nenhum ponto em fazer um formulário móvel em um espaço pequeno como a de uma sub-janela do indicador. Na implementação atual do formulário, o ajuste não for realizado quando o tamanho do gráfico foi alterado, caso foi especificado em suas propriedades que ele não é móvel. É por isso que o ajustamento para os formulários não-móveis deve ser realizado de forma individual no manipulador de eventos interno do gráfico da aplicação MQL em desenvolvimento. Adicione o código abaixo no manipulador CProgram::OnEvent() em todos os arquivos de indicadores criados Program.mqh:

//+------------------------------------------------------------------+
//| Manipulador de eventos do gráfico                                |
//+------------------------------------------------------------------+
void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
   if(id==CHARTEVENT_CHART_CHANGE)
     {
      //--- Se o modo do formulário para toda a largura do gráfico foi selecionado
      if(WindowMode==FULL)
         m_window.ChangeWindowWidth(m_chart.WidthInPixels()-2);
      //--- Se o modo para definir o formulário à direita foi selecionado
      else if(WindowMode==RIGHT)
         m_window.X(m_chart.WidthInPixels()-(m_window.XSize()+1));
     }
  }

Como você pode ver, atualmente o conteúdo de todos os arquivos de todos os indicadores que foram criados são absolutamente idênticos. Finalmente, nós temos abordado a resposta para a questão de como usar o método CWindow::RollUpSubwindowMode() para definir o modo da sub-janela do indicador onde está localizado o formulário dos controles.

Por padrão, os valores das variáveis ​​da classe CWindow, que o programa utiliza para identificar o modo da sub-janela do indicador, são inicializados no construtor de classe para utilização do modo quando for possível alterar a altura da sub-janela do indicador. Por conseguinte, este método pode ser ignorado completamente pelo indicador FreeHeight. O código para a criação de um formulário na sub-janela na classe CProgram é mostrado abaixo. Tenha em mente que a largura do formulário e suas coordenadas são definidas de acordo com a opção selecionada nas configurações externas. O lugar onde o método CWindow::RollUpSubwindowMode() é para ser utilizado nos indicadores com diferentes modos foi destacado em amarelo.

//+------------------------------------------------------------------+
//| Cria o formulário para os controles                              |
//+------------------------------------------------------------------+
bool CProgram::CreateWindow(const string caption_text)
  {
//--- Adiciona um ponteiro de janela para o array da janela
   CWndContainer::AddWindow(m_window);
//--- Tamanhos
   int x_size=(WindowMode!=FULL)? 205 : m_chart.WidthInPixels()-2;
   int y_size=243;
//--- Coordenadas
   int x=(WindowMode!=RIGHT)? 1 : m_chart.WidthInPixels()-(x_size+1);
   int y=1;
//--- Propriedades
   m_window.XSize(x_size);
   m_window.YSize(y_size);
// ...
//--- Criação de um formulário
   if(!m_window.CreateWindow(m_chart_id,m_subwin,caption_text,x,y))
      return(false);
//---
   return(true);
  }

O indicador RollUp será utilizado no modo em que a sub-janela do indicador possui uma altura fixa, não sendo alterado quando o formulário for minimizado. É por isso que a linha de código a seguir deve ser adicionada no lugar destacado no código acima:

m_window.RollUpSubwindowMode(false,true);

O valor do primeiro parâmetro significa que a sub-janela não tem que ser minimizada quando o formulário for minimizado. O valor do segundo parâmetro indica que a sub-janela deve ter uma altura fixa igual à altura do formulário. Os parâmetros no método CWindow::RollUpSubwindowMode() no indicador DownFall deve ser especificado como é mostrado abaixo:

m_window.RollUpSubwindowMode(true,true);

O valor do primeiro parâmetro define o modo quando na minimização do formulário a altura da sub-janela for definida igual à altura do cabeçalho do formulário.

Compile os arquivos que você realizou alterações e os arquivos de indicadores. Teste cada um deles no gráfico. Tudo está funcionando corretamente de acordo com os modos de definição. Você vai notar que quando o tamanho da janela do gráfico for alterado, as coordenadas (no modo RIGHT) e a largura do formulário (no modo FULL) não serão ajustados. Isto não é surpreendente! Agora, o fluxo de eventos não está indo para o manipulador de eventos do gráfico OnEvent() na classe CProgram. Quando nós estávamos construindo a estrutura principal da biblioteca, nós mencionamos que o fluxo de eventos do gráfico podem ser enviados a partir da classe CWndEvents no método ChartEvent() para o manipulador local OnEvent(), que está localizado na classe CProgram. Esta última é a classe do aplicativo que estamos a desenvolver.

Vamos colocar o envio do evento logo após a iteração sobre todos os manipuladores de eventos do controle no método CWndEvents::CheckElementsEvents():

//+------------------------------------------------------------------+
//| Verificação dos eventos de controle                              |
//+------------------------------------------------------------------+
void CWndEvents::CheckElementsEvents(void)
  {
   int elements_total=CWndContainer::ElementsTotal(0);
   for(int e=0; e<elements_total; e++)
      m_wnd[0].m_elements[e].OnEvent(m_id,m_lparam,m_dparam,m_sparam);
//--- Enviando o evento para o arquivo do aplicativo
   OnEvent(m_id,m_lparam,m_dparam,m_sparam);
  }

O ajuste das coordenadas do formulário no modo RIGHT estará incorreto mesmo agora. A razão é que o programa não irá atualizar as coordenadas do formulário se ele chamar o manipulador da classe CWindow com o evento CHARTEVENT_CHART_CHANGE, porque no método UpdateWindowXY() as coordenadas são atualizadas somente quando o formulário for móvel. Como você se lembra, foi acordado anteriormente que o formulário será fixado nos indicadores. Tendo terminado a iteração sobre todos os controles, o programa irá chamar o manipulador gráfico de evento na classe da aplicação CProgram, como foi mostrado no código acima e irá atualizar a coordenada X de acordo com a largura atual da janela do gráfico.

Em seguida, o programa deixará o método CheckElementsEvents() na classe CWndEvents e irá chamar o método CWndEvents::ChartEventMouseMove() e deixá-lo longe já que o evento não é um evento de mouse. Atualmente, a atualização da localização do formulário para as suas coordenadas atuais nas propriedades do formulário é realizada apenas no método CWndEvents::ChartEventMouseMove(). Agora, está na hora de adicionar a manipulação do evento CHARTEVENT_CHART_CHANGE para o método CWndEvents::ChartEvent() como no destaque abaixo em amarelo.

//+------------------------------------------------------------------+
//| Manipulador de eventos do programa                               |
//+------------------------------------------------------------------+
void CWndEvents::ChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Se o array estiver vazio, retorna
   if(CWndContainer::WindowsTotal()<1)
      return;
//--- Inicialização dos campos dos parâmetros de eventos
   InitChartEventsParams(id,lparam,dparam,sparam);
//--- Verificação dos eventos de controle da interface
   CheckElementsEvents();
//--- Evento do movimento do mouse
   ChartEventMouseMove();
//--- Evento de alteração das propriedades do gráfico
   ChartEventChartChange();
  }

Adiciona-se chamando a função de descolamento da janela pelas coordenadas atuais e redesenho o gráfico para o corpo do método CWndEvents::ChartEventChartChange():

//+------------------------------------------------------------------+
//| Evento CHARTEVENT CHART CHANGE                                   |
//+------------------------------------------------------------------+
void CWndEvents::ChartEventChartChange(void)
  {
//--- Evento de alteração das propriedades do gráfico
   if(m_id!=CHARTEVENT_CHART_CHANGE)
      return;
//--- Movendo a janela
   MovingWindow();
//--- Redesenha o gráfico
   m_chart.Redraw();
  }

Compile os arquivos da biblioteca e indicadores. Tente testá-los carregando-os ao gráfico. Agora, as coordenadas do formulário no modo RIGHT são atualizadas corretamente.

Há um outro detalhe que não foi considerado antes. Na implementação atual, quando um dos indicadores criados juntamente com o formulário para os controles são carregados para o gráfico, o número da sub-janela é definida pelo programa automaticamente no construtor da classe do método CWndEvents::DetermineSubwindow(). Se um desses indicadores for carregado para o gráfico, que já contém outro indicador também situado em uma sub-janela, tudo vai funcionar sem problemas enquanto este indicador estiver no gráfico. Quando ele for removido, a sub-janela de nosso indicador receberá um novo número na lista de janelas do gráfico. Como a biblioteca exige o número da janela que ela está funcionando (por exemplo, para a identificação de uma coordenada relativa), então, algumas funcionalidades irão funcionar incorretamente. Por essa razão, é necessário rastrear o número de janelas no gráfico. Logo que o seu número mudar, o valor deve ser declarado nos campos das classes onde ele foi exigido

Quando os programas são carregados e removidos do gráfico, o evento CHARTEVENT_CHART_CHANGE é gerado. É por isso que a verificação deve ser colocada no método CWndEvents::ChartEventChartChange(). Vamos criar um novo método dedicado a essa verificação e chamá-lo de CWndEvents::CheckSubwindowNumber(). Sua declaração e implementação são apresentados no código abaixo. A princípio há uma verificação por uma correspondência da janela do programa e o número, que foi armazenado ao carregar o programa para o gráfico. Quando eles não corresponderem, o número atual da janela onde o programa está localizado tem de ser definido e, em seguida, este número deve ser atualizado em todos os controles do formulário.

class CWndEvents : public CWndContainer
  {
private:
   //--- Verificação dos eventos de controle
   void              CheckSubwindowNumber(void);
   //---
  };
//+------------------------------------------------------------------+
//| Verificar e atualizar o número janela do programa                |
//+------------------------------------------------------------------+
void CWndEvents::CheckSubwindowNumber(void)
  {
//--- Se o programa na sub-janela e os números não coincidem
   if(m_subwin!=0 && m_subwin!=::ChartWindowFind())
     {
      //--- Identifique o número da sub-janela
      DetermineSubwindow();
      //--- Armazene em todos os controles
      int windows_total=CWndContainer::WindowsTotal();
      for(int w=0; w<windows_total; w++)
        {
         int elements_total=CWndContainer::ElementsTotal(w);
         for(int e=0; e<elements_total; e++)
            m_wnd[w].m_elements[e].SubwindowNumber(m_subwin);
        }
     }
  }

A chamada deste método tem de ser realizado no método CWndEvents::ChartEventChartChange():

//+------------------------------------------------------------------+
//| Evento CHARTEVENT CHART CHANGE                                   |
//+------------------------------------------------------------------+
void CWndEvents::ChartEventChartChange(void)
  {
//--- Evento de alteração das propriedades do gráfico
   if(m_id!=CHARTEVENT_CHART_CHANGE)
      return;
//--- Verificando e atualizando o número da janela do programa
   CheckSubwindowNumber();
//--- Movendo a janela
   MovingWindow();
//--- Redesenha o gráfico
   m_chart.Redraw();
  }

Agora, tudo estará funcionando, como ela foi projetada. Todos os indicadores para os testes dos modos discutidos acima podem ser baixados, no final do artigo.

Fig. 2. Teste do formulário sobre os indicadores no gráfico de sub-janela.

Fig. 2. Teste do formulário sobre os indicadores no gráfico de sub-janela

Na implementação atual para funcionar corretamente em um gráfico, recomenda-se não usar mais do que uma aplicação para o desenvolvimento que esta biblioteca foi usada. A imagem acima mostra vários indicadores em um gráfico apenas para uma demonstração compacta de todos os modos e economia de espaço no artigo. No momento da ativação/desativação da rolagem do gráfico, haverá um conflito entre as aplicações onde a biblioteca é utilizada. Há várias ideias sobre a eliminação de tal conflito entre as aplicações. Se os resultados são satisfatórios e que a solução é viável, então um dos futuros artigos desta série será dedicado a isso.

 

Usando o Formulário em Scripts

Nós já consideramos utilizar o formulário em um EA e um indicador. O formulário pode ser usado em scripts? A resposta é sim. Nos scripts o trabalho do formulário será muito limitado. Os scripts não possuem manipuladores de eventos. É por isso que (1) o formulário não será móvel, (2) não há nenhum ponto em definir os controles dedicados para algumas ações no formulário e (3) objetos gráficos não vão reagir com os movimentos do mouse.

No entanto, se você precisa criar rapidamente um painel de informações, que funciona durante as execuções do script e mostra ao usuário alguns dados estatísticos, por que não usar o formulário como uma base para isso? Dito isto, todos os seus aplicativos terão um design não importa que tipo de programa MQL eles pertencem, porque você vai usar uma biblioteca.

Crie uma pasta para colocar o arquivo principal do script e o arquivo Program.mqh. A classe CProgram neste arquivo será derivado na classe CWndEvents como foi considerado acima para o EA e indicadores. Em geral, tudo é o mesmo. A única exceção é que aqui haverá um manipulador OnEvent() e o evento para ele será gerado artificialmente no arquivo principal do script em um loop perpétuo. Este método terá apenas um parâmetro, que é o número de milissegundos para uma pausa antes de sair do método.

//+------------------------------------------------------------------+
//| Classe para a criação de um aplicativo                           |
//+------------------------------------------------------------------+
class CProgram : public CWndEvents
  {
protected:
   //--- Janela
   CWindow           m_window;
   //---
public:
                     CProgram(void);
                    ~CProgram(void);
   //--- Manipulador de evento
   virtual void      OnEvent(const int milliseconds);
   //--- Cria o painel de informações
   bool              CreateInfoPanel(void);
   //---
protected:
   //--- Cria um formulário
   bool              CreateWindow(const string text);
  };

Para imitar algum processo, nós vamos mudar o texto do cabeçalho do formulário no método CProgram::OnEvent() com os intervalos iguais ao número de milissegundos passados. Vamos criar um indicador de processo de elipse. O código a seguir mostra como isso é implementado:

//+------------------------------------------------------------------+
//| Eventos                                                          |
//+------------------------------------------------------------------+
void CProgram::OnEvent(const int milliseconds)
  {
   static int count =0;  // Counter
   string     str   =""; // Line for the header
//--- Formação do cabeçalho com o processo de progresso
   switch(count)
     {
      case 0 : str="SCRIPT PANEL";     break;
      case 1 : str="SCRIPT PANEL .";   break;
      case 2 : str="SCRIPT PANEL ..";  break;
      case 3 : str="SCRIPT PANEL ..."; break;
     }
//--- Atualize a linha de cabeçalho
   m_window.CaptionText(str);
//--- Redesenho do gráfico
   m_chart.Redraw();
//--- Aumenta o contador
   count++;
//--- Se for maior que três, zera,
   if(count>3)
      count=0;
//--- Pausa
   ::Sleep(milliseconds);
  }

Agora, nós só precisamos criar um formulário e organizar uma chamada constante do método CProgram::OnEvent() no arquivo principal do script na função OnStart(). O código abaixo demonstra que este método será chamado a cada 250 milissegundos. Isto estará trabalhando enquanto a função IsStopped() devolver o valor false. Para parar este ciclo perpétuo, o script tem de ser removido do gráfico manualmente. Quando o programa é removido pelo usuário, a função IsStopped() irá retornar o valor true, o loop irá parar e o script vai parar o seu trabalho.

//+------------------------------------------------------------------+
//|                                                    InfoPanel.mq5 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.0"
//--- Incluindo a classe do painel de negociação
#include "Program.mqh"
CProgram program;
//+------------------------------------------------------------------+
//| Script da função de início do programa                           |
//+------------------------------------------------------------------+
void OnStart(void)
  {
//--- Configura o painel de negociação
   if(!program.CreateInfoPanel())
     {
      ::Print(__FUNCTION__," > Falha na criação de uma interface gráfica!");
      return;
     }
//--- O script estará trabalhando até ser removido
   while(!::IsStopped())
     {
      //--- Gera um evento a cada 250 milissegundos
      program.OnEvent(250);
     }
  }
//+------------------------------------------------------------------+

Compile os arquivos e carregue o script para o gráfico:

Fig. 3. Teste do formulário no script.

Fig. 3. Teste do formulário no script.

Você pode ver na imagem acima que a biblioteca identifica o tipo de programa e cria um ícone padrão como um rótulo do formulário. Este script pode ser baixado no final deste artigo. Isto será enriquecido com os elementos que pertencem ao grupo de informação para outros exemplos posteriores. Este script, bem como o EA e os indicadores considerados neste artigo, podem ser usados como um modelo para o desenvolvimento de seus programas.

 

Usando a Biblioteca no MetaTrader 4

A biblioteca para a criação de interfaces gráficas pode ser utilizada na plataforma de negociação MetaTrader 4. Copie todos os arquivos da biblioteca e do programa que foram criados neste artigo nas pastas do terminal MetaTrader 5 para os arquivos do terminal MetaTrader 4. A biblioteca está quase pronta para usar.

A única coisa a ser adicionado para alguns arquivos da biblioteca para evitar erros de compilação é um parâmetro específico adicional que será uma instrução para o compilador usar um modo estrito especial para a verificação de erros. Para isso adicione a seguinte linha de código no início dos arquivos de cabeçalho da biblioteca:

#property strict

Faça isso para os arquivos WndContainer.mqh, Element.mqh e nos principais arquivos dos programas. Isso irá evitar erros ao compilar o código nestes arquivos.

Se tudo estiver correto e todos os arquivos são compilados, teste todos os programas a partir deste artigo no terminal MetaTrader 4. Tudo deve funcionar o mesmo que no terminal MetaTrader 5.

Fig. 4. Teste da biblioteca no terminal MetaTrader 4.

Fig. 4. Teste da biblioteca no terminal MetaTrader 4.

 

Conclusão

Nós podemos avaliar onde nós estamos no desenvolvimento da biblioteca para a criação de interfaces gráficas. Nesta fase, a estrutura da biblioteca pode ser apresentada como se mostra abaixo. Se você leu o artigo completamente, esse esquema deve ser lógico e fácil de entender.

Fig. 5. Estrutura da biblioteca, no atual estágio de desenvolvimento

Fig. 5. Estrutura da biblioteca, no atual estágio de desenvolvimento

Vou lembrá-lo brevemente o que os elementos desta estrutura querem dizer:

  • As setas azuis significam as interconexões entre classes como: base -> derivada 1 -> derivada 2 -> derivada N.
  • As setas amarelas significam incluir um arquivo em outro arquivo. No entanto, se um arquivo de inclusão contém uma classe, então esta não é uma base para a classe em que ela está incluída. Ela pode ser usada como um objeto dentro de uma classe ou classes de uma cadeia (base -> derivada).
  • Retângulos marrom-claro com uma fonte semi-negrito denotam as classes da biblioteca padrão do MQL e o resto pertence à biblioteca personalizada.
  • Retângulos brancos com um quadro cinza são ou classes em um arquivo separado ou adições que pertencem a um determinado grupo, como macros (define) ou enumerações (enum).
  • Se um retângulo cinza com um quadro azul espesso tem um cabeçalho com um nome de arquivo, isso significa que todas as classes, representadas por dentro com retângulos brancos com um quadro azul, estão contidos no arquivo especificado no cabeçalho.
  • Se um retângulo cinza com um quadro azul espesso não tem um cabeçalho, então, todas as classes que estão dentro dele estão localizados em diferentes arquivos, mas tem uma classe base. Se um retângulo cinza com um quadro azul espesso é ligado a uma classe com uma seta azul, isto significa que esta classe é uma base de para todas as classes que estão dentro de uma classe cinza.
  • Um retângulo marrom-claro com o texto CChartObject... no diagrama ligado a um retângulo cinza com um quadro azul espesso significa que uma das classes da biblioteca padrão, que é um objeto primitivo, é uma classe base para uma dessas classes dentro do retângulo cinza.

A primeira parte desta série analisou em detalhes o processo de preparação da estrutura principal da biblioteca para a criação de interfaces gráficas e a criação do elemento de interface principal - formulário ou janela. Outros controles serão adicionados a este formulário. A sua criação será descrita nos seguintes artigos desta série. A continuação desta série será útil, já que iremos discutir em detalhe a criação de seu próprio controle personalizado e a maneira de adicioná-lo à biblioteca para que se torne uma parte bem integrada e não entre em conflito com outros controles.

Os arquivos abaixo com os arquivos da biblioteca no atual estágio de desenvolvimento, imagens e arquivos dos programas considerados no artigo (o EA, indicadores e o script) podem ser baixados para testes nos terminais MetaTrader 4 e MetaTrader 5. 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/2129

Arquivos anexados |
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.
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)
Neste artigo, nós vamos continuar desenvolvendo esta classe, adicionando os métodos que 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.
Lógica difusa na negociação via MQL4 Lógica difusa na negociação via MQL4
Este artigo apresenta exemplos que tratam de como os recursos MQL4 aplicam a teoria de conjuntos difusos na negociação. Além disso, descreve o desenvolvimento de indicador e Expert Advisor, usando a biblioteca FuzzyNet para MQL4.
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.