English Русский 中文 Español Deutsch 日本語
Interfaces Gráficas IX: Os Controles Barra de Progresso e Gráfico de Linha (Capítulo 2)

Interfaces Gráficas IX: Os Controles Barra de Progresso e Gráfico de Linha (Capítulo 2)

MetaTrader 5Exemplos | 8 novembro 2016, 08:42
1 631 0
Anatoli Kazharski
Anatoli Kazharski

Conteúdo

 

Introdução

Por favor, leia o primeiro artigo chamado Interfaces Gráficas I: Preparação da Estrutura da Biblioteca (Capítulo 1) para obter uma melhor compreensão da finalidade desta biblioteca. A lista completa dos links para os artigos se encontra no final de cada capítulo da série. Lá, você também poderá baixar a última versão completa da biblioteca. Os arquivos devem estar localizados nas mesmas pastas que o arquivo baixado.

No artigo anterior, nós estudamos dois elementos interligados do controle: seletor de cores e o botão do seletor de cores. O segundo capítulo será dedicado aos controles da interface barra de progresso e gráfico de linha. Como sempre, teremos exemplos detalhados para revelar como esses controles podem ser utilizados nas aplicações MQL personalizadas.

 

Controle barra de progresso

Durante a execução de um processo longo, um controle de interface precisa mostrar aos usuários em qual fase de execução ele está atualmente, e também uma estimativa de tempo de sua conclusão. Isto geralmente é obtido com o controle barra de progresso da interface gráfica. A implementação simples deste controle implica em dois retângulos: um deles exibe o comprimento total do processo apresentado, o segundo - uma fração deste processo completo. A porcentagem pode ser frequentemente observada em diferentes implementações da barra de progresso, acrescentando uma informação adicional para este controle.

Vamos listar todos os componentes utilizados para a criação de uma barra de progresso na Biblioteca desenvolvida.

  1. Fundo
  2. Descrição
  3. Barra do indicador
  4. Fundo do indicador
  5. Percentagem



Fig. 1. Componentes do controle da barra de progresso.


Vamos olhar detalhadamente a classe usada para criar esse controle.

 

Desenvolvimento da classe CProgressBar

Nós criamos o arquivo ProgressBar.mqh junto da classe CProgressBar com os métodos que padrão para todos os controles, e incluímos ele ao motor da biblioteca (o arquivo WndContainer.mqh). Abaixo está uma lista de controles que estão disponíveis para configuração.

  • Cor do fundo comum do controle
  • Descrição do texto
  • Cor da descrição do texto
  • Deslocamento da descrição pelos dois eixos (x, y)
  • Cor da área e borda da área total do indicador
  • Tamanho do fundo da área do indicador
  • Largura da borda da área do indicador
  • Cor da barra do indicador
  • Rótulo do deslocamento percentual do processo executado
  • Número de casas decimais para a percentagem
class CProgressBar : public CElement
  {
private:
   //--- Cor da área do controle
   color             m_area_color;
   //--- Texto do processo apresentado
   string            m_label_text;
   //--- Cor do texto
   color             m_label_color;
   //--- Rótulo do texto deslocado pelos dois eixos
   int               m_label_x_offset;
   int               m_label_y_offset;
   //--- Cores da área da barra de progresso e da borda de área
   color             m_bar_area_color;
   color             m_bar_border_color;
   //--- Tamanhos da barra de progresso
   int               m_bar_x_size;
   int               m_bar_y_size;
   //--- Barra de progresso deslocado por dois eixos
   int               m_bar_x_offset;
   int               m_bar_y_offset;
   //--- Largura da borda da barra de progresso
   int               m_bar_border_width;
   //--- Cor do Indicador
   color             m_indicator_color;
   //--- Rótulo do andamento da percentagem
   int               m_percent_x_offset;
   int               m_percent_y_offset;
   //--- Número de casas decimais
   int               m_digits;
   //---
public:
   //--- Número de casas decimais
   void              SetDigits(const int digits)        { m_digits=::fabs(digits);         }
   //--- (1) Cor da área, (2) rótulo de texto e (3) a cor do rótulo
   void              AreaColor(const color clr)         { m_area_color=clr;                }
   void              LabelText(const string text)       { m_label_text=text;               }
   void              LabelColor(const color clr)        { m_label_color=clr;               }
   //--- Deslocamento do rótulo de texto (rótulo de texto)
   void              LabelXOffset(const int x_offset)   { m_label_x_offset=x_offset;       }
   void              LabelYOffset(const int y_offset)   { m_label_y_offset=y_offset;       }
   //--- (1) Cor da área e (2) as bordas da barra de progresso, (3) a cor do indicador
   void              BarAreaColor(const color clr)      { m_bar_area_color=clr;            }
   void              BarBorderColor(const color clr)    { m_bar_border_color=clr;          }
   void              IndicatorColor(const color clr)    { m_indicator_color=clr;           }
   //--- (1) largura da borda, (2) tamanhos da área do indicador
   void              BarBorderWidth(const int width)    { m_bar_border_width=width;        }
   void              BarXSize(const int x_size)         { m_bar_x_size=x_size;             }
   void              BarYSize(const int y_size)         { m_bar_y_size=y_size;             }
   //--- (1) deslocamento da barra de progresso por dois eixos, (2) deslocamento do rótulo percentual
   void              BarXOffset(const int x_offset)     { m_bar_x_offset=x_offset;         }
   void              BarYOffset(const int y_offset)     { m_bar_y_offset=y_offset;         }
   //--- Deslocamento do rótulo de texto (percentual do processo)
   void              PercentXOffset(const int x_offset) { m_percent_x_offset=x_offset;     }
   void              PercentYOffset(const int y_offset) { m_percent_y_offset=y_offset;     }
  };

Para criar o controle da barra de progresso, nós vamos usar cinco métodos privados e um público:

class CProgressBar : public CElement
  {
private:
   //--- Objetos para criar o controle
   CRectLabel        m_area;
   CLabel            m_label;
   CRectLabel        m_bar_bg;
   CRectLabel        m_indicator;
   CLabel            m_percent;
   //---
public:
   //--- Métodos para criar o controle
   bool              CreateProgressBar(const long chart_id,const int subwin,const int x,const int y);
   //---
private:
   bool              CreateArea(void);
   bool              CreateLabel(void);
   bool              CreateBarArea(void);
   bool              CreateIndicator(void);
   bool              CreatePercent(void);
  };

Para que a barra de progresso opere como o esperado, nós precisamos indicar o número total de passos (iterações) do processo que o indicador está ligado, e ao índice atual de iteração. Nós vamos precisar de dois métodos auxiliares, o CProgressBar::StepsTotal() e CProgressBar::CurrentIndex(). Eles são necessários para o uso de modo privado. Apenas um argumento é enviado para ambos os métodos, e o seu valor pode ser corrigido para evitar a saída de uma faixa válida. 

class CProgressBar : public CElement
  {
private:
   //--- Número total de passos
   double            m_steps_total;
   //--- Posição do indicador atual
   double            m_current_index;
   //---
private:
   //--- Definição de novos valores para o indicador
   void              CurrentIndex(const int index);
   void              StepsTotal(const int total);
  };
//+------------------------------------------------------------------+
//| Número total de passos da barra de progresso                     |
//+------------------------------------------------------------------+
void CProgressBar::StepsTotal(const int total)
  {
//--- Corrige se está abaixo de 0
   m_steps_total=(total<1)? 1 : total;
//--- Índice correto se sair fora da faixa
   if(m_current_index>m_steps_total)
      m_current_index=m_steps_total;
  }
//+------------------------------------------------------------------+
//| Estado atual do indicador                                        |
//+------------------------------------------------------------------+
void CProgressBar::CurrentIndex(const int index)
  {
//--- Corrige se está abaixo de 0
   if(index<0)
      m_current_index=1;
//--- Índice correto se sair fora da faixa
   else
      m_current_index=(index>m_steps_total)? m_steps_total : index;
  }

Os métodos CProgressBar::StepsTotal() e CProgressBar::CurrentIndex() são chamados no método principal para interagir com o controle a partir do lado de fora — CProgressBar::Update(). Os parâmetros para a barra de progresso responsáveis pela execução dos cálculos necessários e e seu redesenho, são enviadas para este método. O primeiro argumento aqui é o índice de iteração do processo (index), e o segundo — o número total de iterações (total). Após todos esses valores serem verificados, é calculada uma nova largura para a barra do indicador. Este valor pode ser alterado posteriormente, se necessário. Além disso, (1) uma nova largura é definida para a barra do indicador, (2) o valor percentual é calculado e a sua cadeia é formada, e no final do método (3) é definido um novo valor para o rótulo de texto de percentagem. 

class CProgressBar : public CElement
  {
public:
   //--- Atualiza o indicador pelos valores especificados
   void              Update(const int index,const int total);
  };
//+------------------------------------------------------------------+
//| Atualiza a barra de progresso                                    |
//+------------------------------------------------------------------+
void CProgressBar::Update(const int index,const int total)
  {
//--- Define o novo índice
   CurrentIndex(index);
//--- Define o nova intervalo
   StepsTotal(total);
//--- Calcula a largura do indicador
   double new_width=(m_current_index/m_steps_total)*m_bar_bg.XSize();
//--- Corrige se está abaixo de 1
   if((int)new_width<1)
      new_width=1;
   else
     {
      //--- Corrige levando em consideração a largura da borda
      int x_size=m_bar_bg.XSize()-(m_bar_border_width*2);
      //--- Corrige, se a saída está para além da borda
      if((int)new_width>=x_size)
         new_width=x_size;
     }
//--- Define a nova largura para o indicador
   m_indicator.X_Size((int)new_width);
//--- Calcula o percentual e formação da string
   double percent =m_current_index/m_steps_total*100;
   string desc    =::DoubleToString((percent>100)? 100 : percent,m_digits)+"%";
//--- Define o novo valor
   m_percent.Description(desc);
  }

Todos os métodos para a criação e gerenciamento da barra de progresso está pronto. Agora, nós vamos testá-lo e ver o seu comportamento como uma interface gráfica da aplicação MQL. 


Teste da barra de progresso

O Expert Advisor do artigo anterior pode ser escolhido para análise e utilização como modelo. Nós vamos apagar tudo, menos o menu principal e a barra de estado. Oito barras de progresso serão adicionadas à interface gráfica deste Expert Advisor. Para tornar esta tarefa mais interessante, nós usamos um controle deslizante para gerir o número de iterações. 

No corpo da classe personalizada CProgram, nós declaramos as instâncias dos tipos de controles e métodos necessários para a sua criação com os deslocamentos do ponto extremo da formulário:

class CProgram : public CWndEvents
  {
private:
   //--- Controles deslizantes
   CSlider           m_slider1;
   //--- As barras de progresso
   CProgressBar      m_progress_bar1;
   CProgressBar      m_progress_bar2;
   CProgressBar      m_progress_bar3;
   CProgressBar      m_progress_bar4;
   CProgressBar      m_progress_bar5;
   CProgressBar      m_progress_bar6;
   CProgressBar      m_progress_bar7;
   CProgressBar      m_progress_bar8;
   //---
private:
   //--- Controles deslizantes
#define SLIDER1_GAP_X         (7)
#define SLIDER1_GAP_Y         (50)
   bool              CreateSlider1(const string text);
//---
#define PROGRESSBAR1_GAP_X    (7)
#define PROGRESSBAR1_GAP_Y    (100)
   bool              CreateProgressBar1(void);
//---
#define PROGRESSBAR2_GAP_X    (7)
#define PROGRESSBAR2_GAP_Y    (125)
   bool              CreateProgressBar2(void);
//---
#define PROGRESSBAR3_GAP_X    (7)
#define PROGRESSBAR3_GAP_Y    (150)
   bool              CreateProgressBar3(void);
//---
#define PROGRESSBAR4_GAP_X    (7)
#define PROGRESSBAR4_GAP_Y    (175)
   bool              CreateProgressBar4(void);
//---
#define PROGRESSBAR5_GAP_X    (7)
#define PROGRESSBAR5_GAP_Y    (200)
   bool              CreateProgressBar5(void);
//---
#define PROGRESSBAR6_GAP_X    (7)
#define PROGRESSBAR6_GAP_Y    (225)
   bool              CreateProgressBar6(void);
//---
#define PROGRESSBAR7_GAP_X    (7)
#define PROGRESSBAR7_GAP_Y    (250)
   bool              CreateProgressBar7(void);
//---
#define PROGRESSBAR8_GAP_X    (7)
#define PROGRESSBAR8_GAP_Y    (275)
   bool              CreateProgressBar8(void);
  };

Já foi considerado nos artigos anteriores a criação de todos os controles, portanto, nós podemos fornecer um código do método para apenas uma barra de progresso como exemplo (veja o código abaixo). Como nós podemos ver, ela não tem nada que possa causar qualquer problema de compreensão. Tudo é bastante transparente e simples. 

//+------------------------------------------------------------------+
//| A barra de progresso 1 é criada                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateProgressBar1(void)
  {
//--- Armazena o ponteiro ao formulário
   m_progress_bar1.WindowPointer(m_window1);
//--- Coordenadas
   int x=m_window1.X()+PROGRESSBAR1_GAP_X;
   int y=m_window1.Y()+PROGRESSBAR1_GAP_Y;
//--- Define as propriedades antes da criação
   m_progress_bar1.XSize(220);
   m_progress_bar1.YSize(15);
   m_progress_bar1.BarXSize(123);
   m_progress_bar1.BarYSize(11);
   m_progress_bar1.BarXOffset(65);
   m_progress_bar1.BarYOffset(2);
   m_progress_bar1.LabelText("Progress 01:");
//--- Cria o controle
   if(!m_progress_bar1.CreateProgressBar(m_chart_id,m_subwin,x,y))
      return(false);
//--- Adiciona o ponteiro para controlar na base
   CWndContainer::AddToElementsArray(0,m_progress_bar1);
   return(true);
  }

A Chamada dos métodos devem ser realizadas no método principal da criação da interface gráfica. A código abaixo mostra uma versão resumida deste método, apenas com o que precisa ser adicionado a ele: 

//+------------------------------------------------------------------+
//| Cria um painel Expert                                            |
//+------------------------------------------------------------------+
bool CProgram::CreateExpertPanel(void)
  {
//--- Cria um formulário para os elementos dos controles
//--- Criação dos controles:
//    Menu principal
//--- Menu de contexto

//--- Controles deslizantes
   if(!CreateSlider1("Iterations total:"))
      return(false);
//--- As barras de progresso
   if(!CreateProgressBar1())
      return(false);
   if(!CreateProgressBar2())
      return(false);
   if(!CreateProgressBar3())
      return(false);
   if(!CreateProgressBar4())
      return(false);
   if(!CreateProgressBar5())
      return(false);
   if(!CreateProgressBar6())
      return(false);
   if(!CreateProgressBar7())
      return(false);
   if(!CreateProgressBar8())
      return(false);
      
//--- Redesenha o gráfico
   m_chart.Redraw();
   return(true);
  }

Agora, vamos analisar o seu funcionamento. Nós imitamos o processo para todas as barras de progresso do timer da classe personalizada CProgram::OnTimerEvent(). O número de iterações pode ser controlado manualmente com uma barra deslizante, e em todos os eventos do timer, há uma opção para obter o valor atual em sua caixa de edição. Para cada barra de progresso há um contador estático cujo valor nós iremos incrementar com diferentes valores. Desta forma, nós vamos imitar o progresso da barra de progresso em vários processos de forma assíncrona. 

//+------------------------------------------------------------------+
//| Timer                                                            |
//+------------------------------------------------------------------+
void CProgram::OnTimerEvent(void)
  {
   CWndEvents::OnTimerEvent();
//--- Número de iterações
   int total=(int)m_slider1.GetValue();
//--- Oito barras de progresso
   static int count1=0;
   count1=(count1>=total) ? 0 : count1+=8;
   m_progress_bar1.Update(count1,total);
//---
   static int count2=0;
   count2=(count2>=total) ? 0 : count2+=3;
   m_progress_bar2.Update(count2,total);
//---
   static int count3=0;
   count3=(count3>=total) ? 0 : count3+=12;
   m_progress_bar3.Update(count3,total);
//---
   static int count4=0;
   count4=(count4>=total) ? 0 : count4+=6;
   m_progress_bar4.Update(count4,total);
//---
   static int count5=0;
   count5=(count5>=total) ? 0 : count5+=18;
   m_progress_bar5.Update(count5,total);
//---
   static int count6=0;
   count6=(count6>=total) ? 0 : count6+=10;
   m_progress_bar6.Update(count6,total);
//---
   static int count7=0;
   count7=(count7>=total) ? 0 : count7+=1;
   m_progress_bar7.Update(count7,total);
//---
   static int count8=0;
   count8=(count8>=total) ? 0 : count8+=15;
   m_progress_bar8.Update(count8,total);
   
//--- Timer para barra de estado
   static int count9=0;
   if(count9<TIMER_STEP_MSC*10)
     {
      count9+=TIMER_STEP_MSC;
      return;
     }
   count9=0;
   m_status_bar.ValueToItem(1,::TimeToString(::TimeLocal(),TIME_DATE|TIME_SECONDS));
  }

Nós compilamos o programa e enviamos ele ao gráfico. O resultado é o seguinte:

 Fig. 2. Teste do controle barra de progresso.

Fig. 2. Teste do controle barra de progresso.

Nada mal! Abaixo, na aplicação de teste para o gráfico de linha, nós vamos testar a barra de progresso com um exemplo mais específico.

 

Controle gráfico de linha

O gráfico de linha permite visualizar arrays de dados na área retangular designada com uma escala vertical e horizontal, onde os pontos são interligados posteriormente através de uma linha. É difícil subestimar o benefício de tal ferramenta. Por exemplo, um gráfico onde pode ser exibido o saldo e o capital da conta de um trader. Na minha opinião, o tipo padrão de programas do terminal MetaTrader, como o indicador, não é muito conveniente já que não é permitido a exibição do array de dados inteiro, apenas uma parte é visível na janela. 

Os gráficos do terminal podem ter uma escala definidas somente a partir do 0 (o nível mais forte de compressão) até 5. O nível mais forte de compressão implica que um pixel é selecionado para um controle do array, e se todo o array de dados não encaixar na área selecionada, há uma opção para usar uma barra de rolagem do gráfico. A segunda maneira de encaixar mais dados é mudar o período do gráfico para um maior, e se foi definido as velas ou as barras do gráfico, então, pelo menos, nós podemos ver o valor do intervalo completo durante o período especificado. 

Seria conveniente não ter restrições para a escala do gráfico, assim, o array com os dados de qualquer tamanho poderiam caber na área com uma largura maior do que um pixel. Ao mesmo tempo, é necessário que os dados sejam sempre posicionados com precisão a partir do início da área até o seu fim. Ou seja, o primeiro ponto dos dados (controle do array) deve ser magnetizado para a borda esquerda da área, e o último ponto - para a direita. Por exemplo, os gráficos em Excel são feitos dessa maneira. Desta forma, você pode adaptar a qualquer tamanho do array no gráfico de linha, sem perda de mínimos e máximos.

Como exemplo, a imagem da tela abaixo mostra um exemplo de um gráfico de linha do Excel, onde foi possível um array de 50000 controles conseguir se encaixar sem quaisquer perdas (mínimos e máximos).

 Fig. 3. Gráfico de linha no Excel. Tamanho do array de dados - 50000 controles.

Fig. 3. Gráfico de linha no Excel. Tamanho do array de dados - 50000 controles.


Os desenvolvedores do terminal têm fornecido classes para a criação de vários tipos de gráficos na biblioteca padrão. Elas estão localizadas no seguinte diretório: <pasta de dados>\MQLX\Include\Canvas\Charts. Nós listamos essas classes na lista abaixo:

  • CChartCanvas – classe base para a criação de um dos três tipos de gráficos fornecidos.
  • CLineChart – classe derivada da CChartCanvas para a criação do gráfico de linha.
  • CHistogramChart – classe derivada da CChartCanvas para a criação do histograma.
  • CPieChart – classe derivada da CChartCanvas para a criação do gráfico de pizza.

Como uma solução temporária, para que os gráficos já possam ser usados nas interfaces gráficas das aplicações MQL, nós vamos aplicar o que está sendo oferecido pela Biblioteca Padrão. Alguns métodos nessas classes serão alterados, e alguns deles serão totalmente substituídos. Nós também vamos adicionar as opções adicionais. Eventualmente, no processo de escrita dos artigos futuros, nós vamos tentar criar uma biblioteca adicional para desenhar gráficos de maior qualidade.


Melhorando as classes da biblioteca padrão

Como mencionado acima, os arquivos com as classes necessárias estão localizados no seguinte diretório: <pasta de dados>\MQLX\Include\Canvas\Charts. Vamos criar a pasta charts no diretório da biblioteca desenvolvida (<data folder>\MQLX\Include\EasyAndFastGUI\Canvas) e copiar lá os arquivos ChartCanvas.mqh e LineChart.mqh da biblioteca padrão. 

Em primeiro lugar, nós vamos fazer as alterações para a classe base CChartCanvas. No momento, a sua classe base é a CCanvas. Anteriormente, uma cópia da classe da biblioteca padrão CCanvas foi adaptada para a biblioteca desenvolvida e renomeada para CCustomCanvas. Agora ela precisa para se tornar uma classe base para a CChartCanvas. No código abaixo nós mostramos as linhas que foram alteradas no arquivo ChartCanvas.mqh

//+------------------------------------------------------------------+
//|                                                  ChartCanvas.mqh |
//|                   Copyright 2009-2016, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "..\CustomCanvas.mqh"
...
//+------------------------------------------------------------------+
//| Classe CChartCanvas                                              |
//| Uso: Classe base para o gráfico de linha                         |
//+------------------------------------------------------------------+
class CChartCanvas : public CCustomCanvas
  {
...


É preferível ter um fundo gradiente para os gráficos, por isso nós vamos adicionar esta opção. A fim de implementar isso, nós ligamos o arquivo com a classe que trabalha com a cor (CColors) ao arquivo CustomCanvas.mqh

//+------------------------------------------------------------------+
//|                                                 CustomCanvas.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "..\Colors.mqh"
...

Na classe CChartCanvas, nós podemos declarar a instância dessa classe e usá-la no projeto.

Nesta fase de desenvolvimento, considerando que a biblioteca padrão tem de ser escrita a partir do zero, não há razão em fornecer uma descrição detalhada dos métodos de classe que nós vamos usar. Nós vamos indicar brevemente os métodos onde as mudanças foram feitas, e quais métodos foram adicionados, para que você possa fazer as comparações por conta própria, se surgir tal necessidade.

Vamos prosseguir para a enumeração. Pelo menos duas cores são necessárias para construir uma área de gradiente. Na classe CChartCanvas, já há um método para indicar a cor de fundo — CChartCanvas::ColorBackground(). O método semelhante CChartCanvas::ColorBackground2() foi adicionado para definir a segunda cor. Um método especial para inicializar o array com as cores de gradiente, o mesmo que na classe CElements - o método InitColorArray(). Para desenhar um gradiente, o método CChartCanvas::DrawBackground() precisa ser alterado. Se antes era suficiente preencher o fundo com uma cor através de uma simples chamada do método CCustomCanvas::Erase(), enviando a cor escolhida como argumento, agora, no entanto, (1) é necessário declarar um array e definir o tamanho da altura da tela, (2) inicializá-lo com as cores do gradiente, (3) desenhar cada linha no loop usando as cores deste array. Para zerar o array e excluir os objetos, outro método CChartCanvas::DeleteAll() precisa ser adicionado a classe. 

Todas as outras alterações na classe CChartCanvas têm afetado os métodos onde são calculados e definidos os deslocamentos, fonte, tamanho dos marcadores na descrição da série e o estilo da grade. Você pode estudar o código destas adições e alterações nos arquivos anexados neste artigo, comparando-os com a versão inicial da biblioteca padrão.

Além disso, nós vamos considerar as mudanças que afetaram a classe CLineChart. CChartCanvas é a classe base para isso. O método para a destruição dos objetos — CLineChart::DeleteAll() também precisa ser criada aqui. As principais alterações envolvem os dados do desenho no gráfico com o método CLineChart::DrawData(). Primeiramente, foram adicionados a ele os três métodos auxiliares onde são realizados os cálculos: CLineChart::CheckLimitWhile(), CLineChart::CalculateVariables() e CLineChart::CalculateArray(). E o mais importante, o algoritmo de compressão de dados exibidos no gráfico foi corrigido. Na opção sugerida, a compressão é realizada de modo que, quando a largura do array de pixels apresentados exceder a largura da área selecionada do gráfico, este array é dividido em um número de partes iguais ao número de pixels que se estendem além da borda direita do gráfico. Em seguida, cada parte (além da primeira) sobrepõe-se com a anterior. Desta forma, o deslocamento para além da extremidade direita é reduzida, o controle do array está sempre ancorado ao lado direito, e não há nenhuma perda visual dos dados relativos as mínimas e máximas. 

Agora nós precisamos criar a classe para a criação do controle gráfico de linha que é semelhante as classes de todos os controles restantes da biblioteca que foram consideradas nos artigos anteriores.


Desenvolvimento da classe CLineGraph

Em primeiro lugar, é preciso criar um novo tipo de objeto no arquivo Objects.mqh. Ele será a classe CLineChartObject que é semelhante a todas as classes nesse arquivo. A CLineChart será uma classe base, e seus arquivos precisam ser ligados ao arquivo Objects.mqh:

//+------------------------------------------------------------------+
//|                                                      Objects.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "..\Canvas\Charts\LineChart.mqh"
...

Nós vamos criar o arquivo LineGraph.mqh com a classe CLineGraph e ligá-la ao motor da biblioteca (WndContainer.mqh). A classe CLineGraph classe precisa ter os métodos virtuais padrão, o mesmo que o resto dos controles da Biblioteca:

//+------------------------------------------------------------------+
//| Classe para a criação do gráfico de linha                        |
//+------------------------------------------------------------------+
class CLineGraph : public CElement
  {
private:
   //--- Ponteiro para o formulário ao qual o controle será anexado
   CWindow          *m_wnd;
public:
   //--- Armazena o ponteiro do formulário
   void              WindowPointer(CWindow &object)    { m_wnd=::GetPointer(object);  }
   //---
public:
   //--- Manipulador de eventos do gráfico
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) {}
   //--- Timer
   virtual void      OnEventTimer(void) {}
   //--- Mover o controle
   virtual void      Moving(const int x,const int y);
   //--- (1) Exibe, (2) oculta, (3) reseta, (4) remove
   virtual void      Show(void);
   virtual void      Hide(void);
   virtual void      Reset(void);
   virtual void      Delete(void);
  };


Nós vamos listar as propriedades que estarão disponíveis para definir a aparência externa do gráfico de linha.

  • Cores de fundo do gradiente
  • Cor da borda
  • Cor da grade
  • Cor do texto
  • Número de casas decimais (para os valores na escala vertical)
class CLineGraph : public CElement
  {
private:
   //--- Cor do Gradiente
   color             m_bg_color;
   color             m_bg_color2;
   //--- Cor da borda
   color             m_border_color;
   //--- Cor da grade
   color             m_grid_color;
   //--- Cor do texto
   color             m_text_color;
   //--- Número de casas decimais
   int               m_digits;
   //---
public:
   //--- Número de casas decimais
   void              SetDigits(const int digits)       { m_digits=::fabs(digits);     }
   //--- Duas cores para o gradiente
   void              BackgroundColor(const color clr)  { m_bg_color=clr;              }
   void              BackgroundColor2(const color clr) { m_bg_color2=clr;             }
   //--- Cores da (1) borda, (2) e da grade (3) do texto
   void              BorderColor(const color clr)      { m_border_color=clr;          }
   void              GridColor(const color clr)        { m_grid_color=clr;            }
   void              TextColor(const color clr)        { m_text_color=clr;            }
  };

Para criar o gráfico de linha, nós precisamos de um método privado e um público:

class CLineGraph : public CElement
  {
public:
   //--- Métodos para criar o controle
   bool              CreateLineGraph(const long chart_id,const int subwin,const int x,const int y);
   //---
private:
   bool              CreateGraph(void);
  };

E, finalmente, a fim de trabalhar com o gráfico de linha após ele ter sido criado, nós vamos usar os métodos que permitam:

  • definir o número máximo de séries no gráfico;
  • definir o máximo/mínimo para a escala vertical e a quantidade de linhas da grade;
  • adicionar a série de dados;
  • atualizar a série de dados;
  • eliminar a série de dados. 
class CLineGraph : public CElement
  {
public:
   //--- Série de dados Máxima
   void              MaxData(const int total)          { m_line_chart.MaxData(total); }
   //--- Parâmetros de ajuste da escala vertical
   void              VScaleParams(const double max,const double min,const int num_grid);
   //--- Adicionando a série ao gráfico
   void              SeriesAdd(double &data[],const string descr,const color clr);
   //--- Atualiza a série no gráfico
   void              SeriesUpdate(const uint pos,const double &data[],const string descr,const color clr);
   //--- Remove a série do gráfico
   void              SeriesDelete(const uint pos);
  };

Esta configuração mínima é suficiente para usar o gráfico de linha nas aplicações MQL. Em seguida, nós vamos criar uma aplicação que permita testar como tudo funciona.

 

Escrevendo uma aplicação para testar o gráfico de linha

Para os testes, nós podemos fazer uma cópia do EA que já foi usado neste artigo para testar o controle da barra de progresso. Nós vamos eliminar todos os controles a partir dele, além da barra de estado. Nós determinamos quais controles para o gerenciamento gráfico de linha precisam ser criados na interface gráfica. 

Nós implementamos o modo em que a adição e a redução de dados nos arrays das séries serão realizados automaticamente. Este processo será controlado no timer da classe CProgram do usuário. Vamos assegurar que a velocidade deste processo também é controlada através da indicação na caixa de edição com um atraso em milissegundos (parâmetro Delay). Serão introduzidos dois controles adicionais para este modo, e em sua caixa de edição você poderá indicar um intervalo, ou seja, o número mínimo (parâmetro Min. limit size) e o máximo (parâmetro Max. limit size) de controles nos arrays. Ao ativar esse modo, os arrays de cada evento do timer serão incrementados por um controle até o tamanho máximo especificado, e, posteriormente, será reduzido até que atinjam o tamanho mínimo especificado, e então ele é repetido desde o começo. O tamanho atual dos arrays (parâmetro Size of series), que também pode ser gerenciado manualmente, será exibido na caixa de edição do controle separado.

O número de séries (série de dados) pode ser configurado selecionando a partir da lista suspensa de 1 até 24 (parâmetro Number of series). Os dados serão calculados com uma fórmula trigonométrica com base nos valores retornados das funções matemáticas de seno e cosseno. Nós vamos adicionar os controles que permitirão gerir os parâmetros que participam nos cálculos. Aqui será um (1) a taxa de incremento (parâmetro Increment ratio) e (2) o deslocamento de cada série em relação à série de dados anterior (o parâmetro Offset series). Ao alterar a taxa de incremento nós podemos obter diferentes séries de visualização. O parâmetro Increment ratio terá uma caixa de verificação usada para executar a troca automática do valor seguinte, logo que o loop aumentar/diminuir o tamanho da série, se este modo estiver ativado. O aumento da taxa aqui também será realizada até atingirmos a restrição máxima do controle. Assim que a restrição ser atingida, o contador voltará a reduzir os valores do parâmetro Increment ratio. Em outras palavras, o contador será revertido após atingir as restrições mínimas ou máximas.

A fim de tornar esta experiência ainda mais interessante, nós vamos implementar o modo que permite fazer uma animação da série "correndo" e também adicionar o parâmetro adicional que controla a sua velocidade - Run speed. Além disso, nós adicionamos a barra de progresso para a interface gráfica, e ele vai mostrar o tempo restante deste processo, se o modo de adição e redução automática dos dados em uma série de arrays estiver ativado.

Então, nós declaramos as instâncias das classes de controles que são necessários para criar a interface gráfica do programa e também os métodos utilizados para a criação e o deslocamento do ponto extremo do formulário:

class CProgram : public CWndEvents
  {
private:
   //--- Controles
   CSpinEdit         m_delay_ms;
   CComboBox         m_series_total;
   CCheckBoxEdit     m_increment_ratio;
   CSpinEdit         m_offset_series;
   CSpinEdit         m_min_limit_size;
   CCheckBoxEdit     m_max_limit_size;
   CCheckBoxEdit     m_run_speed;
   CSpinEdit         m_series_size;
   CLineGraph        m_line_chart;
   CProgressBar      m_progress_bar;
   //---
private:
   //--- Controles para o gerenciamento do gráfico de linha
#define SPINEDIT1_GAP_X       (7)
#define SPINEDIT1_GAP_Y       (25)
   bool              CreateSpinEditDelay(const string text);
#define COMBOBOX1_GAP_X       (7)
#define COMBOBOX1_GAP_Y       (50)
   bool              CreateComboBoxSeriesTotal(const string text);
#define CHECKBOX_EDIT1_GAP_X  (161)
#define CHECKBOX_EDIT1_GAP_Y  (25)
   bool              CreateCheckBoxEditIncrementRatio(const string text);
#define SPINEDIT2_GAP_X       (161)
#define SPINEDIT2_GAP_Y       (50)
   bool              CreateSpinEditOffsetSeries(const string text);
#define SPINEDIT3_GAP_X       (330)
#define SPINEDIT3_GAP_Y       (25)
   bool              CreateSpinEditMinLimitSize(const string text);
#define CHECKBOX_EDIT2_GAP_X  (330)
#define CHECKBOX_EDIT2_GAP_Y  (50)
   bool              CreateCheckBoxEditMaxLimitSize(const string text);
#define CHECKBOX_EDIT3_GAP_X  (501)
#define CHECKBOX_EDIT3_GAP_Y  (25)
   bool              CreateCheckBoxEditRunSpeed(const string text);
#define SPINEDIT4_GAP_X       (501)
#define SPINEDIT4_GAP_Y       (50)
   bool              CreateSpinEditSeriesSize(const string text);
   //--- Gráfico de linha
#define LINECHART1_GAP_X      (5)
#define LINECHART1_GAP_Y      (75)
   bool              CreateLineChart(void);
   //--- Indicador de desempenho
#define PROGRESSBAR1_GAP_X    (5)
#define PROGRESSBAR1_GAP_Y    (364)
   bool              CreateProgressBar(void);
  };

Todos os métodos para criar os controles na classe personalizada já foram consideradas em artigos anteriores. O código do método para a criação do gráfico de linha será mostrado aqui. Mas antes disso, nós precisamos criar o método para trabalhar com os arrays e calcular os dados para eles. Nós declaramos a estrutura Series para exibir o array data[] e o array auxiliar data_temp[] para os cálculos preliminares. Arrays onde as cores e as descrições das séries também são necessárias. 

class CProgram : public CWndEvents
  {
private:
   //--- Estrutura da série no gráfico
   struct Series
     {
      double            data[];      // array do dado exibido
      double            data_temp[]; // array auxiliar para os cálculos
     };
   Series            m_series[];

   //--- (1) Nomes e (2) as cores das séries
   string            m_series_name[];
   color             m_series_color[];
  };

O método CProgram::ResizeDataArrays() será utilizado para definir um novo tamanho dos arrays. O número atual das séries e o seu tamanho é obtido a partir dos controles: 

class CProgram : public CWndEvents
  {
private:
   //--- Define um novo tamanho das série
   void              ResizeDataArrays(void);
  };
//+------------------------------------------------------------------+
//| Define um novo tamanho de arrays                                 |
//+------------------------------------------------------------------+
void CProgram::ResizeDataArrays(void)
  {
   int total          =(int)m_series_total.ButtonText();
   int size_of_series =(int)m_series_size.GetValue();
//---
   for(int s=0; s<total; s++)
     {
      //--- Define um novo tamanho de arrays
      ::ArrayResize(m_series[s].data,size_of_series);
      ::ArrayResize(m_series[s].data_temp,size_of_series);
     }
  }

Depois de definir o tamanho, o array utilizado para os cálculos devem ser inicializados com os novos dados. Nós vamos aplicar o método CProgram::InitArrays() para este finalidade. Para os cálculos preliminares, nós vamos utilizar os parâmetros desde Offset series e Increment ratio. Para o modo da série "correndo", será necessário o contador m_run_speed_counter. Ao ligar o método CProgram::ShiftLineChartSeries() no timer, ele será incrementado pelo valor desde a caixa de edição do elemento Run Speed, se a caixa de verificação deste controle estiver habilitada. 

class CProgram : public CWndEvents
  {
private:
   //--- Contador de velocidade da série "correndo"
   double            m_run_speed_counter;

   //--- Inicialização dos arrays auxiliares para os cálculos
   void              InitArrays(void);

   //--- Deslocamento das série do gráfico de linha (gráfico "correndo")
   void              ShiftLineChartSeries(void);
  };
//+------------------------------------------------------------------+
//| Construtor                                                       |
//+------------------------------------------------------------------+
CProgram::CProgram(void) : m_run_speed_counter(0.0)
  {
//--- ...
  }
//+------------------------------------------------------------------+
//| Deslocando a série do gráfico de linha                           |
//+------------------------------------------------------------------+
void CProgram::ShiftLineChartSeries(void)
  {
   if(m_run_speed.CheckButtonState())
      m_run_speed_counter+=m_run_speed.GetValue();
  }
//+------------------------------------------------------------------+
//| Inicialização dos arrays auxiliares para os cálculos             |
//+------------------------------------------------------------------+
void CProgram::InitArrays(void)
  {
   int total=(int)m_series_total.ButtonText();
//---
   for(int s=0; s<total; s++)
     {
      int size_of_series=::ArraySize(m_series[s].data_temp);
      //---
      for(int i=0; i<size_of_series; i++)
        {
         if(i==0)
           {
            if(s>0)
               m_series[s].data_temp[i]=m_series[s-1].data_temp[i]+m_offset_series.GetValue();
            else
               m_series[s].data_temp[i]=m_run_speed_counter;
           }
         else
            m_series[s].data_temp[i]=m_series[s].data_temp[i-1]+(int)m_increment_ratio.GetValue();
        }
     }
  }

Por exemplo, nós podemos fazer o cálculo das séries baseadas em três fórmulas que podem ser selecionadas nos parâmetros externos do Expert Advisor. Para esta finalidade, nós declaramos a enumeração ENUM_FORMULA. As configuração dos parâmetros das cores das séries foi movida para os parâmetros externos (veja o código abaixo).   

//--- Enumeração das funções
enum ENUM_FORMULA
  {
   FORMULA_1=0, // Fórmula 1
   FORMULA_2=1, // Fórmula 2
   FORMULA_3=2  // Fórmula 3
  };
//--- Parâmetros externos
input ENUM_FORMULA Formula        =FORMULA_1;       // Fórmula
input color        ColorSeries_01 =clrRed;          // Cor das séries 01
input color        ColorSeries_02 =clrDodgerBlue;   // Cor das séries 02
input color        ColorSeries_03 =clrWhite;        // Cor das séries 03
input color        ColorSeries_04 =clrYellow;       // Cor das séries 04
input color        ColorSeries_05 =clrMediumPurple; // Cor das séries 05
input color        ColorSeries_06 =clrMagenta;      // Cor das séries 06

A definição do tamanho inicial dos arrays das séries, e também a inicialização dos arrays de descrições e cor das séries são realizadas no construtor da classe CProgram

//+------------------------------------------------------------------+
//| Construtor                                                       |
//+------------------------------------------------------------------+
CProgram::CProgram(void) : m_run_speed_counter(0.0)
  {
//--- Define o tamanho dos arrays das séries
   int number_of_series=24;
   ::ArrayResize(m_series,number_of_series);
   ::ArrayResize(m_series_name,number_of_series);
   ::ArrayResize(m_series_color,number_of_series);
//--- Inicialização do array de nomes das séries
   for(int i=0; i<number_of_series; i++)
      m_series_name[i]="Series "+string(i+1);
//--- Inicialização dos arrays da cor da série
   m_series_color[0] =m_series_color[6]  =m_series_color[12] =m_series_color[18] =ColorSeries_01;
   m_series_color[1] =m_series_color[7]  =m_series_color[13] =m_series_color[19] =ColorSeries_02;
   m_series_color[2] =m_series_color[8]  =m_series_color[14] =m_series_color[20] =ColorSeries_03;
   m_series_color[3] =m_series_color[9]  =m_series_color[15] =m_series_color[21] =ColorSeries_04;
   m_series_color[4] =m_series_color[10] =m_series_color[16] =m_series_color[22] =ColorSeries_05;
   m_series_color[5] =m_series_color[11] =m_series_color[17] =m_series_color[23] =ColorSeries_06;
  }

Para calcular a série baseada na fórmula indicada nos parâmetros externos, nós aplicamos o método CProgram::CalculateSeries(): 

class CProgram : public CWndEvents
  {
private:
   //--- Calcula as séries
   void              CalculateSeries(void);
  };
//+------------------------------------------------------------------+
//| Calcula as séries                                                |
//+------------------------------------------------------------------+
void CProgram::CalculateSeries(void)
  {
   int total=(int)m_series_total.ButtonText();
//---
   for(int s=0; s<total; s++)
     {
      int size_of_series=::ArraySize(m_series[s].data_temp);
      //---
      for(int i=0; i<size_of_series; i++)
        {
         m_series[s].data_temp[i]+=m_offset_series.GetValue();
         //---
         switch(Formula)
           {
            case FORMULA_1 :
               m_series[s].data[i]=::sin(m_series[s].data_temp[i])-::cos(m_series[s].data_temp[i]);
               break;
            case FORMULA_2 :
               m_series[s].data[i]=::sin(m_series[s].data_temp[i]-::cos(m_series[s].data_temp[i]));
               break;
            case FORMULA_3 :
               m_series[s].data[i]=::sin(m_series[s].data_temp[i]*10)-::cos(m_series[s].data_temp[i]);
               break;
           }

        }
     }
  }

Após todos os dados calculados de série serem movidos para os arrays, nós podemos adicioná-los (1) ao gráfico usando o método CProgram::AddSeries(), ou se eles tiverem sido adicionados anteriormente, (2) atualiza com o método CProgram::UpdateSeries()

class CProgram : public CWndEvents
  {
private:
   //--- Adiciona as séries para o gráfico
   void              AddSeries(void);
   //--- Atualiza a série no gráfico
   void              UpdateSeries(void);
  };
//+------------------------------------------------------------------+
//| Calcula e define as séries no diagrama                           |
//+------------------------------------------------------------------+
void CProgram::AddSeries(void)
  {
   int total=(int)m_series_total.ButtonText();
   for(int s=0; s<total; s++)
      m_line_chart.SeriesAdd(m_series[s].data,m_series_name[s],m_series_color[s]);
  }
//+------------------------------------------------------------------+
//| Calcula e atualiza as séries no diagrama                         |
//+------------------------------------------------------------------+
void CProgram::UpdateSeries(void)
  {
   int total=(int)m_series_total.ButtonText();
   for(int s=0; s<total; s++)
      m_line_chart.SeriesUpdate(s,m_series[s].data,m_series_name[s],m_series_color[s]);
  }


Após a criação do gráfico de linha, (1) o tamanho do array da série é definido e (2) é realizado a inicialização dos arrays auxiliares. Em seguida, as séries são (3), calculadas e (4) adicionadas ao gráfico. 

//+------------------------------------------------------------------+
//| Cria o gráfico de linha                                          |
//+------------------------------------------------------------------+
bool CProgram::CreateLineChart(void)
  {
//--- Armazena o ponteiro da janela
   m_line_chart.WindowPointer(m_window1);
//--- Coordenadas
   int x=m_window1.X()+LINECHART1_GAP_X;
   int y=m_window1.Y()+LINECHART1_GAP_Y;
//--- Define as propriedades antes da criação
   m_line_chart.XSize(630);
   m_line_chart.YSize(280);
   m_line_chart.BorderColor(clrSilver);
   m_line_chart.VScaleParams(2,-2,4);
   m_line_chart.MaxData(int(m_series_total.ButtonText()));
//--- Cria o controle
   if(!m_line_chart.CreateLineGraph(m_chart_id,m_subwin,x,y))
      return(false);
//--- (1) Define o tamanho do arrays e (2) inicializa eles
   ResizeDataArrays();
   InitArrays();
//--- (1) Calcula e (2) adiciona as séries ao gráfico
   CalculateSeries();
   AddSeries();
//--- Adiciona o ponteiro para controlar na base
   CWndContainer::AddToElementsArray(0,m_line_chart);
   return(true);
  }

A mesma sequência de ações precisa frequentemente ser repetida, mas somente com a atualização das séries, quando estiver ligadas com os controles gráfico de linha. Portanto, nós vamos escrever um método auxiliar CProgram::RecalculatingSeries() que simplifica o código, chamando apenas um método em vez de quatro: 

class CProgram : public CWndEvents
  {
private:
   //--- Recalcula a série no gráfico
   void              RecalculatingSeries(void);
  };
//+------------------------------------------------------------------+
//| Recalcula a série no gráfico                                     |
//+------------------------------------------------------------------+
void CProgram::RecalculatingSeries(void)
  {
//--- (1) Define o tamanho do arrays e (2) inicializa eles
   ResizeDataArrays();
   InitArrays();
//--- (1) Calcula e (2) atualiza as séries
   CalculateSeries();
   UpdateSeries();
  }

Na etapa atual de desenvolvimento, você terá o seguinte resultado se baixar a aplicação no gráfico:

Fig. 4. Teste do controle gráfico de linha.

Fig. 4. Teste do controle gráfico de linha.

Se o parâmetro caixa de seleção Max. limit size estiver habilitado, então, é realizado o redimensionamento automático dos arrays da série no método CProgram::AutoResizeLineChartSeries(). Este algoritmo já foi descrito exaustivamente no início desta seção, assim, as os comentários detalhados já são suficientes para estudar o código deste método (veja o código abaixo). 

class CProgram : public CWndEvents
  {
private:
   //--- Redimensionamento automático das séries do gráfico de linha
   void              AutoResizeLineChartSeries(void);
  };
//+------------------------------------------------------------------+
//| Redimensionamento automático das séries do gráfico de linha      |
//+------------------------------------------------------------------+
void CProgram::AutoResizeLineChartSeries(void)
  {
//--- Sai se incremento do array da série com o timer estiver desativado
   if(!m_max_limit_size.CheckButtonState())
      return;
//--- Para indicar a direção da alteração do tamanho dos arrays
   static bool resize_direction=false;
//--- Se atingir o tamanho mínimo do array
   if((int)m_series_size.GetValue()<=m_min_limit_size.GetValue())
     {
      //--- Altera a direção para aumentar o array
      resize_direction=false;
      //--- Se o valor X precisar ser alterado
      if(m_increment_ratio.CheckButtonState())
        {
         //--- Para indicar a direção da taxa de incremento
         static bool increment_ratio_direction=true;
         //--- Se o contador é direcionado rumo ao incremento
         if(increment_ratio_direction)
           {
            //--- Se a restrição máxima for atingida, nós mudamos a direção contraria para a oposta
            if(m_increment_ratio.GetValue()>=m_increment_ratio.MaxValue()-1)
               increment_ratio_direction=false;
           }
         //--- Se o contador for direcionado para a redução
         else
           {
            //--- Se a restrição mínima foi atingida, altera-se o sentido do contador para o lado oposto
            if(m_increment_ratio.GetValue()<=m_increment_ratio.MinValue()+1)
               increment_ratio_direction=true;
           }
         //--- Obtém o valor atual do parâmetro "Increment ratio" e altera ele na direção indicada
         int increase_value=(int)m_increment_ratio.GetValue();
         m_increment_ratio.ChangeValue((increment_ratio_direction)? ++increase_value : --increase_value);
        }
     }
//--- Troca a direção rumo a diminuição do array ao atingir a máxima
   if((int)m_series_size.GetValue()>=m_max_limit_size.GetValue())
      resize_direction=true;

//--- Exibe o processo se a barra de progresso está habilitada
   if(m_progress_bar.IsVisible())
     {
      if(!resize_direction)
         m_progress_bar.Update((int)m_series_size.GetValue(),(int)m_max_limit_size.GetValue());
      else
         m_progress_bar.Update(int(m_max_limit_size.GetValue()-m_series_size.GetValue()),(int)m_max_limit_size.GetValue());
     }
//--- Altera o tamanho do array de acordo com a direção
   int size_of_series=(int)m_series_size.GetValue();
   m_series_size.ChangeValue((!resize_direction)? ++size_of_series : --size_of_series);
//--- Define um novo tamanho de arrays
   ResizeDataArrays();
  }

A animação dos gráficos, como mencionado anteriormente, é realizada com um timer. Todas as medidas necessárias estão localizadas no método CProgram::UpdateLineChartByTimer(). O programa sai de imediato do método, se (1) o formulário é minimizado, ou (2) se os modos onde é realizado as atualizações de série pelo timer estiverem desativados. Além disso, temos mais um obstáculo aqui, que é causado pela demora da caixa de edição. Se todas as verificações forem concluídas, então os cálculos necessários para os modos habilitados são realizadas posteriormente, e as séries no gráfico de linha são atualizadas.  

class CProgram : public CWndEvents
  {
private:
   //--- Atualizando o gráfico de linha pelo timer
   void              UpdateLineChartByTimer(void);
  };
//+------------------------------------------------------------------+
//| Atualiza o timer                                                 |
//+------------------------------------------------------------------+
void CProgram::UpdateLineChartByTimer(void)
  {
//--- Sai se o formulário for minimizado ou está em processo de deslocamento
   if(m_window1.IsMinimized())
      return;
//--- Sai se a animação está desativada
   if(!m_max_limit_size.CheckButtonState() && !m_run_speed.CheckButtonState())
      return;
//--- Delay
   static int count=0;
   if(count<m_delay_ms.GetValue())
     {
      count+=TIMER_STEP_MSC;
      return;
     }
   count=0;
//--- Se a opção "série correndo" está habilitada, então, nós vamos deslocar o primeiro valor da série
   ShiftLineChartSeries();
//--- Se o gerenciamento do tamanho dos arrays da série pelo timer está habilitado
   AutoResizeLineChartSeries();
//--- Inicializa os arrays
   InitArrays();
//--- (1) Calcula e (2) atualiza as séries
   CalculateSeries();
   UpdateSeries();
  }

Nós vamos dar uma olhada rápida no manipulador de eventos CProgram::OnEvent() da aplicação desenvolvida. Para realizar as alterações visuais em tempo real sobre o gráfico de linha, utilizaremos os controles mencionados abaixo.

  • O parâmetro Number of series (combobox). Cada vez que se seleciona um novo valor neste controle, o número das séries no gráfico de linha será alterado. O bloco de código para lidar com este evento é exibido abaixo:
