English Русский 中文 Español Deutsch 日本語
Desenhando Medidores com Mostrador usando a classe CCanvas

Desenhando Medidores com Mostrador usando a classe CCanvas

MetaTrader 5Exemplos | 4 agosto 2015, 12:41
2 696 0
Serhii Shevchuk
Serhii Shevchuk

Índice


Introdução

Tudo começou quando eu comecei a me familiarizar com a classe CCanvas. No que diz a prática, eu me deparei com uma ideia em desenhar um indicador medidor com mostrador. Meus primeiros medidores foram bem grosseiros, mas, eventualmente, eles forma complementados por novos elementos e tornaram-se visualmente agradáveis. E, como resultado, eu tenho uma pequena biblioteca que agora permite adicionar um medidor a um indicador ou um EA de uma maneira simples e fácil. Neste artigo, vamos considerar a estrutura dos medidores, se familiarizar com as funções necessárias para desenhar e definir o aspecto visual, e avaliar a intensidade dos recursos.

Medidores com mostrador

Fig.1. Medidores com mostrador


1. Coordenadas e Âncora

Existem dois tipos de posicionar um medidor em um gráfico: absoluta e relativa.

No caso de posicionamento absoluto, as coordenadas representam as distâncias em pixeis a partir de uma âncora do canto ao longo dos eixos X e Y.

No caso de posicionamento relativo , a origem local de coordenadas é criada de acordo com o tipo específico do posicionamento relativo. Quando o tipo vertical é selecionado, a origem está localizada abaixo ou acima de um objeto de referência (se for selecionado um canto de ancoragem superior ou inferior, respectivamente). Quando o tipo vertical é selecionado, ela situa-se à esquerda ou à direita na direção do canto da âncora. Neste caso, as coordenadas especificadas representam um deslocamento a partir da sua origem local. Deslocamentos positivos levam ao movimento de um objeto para longe do objeto de referência. Em caso de desvios negativos, o objecto irá interferir no de referência.

O objeto de referência pode ser representado apenas por um objeto de outro medidor. É essencial que ambos os objetos tenham a mesmo âncora.

Fig. 2 representa um exemplo de posicionamento relativo.

O posicionamento relativo

Fig.2. O posicionamento relativo dos medidores

Vamos rever as configurações de cada medidor:

  • O indicador "gg01": o posicionamento relativo está desativado. Deslocamento horizontal - 40, deslocamento vertical - 40.
  • O indicador "gg02": posicionamento relativo - horizontal, objeto de referência - "gg01". Deslocamento horizontal das coordenadas do local de origem (ponto A) - 15, deslocamento vertical - 0.
  • O indicador "gg03": posicionamento relativo - vertical, objeto de referência - "gg01". Deslocamento horizontal das coordenadas do local de origem (ponto B) - 0, deslocamento vertical - 15.
  • O indicador "gg04": posicionamento relativo - vertical, objeto de referência - "gg02". Deslocamento horizontal das coordenadas do local de origem (ponto C) - 50, deslocamento vertical - 15.

O posicionamento relativo facilita a configuração de entrada se o gráfico tem vários indicadores que contenham medidores. Se você decidir alterar o tamanho de um medidor, as coordenadas de outros medidores serão recalculadas automaticamente.

A função GaugeCreate() define o tipo de posicionamento e as coordenadas.


2. Elementos do Medidor

O Medidor com mostrador é composto por dois objetos gráficos. Um deles é chamado de camada de escala, o outro é chamado de camada de agulha. Ambos os objetos gráficos têm as mesmas coordenadas. A camada de agulha é colocada sobre a camada de escala. O nome do medidor definido nos parâmetros de entrada serve como um prefixo para os nomes de ambos os objetos. Por exemplo, se o nome do medidor é o "Gauge01", a camada de escala será chamado "Gauge01_s", e a camada de agulha terá o nome "Gauge01_n".

Fig.3 mostra a estrutura do indicador.

Fig.3. Estrutura do medidor

Fig.3. Estrutura do medidor

A camada de escala contém:

  • limite (1)
  • marcas de graduação de escala (5, 6, 7)
  • etiquetas de graduação de escala (4)
  • intervalos destacados (2, 12)
  • legendas (3, 10, 11)

As legendas são distinguidas por finalidades:

  • Descrição do medidor (3)
  • unidades de medida (11)
  • valor atual (10)
  • multiplicador de marcas de escala (omitido)

A graduação da escala é dividida em:

  • principal (7)
  • médio (5)
  • menor (6)

Somente os pontos da graduação principal possuem etiquetas. O passo da graduação é definido como um valor numérico. O passo da graduação média é calculada de acordo com um determinado número de marcas médias entre os as principais. O passo da graduação menor é calculado de acordo com um determinado número de marcas menores entre as do meio. A graduação menor e do meio pode ser omitido.

A camada de agulha contém:

  • agulha (8)
  • centro da agulha (9)


2.1. Tamanhos

Fig.3 mostra os tamanhos de alguns elementos do medidor:

  • d - tamanho do medidor que corresponde ao diâmetro da linha de contorno externa do medidor
  • b - tamanho da borda
  • g - tamanho do espaço entre uma borda e os elementos de escala
  • c - o tamanho do centro da agulha.

NB. O diâmetro do medidor é o único tamanho definido em pixels ("d" na fig.3). Todos os outros elementos e fontes são definidas em unidades condicionais e os seus tamanhos são calculados como a percentagem do diâmetro. Ela é feita para facilitar a escala. Altere o diâmetro, e todos os outros tamanhos serão proporcionalmente recalculados. Os coeficientes de cálculo estão listados na seção Substituição de Macro e pode ser modificado pelo utilizador.


2.2. Formato do Corpo

Existem dois tipos de formas para o corpo do medidor: circular e de setor. A forma de setor é mais conveniente se o ângulo da faixa de escala é inferior a 180 graus.

Formato do medidor

Fig.4. Formato do medidor

Fig.4 descreve um medidor circular (a) e dois medidores na forma de setor (b, c). A função GaugeSetCaseParameters () é utilizada para definir a forma do corpo desejado.


2.3. Escala

Este é o elemento mais importante do indicador. A legibilidade dos dados depende da sua aparência. A escala não deve ser complicada, mas, ao mesmo tempo ela deve ser suficientemente informativa. A seleção de valores extremos da escala, bem como a etapa marcações principais, requerem uma atenção especial. A função GaugeSetScaleParameters() permite definir o intervalo de escala, a sua rotação e os valores extremos (mínimo e máximo). O valor mínimo pode ser à esquerda (ordem direta) ou à direita (ordem inversa).

A faixa de escala é um ângulo contido por dois vetores de raios de valores extremos da escala. Ela é demonstrada na Fig.5.

Faixa de escala

Fig.5. Faixa de escala

A escala de rotação é um ângulo de desvio da bissetriz angular da faixa de escala da linha que vem de cima e na vertical a partir do centro do medidor. Ela é demonstrada na fig.6.

Ângulo da escada de rotação

Fig.6. Ângulo da escada de rotação

A combinação do ângulo da faixa de escala e o ângulo de rotação podem ajudá-lo a definir a aparência do medidor de uma forma bastante flexível. Fig.4(c) demonstra um medidor com um intervalo de 90 graus e 45 graus de rotação.

Os valores máximos e mínimos da escala são parâmetros importantes, que devem ser selecionados dependendo do intervalo dos valores permitidos da variável exibida. A marca zero pode ser omitida por uma questão de conveniência. Não há nenhuma razão de elaborar a escala do zero se as suas variáveis se alteram ​​na faixa de 400-600. Fig.7 mostra alguns exemplos de valores máximos e mínimos da escala.

Os valores máximos e mínimos da escala

Fig.7. Os valores máximos e mínimos da escala

  • a) valores de 0 a 500, ordem direta
  • b) valores de -200 a 400, ordem direta
  • c) valores de -400 a 0, ordem direta
  • d) Valores de 500-0, ordem inversa
  • e) valores de 200-800, ordem direta
  • f) valores de 0 a -800, ordem inversa


2.4. Marcações de Graduação

A definição da marcação de graduação consiste em selecionar o tamanho das marcas e o método de alinhamento.

O alinhamento pode ser como se segue:

  • borda interna da escala
  • borda externa da escala
  • centro

Fig.8 mostra exemplos da marcações de graduação da escala de alinhamento:

  • a - centro
  • b - borda interna
  • c - borda externa

A função GaugeSetMarkParameters() é utilizada para definí-la.

A posição dos rótulos das marcações é referido as configurações de escala e ajustado utilizando a função GaugeSetScaleParameters().

Fig.8 (a) representa um exemplo de marcadores de posicionamento dentro da escala, Fig.8(b) e 8(c) - fora da escala.

Recomenda-se a utilização de um multiplicador, um coeficiente de todos os valores exibidos será dividido por ele, para que os rótulos não ocupem muito espaço na escala. O multiplicador pode ter valores de 0.0001 até 10000. A Fig.4(c) ilustra um exemplo da aplicação de um multiplicador igual a 100, o que permitiu a utilização dos números de um dígito, em vez dos números de três dígitos na etiquetas. Fig.1 descreve uma situação em que nós usamos um multiplicador igual a 0.0001 para o ATR, o que permitiu não utilizar o ponto decimal e zeros nas etiquetas. A função GaugeSetScaleParameters() define o multiplicador.

Marcações de posicionamento e etiquetas

Fig.8. Marcações de posicionamento e etiquetas


2.5. Legendas

As legendas são destinadas para a exibição de informações suplementares, podendo ser de quatro tipos:

  • Descrição do medidor
  • unidades de medida
  • valor atual
  • multiplicador

Qualquer legenda pode ser escondida. Apenas a descrição do medidor que é exibido por padrão.

O posicionamento da legenda é definido pelo ângulo e o raio. O ângulo é ajustado em graus e o seu valor é igual ao ângulo entre a linha que vem de cima e na vertical a partir do centro do medidor, e um segmento imaginário, que liga o centro do medidor e o centro da legenda. O raio é definido em unidades condicionais. Ele pode ter valores de 0 a 10, em que 0 corresponde ao raio do centro da agulha e 10 corresponde ao raio fora da escala.

Fig.9 representa um exemplo de posicionamento da legenda.

  • A legenda "Profit" (descrição do medidor) tem as seguintes coordenadas: ângulo - 0, raio - 3.
  • A legenda "0.00" (valor atual) tem as seguintes coordenadas: ângulo - 225, raio - 4.
  • A legenda "USD" (unidades de medida) tem as seguintes coordenadas: ângulo - 215, raio - 8.

A função GaugeSetLegendParameters() é utilizada para definir os parâmetros da legenda.

Coordenadas da Legenda

Fig.9. Coordenadas da Legenda

NB. As legendas não são fixas na escala e seus ângulos não está conectados com o ângulo da escala de rotação.


2.6. Intervalos de Destaque

As faixas dos dados destacadas representam um elemento inerente de qualquer medidor. Eles ajudam a ver que a variável tem assumido um valor de emergência ou entrou em alguma faixa especial. A função GaugeSetRangeParameters() pode definir até quatro faixas de destaque. Para fazer isso, você precisa definir os valores extremos e a cor para o destaque. Fig.1 mostra o indicador de lucro com duas faixas destacadas: de 200 para 400 é a faixa verde, que indica a hora de obter o lucro, e de -200 a -400 é a faixa laranja, advertindo sobre um grande rebaixamento.


2.7. Agulha

A função GaugeSetNeedleParameters() define o tamanho do centro da agulha e do tipo de área de preenchimento. O tipo de preenchimento de área influência na intensidade de recursos do indicador, já que a camada da agulha está totalmente redesenhada toda vez após a atualização dos dados. Fig.10 ilustra um exemplo de preenchimento da área.

  • agulha preenchida com a utilização do algoritmo de suavização (a)
  • agulha preenchida sem a utilização do algoritmo de suavização (b)
  • agulha não preenchida com a linha de contorno anti-aliasing (c)

Métodos de preenchimento da área da agulha

Fig.10. Métodos de preenchimento da área da agulha

Os prós e contras de cada método são descritos nas seções dedicadas a modificação da classe CCanvas e na avaliação da intensidade de recursos.


3. Funções

A tabela 1 lista as funções de elaboração dos medidores e definem sua aparência.

Função
Comportamento
GaugeCalcLocation
Calcula as coordenadas do centro do medidor
GaugeCreate
Cria o medidor
GaugeDelete
Exclui o medidor
GaugeNewValue
Atualiza a posição da agulha e o valor apresentado
GaugeRedraw
Redesenha o medidor
GaugeRelocation
Muda a localização dos objetos do medidor no gráfico
GaugeSetCaseParameters
Define os parâmetros do corpo do medidor
GaugeSetLegendParameters
Define os parâmetros da legenda
GaugeSetMarkLabelFont
Define a fonte das marcações de graduação
GaugeSetMarkParameters
Define os parâmetros escalares da graduação
GaugeSetNeedleParameters
Define os parâmetros da agulha
GaugeSetRangeParameters
Define os parâmetros do intervalo
GaugeSetScaleParameters
Define os parâmetros de escala

Tabela 1. Lista de funções

Vamos considerar cada função a fundo. Elas são representadas na ordem em que recomendamos em chamá-las ao inicializar.


3.1. GaugeCreate

Cria o medidor.

bool GaugeCreate(
   string name,              // Nome do medidor
   GAUGE_STR &g,             // Referência à estrutura do medidor
   int x,                    // Travessão horizontal a partir do canto âncora
   int y,                    // Travessão vertical a partir do canto âncora
   int size,                 // tamanho do medidor
   string ObjRel,            // Nome de um objeto gráfico relativo a sua posição que foi definida
   ENUM_REL_MODE rel_mode,   // Posicionamento relativo
   ENUM_BASE_CORNER corner,  // Canto âncora
   bool back,                // Objetos no fundo
   uchar scale_transparency, // Transparência da escala
   uchar needle_transparency // Transparência da agulha 
 );

Parâmetros

 name

   [Entrada] nome do medidor. Utilizado como um prefixo para os nomes dos objetos gráficos que compõem o medidor.

 g

   [Saída] Referência à estrutura do medidor.

 x

   [Entrada] Distância em pixels do canto âncora ao longo do eixo X. No caso de posicionamento relativo - distância do local de origem das coordenadas.

 y

   [Entrada] Distância em pixels do canto âncora ao longo do eixo Y. No caso de posicionamento relativo - distância do local de origem das coordenadas.

 size

   [Entrada] Tamanho do medidor. Representado como o diâmetro do corpo.

 ObjRel

   [Entrada] Nome de outro objeto gráfico relativo a posição que ele foi definido. Permanece pertinente somente se o posicionamento relativo é definido.

 rel_mode

   [Entrada] Método de posicionamento relativo. Pode ter qualquer valor listado na ENUM_REL_MODE.

 corner

   [Entrada] Canto Gráfico para ancorar o objeto gráfico. Pode ter qualquer valor listado na ENUM_BASE_CORNER.

 back

   [Entrada] Objetos no fundo.

 scale_transparency

   [Entrada] Nível de transparência da escala. Podem ter valores de 0 a 255.

 needle_transparency

   [Entrada] Nível de transparência da agulha. Podem ter valores de 0 a 255.

Valor de retorno

  Retorna true se os objetos da camada de escala e a camada da agulha forem criados. Caso contrário, retorna false.


3.2. GaugeSetCaseParameters

Define os parâmetros do corpo do medidor.

void GaugeSetCaseParameters(
   GAUGE_STR &g,                  // referência à estrutura do medidor
   ENUM_CASE_STYLE style,         // Tipo do corpo
   color ccol,                    // Cor do corpo
   ENUM_CASE_BORDER_STYLE bstyle, // Estilo da borda
   color bcol,                    // Cor da borda
   ENUM_SIZE border_gap_size      // Tamanho do espaço entre uma borda e os elementos de escala
);

Parâmetros

 g

   [Saída] Referência à estrutura do medidor.

 style

   [Entrada] Estilo do corpo. Pode ter qualquer valor listado na ENUM_CASE_STYLE.

 ccol

   [Entrada] Cor do corpo.

 bstyle

   [Entrada] Estilo da borda. Pode ter qualquer valor listado na ENUM_CASE_BORDER_STYLE.

 bcol

   [Entrada] Cor da borda.

 gap_size

   [Entrada] Área entre a linha interna da borda e o elemento mais próxima da escala ("g" na fig.3). Pode ter qualquer valor listado na ENUM_SIZE.


3.3. GaugeSetScaleParameters

Define os parâmetros da escala.

void GaugeSetScaleParameters(
   GAUGE_STR &g,           // Referência à estrutura do medidor
   int range,              // Faixa da escala
   int rotation,           // Ângulo de rotação
   double min,             // Valor mínimo (esquerda)
   double max,             // valor máximo (direita)
   ENUM_MUL_SCALE mul,     // Multiplicador das marcações da escala
   ENUM_SCALE_STYLE style, // Estilo da escala
   color col,              // Cor da escala
   bool display_arc        // Exibição da linha da escala
);

Parâmetros

 g

   [Saída] Referência à estrutura do medidor.

 range

   [Entrada] Faixa da escala. Definido como um ângulo contido por dois vetores de raio de marcações extremas de escala. Podem ter valores de 30-320 graus (Fig.5).

 rotation

   [Entrada] Ângulo de rotação da escala (Fig.6).

 min

   [Entrada] Valor mínimo da escala em caso de atribuição de um número direto.

 max

   [Entrada] O valor máximo da escala em caso de atribuição de um número direto.

 mul

   [Entrada] Multiplicador da marcações da escala. Pode ter qualquer valor listado na ENUM_MUL_SCALE.

 style

   [Entrada] Estilo da escala. Pode ter qualquer valor listado na ENUM_SCALE_STYLE.

 col

   [Entrada] Cores da escala.

 display_arc=false

   [Entrada] Exibe a linha de escala.


3.4. GaugeSetMarkParameters

Define as marcações da escala.

void GaugeSetMarkParameters(  
   GAUGE_STR &g,          // Referência à estrutura do medidor
   ENUM_MARK_STYLE style, // Estilo de marcas da escala
   ENUM_SIZE size,        // Tamanho das marcas
   double major_tmi,      // Intervalo da marcação principal
   int middle_tmarks,     // Número de marcações médias entre as principais
   int minor_tmarks       // Número de marcações menores entre as médias
);

Parâmetros

 g

   [Saída] Referência à estrutura do medidor.

 style

   [Entrada] Estilo da graduação da escala. Pode ter qualquer valor listado na ENUM_MARK_STYLE.

 size

   [Entrada] Tamanho da marcação. Pode ter qualquer valor listado na ENUM_SIZE.

 major_tmi

   [Entrada] Passo da graduação das marcações principais. As principais marcas são acompanhadas por etiquetas com valores relevantes.

 middle_tmarks

   [Entrada] Número de marcações médias entre as principais. Pode ter qualquer valor positivo. Não há restrições de tamanho. Se definido como 0, as marcações médias não são exibidas.

 minor_tmarks

   [Entrada] Número de marcações menores entre as médias (ou entre as as principais se as médias não forem apresentadas). Pode ter qualquer valor positivo. Não há restrições de tamanho. Se definido como 0, as marcações menores não são exibidas.


3.5. GaugeSetMarkLabelFont

Define a fonte da marcações de graduação.

void GaugeSetMarkLabelFont(
   GAUGE_STR &g,        // Referência à estrutura do medidor
   ENUM_SIZE font_size, // Tamanho da fonte 
   string font,         // Fonte
   bool italic,         // Itálico
   bool bold,           // Negrito
   color col            // Cor
);

Parâmetros

 g

   [Saída] Referência à estrutura do medidor.

 font_size

   [Entrada] Tamanho da fonte da marcações de graduação. Pode ter qualquer valor listado na ENUM_SIZE.

 font

   [Entrada] Fonte.

 italic

   [Entrada] Itálico.

 bold

   [Entrada] Negrito.

 col

   [Entrada] Cor da fonte.


3.6. GaugeSetLegendParameters

Define os parâmetros da legenda.

void GaugeSetLegendParameters(
   GAUGE_STR &g,         // referência à estrutura do medidor
   ENUM_GAUGE_LEGEND gl, // Tipo de legenda
   bool enable,          // Exibe legenda
   string str,           // String (ou um parâmetro complementar)
   int radius,           // Coordenadas - raio
   double angle,         // Coordenadas - ângulo
   uint font_size,       // Tamanho da fonte
   string font,          // Fonte
   bool italic,          // Itálico
   bool bold,            // Negrito
   color col             // Cor
);

Parâmetros

 g

   [Saída] Referência à estrutura do medidor

 gl

   [Entrada] Tipo da legenda. Pode ter qualquer valor listado na ENUM_GAUGE_LEGEND.

 enable

   [Entrada] Mostra a legenda.

 str

   [Entrada] Esta é uma string exibida para as legendas do tipo LEGEND_DESCRIPTION ou LEGEND_UNITS. Este parâmetro é ignorado para uma legenda do tipo LEGEND_MUL. Número de casas decimais para uma legenda do tipo LEGEND_VALUE. Podem ter valores de "0" a "8". Quaisquer outros valores são percebidos como "0". Por exemplo, a string "2" significa duas casas decimais. A cadeia "hello" significa 0 casas decimais.

 radius

   [Entrada] Raio. Distância do centro do medidor para o centro da legenda em unidades condicionais (fig. 9).

 angle

   [Entrada] Coordenadas angulares. O seu valor é igual ao ângulo entre a linha que vem de cima e na vertical do centro do medidor, e um segmento imaginário, que liga o centro do medidor e o centro da legenda (Fig.9).

 font_size

   [Entrada] Tamanho da fonte da legenda.

 font

   [Entrada] Fonte.

 italic

   [Entrada] Itálico.

 bold

   [Entrada] Negrito.

 col

   [Entrada] Cor da fonte.


3.7. GaugeSetRangeParameters

Define os parâmetros da faixa destacada.

void GaugeSetRangeParameters(
   GAUGE_STR &g, // Referência à estrutura do medidor
   int index,    // Índice do intervalo
   bool enable,  // Intervalo de exibição
   double start, // Valor inicial
   double end,   // Valor final
   color col     // Cor
);

Parâmetros

 g

   [Saída] Referência à estrutura do medidor.

 index

   [Entrada] ïndice do intervalo Podem ter valores de 0 a 3.

 enable

   [Entrada] Faixa de exibição.

 start

   [Entrada] Valor inicial.

 end

   [Entrada] Valor final.

 col

   [Entrada] Cor para realçar o intervalo.


3.8. GaugeSetNeedleParameters

Define os parâmetros da agulha.

void GaugeSetNeedleParameters(
   GAUGE_STR &g,                     // Referência à estrutura do medidor
   ENUM_NCENTER_STYLE ncenter_style, // Estilo do centro da agulha
   color ncenter_col,                // Cor do entro da agulha
   color needle_col,                 // Cor da agulha
   ENUM_NEEDLE_FILL needle_fill      // Método de preenchimento da área da agulha
);

Parâmetros

 g

   [Saída] Referência à estrutura do medidor.

 ncenter_style

   [Entrad] Estilo do centro da agulha. Pode ter qualquer valor listado na ENUM_NCENTER_STYLE.

 ncenter_col

   [Entrada] Cor do centro da agulha.

 needle_col

   [Entrada] Cor da agulha.

 needle_fill

   [Entrada] Método de preenchimento da área da agulha. Pode ter qualquer valor listado na ENUM_NEEDLE_FILL.


3.9. GaugeRedraw

Redesenha o medidor. A função tem de ser chamado depois de alterar quaisquer parâmetros para aplicar essas alterações.

void GaugeRedraw(
   GAUGE_STR &g       // Referência à estrutura do medidor
); 

Parâmetros

 g

   [Entrada] Referência à estrutura do medidor.


3.10. GaugeNewValue

Atualiza a posição da agulha e o valor apresentado.

void GaugeNewValue(
   GAUGE_STR &g,     // Referência à estrutura do medidor
   double v          // Valor da variável
);

Parâmetros

 g

   [Entrada] Referência à estrutura do medidor.

 v

   [Entrada] O valor atual da variável.


3.11. GaugeDelete

Exclui os objetos gráficos que compõem o medidor. Chame a função do manipulador OnDeinit().

void GaugeDelete(
   GAUGE_STR &g      // Referência à estrutura do medidor
);

Parâmetros

 g

   [Entrada] Referência à estrutura do medidor.


3.12. GaugeCalcLocation

Calcula as coordenadas dos objetos do medidor. Se o posicionamento relativo está desativado, ele sempre retornará as mesmas coordenadas. Caso contrário, as coordenadas podem ser diferentes dos valores anteriores se o objeto de referência mudou sua localização ou de tamanho.

bool GaugeCalcLocation( 
   GAUGE_STR& g         // Referência à estrutura do medidor
);

Parâmetros

 g

   [Entrada] Referência à estrutura do medidor.

Valor de retorno

  Retorna true se os valores das coordenadas diferir das anteriores. Caso contrário, retorna false. Se a função retorna true, chama a função GaugeRelocation() para aplicar os parâmetros calculados.


3.13. GaugeRelocation

Localiza os objetos gráficos que compõem o medidor no local especificado do gráfico. Necessário chamar se o posicionamento relativo é definido e a função GaugeCalcLocation() retornar true.

void GaugeRelocation(
   GAUGE_STR &g       // Referência à estrutura do medidor
);

Parâmetros

 g

   [Entrada] Referência à estrutura do medidor.


4. Enumerações

A Tabela 2 lista as enumerações usadas como parâmetros de função.

Enumeração
Descrição
ENUM_CASE_BORDER_STYLEEstilo de borda
ENUM_CASE_STYLE
Estilo do corpo
ENUM_GAUGE_LEGEND
Tipo de legenda
ENUM_MARK_STYLE
Estilo de graduação da escala
ENUM_MUL_SCALE
Multiplicador das marcações de graduação de escala
ENUM_NCENTER_STYLEEstilo do centro da agulha
ENUM_NEEDLE_FILLMétodo de preenchimento da área da agulha
ENUM_REL_MODEMétodo de posicionamento relativo
ENUM_SCALE_STYLEEstilo da escala
ENUM_SIZETamanho

Tabela 2. Lista de enumerações


4.1. ENUM_CASE_BORDER_STYLE

Estilo de borda. Os valores são listados na tabela 3.

Identificador
Descrição
CASE_BORDER_NONE
Sem borda
CASE_BORDER_THINBorda fina
CASE_BORDER_THICK
Borda espessa

Tabela 3. Valores de ENUM_CASE_BORDER_STYLE

4.2. ENUM_CASE_STYLE

Estilo do corpo. Os valores estão listados na tabela 4.

Identificador
Descrição
CASE_ROUND
Corpo Circular
CASE_SECTOR
Tipo do setor do corpo

Tabela 4. Valores de ENUM_CASE_STYLE

4.3. ENUM_GAUGE_LEGEND

Tipo de Legenda. Os valores estão listados na tabela 5.

Identificador
 Descrição
LEGEND_DESCRIPTIONDescrição do medidor
LEGEND_UNITSUnidades de medida
LEGEND_MULMultiplicador das marcações de escala
LEGEND_VALUEO valor atual da variável

Tabela 5. Valores de ENUM_GAUGE_LEGEND

4.4. ENUM_MARK_STYLE

Estilo de graduação da escala. Os valores estão listados na tabela 6.

Identificador
 Descrição
MARKS_INNERMarcações de alinhando pela borda interna
MARKS_MIDDLEMarcações de alinhando pelo centro
MARKS_OUTERMarcações de alinhando pela borda externa

Tabela 6. Valores de ENUM_MARK_STYLE

4.5. ENUM_MUL_SCALE

Multiplicador das marcações de graduação da escala. Os valores estão listados na tabela 7.

 IdentificadorSignificado
Exibição
MUL_1000010000
 х10k
MUL_10001000
 х1k
MUL_100100
 х100
MUL_1010
 х10
MUL_11
 Sem exibição
MUL_010.1
 /10
MUL_0010.01
 /100
MUL_00010.001
 /1k
MUL_000010.0001
 /10k

Tabela 7. Valores de ENUM_MUL_SCALE

4.6. ENUM_NCENTER_STYLE

Estilo do centro da agulha. Os valores estão listados na Tabela 8.

Identificador
Descrição
NDCS_NONENão exibe o centro da agulha
NDCS_SMALLExibição pequena
NDCS_LARGEExibição grande

Tabela 8. Valores de ENUM_NCENTER_STYLE

4.7. ENUM_NEEDLE_FILL

Método de preenchimento da área da agulha. Os valores estão listados na Tabela 9.

 Identificador Descrição
NEEDLE_FILLPreenche a agulha sem suavização das bordas
NEEDLE_FILL_AAPreencha a agulha com suavização das bordas
NEEDLE_NOFILL_AANão preenche a agulha, mas aplica a suavização de bordas

Tabela 9. Valores de ENUM_NEEDLE_FILL

4.8. ENUM_REL_MODE

Método de posicionamento relativo. Os valores estão listados na tabela 10.

 Identificador Descrição
RELATIVE_MODE_NONEO posicionamento relativo é desativado
RELATIVE_MODE_HORHorizontalmente
RELATIVE_MODE_VERTVerticalmente
RELATIVE_MODE_DIAGDiagonalmente

Tabela 10. Valores de ENUM_REL_MODE

4.9. ENUM_SCALE_STYLE

Estilo da escala Os valores estão listados na tabela 11.

Identificador
 Descrição
SCALE_INNEREtiquetas de graduação dentro da escala
SCALE_OUTEREtiquetas de graduação fora da escala

Tabela 11. Valores de ENUM_SCALE_STYLE

4.10. ENUM_SIZE

Tamanho. Os valores estão listados na tabela 12.

Identificador
 Descrição
SIZE_SMALLPequeno
SIZE_MIDDLEMédio
SIZE_LARGEGrande

Tabela 12. Valores de ENUM_SIZE


5. Substituição de Macro

Coeficientes para os tamanhos:

#define DIAM_TO_NDCSL_RATIO   5   // Diâmetro do centro da agulha (pequeno) como percentual do diâmetro do corpo
#define DIAM_TO_NDCSB_RATIO   7.5 // Diâmetro do centro da agulha (grande) como percentagem do diâmetro do corpo
//---
#define DIAM_TO_BD_SIZE_S     2 // Largura da borda (pequeno) como percentual do diâmetro do corpo
#define DIAM_TO_BD_SIZE_B     5 // Largura da borda (grande) como percentual do diâmetro do corpo
//---
#define DIAM_TO_BD_GAP_S      2.0 // Espaço da borda do corpo de elementos interiores do medidor (pequena) como percentagem do diâmetro do corpo
#define DIAM_TO_BD_GAP_M      3.0 // Espaço da borda do corpo de elementos interiores do medidor (médio ), como percentagem do diâmetro do corpo
#define DIAM_TO_BD_GAP_L      7.0 // Espaço da borda do corpo de elementos interiores do medidor (grande) como percentagem do diâmetro do corpo
//---
#define DIAM_TO_MSZ_MJ_S      3.3 // Tamanho da graduação da escala principal (pequena) em percentagem do diâmetro do corpo
#define DIAM_TO_MSZ_MD_S      2.3 // Tamanho da graduação da escala média (pequena) como percentagem do diâmetro do corpo
#define DIAM_TO_MSZ_MN_S      1.3 // Tamanho da graduação da escala menor (pequena) como percentual do diâmetro do corpo
//---
#define DIAM_TO_MSZ_MJ_M      6.5 // Tamanho da graduação da escala principal (média) como percentual do diâmetro do corpo
#define DIAM_TO_MSZ_MD_M      4.8 // Tamanho da graduação da escala média (média) como percentual do diâmetro do corpo
#define DIAM_TO_MSZ_MN_M      3.0 // Tamanho da graduação da escala menor (média) como percentual do diâmetro do corpo
//---
#define DIAM_TO_MSZ_MJ_L      10.0 // Tamanho da graduação da escala principal (grande) como percentagem do diâmetro do corpo
#define DIAM_TO_MSZ_MD_L      7.5  // Tamanho da graduação da escala média (grande) como percentagem do diâmetro do corpo
#define DIAM_TO_MSZ_MN_L      5.0  // Tamanho da graduação da escala menor (grande) como percentual do diâmetro do corpo
//---
#define DIAM_TO_MFONT_SZ_S    4   // Tamanho da fonte da graduação da escala das marcações (pequenas) como percentagem do diâmetro do corpo
#define DIAM_TO_MFONT_SZ_M    6.5 // Tamanho da fonte da graduação da escala das marcações (média) como percentagem do diâmetro do corpo
#define DIAM_TO_MFONT_SZ_L    10  // Tamanho da fonte da graduação da escala das marcações (grande) como percentagem do diâmetro do corpo

