Interfaces Gráficas IX: Os Controles Barra de Progresso e Gráfico de Linha (Capítulo 2)
Conteúdo
- Introdução
- Controle barra de progresso
- Controle gráfico de linha
- Melhorando as classes da biblioteca padrão
- Desenvolvimento da classe CLineGraph
- Escrevendo uma aplicação para testar o gráfico de linha
- Conclusão
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.
- Fundo
- Descrição
- Barra do indicador
- Fundo do indicador
- 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.
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.
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.
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.
- 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.
- 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".
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.
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.
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:
- Interfaces Gráficas IX: O Controle Seletor de Cores (Capítulo 1)
- Interfaces Gráficas IX: Os Controles Barra de Progresso e Gráfico de Linha (Capítulo 2)
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/2580
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso