Interfaces gráficas XI: Integração da Biblioteca Gráfica Padrão (build 16)
Anatoli Kazharski | 30 outubro, 2017
Conteúdo
- Introdução
- Alterações no esquema da biblioteca
- Aplicação para testar as propriedades do gráfico
- Aplicação para testar as propriedades das curvas do gráfico
- Aplicação com um gráfico de hipocicloide animado
- Nova versão da aplicação de teste das atualizações anteriores
- Conclusão
Introdução
O primeiro artigo Interfaces gráficas I: Preparação da Estrutura da Biblioteca (Capítulo 1) considera em detalhes a finalidade desta biblioteca. Você encontrará uma lista de artigos com os links no final de cada capítulo. Lá, você também pode encontrar e baixar a versão completa da biblioteca, no estágio de desenvolvimento atual. Os arquivos devem estar localizados nas mesmas pastas que o arquivo baixado.
O segundo capítulo da nona parte da série, Interfaces gráficas IX: Os controles Barra de Progresso e Gráfico de Linha (Capítulo 2), demonstrou um exemplo de como uma classe para criar gráficos de linha pode ser integrada na biblioteca. Essa foi uma solução temporária, já que as capacidades desta parte da biblioteca eram extremamente insuficientes. Uma nova versão da biblioteca gráfica para a criação de gráficos científicos (a classe CGraphic) foi apresentada recentemente. A descrição de algumas funções desta classe foi apresentada em Visualize isso! Biblioteca Gráfica em Linguagem MQL5 como Equivalente a Plot de R. Esta atualização da biblioteca desenvolvida para criar interfaces gráficas irá introduzir uma versão com um novo controle para a criação de gráficos. Agora está ainda mais fácil de visualizar os dados de diferentes tipos.
Alterações no esquema da biblioteca
Anteriormente, a biblioteca desenvolvida usou uma cópia da classe CCanvas projetada para desenho. Devido à recente refatoração global do código da biblioteca, esta cópia não é mais necessária e pode ser removida substituindo-a pela versão original da biblioteca padrão. Isso reduziu o volume da biblioteca por aproximadamente 10% e quase por 40% em relação à versão apresentada antes da refatoração nos artigos Interfaces gráficas XI: Refatoração do código da biblioteca (build 14.1) e Interfaces gráficas XI: Controles renderizados (build 14.2).
A classe CGraphic agora será usada para criar gráficos, portanto, inclua o arquivo Graphic.mqh no arquivo Objects.mqh. Já que o arquivo com a classe CCanvas já está incluída em um dos arquivos de inclusão no arquivo Graphic.mqh, ele também ficará disponível para toda a biblioteca.
//+------------------------------------------------------------------+ //| Objects.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include "Enums.mqh" #include "Defines.mqh" #include "Fonts.mqh" #include "Colors.mqh" #include <Graphics\Graphic.mqh> #include <ChartObjects\ChartObjectSubChart.mqh> ...
A classe CLineChart foi renomeada para CGraph. Seu conteúdo interno também foi alterado. Agora, esta classe contém apenas os métodos para gerenciar as propriedades gerais e os estados do controle.
class CGraph : public CElement { public: //--- Manipulador de eventos do gráfico virtual void OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam); //--- Mover o controle virtual void Moving(const bool only_visible=true); //--- Gerenciamento virtual void Show(void); virtual void Hide(void); virtual void Reset(void); virtual void Delete(void); //--- Aplica as últimas alterações virtual void Update(const bool redraw=false); //--- private: //--- Redimensionamento void Resize(const int width,const int height); //--- Altera a largura da margem direita da janela virtual void ChangeWidthByRightWindowSide(void); //--- Altera a altura na borda inferior da janela virtual void ChangeHeightByBottomWindowSide(void); };
As propriedades do gráfico podem ser controladas usando o método CGraphic::GetGraphicPointer() para obter o ponteiro para uma instância da classe CGraphic:
class CGraph : public CElement { private: //--- Objetos para criação do controle CGraphic m_graph; //--- public: //--- Retorna o ponteiro para o gráfico CGraphic *GetGraphicPointer(void) { return(::GetPointer(m_graph)); } };
Classes adicionais foram incluídas na classe CGraphic para gerenciar as propriedades dos eixos (CAxis) e curvas (CCurve) do gráfico. A classe CColorGenerator foi projetada para gerar as cores da curva. Todas essas classes estão contidas em arquivos separados, que estão incluídos no arquivo Graphic.mqh:
//+------------------------------------------------------------------+ //| Graphic.mqh | //| Copyright 2016-2017, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include <Arrays\ArrayObj.mqh> #include "Curve.mqh" #include "Axis.mqh" #include "ColorGenerator.mqh" ...
O arquivo com a classe CCanvas está incluída no arquivo Curve.mqh, e daqui estará disponível para toda a biblioteca.
//+------------------------------------------------------------------+ //| Curve.mqh | //| Copyright 2016-2017, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include <Object.mqh> #include <Canvas\Canvas.mqh> ...
Todas as interconexões acima entre os arquivos e as classes são mostradas na figura abaixo:
Fig. 1. Interconexões entre as classes das bibliotecas padrão e desenvolvidas.
Assim, as classes da biblioteca padrão para se trabalhar com arrays e arquivos se tornam automaticamente disponíveis para a biblioteca e para os arquivos da aplicação em que ele é utilizado. Vários aplicativos MQL de teste serão demonstrados mais adiante no artigo para ajudar a entender quais novos recursos estão disponíveis agora.
Aplicação para testar as propriedades do gráfico
O primeiro aplicativo MQL de teste implementará uma interface gráfica com controles para gerenciar certas propriedades de um tipo gráfico da CGraphic. Um controle do tipo CTabs está localizado na parte superior do formulário. Neste caso, é um grupo de quatro guias. Um gráfico com duas curvas com valores gerados aleatoriamente estará localizado abaixo da área de trabalho das guias.
A primeira guia (Background) terá os controles para gerenciar as seguintes propriedades do gráfico:
- Cor de fundo.
- O texto principal do gráfico (exibido na parte superior).
- Texto auxiliar do gráfico (exibido na parte inferior).
- Cor do texto principal.
- Cor do texto auxiliar.
- Tamanho da fonte do texto principal.
- Tamanho da fonte do texto auxiliar.
Para definir e obter essas propriedades, a classe CGraphic fornece os métodos públicos correspondentes:
//+------------------------------------------------------------------+ //| Estrutura CBackground | //| Uso: plano de fundo em gráficos bidimensionais | //+------------------------------------------------------------------+ struct CBackground { uint clr; uint clr_main; uint clr_sub; string main; string sub; int size_main; int size_sub; }; //+------------------------------------------------------------------+ //| Classe CGraphic | //| Uso: classe para desenhar gráficos bidimensionais | //+------------------------------------------------------------------+ class CGraphic { protected: //--- elemento do gráfico CBackground m_background; // fundo de tela //--- public: //--- obtém as propriedades do fundo uint BackgroundColor(void) const { return(m_background.clr); } uint BackgroundMainColor(void) const { return(m_background.clr_main); } uint BackgroundSubColor(void) const { return(m_background.clr_sub); } string BackgroundMain(void) const { return(m_background.main); } string BackgroundSub(void) const { return(m_background.sub); } int BackgroundMainSize(void) const { return(m_background.size_main); } int BackgroundSubSize(void) const { return(m_background.size_sub); } //--- define as propriedades do fundo void BackgroundColor(const uint clr) { m_background.clr=clr; } void BackgroundMainColor(const uint clr) { m_background.clr_main=clr; } void BackgroundSubColor(const uint clr) { m_background.clr_sub=clr; } void BackgroundMain(const string main) { m_background.main=main; } void BackgroundSub(const string sub) { m_background.sub=sub; } void BackgroundMainSize(const int size) { m_background.size_main=size; } void BackgroundSubSize(const int size) { m_background.size_sub=size; } };
É assim que ele se parece:
Fig. 2. Controles da primeira guia (Background) do aplicativo MQL de teste.
A segunda guia (Indents & history) conterá os controles para configurar as seguintes propriedades:
- Indentação (esquerda, direita, superior, inferior).
- Largura da legenda.
- Tamanho da fonte da legenda.
- Tamanho dos marcadores da legenda.
- Indentações comuns para todos os elementos do gráfico.
- Tamanho das marcas das escalas do eixo do gráfico.
Os métodos da CGraphic mostrado na lista abaixo pode ser usado para obter e definir essas propriedades:
//+------------------------------------------------------------------+ //| Estrutura CCurveHistory | //| Uso: histórico de curvas em gráficos bidimensionais | //+------------------------------------------------------------------+ struct CCurveHistory { int name_width; int name_size; int symbol_size; int count_total; int count_points; int count_lines; int count_histogram; int count_custom; }; //+------------------------------------------------------------------+ //| Classe CGraphic | //| Uso: classe para desenhar gráficos bidimensionais | //+------------------------------------------------------------------+ class CGraphic { protected: //--- elemento do gráfico CCurveHistory m_history; // histórico //--- public: //--- obtém ou define as indentações int IndentUp(void) const { return(m_up0); } void IndentUp(const int up) { m_up0=up; } int IndentDown(void) const { return(m_down0); } void IndentDown(const int down) { m_down0=down; } int IndentLeft(void) const { return(m_left0); } void IndentLeft(const int left) { m_left0=left; } int IndentRight(void) const { return(m_right0); } void IndentRight(const int right) { m_right0=right; } //--- gets or sets gap int GapSize(void) const { return(m_gap); } void GapSize(const int size) { m_gap=size; } //--- obtém ou define o tamanho de marca principal int MajorMarkSize(void) const { return(m_mark_size); } void MajorMarkSize(const int size) { m_mark_size=size; } //--- obtém as propriedades do histórico da curva int HistoryNameWidth(void) const { return(m_history.name_width); } int HistoryNameSize(void) const { return(m_history.name_size); } int HistorySymbolSize(void) const { return(m_history.symbol_size); } //--- define as propriedades do histórico da curva void HistoryNameWidth(const int width) { m_history.name_width=width; } void HistoryNameSize(const int size) { m_history.name_size=size; } void HistorySymbolSize(const int size) { m_history.symbol_size=size; } };
Abaixo encontramos o aspecto da interface gráfica do aplicativo MQL de teste:
Fig. 3. Controles da segunda guia (Indents & history) do aplicativo MQL de teste.
A terceira guia (Grid) contém os controles para definir as propriedades de grade listadas abaixo:
- Cor das linhas da grade.
- Cor da linha do eixo zero.
- Cor de fundo da grade.
- Desenho de pontos nos nós da grade.
- Raio de pontos.
- Cor dos pontos.
Os métodos apropriados estão presentes na classe CGraphic para obter e definir essas propriedades (veja a lista de códigos abaixo):
//+------------------------------------------------------------------+ //| Estrutura CGrid | //| Uso: grade em gráficos bidimensionais | //+------------------------------------------------------------------+ struct CGrid { uint clr_line; uint clr_background; uint clr_circle; uint clr_axis_line; uint clr_frame; int r_circle; bool has_circle; }; //+------------------------------------------------------------------+ //| Classe CGraphic | //| Uso: classe para desenhar gráficos bidimensionais | //+------------------------------------------------------------------+ class CGraphic { protected: //--- elemento do gráfico CGrid m_grid; // grid //--- public: //--- obtém as propriedades da grade uint GridLineColor(void) const { return(m_grid.clr_line); } uint GridAxisLineColor(void) const { return(m_grid.clr_axis_line); } uint GridBackgroundColor(void) const { return(m_grid.clr_background); } int GridCircleRadius(void) const { return(m_grid.r_circle); } uint GridCircleColor(void) const { return(m_grid.clr_circle); } bool GridHasCircle(void) const { return(m_grid.has_circle); } //--- define as propriedades da grade void GridLineColor(const uint clr) { m_grid.clr_line=clr; } void GridAxisLineColor(const uint clr) { m_grid.clr_axis_line=clr; } void GridBackgroundColor(const uint clr) { m_grid.clr_background=clr; } void GridCircleRadius(const int r) { m_grid.r_circle=r; } void GridCircleColor(const uint clr) { m_grid.clr_circle=clr; } void GridHasCircle(const bool has) { m_grid.has_circle=has; } };
Abaixo é o que veremos no final:
Fig. 4. Controles da terceira guia (Grid) do aplicativo MQL de teste.
Os controles para gerenciar as propriedades dos eixos do gráfico serão colocados na quarta guia (Axes). Os botões de radio na parte esquerda da área de trabalho das guias permitem alternar entre a configuração de um determinado eixo. Estes botões estão separados dos outros controles da guia Axes por uma linha de separação.
Aqui estão as propriedades que estarão disponíveis para modificação:
- Escala automática.
- Valor mínimo do eixo.
- Valor máximo do eixo.
- Valor de tolerância para a mínima do eixo.
- Valor de tolerância para a máxima do eixo.
- Tamanho dos números do eixo.
- O comprimento máximo de exibição dos números dos eixos.
- Tamanho da fonte para o nome do eixo.
- Valor do passo inicial para o eixo.
- Quantidade máxima de números no eixo.
- Nome do eixo.
- Cor do texto do nome do eixo.
A lista abaixo mostra os nomes dos métodos da classe CAxis para obter e definir as propriedades acima:
//+------------------------------------------------------------------+ //| Classe CAxis | //| Uso: classe para criar os eixos em gráficos bidimensionais | //+------------------------------------------------------------------+ class CAxis { private: double m_min; double m_max; uint m_clr; string m_name; int m_name_size; int m_values_size; int m_values_width; bool m_auto_scale; double m_default_step; // tamanho do passo padrão double m_max_labels; // o número máixmo de marcações double m_min_grace; // valor "grace" aplicado ao intervalo mínimo de dados double m_max_grace; // valor "grace" aplicado ao intervalo máximo de dados //--- public: CAxis(void); ~CAxis(void); //--- propriedades double Min(void) const { return(m_min); } void Min(const double min) { m_min=min; } double Max(void) const { return(m_max); } void Max(const double max) { m_max=max; } string Name(void) const { return(m_name); } void Name(const string name) { m_name=name; } //--- propriedades padrão uint Color(void) const { return(m_clr); } void Color(const uint clr) { m_clr=clr; } bool AutoScale(void) const { return(m_auto_scale); } void AutoScale(const bool auto) { m_auto_scale=auto; } int ValuesSize(void) const { return(m_values_size); } void ValuesSize(const int size) { m_values_size=size; } int ValuesWidth(void) const { return(m_values_width); } void ValuesWidth(const int width) { m_values_width=width; } int NameSize(void) const { return(m_name_size); } void NameSize(const int size) { m_name_size=size; } double DefaultStep(void) const { return(m_default_step); } void DefaultStep(const double value) { m_default_step=value; } double MaxLabels(void) const { return(m_max_labels); } void MaxLabels(const double value) { m_max_labels=value; } double MinGrace(void) const { return(m_min_grace); } void MinGrace(const double value) { m_min_grace=value; } double MaxGrace(void) const { return(m_max_grace); } void MaxGrace(const double value) { m_max_grace=value; } };
O resultado é mostrado abaixo:
Fig. 5. Controles da quarta guia (Axes) da aplicação MQL de teste.
O aplicativo de teste apresentado no artigo pode ser baixado usando o link abaixo para um estudo mais detalhado.
Aplicação para testar as propriedades das curvas do gráfico
Um aplicativo MQL separado foi escrito para testar certas propriedades das curvas em um gráfico com o tipo CGraphic. Os controles para gerenciar as propriedades das curvas do gráfico estarão localizados no topo do formulário dessa aplicação, com dois gráficos do tipo CGraphic (o controle CGraph) logo abaixo deles. O primeiro gráfico exibirá uma sériee de dados aleatórios, e o segundo irá traçar seus derivativos, que são calculados com base na fórmula do indicador Momentum, como exemplo.
Aqui estão os controles para gerenciar as propriedades das curvas do gráfico:
- Caixa de seleção Animate - inicia a entrada automática de dados no gráfico.
- Caixa de edição spin Array size - o número atual de elementos no array de dados que são exibidos no gráfico.
- Botão Random - gera sequências de dados aleatórias em série no gráfico.
- Caixa de edição spin Period - valor da variável para calcular o indicador Momentum.
- Caixa combinada Curve type – tipo de curvas no gráfico.
- Caixa combinada Point type - tipo de dados em pontos que são usados para traçar as curvas no gráfico.
A classe personalizada da aplicação (CProgram) implementa os métodos que são relacionados aos controles listados acima e executa as seguintes tarefas:
- Configuração do tamanho dos arrays de dados para exibição no gráfico.
- Inicialização os arrays com dados.
- Atualização dos gráficos para refletir as mudanças recentes.
- Adição de um elemento ao final dos arrays.
- Exclusão de um elemento no final dos arrays.
- Atualização dos gráficos pelo timer.
- Animação dos gráficos com entrada automática de novos dados.
Abaixo está a listagem de códigos para todos os métodos que implementam essas funções. Baixe os arquivos no final do artigo para encontrar mais detalhes sobre o código desses métodos.
class CProgram : public CWndEvents { protected: //--- Arrays de dados para a saída no gráfico double data1[]; double data2[]; //--- double data3[]; double data4[]; //--- private: //--- Redimensiona os arrays void ResizeGraph1Arrays(void); void ResizeGraph2Arrays(void); void ResizeGraph1Arrays(const int new_size); void ResizeGraph2Arrays(const int new_size); //--- Inicialização dos arrays void InitGraph1Arrays(void); void InitGraph2Arrays(void); //--- Zera os arrays void ZeroGraph1Arrays(void); void ZeroGraph2Arrays(void); //--- Define o valor aleatório no índice especificado void SetGraph1Value(const int index); void SetGraph2Value(const int index); //--- Atualiza as séries no gráfico void UpdateGraph(void); void UpdateGraph1(void); void UpdateGraph2(void); //--- Recalcula a série no gráfico void RecalculatingSeries(void); //--- Adiciona um valor a mais ao final dos arrays void AddValue(void); //--- Remove um valor no final dos arrays void DeleteValue(void); //--- Atualiza o gráfico pelo timer void UpdateGraphByTimer(void); //--- Anima a série do gráfico void AnimateGraphSeries(void); };
É assim que ele se parece:
Fig. 6. Interface gráfica do aplicativo para testar as propriedades das curvas do gráfico.
O aplicativo de teste apresentado no artigo pode ser baixado usando o link abaixo para um estudo mais detalhado.
Aplicação com um gráfico de hipocicloide animado
Em um de seus livros sobre programação em VBA no Microsoft Excel, John Walkenbach fornece aos leitores um CD com arquivos de teste. Um dos arquivos implementa um diagrama, onde um número infinito de hipocicloides é gerado.
Para referência: A Wikipedia dá a seguinte definição:
Um hipocicloide (do grego ὑπό — embaixo, abaixo e κύκλος — círculo, circunferência) é uma curva de plano especial gerada pelo traço de um ponto fixo em um pequeno círculo que rola dentro de um círculo maior.
Definição de John Walkenbach dada em seu livro:
Vamos implementar uma aplicação semelhante no MQL e adicionar uma interface gráfica para gerenciar os parâmetros. Vamos ver como funciona em detalhes.
Três parâmetros são usados para gerar um novo hipocicloide, que é usado para inicializar as sequências numéricas com o passo especificado. Em seguida, os cálculos são realizados com base nos valores nessas sequências para obter as coordenadas de pontos no gráfico. Depois disso, os resultados obtidos são normalizados.
Na classe personalizada, nós declaramos vários arrays para calcular as sequências e os campos para calcular a média e desvio padrão.
//+------------------------------------------------------------------+ //| Program.mqh | //| Copyright 2017, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include <Math\Stat\Stat.mqh> #include <EasyAndFastGUI\WndEvents.mqh> #include <EasyAndFastGUI\TimeCounter.mqh> //+------------------------------------------------------------------+ //| Classe para a criação de uma aplicação | //+------------------------------------------------------------------+ class CProgram : public CWndEvents { protected: ... //--- Arrays de dados para cálculos double a_inc[]; double b_inc[]; double t_inc[]; double x_source[]; double y_source[]; //--- Arrays de dados para a saída no gráfico double x_norm[]; double y_norm[]; //--- Para calcular a média e o desvio padrão double x_mean; double y_mean; double x_sdev; double y_sdev; ... }; //+------------------------------------------------------------------+ //| Construtor | //+------------------------------------------------------------------+ CProgram::CProgram(void) : x_mean(0), y_mean(0), x_sdev(0), y_sdev(0) { ... }
Os valores serão calculados no método CProgram::InitArrays(). Aqui, o primeiro ciclo calcula os dados iniciais. Em seguida, o desvio padrão e a média são obtidos, e o segundo ciclo normaliza os dados. Os tamanhos dos arrays são definidos usando o método CProgram::ResizeArrays(). Os valores para os tamanhos do array são retirados do controle da Caixa de texto (CTextEdit) da interface gráfica da aplicação.
class CProgram : public CWndEvents { private: //--- Redimensiona os arrays void ResizeArrays(void); //--- Inicialização dos arrays auxiliares para cálculos void InitArrays(void); }; //+------------------------------------------------------------------+ //| Redimensiona os arrays | //+------------------------------------------------------------------+ void CProgram::ResizeArrays(void) { int array_size =::ArraySize(x_norm); int new_size =(int)m_array_size.GetValue(); //--- Retorna, se o tamanho não mudou if(array_size==new_size) return; //--- Define o novo tamanho ::ArrayResize(a_inc,new_size); ::ArrayResize(b_inc,new_size); ::ArrayResize(t_inc,new_size); ::ArrayResize(x_source,new_size); ::ArrayResize(y_source,new_size); ::ArrayResize(x_norm,new_size); ::ArrayResize(y_norm,new_size); } //+------------------------------------------------------------------+ //| Inicialização dos arrays | //+------------------------------------------------------------------+ void CProgram::InitArrays(void) { //--- Redimensiona os arrays ResizeArrays(); //--- Calcula os valores usando fórmulas int total=(int)m_array_size.GetValue(); for(int i=0; i<total; i++) { if(i<1) { a_inc[i] =1+(double)m_animate.GetValue(); b_inc[i] =1+(double)m_animate.GetValue(); t_inc[i] =1+(double)m_animate.GetValue(); } else { a_inc[i] =a_inc[i-1]+(double)m_a_inc.GetValue(); b_inc[i] =b_inc[i-1]+(double)m_b_inc.GetValue(); t_inc[i] =t_inc[i-1]+(double)m_t_inc.GetValue(); } //--- double a=a_inc[i]; double b=b_inc[i]; double t=t_inc[i]; //--- x_source[i] =(a-b)*cos(t)+b*cos((a/b-1)*t); y_source[i] =(a-b)*sin(t)+b*sin((a/b-1)*t); } //--- Calcule a média x_mean=MathMean(x_source); y_mean=MathMean(y_source); //--- Calcula o desvio padrão x_sdev=MathStandardDeviation(x_source); y_sdev=MathStandardDeviation(y_source); //--- Ajuste para evitar a divisão por zero x_sdev =(x_sdev==0)? 1 : x_sdev; y_sdev =(y_sdev==0)? 1 : y_sdev; //--- Normaliza os dados for(int i=0; i<total; i++) { x_norm[i] =(x_source[i]-x_mean)/x_sdev; y_norm[i] =(y_source[i]-y_mean)/y_sdev; } }
A classe CGraphic contém os métodos que permitem a adição de cortes, linhas e texto adicionais às escalas de eixos dentro da área de trabalho do gráfico criado.
Em nosso caso, o método CProgram::TextAdd() será usado para produzir os valores da média e o desvio padrão para as sequências X e Y no canto superior esquerdo do diagrama. O métodos CGraphic::ScaleX() e CGraphic::ScaleY() são usados para obter as coordenadas do ponto extremo (canto superior esquerdo) do diagrama. Eles são projetados para dimensionar os valores reais do gráfico em coordenadas de pixels. Aqui a mínima ao longo do eixo X e a máxima ao longo do eixo Y são alimentados como valores reais.
class CProgram : public CWndEvents { private: //--- Adiciona o texto ao gráfico void TextAdd(void); }; //+------------------------------------------------------------------+ //| Adiciona o texto ao gráfico | //+------------------------------------------------------------------+ void CProgram::TextAdd(void) { //--- Obtém o ponteiro para o gráfico CGraphic *graph=m_graph1.GetGraphicPointer(); //--- int x =graph.ScaleX(graph.XAxis().Min())+50; int y =graph.ScaleY(graph.YAxis().Max())+10; int y2 =y+20; uint clr =::ColorToARGB(clrBlack); uint align =TA_RIGHT; //--- string str[8]; str[0] ="x mean:"; str[1] ="y mean:"; str[2] =::DoubleToString(x_mean,2); str[3] =::DoubleToString(y_mean,2); str[4] ="x sdev:"; str[5] ="y sdev:"; str[6] =::DoubleToString(x_sdev,2); str[7] =::DoubleToString(y_sdev,2); //--- Calcula as coordenadas e publica o texto no gráfico int l_x=0,l_y=0; for(int i=0; i<8; i++) { if(i<2) l_x=x; else if(i<6) l_x=(i%2==0)? l_x+50 : l_x; else l_x=(i%2==0)? l_x+60 : l_x; //--- l_y=(i%2==0)? y : y2; //--- graph.TextAdd(l_x,l_y,str[i],clr,align); } }
Após todos os dados necessários serem definidos no gráfico, é necessário redesenhá-lo para refletir as mudanças mais recentes. Isso é feito pelo método CProgram::UpdateSeries(). Aqui, ele verifica primeiro se há alguma série no gráfico. Se houver, então ele define os últimos dados calculados. Além disso, as propriedades da curva são definidas usando os controles da interface gráfica. Aqui, são (1) a suavização da linha, (2) o tipo de pontos e (3) o tipo da curva. Deve-se notar que o texto deve ser aplicado ao gráfico após todas as outras propriedades e dados forem definidas e renderizadas. No final, é necessário atualizar o gráfico para ver o resultado.
class CProgram : public CWndEvents { private: //--- Define e atualiza as séries no gráfico void UpdateSeries(void); }; //+------------------------------------------------------------------+ //| Define e atualiza as séries no gráfico | //+------------------------------------------------------------------+ void CProgram::UpdateSeries(void) { //--- Obtém o ponteiro para o gráfico CGraphic *graph=m_graph1.GetGraphicPointer(); //--- Atualiza todas as séries do gráfico int total=graph.CurvesTotal(); if(total>0) { //--- Obtém o ponteiro da curva CCurve *curve=graph.CurveGetByIndex(0); //--- Define os arrays de dados curve.Update(x_norm,y_norm); //--- Obtém os valores das propriedades da curva ENUM_CURVE_TYPE curve_type =(ENUM_CURVE_TYPE)m_curve_type.GetListViewPointer().SelectedItemIndex(); ENUM_POINT_TYPE point_type =(ENUM_POINT_TYPE)m_point_type.GetListViewPointer().SelectedItemIndex(); //--- Define as propriedades curve.LinesSmooth(m_line_smooth.IsPressed()); curve.PointsType(point_type); curve.Type(curve_type); } //--- Aplicar graph.Redraw(true); //--- Saída do texto TextAdd(); //--- Atualiza o gráfico graph.Update(); }
O método CProgram::RecalculatingSeries() é usado para calcular e aplicar os resultados obtidos em uma única chamada:
class CProgram : public CWndEvents { private: //--- Recalcula a série no gráfico void RecalculatingSeries(void); }; //+------------------------------------------------------------------+ //| Recalcula as séries no gráfico | //+------------------------------------------------------------------+ void CProgram::RecalculatingSeries(void) { //--- Calcula os valores e inicializa os arrays InitArrays(); //--- Atualiza a série UpdateSeries(); }
O diagrama plotado com base nessas fórmulas ficará mais interessante se ele se tornar animado. Para definir as sequências calculadas em movimento, é necessário alterar o valor inicial dessas sequências. Isso pode ser alcançado inserindo os valores através da caixa de edição spin ou executando o processo no modo automático. No modo automático, o valor nesta caixa de edição é incrementado ou diminuído pelo método CProgram::AnimateGraphSeries(). Este método é chamado no método CProgram::UpdateGraphByTimer(), que por sua vez é invocado no timer da aplicação.
class CProgram : public CWndEvents { private: //--- Atualiza o gráfico pelo timer void UpdateGraphByTimer(void); //--- Anima a série do gráfico void AnimateGraphSeries(void); }; //+------------------------------------------------------------------+ //| Timer | //+------------------------------------------------------------------+ void CProgram::OnTimerEvent(void) { CWndEvents::OnTimerEvent(); //--- Atualiza o gráfico pelo timer if(m_counter1.CheckTimeCounter()) { UpdateGraphByTimer(); } ... } //+------------------------------------------------------------------+ //| Atualiza o gráfico pelo timer | //+------------------------------------------------------------------+ void CProgram::UpdateGraphByTimer(void) { //--- Retorna, se (1) o formulário for minimizado ou (2) a animação estiver desativada if(m_window.IsMinimized() || !m_animate.IsPressed()) return; //--- Anima a série do gráfico AnimateGraphSeries(); //--- Atualiza os arrays e séries no gráfico RecalculatingSeries(); } //+------------------------------------------------------------------+ //| Anima a série do gráfico | //+------------------------------------------------------------------+ void CProgram::AnimateGraphSeries(void) { //--- Para especificar a direção para redimensionar os arrays static bool counter_direction=false; //--- Muda a direção se o mínima for atingida if((double)m_animate.GetValue()<=(double)m_animate.MinValue()) counter_direction=false; //--- Muda a direção se a máxima for alcançada if((double)m_animate.GetValue()>=(double)m_animate.MaxValue()) counter_direction=true; //--- Redimensiona o array na direção especificada string value=""; if(!counter_direction) value=string((double)m_animate.GetValue()+m_animate.StepValue()); else value=string((double)m_animate.GetValue()-m_animate.StepValue()); //--- Define o novo valor e atualiza a caixa de texto m_animate.SetValue(value,false); m_animate.GetTextBoxPointer().Update(true); }
O resultado obtido é exibido abaixo:
Fig. 7. Demonstração de um hipocicloide animado.
O aplicativo de teste apresentado no artigo pode ser baixado usando o link abaixo para um estudo mais detalhado.
Nova versão da aplicação de teste das atualizações anteriores
A aplicação de teste demonstrada no artigo Interfaces Gráficas IX: Os Controles Barra de Progresso e Gráfico de Linha (Capítulo 2) foi atualizado de acordo com as alterações nesta atualização.
A nova versão deste aplicativo MQL com a interface gráfica atualizada é exibida abaixo:
Fig. 8. Nova versão do aplicativo de teste das atualizações anteriores.
O aplicativo de teste apresentado no artigo pode ser baixado usando o link abaixo para um estudo mais detalhado.
Conclusão
Neste artigo, foi integrado uma parte da biblioteca padrão para traçar gráficos científicos com a biblioteca desenvolvida para a criação de interfaces gráficas. Todos os exemplos demonstrados podem ser baixados dos arquivos anexados a este artigo para estudar o código fonte em mais detalhes.
A biblioteca no estágio atual de desenvolvimento se parece com o esquema abaixo:
Fig. 9. Estrutura da biblioteca, no atual estágio de desenvolvimento
O código da biblioteca apresentado é gratuito. Você pode usá-lo em seus projetos, inclusive comercialmente, escrever artigos e cumprir ordens.