Cores padrão:

#define DEF_COL_SCALE      clrBlack
#define DEF_COL_MARK_FONT  clrBlack
#define DEF_COL_CASE       clrMintCream
#define DEF_COL_BORDER     clrLightSteelBlue
#define DEF_COL_LAB        clrDarkGray
#define DEF_COL_NCENTER    clrLightSteelBlue
#define DEF_COL_NEEDLE     clrDimGray


6. Modificando a Classe CCanvas


6.1. Desenhando um Segmento Usando o Algoritmo de Anti-Aliasing

O método LineAA permite desenhar um segmento usando o algoritmo de suavização. Mas um problema aparece quando tiramos as marcações da escala circular. Quando nós convertemos as coordenadas dos pontos iniciais e finais do segmento do sistema de coordenada polar para a cartesiana, tivemos números fracionários que arredondamos para um número inteiro. Consequentemente as marcas pareceram curvadas, o qual é mostrado na Fig.11 (b).

É por isso que nós adicionamos o método LineAA2, que difere de LineAA pelo fato do tipo dos parâmetros de entrada x1, y1, x2, y2 terem alterados para o dobro. Assim, podemos entregar valores fracionários de coordenadas e se livrar do problema mencionado, que é vividamente visto na figura 11(c).

//+------------------------------------------------------------------+
//| Desenha a Linha com suavização (com estilo) v.2                  |
//+------------------------------------------------------------------+
void CCanvas::LineAA2(const double x1,const double y1,const double x2,const double y2,const uint clr,const uint style)
  {
//--- Linha está fora dos limites da imagem
   if((x1<0 && x2<0) || (y1<0 && y2<0))
      return;
   if(x1>=m_width && x2>=m_width)
      return;
   if(y1>=m_height && y2>=m_height)
      return;
//--- Verifica
   if(x1==x2 && y1==y2)
     {
      PixelSet(int(x1),int(y1),clr);
      return;
     }
//--- Definir o estilo de linha
   if(style!=UINT_MAX)
      LineStyleSet(style);
//--- Cálculos preliminares
   double dx=x2-x1;
   double dy=y2-y1;
   double xy=sqrt(dx*dx+dy*dy);
   double xx=x1;
   double yy=y1;
   uint   mask=1<<m_style_idx;
//--- Define os pixels
   dx/=xy;
   dy/=xy;
   do
     {
      if((m_style&mask)==mask)
         PixelSetAA(xx,yy,clr);
      xx+=dx;
      yy+=dy;
      mask<<=1;
      if(mask==0x1000000)
         mask=1;
     }
   while(fabs(x2-xx)>=fabs(dx) && fabs(y2-yy)>=fabs(dy));
  } 

Fig.11 ilustra os exemplos de vários métodos de marcas da escala de desenho:

Método de marcações da escala do desenho

Fig. 11. Vários métodos de desenhar as marcações da escala (um aumento de 200%)


6.2. Preenchendo uma Área com Arestas Anti-Aliasing

O método Fill é destinado a preencher uma área delimitada por segmentos desenhados sem o uso de algoritmo anti-aliasing. Se nós utilizarmos este método para o preenchimento de uma zona delimitada por segmentos desenhados pelo método LineAA, a área será preenchida de forma incompleta, o que é visto na Fig.12 (a).

Preenchendo uma Área com Arestas Anti-Aliasing

Fig.12. Preenchendo uma área com bordas suavizadas (aumento de 200%)

Então, nós adicionamos o método Fill2. A diferença é que ele não preenche a cor de fundo, mas qualquer cor diferente da cor dos segmentos que se ligam a área. Ela permite preencher um meio-tom, que não pode ser feito usando o método Fill. Fig.12 (b) mostra um exemplo.

//+------------------------------------------------------------------+
//| Preenche uma região fechada com cor (v.2)                        |
//+------------------------------------------------------------------+
void CCanvas::Fill2(int x,int y,const uint clr)
  {
//--- Verifica
   if(x<0 || x>=m_width || y<0 || y>=m_height)
      return;
//---
   int  index=y*m_width+x;
   uint old_clr=m_pixels[index];
//--- Verifica se a substituição é necessária
   if(old_clr==clr)
      return;
//--- Usa um pseudo empilhamento para emular as chamadas recursivas muito aninhadas
   int  stack[];
   uint count=1;
   int  idx;
   int  total=ArraySize(m_pixels);
//--- Aloca memória para a pilha
   if(ArrayResize(stack,total)==-1)
      return;
   stack[0]=index;
   m_pixels[index]=clr;
   for(uint i=0;i<count;i++)
     {
      index=stack[i];
      x=index%m_width;
      //--- Ponto adjacente da esquerda
      idx=index-1;
      if(x>0 && m_pixels[idx]!=clr)
        {
         stack[count]=idx;
         if(m_pixels[idx]==old_clr)
            count++;
         m_pixels[idx]=clr;
        }
      //--- Ponto adjacente superior
      idx=index-m_width;
      if(idx>=0 && m_pixels[idx]!=clr)
        {
         stack[count]=idx;
         if(m_pixels[idx]==old_clr)
            count++;
         m_pixels[idx]=clr;
        }
      //--- Ponto adjacente à direita
      idx=index+1;
      if(x<m_width-1 && m_pixels[idx]!=clr)
        {
         stack[count]=idx;
         if(m_pixels[idx]==old_clr)
            count++;
         m_pixels[idx]=clr;
        }
      //--- Ponto adjacente inferior
      idx=index+m_width;
      if(idx<total && m_pixels[idx]!=clr)
        {
         stack[count]=idx;
         if(m_pixels[idx]==old_clr)
            count++;
         m_pixels[idx]=clr;
        }
     }
//--- Desalocar memória
   ArrayFree(stack);
  }  

No entanto, este método também tem desvantagens. Se há um pequeno ângulo agudo, a sua parte permanecerá não preenchida, o qual é mostrado na Fig.12 (c). Então, nós encontramos uma maneira de sair deste problema.

   1) Primeiro todo a tela é preenchida (camada de agulha) com a cor significativa para a agulha:

n.canvas.Fill(10, 10, ColorToARGB(n.needle.c, n.transparency));

   2) Em seguida, desenhamos a agulha composta por três segmentos usando o método LineAA2:

n.canvas.LineAA2(db_xbuf[0], db_ybuf[0], db_xbuf[1], db_ybuf[1], 0);
n.canvas.LineAA2(db_xbuf[1], db_ybuf[1], db_xbuf[2], db_ybuf[2], 0);
n.canvas.LineAA2(db_xbuf[2], db_ybuf[2], db_xbuf[0], db_ybuf[0], 0);

  3) Após isso, preencher a área em torno da agulha com a cor transparente usando o método Fill2:

n.canvas.Fill2(10, 10, 0);

Este método não é o melhor, mas permite desenhar a agulha adequada.

Métodos de preenchimento da área da agulha

Fig.13. Agulhas preenchidas usando métodos diferentes

Fig.13 descreve agulhas cheias usando métodos diferentes.

  • a) A agulha composta por três segmentos desenhados utilizando o método LineAA2 e preenchido com o método Fill2.
  • b) A agulha desenhada através do método FillTriangle.
  • c) A agulha não preenchida composta por três segmentos desenhados usando o método LineAA2.

Como podemos ver, a agulha mostrada na fig.13 (b) é escarpada e tem pequenos desvios a partir dos ângulos divisíveis por 90 graus. Além disso, podemos ver que a agulha é deslocada do centro, que é causada pelo arredondamento dos valores das coordenadas quando convertê-mos o sistema de coordenadas de polar para cartesiana. Mas, ao mesmo tempo, este método é o mais prático no contexto da intensidade de recursos (voltaremos a este assunto mais tarde). A agulha mostrada na fig.13 (c) é uma compensação dos dois métodos descritos acima. Ele é composto por três segmentos desenhados utilizando o método LineAA2 mas sem preenchimento da área.