...
//--- Evento de selecionar o elemento na caixa de combinação
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_COMBOBOX_ITEM)
     {
      //--- Obtém o novo total de séries
      m_line_chart.MaxData((int)m_series_total.ButtonText());
      //--- (1) Define o tamanho do arrays e (2) inicializa eles
      ResizeDataArrays();
      InitArrays();
      //--- (1) Calcula, (2) adiciona ao gráfico e (3) atualiza as séries
      CalculateSeries();
      AddSeries();
      UpdateSeries();
      return;
     }
...

Por exemplo, exibindo as seis séries na caixa de combinação por padrão (valor 6). Mude para 3. O resultado aparece na imagem abaixo:

 Fig. 5. Teste da alteração do total de séries no gráfico de linha.

Fig. 5. Teste da alteração do total de séries no gráfico de linha.

  • Parâmetros Max limit size (caixa de seleção com a caixa de edição) e Size of series (caixa de edição). O evento com o identificador ON_CLICK_LABEL é gerado quando ao clicar sobre esses controles. Se houve o clique no controle Size of series, então o valor na caixa de edição será resetado para o mínimo. Clicando no controle Max limit size irá alterar o estado de sua caixa de seleção para o oposto. O estado da caixa de seleção depende se a barra de progresso que é demonstrada ao ativar o modo de redimensionamento automático da série no gráfico de linha deve ser exibida ou ocultada. 
...
//--- Evento do clique sobre o rótulo de texto do controle
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_LABEL)
     {
      //--- Se esta mensagem é do controle "Size of series"
      if(sparam==m_series_size.LabelText())
        {
         //--- Recalcula a série no gráfico
         RecalculatingSeries();
         return;
        }
      //--- Se esta mensagem é do controle "Max. Limit Size'
      if(sparam==m_max_limit_size.LabelText())
        {
         //--- Exibe ou oculta a barra de progresso, dependendo do estado da caixa de seleção do controle 'Max. limit size'
         if(m_max_limit_size.CheckButtonState())
            m_progress_bar.Show();
         else
            m_progress_bar.Hide();
         //---
         return;
        }
     }
...

A imagem abaixo exibe o exemplo de como é no processo:

 Fig. 6. Teste do gráfico de linha no modo de redimensionamento automático da série.

Fig. 6. Teste do gráfico de linha no modo de redimensionamento automático da série.

  • Ao digitar os valores na caixa de edição dos controles Increment ratio, Offset series e Size of series, o método CProgram::RecalculatingSeries() é chamado para recalcular a série (veja o código abaixo). 
...
//--- Evento de digitar um novo valor na caixa de edição
   if(id==CHARTEVENT_CUSTOM+ON_END_EDIT)
     {
      //--- Se for uma mensagem dos controles 'Increment ratio' ou 'Offset series' ou 'Size of series'
      if(sparam==m_increment_ratio.LabelText() ||
         sparam==m_offset_series.LabelText() ||
         sparam==m_series_size.LabelText())
        {
         //--- Recalcula a série no gráfico
         RecalculatingSeries();
         return;
        }
      return;
     }
//--- Evento do clique nos botões comutadores da caixa de edição
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_INC || id==CHARTEVENT_CUSTOM+ON_CLICK_DEC)
     {
      //--- Se for uma mensagem dos controles 'Increment ratio' ou 'Offset series' ou 'Size of series'
      if(sparam==m_increment_ratio.LabelText() ||
         sparam==m_offset_series.LabelText() ||
         sparam==m_series_size.LabelText())
        {
         //--- Recalcula a série no gráfico
         RecalculatingSeries();
         return;
        }
      return;
     }
...

A imagem abaixo mostra um outro exemplo. Tente definir os mesmos parâmetros em sua cópia, e veja como ela fica no modo de animação.

 Fig. 7. Teste do modo "série correndo".

Fig. 7. Teste do modo "série correndo".

Depois de fazer as alterações nas cópias das classes da biblioteca padrão, vários arrays de elementos estão colocados corretamente na área das séries do gráfico de linha. A imagem abaixo mostra um exemplo quando o tamanho da série é igual a mil controles (veja o parâmetro Size of series).

 Fig. 8. Teste das séries com um grande número de dados.

Fig. 8. Teste das séries com um grande número de dados.

A aplicação para os testes está pronta. Você pode baixar o arquivo desse Expert Advisor, no final do artigo e testá-lo ainda mais. 

 

Conclusão

Este artigo apresentou o código das classes que são usados ​​para a criação da barra de progresso e os controles da interface gráfico de linha. 

A esquemática da biblioteca para a criação das interfaces gráficas no atual estágio de desenvolvimento é parecido com a imagem abaixo Alguns fragmentos do esquema são apenas uma solução temporária e algumas mudanças serão introduzidas com o desenvolvimento da biblioteca.

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

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

Isto conclui a parte principal da série de artigos sobre o desenvolvimento da Biblioteca Easy And Fast para a criação das interfaces gráficas no MetaTrader. Os artigos a seguir serão focados no trabalho com esta biblioteca. Será fornecido vários exemplos, adições e atualizações. Se você desejar, você pode se juntar ao processo de desenvolvimento deste projeto. Se você está feliz com o resultado, por favor teste a biblioteca em seus projetos, relate os erros que você possa encontrar e faça perguntas.

Abaixo estão todos os materiais da parte nove da série disponíveis para baixar e poder realizar os testes. 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 parte nove:


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

Arquivos anexados |
Interfaces gráficas X: Atualizações para a Biblioteca Easy And Fast (Build 2) Interfaces gráficas X: Atualizações para a Biblioteca Easy And Fast (Build 2)
Desde a publicação do artigo anterior da série, a biblioteca Easy And Fast tem recebido algumas funcionalidades novas. A estrutura e o código da biblioteca foram parcialmente otimizados, reduzindo ligeiramente a carga da CPU. Alguns métodos recorrentes em muitas classes de controle foram transferidos para a classe base CElement.
Sistema de negociação 'Turtle Soup' e sua modificação 'Turtle Soup Plus One' Sistema de negociação 'Turtle Soup' e sua modificação 'Turtle Soup Plus One'
No artigo regras de estratégias de negociação formalizadas e programadas Turtle Soup e Turtle Soup Plus One a partir do livro de Linda Raschke e Laurence Connors Street Smarts: High Probability Short-Term Trading Strategies. As estratégias descritas no livro receberam uma ampla acolhida, no entanto é importante entender que os autores conceberam suas ideias com base no comportamento do mercado de há 15-20 anos.
Interfaces gráficas X: Atualizações para a Biblioteca Easy And Fast(Build 3) Interfaces gráficas X: Atualizações para a Biblioteca Easy And Fast(Build 3)
Apresentamos neste artigo a próxima versão da biblioteca Easy And Fast (build 3). Foi corrigido certas falhas e adicionado novos recursos. Para maiores informações leia a continuação do artigo.
Distribuição Estatística no MQL5 - tirando o melhor de R e o fazendo mais rápido Distribuição Estatística no MQL5 - tirando o melhor de R e o fazendo mais rápido
As funções para trabalhar com as distribuições estatísticas básicas implementadas na linguagem R são consideradas. as distribuições de Cauchy, Weibull, normal, log-normal, logistic, exponential, uniform, gamma, beta central e não-central, qui-quadrado, F de Fisher-Snedecor, t de Student, assim como as distribuições binomiais discretas e binomiais negativas, distribuições geométricas, hipergeométricas e de Poisson. Existem funções para o cálculo de momentos teóricos de distribuições, que permitem avaliar o grau de conformidade da distribuição real com o modelado.