7. Exemplos de Aplicação

Vamos rever a aplicação da biblioteca do medidor através de vários exemplos.


7.1. Indicador de Lucro Atual

Vamos começar com o mais simples. O exemplo a seguir demonstra o conjunto básico para adicionar o medidor a um EA ou um indicador.

//+------------------------------------------------------------------+
//|                                       profit_gauge_indicator.mq5 |
//|                                         Copyright 2015, Decanium |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Decanium"
#property version   "1.00"
#property indicator_plots 0
#property indicator_chart_window

#include <Gauge\gauge_graph.mqh>

input string inp_gauge_name="gg01";                  // Nome do Indicador
input int inp_x = 40;                                // Deslocamento Horizontal
input int inp_y = 40;                                // Deslocamento Vertical
input int inp_size=300;                              // Tamanho do Indicador
input string inp_ObjRel="";                          // Nome do indicador de base em caso de posicionamento relativo
input ENUM_REL_MODE inp_rel_mode=RELATIVE_MODE_NONE; // Modo de posicionamento relativo
input ENUM_BASE_CORNER inp_corner=CORNER_LEFT_UPPER; // Canto âncora
input bool inp_back=false;                           // Indicador está em segundo plano
input uchar inp_scale_transparency=0;                // Nível de transparência da escala, 0..255
input uchar inp_needle_transparency=0;               // Nível de transparência da agulha, 0..255

// --- Declaração da estrutura do medidor
GAUGE_STR g0;
//+------------------------------------------------------------------+
//| Função de inicialização do indicador personalizado               |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Criando o medidor
   if(GaugeCreate(inp_gauge_name,g0,inp_x,inp_y,inp_size,inp_ObjRel,inp_rel_mode,
      inp_corner,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- Desenho do medidor
   GaugeRedraw(g0);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Função de desinicialização do indicador personalizado            |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Exclusão do medidor
   GaugeDelete(g0);
   ChartRedraw();
  }    
//+------------------------------------------------------------------+
//| Função de iteração do indicador personalizado                    |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
//--- Atualização das leituras
   double profit=AccountInfoDouble(ACCOUNT_PROFIT);
   GaugeNewValue(g0,profit);
//---
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Função ChartEvent                                                |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//---
   if(id==CHARTEVENT_CHART_CHANGE)
     {
      if(GaugeCalcLocation(g0)==true)
         GaugeRelocation(g0);
     }
  }
//+------------------------------------------------------------------+

Primeiro, precisamos declarar a estrutura do medidor. Em seguida, continuamos com a função de inicialização onde criamos o medidor usando a função GaugeCreate() e chamamos a função de desenho - GaugeRedraw(). GaugeNewValue() pode ser usado para atualizar as leituras. Neste exemplo, ele é chamado do manipulador OnCalculate().

O medidor vai parecer como mostrado na fig.14.

Aparência do indicador, parâmetros padrão

Fig.14. Aparência padrão do medidor

Agora vamos adicionar a capacidade de definir o intervalo de escala e o ângulo de rotação. Ele irá adicionar dois parâmetros para a lista de entradas.

input int inp_scale_range=270;   // Faixa de escala, 30..320 graus
input int inp_rotation=45;       // Escala da rotação, 0..359 graus

Agora nós estendemos o código de inicialização com a chamada da função para definir os parâmetros de escala.

//--- Parâmetros de ajuste da escala e marcas na escala
   GaugeSetScaleParameters(g0,inp_scale_range,inp_rotation,-200,400,MUL_1,SCALE_INNER,clrBlack);

Nós também iremos definir novos parâmetros de entrada complementares:

  • nova valores máximos e mínimos (-200 e 400, respectivamente)
  • multiplicador de marcas de graduação da escala (MUL_1)
  • estilo da escala (SCALE_INNER - etiquetas de graduação internas)
  • cor das etiquetas (clrBlack)

À medida que forem alterados os valores extremos da escala, é desejável corrigir um passo da marcação principal. O melhor valor é de 100, já que exclui a abundância do texto. Naquele nós vamos colocar uma marcação média entre os dois principais e os quatro menores entre as duas marcas médias. Assim teremos um passo mínimo entre as marcas iguais a 10.

   GaugeSetMarkParameters(g0,MARKS_INNER,SIZE_MIDDLE,100,1,4);

Agora vamos destacar dois intervalos de dados. O intervalo com índice 0, que começa com 200 e termina com 400, será destacado com a cor clrLimeGreen. O intervalo com índice 1, que começa com -100 e termina com -200, será destacado com a cor clrCoral.

//--- Intervalos de destaque na escala
   GaugeSetRangeParameters(g0,0,true,200,400,clrLimeGreen);
   GaugeSetRangeParameters(g0,1,true,-100,-200,clrCoral);

Agora nós vamos definir as legendas. Nós determinamos a descrição do medidor, unidades de medida e o valor atual com uma casa decimal. Vamos revisá-las uma por uma.

Descrição do medidor

   GaugeSetLegendParameters(g0,LEGEND_DESCRIPTION,true,"Profit",3,0,14,"Arial",false,false);

Exibição do texto "Profit", raio é 3, ângulo é 0, fonte é 14 unidades condicionais.

Unidades de medida:

   GaugeSetLegendParameters(g0,LEGEND_UNITS,true,"USD",8,215,10,"Arial",true,false);

Exibição do texto "USD", raio é 8, ângulo é 215, fonte é 10 unidades condicionais.

Valor atual:

   GaugeSetLegendParameters(g0,LEGEND_VALUE,true,"1",4,225,20,"Arial",true,false);

Aqui o texto "1", significa a forma de exibição (uma casa decimal). Coordenadas: raio é 4, o ângulo é de 255. Tamanho da fonte é de 20 unidades condicionais.

Assim, depois de ter realizado algumas configurações adicionais, o medidor vai parecer como mostrado na fig.15.

Indicador de Lucro Atual

Fig.15. Aparência do medidor após ajuste adicional


7.2. O Indicador de Painel

Agora nós vamos rever o exemplo mais complicado, ou seja, o Painel indicador. Ele é mostrado na fig.1. O indicador mostra o lucro atual, spread, margem livre como percentuais e atuais valores de ATR, Índice de Força e RSI.

Primeiro vamos declarar o array de estrutura do medidor. 

//--- Declaração do array de estrutura do medidor
GAUGE_STR gg[6];

Então, vamos criar e ajustar o medidor.

O indicador do nível da margem será colocado no canto inferior esquerdo. Ela terá coordenadas absolutas, e todos os outros indicadores serão localizados em relação a este indicador ou a um vizinho.

//--- Construindo o medidor gg00, nível de margem
   if(GaugeCreate("gg00",gg[0],5,-90,240,"",RELATIVE_MODE_NONE,
      CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- Definindo os parâmetros do corpo
   GaugeSetCaseParameters(gg[0],CASE_SECTOR,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- Parâmetros de ajuste da escala e marcas na escala
   GaugeSetScaleParameters(gg[0],120,35,800,2000,MUL_100,SCALE_INNER,clrBlack);
   GaugeSetMarkParameters(gg[0],MARKS_INNER,SIZE_MIDDLE,200,1,4);
   GaugeSetMarkLabelFont(gg[0],SIZE_MIDDLE,"Arial",false,false,DEF_COL_MARK_FONT);
//--- Intervalos de destaque na escala
   GaugeSetRangeParameters(gg[0],0,true,1400,2000,clrLimeGreen);
   GaugeSetRangeParameters(gg[0],1,true,1000,800,clrCoral);
//--- Configuração dos rótulos de texto
   GaugeSetLegendParameters(gg[0],LEGEND_DESCRIPTION,true,"Margin lvl",4,15,12,"Arial",false,false);
   GaugeSetLegendParameters(gg[0],LEGEND_VALUE,true,"0",3,80,16,"Arial",true,false);
   GaugeSetLegendParameters(gg[0],LEGEND_MUL,true,"",4,55,8,"Arial",true,false);
//--- Configuração dos Parâmetros da agulha
   GaugeSetNeedleParameters(gg[0],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

Continuamos organizando a linha inferior. O próximo é o indicador de lucro atual.

//--- Construindo o medidor gg01, o lucro atual
   if(GaugeCreate("gg01",gg[1],-80,20,320,"gg00",RELATIVE_MODE_HOR,
      CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- Definindo os parâmetros do corpo
   GaugeSetCaseParameters(gg[1],CASE_SECTOR,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- Parâmetros de ajuste da escala e marcas na escala
   GaugeSetScaleParameters(gg[1],200,0,-400,400,MUL_1,SCALE_INNER,clrBlack);
   GaugeSetMarkParameters(gg[1],MARKS_INNER,SIZE_MIDDLE,100,1,4);
   GaugeSetMarkLabelFont(gg[1],SIZE_MIDDLE,"Arial",false,false,DEF_COL_MARK_FONT);
//--- Intervalos de destaque na escala
   GaugeSetRangeParameters(gg[1],0,true,200,400,clrLimeGreen);
   GaugeSetRangeParameters(gg[1],1,true,-200,-400,clrCoral);
//--- Configuração dos rótulos de texto
   GaugeSetLegendParameters(gg[1],LEGEND_DESCRIPTION,true,"Profit",3,0,16,"Arial",false,false);
   GaugeSetLegendParameters(gg[1],LEGEND_UNITS,true,"USD",3,-90,10,"Arial",true,false);
   GaugeSetLegendParameters(gg[1],LEGEND_VALUE,true,"1",3,90,12,"Arial",true,false);
//--- Configuração dos Parâmetros da agulha
   GaugeSetNeedleParameters(gg[1],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

O indicador de spread fecha a linha de fundo.

//--- Construção do medidor gg02, spread
   if(GaugeCreate("gg02",gg[2],-80,-20,240,"gg01",RELATIVE_MODE_HOR,
      CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- Definindo os parâmetros do corpo
   GaugeSetCaseParameters(gg[2],CASE_SECTOR,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- Parâmetros de ajuste da escala e marcas na escala
   GaugeSetScaleParameters(gg[2],120,-35,60,0,MUL_1,SCALE_INNER,clrBlack);
   GaugeSetMarkParameters(gg[2],MARKS_INNER,SIZE_MIDDLE,10,1,4);
   GaugeSetMarkLabelFont(gg[2],SIZE_MIDDLE,"Arial",false,false,DEF_COL_MARK_FONT);
//--- Intervalos de destaque na escala
   GaugeSetRangeParameters(gg[2],0,true,35,10,clrLimeGreen);
   GaugeSetRangeParameters(gg[2],1,true,50,60,clrCoral);
//--- Configuração dos rótulos de texto
   GaugeSetLegendParameters(gg[2],LEGEND_DESCRIPTION,true,"Spread",4,-15,14,"Arial",false,false);
   GaugeSetLegendParameters(gg[2],LEGEND_VALUE,true,"0",3,-80,16,"Arial",true,false);
//--- Configuração dos Parâmetros da agulha
   GaugeSetNeedleParameters(gg[2],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

O indicador ATR (a esquerda na fila superior) é colocado relativamente ao indicador de margem livre.

// --- Construção do medidor gg03, ATR
   if(GaugeCreate("gg03",gg[3],30,0,180,"gg00",RELATIVE_MODE_VERT,
      CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- Definindo os parâmetros do corpo
   GaugeSetCaseParameters(gg[3],CASE_ROUND,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- Parâmetros de ajuste da escala e marcas na escala
   GaugeSetScaleParameters(gg[3],270,45,0.001,0.004,MUL_00001,SCALE_INNER,clrBlack);
   GaugeSetMarkParameters(gg[3],MARKS_INNER,SIZE_LARGE,0.001,9,3);
   GaugeSetMarkLabelFont(gg[3],SIZE_LARGE,"Arial",false,false,DEF_COL_MARK_FONT);
//--- Intervalos de destaque na escala
   GaugeSetRangeParameters(gg[3],0,true,0.002,0.001,clrYellow);
//--- Configuração dos rótulos de texto
   GaugeSetLegendParameters(gg[3],LEGEND_DESCRIPTION,true,"ATR",7,-140,26,"Arial",false,false);
//GaugeSetLegendParameters(gg[3],LEGEND_UNITS,true,"USD",8,180,5,"Arial",true,false);
   GaugeSetLegendParameters(gg[3],LEGEND_VALUE,true,"5",2,180,14,"Arial",true,false);
   GaugeSetLegendParameters(gg[3],LEGEND_MUL,true,"",2,0,20,"Arial",true,false);
//--- Configuração dos Parâmetros da agulha
   GaugeSetNeedleParameters(gg[3],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

O indicador RSI é colocado relativamente ao indicador de spread (acima).

//--- Construção do medidor gg04, RSI
   if(GaugeCreate("gg04",gg[4],-30,0,180,"gg02",RELATIVE_MODE_VERT,
      CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- Definindo os parâmetros do corpo
   GaugeSetCaseParameters(gg[4],CASE_ROUND,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- Parâmetros de ajuste da escala e marcas na escala
   GaugeSetScaleParameters(gg[4],270,45,0,100,MUL_10,SCALE_INNER,clrBlack);
   GaugeSetMarkParameters(gg[4],MARKS_INNER,SIZE_LARGE,10,1,4);
   GaugeSetMarkLabelFont(gg[4],SIZE_LARGE,"Arial",false,false,DEF_COL_MARK_FONT);
//--- Configuração dos rótulos de texto
   GaugeSetLegendParameters(gg[4],LEGEND_DESCRIPTION,true,"RSI",7,-140,26,"Arial",false,false);
   GaugeSetLegendParameters(gg[4],LEGEND_VALUE,true,"2",2,180,16,"Arial",true,false);
   GaugeSetLegendParameters(gg[4],LEGEND_MUL,true,"",2,0,20,"Arial",true,false);
//--- Configuração dos Parâmetros da agulha
   GaugeSetNeedleParameters(gg[4],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

O indicador Índice de Força é colocado acima do indicador de lucro atual.

//--- Constuindo medidor gg05, Force
   if(GaugeCreate("gg05",gg[5],-10,60,180,"gg03",RELATIVE_MODE_HOR,
      CORNER_LEFT_LOWER,inp_back,inp_scale_transparency,inp_needle_transparency)==false)
      return(INIT_FAILED);
//--- Definindo os parâmetros do corpo
   GaugeSetCaseParameters(gg[5],CASE_ROUND,DEF_COL_CASE,CASE_BORDER_THIN,DEF_COL_BORDER,SIZE_MIDDLE);
//--- Parâmetros de ajuste da escala e marcas na escala
   GaugeSetScaleParameters(gg[5],270,45,-4,4,MUL_1,SCALE_INNER,clrBlack);
   GaugeSetMarkParameters(gg[5],MARKS_INNER,SIZE_LARGE,1,1,4);
   GaugeSetMarkLabelFont(gg[5],SIZE_LARGE,"Arial",false,false,DEF_COL_MARK_FONT);
//--- Intervalos de destaque na escala
   GaugeSetRangeParameters(gg[5],0,true,-1,-4,clrMediumSeaGreen);
   GaugeSetRangeParameters(gg[5],1,true,1,4,clrCrimson);
//--- Configuração dos rótulos de texto
   GaugeSetLegendParameters(gg[5],LEGEND_DESCRIPTION,true,"Force",7,-140,20,"Arial",false,false);
   GaugeSetLegendParameters(gg[5],LEGEND_VALUE,true,"5",2,180,14,"Arial",true,false);
   GaugeSetLegendParameters(gg[5],LEGEND_MUL,true,"",3,0,10,"Arial",true,false);
//--- Configuração dos Parâmetros da agulha
   GaugeSetNeedleParameters(gg[5],NDCS_SMALL,DEF_COL_NCENTER,DEF_COL_NEEDLE,NEEDLE_FILL_AA);

Medidores podem ser desenhados de maneira cíclica.

//--- Desenhando Medidores
   for(int i=0; i<6;i++)
     {
      GaugeRedraw(gg[i]);
      GaugeNewValue(gg[i],0);
     }

Quando o evento OnCalculate() ocorre, nós recalculamos os valores atuais e chamamos a função GaugeNewValue() para cada indicador.

//--- Atualização das leituras
//--- Spread
   GaugeNewValue(gg[2],spread[rates_total-1]);
//--- Lucro atual   
   double profit=AccountInfoDouble(ACCOUNT_PROFIT);
   GaugeNewValue(gg[1],profit);
//--- Nível da margem
   double margin_level=AccountInfoDouble(ACCOUNT_MARGIN_LEVEL);
   GaugeNewValue(gg[0],margin_level);
//--- O indicador de ATR
   calculated=BarsCalculated(handle_ATR);
   if(calculated>0)
     {
      double ival[1];
      if(CopyBuffer(handle_ATR,0,0,1,ival)<0)
         Print("ATR CopyBuffer error");
      else
         GaugeNewValue(gg[3],ival[0]);
     }
//--- O indicador RSI
   calculated=BarsCalculated(handle_RSI);
   if(calculated>0)
     {
      double ival[1];
      if(CopyBuffer(handle_RSI,0,0,1,ival)<0)
         Print("RSI CopyBuffer error");
      else
         GaugeNewValue(gg[4],ival[0]);
     }
//--- O indicador índice de força
   calculated=BarsCalculated(handle_Force);
   if(calculated>0)
     {
      double ival[1];
      if(CopyBuffer(handle_Force,0,0,1,ival)<0)
         Print("Force Index CopyBuffer error");
      else
         GaugeNewValue(gg[5],ival[0]);
     }

Por favor, note que não há nenhum ponto para chamar a função GaugeRelocation() do evento OnChartEvent() no exemplo dado. Embora o posicionamento relativo é utilizado aqui, não precisamos recalcular as coordenadas dos medidores se a posição ou o tamanho de um deles mudoar, já que os medidores são inicializados todos de uma vez.


8. Avaliação da Intensidade dos Recursos

A camada de agulha está totalmente redesenhando sempre que ocorrem uma atualização nas leituras. Pode acontecer com bastante frequência, mesmo várias vezes por segundo em alguns casos. É por isso que o problema da intensidade de recursos de desenho da agulha é bastante aguda. Vamos escrever um pequeno script para avaliar a sobrecarga do CPU para desenhar a agulha usando vários métodos de preenchimento da área.

//+------------------------------------------------------------------+
//|                                                    test_fill.mq5 |
//|                                         Copyright 2015, Decanium |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Decanium"
#property version   "1.00"

#include <Canvas/Canvas2.mqh>

CCanvas canvas;
//+------------------------------------------------------------------+
//| Script da função de início do programa                           |
//+------------------------------------------------------------------+
void OnStart()
  {
   Print("***** start test *****");
//---
   string ObjName="test";
   ObjectDelete(0,ObjName);
   canvas.CreateBitmapLabel(ObjName,10,10,400,400,COLOR_FORMAT_ARGB_NORMALIZE);
//---
   int x[3]={200,185,215};
   int y[3]={70, 250,250};
   int cycles=1000;
   uint col=ColorToARGB(clrRed,255);
   uint c1,c2;
//--- Testando o preenchimento da área de arestas suavizadas
   canvas.Erase();
   c1=GetTickCount();
   for(int i=0; i<cycles; i++)
     {
      canvas.Fill(10, 10, col);
      canvas.LineAA2(x[0], y[0], x[1], y[1], 0);
      canvas.LineAA2(x[1], y[1], x[2], y[2], 0);
      canvas.LineAA2(x[2], y[2], x[0], y[0], 0);
      canvas.Fill2(10, 10, 0);
     }
   c2=GetTickCount();
   canvas.Update(true);
   Print("Filled AA: ",c2-c1," ms, ",cycles," cycles, ",
         DoubleToString(double(c2-c1)/double(cycles),2)," ms per cycle");
//--- Testando o contorno anti-aliasing sem preencher
   canvas.Erase();
   c1=GetTickCount();
   for(int i=0; i<cycles; i++)
     {
      canvas.LineAA2(x[0], y[0], x[1], y[1], col);
      canvas.LineAA2(x[1], y[1], x[2], y[2], col);
      canvas.LineAA2(x[2], y[2], x[0], y[0], col);
     }
   c2=GetTickCount();
   canvas.Update(true);
   Print("Not filled AA: ",c2-c1," ms, ",cycles," cycles, ",
         DoubleToString(double(c2-c1)/double(cycles),2)," ms per cycle");
//--- Testando o preenchimento da área sem suavização
   canvas.Erase();
   c1=GetTickCount();
   for(int i=0; i<cycles; i++)
     {
      canvas.FillTriangle(x[0],y[0],x[1],y[1],x[2],y[2],col);
      canvas.LineAA2(x[0], y[0], (x[1]+x[2])/2, y[1], col);
     }
   c2=GetTickCount();
   canvas.Update(true);
   Print("Filled: ",c2-c1," ms, ",cycles," cycles, ",
         DoubleToString(double(c2-c1)/double(cycles),2)," ms per cycle");
  }
//+------------------------------------------------------------------+

O script lança cada método de desenho da agulha 1.000 vezes em um ciclo e mede o tempo gasto para este processo em milissegundos.

Teste da intensidade de recursos

Fig.16. Os resultados do teste de intensidade de recursos

Como você pode ver pelos resultados, a agulha preenchido com bordas suavizadas é desenhada centenas de vezes mais do que a agulha cheia sem anti-aliasing e dezenas de vezes mais do que apenas uma linha de contorno suavizada sem preencher. Neste caso, a beleza realmente tem o seu preço.


Conclusão

Neste artigo, nós analisamos o conjunto de funções para desenhar medidores. O principal alvo da criação da biblioteca foi a simplicidade de adicionar medidores para um EA ou um indicador sem se aprofundar nos detalhes do desenho e geometria. No entanto, cabe a você decidir se atingimos essa meta.

Especial atenção deve ser dada à intensidade dos recursos. Cálculos demorados no manipulador OnCalculate() podem causar a suspensão do terminal. Então, nós recomendamos a aplicação do método de desenhar a agulha suavizada sem preenchimento.

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

Arquivos anexados |
dashboard.mq5 (22.69 KB)
canvas2.mqh (171.52 KB)
gauge_graph.mqh (154.69 KB)
Otimização. Algumas idéias simples Otimização. Algumas idéias simples
O processo de otimização pode exigir recursos significativos de seu computador ou mesmo dos agentes de teste da MQL5 Cloud Network. Este artigo compreende algumas idéias simples que eu uso para a facilitação do trabalho e a melhoria do Strategy Tester do MetaTrader 5. Eu tive essas idéias a partir da documentação, fórum e artigos.
Redes Neurais de Terceira Geração: Redes Profundas Redes Neurais de Terceira Geração: Redes Profundas
Este artigo é dedicado a uma nova perspectiva na direção da aprendizagem de máquina - o aprendizado profundo ou, para ser mais preciso, redes neurais profundas. Esta é uma breve revisão das redes neurais de segunda geração, a arquitetura de suas conexões e tipos principais, os métodos e regras de aprendizagem e suas principais desvantagens seguido pela história do desenvolvimento da rede neural de terceira geração, os seus principais tipos, peculiaridades e métodos de treinamento. Conduzida por experimentos práticos sobre a construção e treinamento de uma rede neural profunda, iniciada pelos pesos de uma pilha de autoencoders (Stacked Autoencoders) contendo dados reais. Todas as etapas, desde a seleção dos dados de entrada até a derivação métrica, serão discutidas em detalhe. A última parte do artigo contém uma implementação de um programa de rede neural profunda em um Expert Advisor com um indicador embutido, baseado em MQL4/R.
Identificando Setups de Negociação pelo Suporte, Resistência e Ação do Preço Identificando Setups de Negociação pelo Suporte, Resistência e Ação do Preço
Este artigo mostra como a ação do preço (Price Action) e a monitorização dos níveis de suporte e resistência podem ser usados para a entrada no mercado no timing correto. Discute-se um sistema de negociação que combina eficazmente os dois para a determinação de setups de negociação. O código em MQL4 correspondente é explicado, podendo ser utilizado nos EAs baseados nestes conceitos de negociação.
Florestas Aleatórias na Previsão das Tendências Florestas Aleatórias na Previsão das Tendências
Este artigo considera o uso do pacote Rattle na busca automática de padrões para prever as posições compradas ou vendidas dos pares de moedas no Forex. Este artigo pode ser útil tanto para novatos quanto para profissionais experientes.