English Русский 中文 Español Deutsch 日本語
Interfaces Gráficas X: Atualizações para a tabela Renderizada e otimização do código (build 10)

Interfaces Gráficas X: Atualizações para a tabela Renderizada e otimização do código (build 10)

MetaTrader 5Exemplos | 11 abril 2017, 10:45
807 0
Anatoli Kazharski
Anatoli Kazharski

Conteúdo

 

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ê irã 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.

Nós continuamos a complementar a tabela renderizada (CCanvasTable) com novas funcionalidades. Os seguintes recursos serão adicionados neste momento.

  • Realce das linhas da tabela quando o mouse estiver em cima.
  • Possibilidade de adicionar um array de ícones para cada célula e um método para trocá-los.
  • Possibilidade de definir e modificar o texto na célula durante a execução do programa. 

Além disso, o código e alguns algoritmos foram otimizados para que a tabela fosse redesenhada de forma mais rápida. 

 

Coordenadas relativas ao cursor na tela especificada

Para eliminar o código duplicado em muitas classes e métodos ao calcular as coordenadas relativas na tela, os métodos CMouse::RelativeX() e CMouse::RelativeY() foram adicionados à classe CMouse para recuperar as coordenadas. Uma referência a um objeto do tipo CRectCanvas deve ser passada ​​para estes métodos, a fim de calcular a coordenada relativa levando em consideração o atual deslocamento da parte visível da tela.

//+------------------------------------------------------------------+
//| Classe para obter os parâmetros do mouse                         |
//+------------------------------------------------------------------+
class CMouse
  {
public:
   //--- Retorna as coordenadas relativas ao cursor do mouse do objeto de tela passado
   int               RelativeX(CRectCanvas &object);
   int               RelativeY(CRectCanvas &object);
  };
//+------------------------------------------------------------------+
//| Retorna a coordenada X relativa ao cursor do mouse               |
//| do objeto de tela passado                                        |
//+------------------------------------------------------------------+
int CMouse::RelativeX(CRectCanvas &object)
  {
   return(m_x-object.X()+(int)object.GetInteger(OBJPROP_XOFFSET));
  }
//+------------------------------------------------------------------+
//| Retorna a coordenada Y relativa ao cursor do mouse               |
//| do objeto de tela passado                                        |
//+------------------------------------------------------------------+
int CMouse::RelativeY(CRectCanvas &object)
  {
   return(m_y-object.Y()+(int)object.GetInteger(OBJPROP_YOFFSET));
      }

Na continuação do desenvolvimento da biblioteca, estes métodos serão usados para se obter as coordenadas relativas de todos os controles desenhados. 

 

Mudanças na estrutura da tabela

Para otimizar a execução do código da tabela renderizada o máximo possível, foi necessário modificar um pouco e complementar a estrutura da tabela do tipo CTOptions, adicionando novas estruturas que permitem a construção de arrays multidimensionais. A tarefa aqui é fazer com que certos fragmentos da tabela se redesenhem com base nos valores calculados anteriormente. Por exemplo, estes podem ser as coordenadas das colunas da tabela e as bordas das linhas.

Por exemplo, o cálculo e armazenamento da coordenada X das bordas da coluna é conveniente somente no método CCanvasTable::DrawGrid(), que é utilizado para desenhar a grade, e somente quando se desenha a tabela inteira. E quando o usuário seleciona uma linha da tabela, pode-se utilizar os valores predeterminados. O mesmo se aplica para o realce das linhas da tabela quando o mouse estiver em cima (isto irá ser discutido mais adiante no artigo). 

Crie uma estrutura separada (CTRowOptions) e declare um array de suas instâncias para armazenar a coordenada Y das linhas da tabela, e possivelmente, outras propriedades da linha no futuro. A coordenada Y das linhas são calculadas no método CCanvasTable::DrawRows(), designado para desenhar o fundo das linhas. Como este método é chamado antes de desenhar a grade, o método CCanvasTable::DrawGrid() usa os valores pré-calculados a partir da estrutura CTRowOptions

Crie uma estrutura separada do tipo CTCell para armazenar as propriedades das células da tabela. O array de instâncias na estrutura CTRowOptions é declarada com este tipo, como um array de linhas da tabela. Esta estrutura irá armazenar:

  • Array de ícones
  • Arrays dos tamanhos dos ícones
  • Índice do ícone selecionado (exibido) na célula
  • Texto completo
  • Texto reduzido
  • Cor do texto

Como cada ícone é um array de pixels, será necessário uma estrutura separada (CTImage) com um array dinâmico para armazená-los. O código destas estruturas podem ser encontrados na lista abaixo:

class CCanvasTable : public CElement
  {
private:
   //--- Array de pixels do ícone
   struct CTImage { uint m_image_data[]; };
   //--- Propriedades das células da tabela
   struct CTCell
     {
      CTImage           m_images[];       // Array de ícones
      uint              m_image_width[];  // Array da largura dos ícones
      uint              m_image_height[]; // Array da altura dos ícones
      int               m_selected_image; // Índice do ícone selecionado (exibido)
      string            m_full_text;      // Texto completo
      string            m_short_text;     // Texto reduzido
      color             m_text_color;     // Cor do texto
     };
   //--- Array de linhas e propriedades das colunas da tabela
   struct CTOptions
     {
      int               m_x;             // Coordenada X da borda esquerda da coluna
      int               m_x2;            // Coordenada X da borda direita da coluna
      int               m_width;         // Largura da coluna
      ENUM_ALIGN_MODE   m_text_align;    // Modo de alinhamento do texto nas células da coluna
      int               m_text_x_offset; // Deslocamento do texto
      string            m_header_text;   // Texto do cabeçalho da coluna
      CTCell            m_rows[];        // Array de linhas da tabela
     };
   CTOptions         m_columns[];
   //--- Array das propriedades da linha da tabela
   struct CTRowOptions
     {
      int               m_y;  // Coordenada Y da borda superior da linha
      int               m_y2; // Coordenada Y da borda inferior da linha
     };
   CTRowOptions      m_rows[];
      };

As alterações apropriadas foram feitas para todos os métodos de onde esses tipos de dados são usados. 

 

Determinação do intervalo das linhas na parte visível

Já que uma tabela pode ter várias linhas, a busca do foco em uma linha seguido pelo redesenho da tabela pode desacelerar significativamente o processo. O mesmo aplica-se a seleção de uma linha e ajustando o comprimento do texto durante a alteração da largura da coluna manualmente. A fim de evitar atraso, é necessário determinar o primeiro e o último índice na parte visível da tabela e organizar um ciclo para iterar apenas nesse intervalo. O método CCanvasTable::VisibleTableIndexes() foi implementado para essa finalidade. Primeiro ele determina os limites da parte visível. O limite superior é o deslocamento da parte visível ao longo do eixo Y e o limite inferior é definido como a parte superior + o tamanho da parte visível ao longo do eixo Y.

Agora é conveniente dividir os valores das bordas obtidos pela altura da linha definida nas configurações da tabela a fim de determinar os índices das linhas superior e inferior da parte visível. No caso de exceder o intervalo da última linha da tabela, o ajuste é realizado no final do método.

class CCanvasTable : public CElement
  {
private:
   //--- Para determinar os índices da parte visível da tabela
   int               m_visible_table_from_index;
   int               m_visible_table_to_index;
   //---
private:
   //--- Determinando os índices da parte visível da tabela
   void              VisibleTableIndexes(void);
  };
//+------------------------------------------------------------------+
//| Construtor                                                       |
//+------------------------------------------------------------------+
CCanvasTable::CCanvasTable(void) : m_visible_table_from_index(WRONG_VALUE),
                                   m_visible_table_to_index(WRONG_VALUE)
  {
...
  }
//+------------------------------------------------------------------+
//| Determinando os índices da parte visível da tabela               |
//+------------------------------------------------------------------+
void CCanvasTable::VisibleTableIndexes(void)
  {
//--- Determinando os limites que levam em conta o deslocamento da parte visível da tabela
   int yoffset1 =(int)m_table.GetInteger(OBJPROP_YOFFSET);
   int yoffset2 =yoffset1+m_table_visible_y_size;
//--- Determinando o primeiro e os últimos índices da parte visível da tabela
   m_visible_table_from_index =int(double(yoffset1/m_cell_y_size));
   m_visible_table_to_index   =int(double(yoffset2/m_cell_y_size));
//--- Aumenta o índice inferior por um, se ele não estiver fora do intervalo
   m_visible_table_to_index=(m_visible_table_to_index+1>m_rows_total)? m_rows_total : m_visible_table_to_index+1;
      }

Os índices serão determinados no método CCanvasTable::DrawTable(). Neste método pode ser passado um argumento para especificar que é necessário redesenhar somente a parte visível da tabela. O valor padrão do argumento é false, na qual indica o redesenho da tabela inteira. O código a seguir mostra a versão resumida deste método.

//+------------------------------------------------------------------+
//| Desenha a tabela                                                 |
//+------------------------------------------------------------------+
void CCanvasTable::DrawTable(const bool only_visible=false)
  {
//--- Se não foi indicado para redesenhar apenas a parte visível da tabela
   if(!only_visible)
     {
      //--- Define os índices da linha de toda a tabela desde o início até o fim
      m_visible_table_from_index =0;
      m_visible_table_to_index   =m_rows_total;
     }
//--- Obtém os índices da linha da parte visível da tabela
   else
      VisibleTableIndexes();
//--- Desenha o fundo das linhas da tabela
//--- Desenha uma linha selecionada
//--- Desenha a grade
//--- Desenha o ícone
//--- Desenha o texto
//--- Exibe as mudanças mais recentes extraídas
//--- Atualiza cabeçalhos, se eles estão habilitados
//--- Ajusta a tabela em relação às barras de rolagem
      }

É necessário realizar no método uma chamada à CCanvasTable::VisibleTableIndexes() para determinar o foco sobre as linhas da tabela: 

//+------------------------------------------------------------------+
//| Verificação do foco sobre as linhas da tabela                    |
//+------------------------------------------------------------------+
int CCanvasTable::CheckRowFocus(void)
  {
   int item_index_focus=WRONG_VALUE;
//--- Obtém a coordenada Y relativa abaixo do cursor do mouse
   int y=m_mouse.RelativeY(m_table);
//--- Obtém os índices do local da tabela
   VisibleTableIndexes();
//--- Busca o foco
   for(int i=m_visible_table_from_index; i<m_visible_table_to_index; i++)
     {
      //--- Se o foco da linha mudou
      if(y>m_rows[i].m_y && y<=m_rows[i].m_y2)
        {
         item_index_focus=i;
         break;
        }
     }
//--- Retorna o índice da linha com o foco
   return(item_index_focus);
      }

 

 

Ícones nas células da tabela

Vários ícones podem ser atribuídos a cada célula, que podem ser alterados durante a execução do programa. Adiciona os campos e métodos para definir os deslocamentos do ícone a partir das bordas superior e esquerda da célula:

class CCanvasTable : public CElement
  {
private:
   //--- Deslocamentos das bordas das celulas
   int               m_image_x_offset;
   int               m_image_y_offset;
   //---
public:
   //--- Deslocamentos das bordas das celulas
   void              ImageXOffset(const int x_offset)     { m_image_x_offset=x_offset;       }
   void              ImageYOffset(const int y_offset)     { m_image_y_offset=y_offset;       }
      };

Para atribuir os ícones para a célula especificada, é necessário passar um array com os seus caminhos na pasta local do terminal. Antes disso, eles devem ser incluídos na aplicação MQL como um recurso (#resource). O método CCanvasTable::SetImages() é projetado para esta finalidade. Aqui, se um array vazio é passado ou for detectado que seu tamanho foi excedido, o programa sai do método.

Se ele passar nas verificações, os arrays da célula são redimensionadas. Depois disso, o método ::ResourceReadImage() é utilizado em um loop para ler o conteúdo do ícone para um array unidimensional, armazenando a cor de cada pixel no array. Os tamanhos dos ícones são armazenados aos arrays correspondentes. Eles serão necessários para organizar os loops para desenhar os ícones na tela. O primeiro ícone do array será selecionado na célula por padrão.

class CCanvasTable : public CElement
  {
public:
   //--- Ajusta os ícones para a célula especificada
   void              SetImages(const uint column_index,const uint row_index,const string &bmp_file_path[]);
  };
//+------------------------------------------------------------------+
//| Ajusta os ícones para a célula especificada                      |
//+------------------------------------------------------------------+
void CCanvasTable::SetImages(const uint column_index,const uint row_index,const string &bmp_file_path[])
  {
   int total=0;
//--- Sai se um array de tamanho zero foi aprovado
   if((total=CheckArraySize(bmp_file_path))==WRONG_VALUE)
      return;
//--- Verifica se o tamanho do array não excedeu
   if(!CheckOutOfRange(column_index,row_index))
      return;
//--- Redimensiona os arrays
   ::ArrayResize(m_columns[column_index].m_rows[row_index].m_images,total);
   ::ArrayResize(m_columns[column_index].m_rows[row_index].m_image_width,total);
   ::ArrayResize(m_columns[column_index].m_rows[row_index].m_image_height,total);
//---
   for(int i=0; i<total; i++)
     {
      //--- O primeiro ícone do array é selecionado por padrão
      m_columns[column_index].m_rows[row_index].m_selected_image=0;
      //--- Escreve o ícone passado para o array e armazena o seu tamanho
      if(!ResourceReadImage(bmp_file_path[i],m_columns[column_index].m_rows[row_index].m_images[i].m_image_data,
         m_columns[column_index].m_rows[row_index].m_image_width[i],
         m_columns[column_index].m_rows[row_index].m_image_height[i]))
        {
         Print(__FUNCTION__," > error: ",GetLastError());
         return;
        }
     }
      }

Para descobrir quantos ícones uma célula em particular tem, use o método CCanvasTable::ImagesTotal():

class CCanvasTable : public CElement
  {
public:
   //--- Retorna o número total de ícones na célula especificada
   int               ImagesTotal(const uint column_index,const uint row_index);
  };
//+------------------------------------------------------------------+
//| Retorna o número total de ícones na célula especificada          |
//+------------------------------------------------------------------+
int CCanvasTable::ImagesTotal(const uint column_index,const uint row_index)
  {
//--- Verifica se o tamanho do array não excedeu
   if(!CheckOutOfRange(column_index,row_index))
      return(WRONG_VALUE);
//--- Retorna o tamanho do array de ícones
   return(::ArraySize(m_columns[column_index].m_rows[row_index].m_images));
      }

Agora, considere os métodos que serão utilizados para desenhar os ícones. Primeiramente, um novo método CColors::BlendColors() foi adicionado a classe CColors, que permite misturar corretamente as cores superiores e inferiores levando em consideração a transparência do ícone de sobreposição. Bem como um método auxiliar CColors::GetA() para obter o valor de transparência da cor passada.

No método CColors::BlendColors(), as cores passadas ​​são divididas primeiro em componentes RGB, e o canal alfa é extraído a partir de sua cor superior. O canal alfa é convertido para um valor entre zero e um. Se a cor passada não conter transparência, a mistura não é executada. No caso de haver transparência, então, cada componente das duas cores passadas será misturado tendo em conta a transparência da cor superior. Depois disso, os valores dos componentes obtidos são ajustadas, no caso deles estarem fora do intervalo (255). 

//+------------------------------------------------------------------+
//| Classe para trabalhar com as cores                               |
//+------------------------------------------------------------------+
class CColors
  {
public:
   double            GetA(const color aColor);
   color             BlendColors(const uint lower_color,const uint upper_color);
  };
//+------------------------------------------------------------------+
//| Obtendo o valor do componente A                                  |
//+------------------------------------------------------------------+
double CColors::GetA(const color aColor)
  {
   return(double(uchar((aColor)>>24)));
  }
//+------------------------------------------------------------------+
//| Misturando duas cores considerando a transparência da cor superior |
//+------------------------------------------------------------------+
color CColors::BlendColors(const uint lower_color,const uint upper_color)
  {
   double r1=0,g1=0,b1=0;
   double r2=0,g2=0,b2=0,alpha=0;
   double r3=0,g3=0,b3=0;
//--- Converte as cores em formato ARGB
   uint pixel_color=::ColorToARGB(upper_color);
//--- Obtém os componentes das cores inferior e superior
   ColorToRGB(lower_color,r1,g1,b1);
   ColorToRGB(pixel_color,r2,g2,b2);
//--- Obtém o percentual de transparência de 0.00 até 1.00
   alpha=GetA(upper_color)/255.0;
//--- Se não houver transparência
   if(alpha<1.0)
     {
      //--- Mistura os componentes, levando em consideração o canal alfa
      r3=(r1*(1-alpha))+(r2*alpha);
      g3=(g1*(1-alpha))+(g2*alpha);
      b3=(b1*(1-alpha))+(b2*alpha);
      //--- Ajuste dos valores obtidos
      r3=(r3>255)? 255 : r3;
      g3=(g3>255)? 255 : g3;
      b3=(b3>255)? 255 : b3;
     }
   else
     {
      r3=r2;
      g3=g2;
      b3=b2;
     }
//--- Combina os componentes obtidos e retorna a cor
   return(RGBToColor(r3,g3,b3));
      }

Agora é fácil escrever um método para desenhar os ícones. O código do método CCanvasTable::DrawImage() é apresentado abaixo. Os índices das células da tabela de devem ser passados, onde o ícone será desenhado. No início do método, as coordenadas do ícone são obtidas levando em consideração os desvios, bem como o índice da célula selecionada e do seu tamanho. Em seguida, um loop duplo gera o pixel do ícone por pixel. Se o pixel especificado está vazio (não tem cor), então, o loop passa para o próximo pixel. Se há uma cor, então, a cor de fundo da célula e a cor do pixel atual são determinados, em seguida, estas duas cores são misturadas levando em consideração a transparência da cor de sobreposição. A cor resultante é desenhada na tela.

class CCanvasTable : public CElement
  {
private:
   //--- Desenha um ícone na célula especificada
   void              DrawImage(const int column_index,const int row_index);
  };
//+------------------------------------------------------------------+
//| Desenha um ícone na célula especificada                          |
//+------------------------------------------------------------------+
void CCanvasTable::DrawImage(const int column_index,const int row_index)
  {
//--- Calcula as coordenadas
   int x =m_columns[column_index].m_x+m_image_x_offset;
   int y =m_rows[row_index].m_y+m_image_y_offset;
//--- O ícone selecionado na célula e seu tamanho
   int  selected_image =m_columns[column_index].m_rows[row_index].m_selected_image;
   uint image_height   =m_columns[column_index].m_rows[row_index].m_image_height[selected_image];
   uint image_width    =m_columns[column_index].m_rows[row_index].m_image_width[selected_image];
//--- Desenha
   for(uint ly=0,i=0; ly<image_height; ly++)
     {
      for(uint lx=0; lx<image_width; lx++,i++)
        {
         //--- Se não há cor, vai para o próximo pixel
         if(m_columns[column_index].m_rows[row_index].m_images[selected_image].m_image_data[i]<1)
            continue;
         //--- Obtém a cor da camada inferior (fundo da célula) e a cor do pixel especificado do ícone
         uint background  =(row_index==m_selected_item)? m_selected_row_color : m_table.PixelGet(x+lx,y+ly);
         uint pixel_color =m_columns[column_index].m_rows[row_index].m_images[selected_image].m_image_data[i];
         //--- Mistura as cores
         uint foreground=::ColorToARGB(m_clr.BlendColors(background,pixel_color));
         //--- Desenha o pixel da sobreposição do ícone
         m_table.PixelSet(x+lx,y+ly,foreground);
        }
     }
      }

O método CCanvasTable::DrawImages() é designado para extrair todos os ícones da tabela de uma só vez, levando em consideração quando é necessário desenhar apenas a parte visível da tabela. Na versão atual da tabela, os ícones podem ser desenhados somente se o texto nas colunas estiver alinhado à esquerda. Além disso, cada iteração verifica se um ícone é atribuído à célula, e também se o array de seus pixels é vazio. Se ele é passado em todas as verificações, o método CCanvasTable::DrawImage() é chamado para desenhar o ícone

class CCanvasTable : public CElement
  {
private:
   //--- Desenha todos os ícones da tabela
   void              DrawImages(void);
  };
//+------------------------------------------------------------------+
//| Desenha todos os ícones da tabela                                |
//+------------------------------------------------------------------+
void CCanvasTable::DrawImages(void)
  {
//--- Para calcular as coordenadas
   int x=0,y=0;
//--- Colunas
   for(int c=0; c<m_columns_total; c++)
     {
      //--- Se o texto não está alinhado à esquerda, vá para a próxima coluna
      if(m_columns[c].m_text_align!=ALIGN_LEFT)
         continue;
      //--- Linhas
      for(int r=m_visible_table_from_index; r<m_visible_table_to_index; r++)
        {
         / --- Vá para a próxima, se esta célula não contém ícones
         if(ImagesTotal(c,r)<1)
            continue;
         //--- O ícone selecionado na célula (o primeiro [0] é selecionada por padrão)
         int selected_image=m_columns[c].m_rows[r].m_selected_image;
         //--- Vá para a próxima, se o array de pixels for vazio
         if(::ArraySize(m_columns[c].m_rows[r].m_images[selected_image].m_image_data)<1)
            continue;
         //--- Desenha o ícone
         DrawImage(c,r);
        }
     }
      }

A imagem abaixo demonstra um exemplo de uma tabela com ícones nas células:

 Fig. 1. Tabela com ícones nas células.

Fig. 1. Tabela com ícones nas células. 


 

Realce das linhas da tabela quando o mouse estiver em cima

Serão requeridos campos e métodos adicionais para as linhas da tabela renderizada que serão realçadas quando o mouse estiver em cima. Use o método CCanvasTable::LightsHover() para ativar o modo de realce. A cor da linha pode ser definida com a ajuda do método CCanvasTable::CellColorHover().

class CCanvasTable : public CElement
  {
private:
   //--- Cor das células em diferentes estados
   color             m_cell_color;
   color             m_cell_color_hover;
   //--- Modo de realce quando o cursor estiver pairando sobre a coluna
   bool              m_lights_hover;
   //---
public:
   //--- Cor das células em diferentes estados
   void              CellColor(const color clr)           { m_cell_color=clr;                }
   void              CellColorHover(const color clr)      { m_cell_color_hover=clr;          }
   //--- Modo de realce quando o cursor estiver pairando sobre a coluna
   void              LightsHover(const bool flag)         { m_lights_hover=flag;             }
      };

O realce de uma linha não requer o redesenho de toda a tabela novamente quando o cursor do mouse se move. Além disso, é altamente recomendável não fazer isso, pois ele diminui drasticamente o desempenho da aplicação, consumindo muitos recursos da CPU. Na primeira/nova entrada do cursor do mouse para a área da tabela, é conveniente olhar para o foco apenas uma vez (iteração sobre todo o array de linhas). O método CCanvasTable::CheckRowFocus() é utilizado para esta finalidade. Uma vez que o foco é encontrado e o índice da linha é armazenado, basta verificar se o foco da linha com o índice armazenado se alterou quando o cursor é movido. O algoritmo descrito é implementado no método CCanvasTable::ChangeRowsColor(), mostrado na lista abaixo. O método CCanvasTable::RedrawRow() é usado para mudar a cor da linha, seu código será apresentado mais tarde. O método CCanvasTable::ChangeRowsColor() é chamado no método CCanvasTable::ChangeObjectsColor() para alterar as cores dos objetos da tabela. 

class CCanvasTable : public CElement
  {
private:
   //--- Para determinar o foco da linha
   int               m_item_index_focus;
   //--- Para determinar o momento de transição do cursor do mouse de uma linha para outra
   int               m_prev_item_index_focus;
   //---
private:
   //--- Mudando a cor da linha quando o mouse estiver em cima
   void              ChangeRowsColor(void);
  };
//+------------------------------------------------------------------+
//| Mudando a cor da linha quando o mouse estiver em cima            |
//+------------------------------------------------------------------+
void CCanvasTable::ChangeRowsColor(void)
  {
//--- Sai, se o realce da linha quando o mouse estiver em cima está desativado
   if(!m_lights_hover)
      return;
//--- Se não estiver em foco
   if(!m_table.MouseFocus())
     {
      //--- Se ainda não indicou que não está no foco
      if(m_prev_item_index_focus!=WRONG_VALUE)
        {
         m_item_index_focus=WRONG_VALUE;
         //--- Altera a cor
         RedrawRow();
         m_table.Update();
         //--- Reseta o foco
         m_prev_item_index_focus=WRONG_VALUE;
        }
     }
//--- Se está em foco
   else
     {
      //--- Verifica o foco nas linhas
      if(m_item_index_focus==WRONG_VALUE)
        {
         //--- Obtém o índice da linha com o foco
         m_item_index_focus=CheckRowFocus();
         //--- Muda a cor da linha
         RedrawRow();
         m_table.Update();
         //--- Armazena como o índice anterior em foco
         m_prev_item_index_focus=m_item_index_focus;
         return;
        }
      //--- Obtém a coordenada Y relativa abaixo do cursor do mouse
      int y=m_mouse.RelativeY(m_table);
      //--- Verificando o foco
      bool condition=(y>m_rows[m_item_index_focus].m_y && y<=m_rows[m_item_index_focus].m_y2);
      //--- Se o foco mudou
      if(!condition)
        {
         //--- Obtém o índice da linha com o foco
         m_item_index_focus=CheckRowFocus();
         //--- Muda a cor da linha
         RedrawRow();
         m_table.Update();
         //--- Armazena como o índice anterior em foco
         m_prev_item_index_focus=m_item_index_focus;
        }
     }
      }

O método CCanvasTable::RedrawRow() para o redesenho rápido das linhas da tabela opera em dois modos:

  •  quando se seleciona uma fileira
  •  no modo de realce de uma linha quando o mouse estiver em cima.

O método precisa ser passado para o argumento correspondente para especificar o modo desejado. Por padrão, o argumento é definido como false, indicando a utilização do método no modo de realce das linhas da tabela. A classe contém os campos especiais para ambos os modos para determinar a linha da tabela selecionada/realçada atual e anterior. Assim, marcando outra linha exige somente o redesenho das linhas anterior e atual, e não da tabela inteira.

O programa deixa o método se os índices não estão definidos (WRONG_VALUE). Em seguida, é necessário determinar quantos índices serão definidos. Se esta é a primeira entrada na tabela e apenas um índice (atual) foi definido, então, a cor será alterada apenas na linha atual. Se entrou novamente, a cor será alterada em duas linhas (a atual e a anterior). 

Agora é necessário determinar a sequência para alterar as cores da linha. Se o índice da linha atual for maior que o anterior, isso significa que o cursor se moveu para baixo. Primeiro altere a cor no índice anterior, e depois no atual. Numa situação inversa, fazer o oposto. O método também considera o momento de sair da área da tabela, quando o índice da linha atual não estiver definida, enquanto o índice da linha anterior ainda está presente.

Uma vez que todas as variáveis ​​locais para a operação são inicializados, o fundo das linhas, a grade, os ícones e o texto são desenhadas em sequência estrita.

class CCanvasTable : public CElement
  {
private:
   //--- Redesenha a linha da tabela especificada de acordo com o modo especificado
   void              RedrawRow(const bool is_selected_row=false);
  };
//+----------------------------------------------------------------------------+
//| Redesenha a linha da tabela especificada de acordo com o modo especificado |
//+----------------------------------------------------------------------------+
void CCanvasTable::RedrawRow(const bool is_selected_row=false)
  {
/--- Os índices das linhas atual e anterior
   int item_index      =WRONG_VALUE;
   int prev_item_index =WRONG_VALUE;
//--- Inicialização dos índices da linha em relação ao modo especificado
   if(is_selected_row)
     {
      item_index      =m_selected_item;
      prev_item_index =m_prev_selected_item;
     }
   else
     {
      item_index      =m_item_index_focus;
      prev_item_index =m_prev_item_index_focus;
     }
//--- Sai, se os índices não estão definidos
   if(prev_item_index==WRONG_VALUE && item_index==WRONG_VALUE)
      return;
//--- O número de linhas e colunas para desenhar
   int rows_total    =(item_index!=WRONG_VALUE && prev_item_index!=WRONG_VALUE)? 2 : 1;
   int columns_total =m_columns_total-1;
//--- Coordenadas
   int x1=1,x2=m_table_x_size;
   int y1[2]={0},y2[2]={0};
//--- Array para os valores de uma determinada sequência
   int indexes[2];
//--- Se (1) o cursor do mouse se mover para baixo ou, se (2) entrou pela primeira vez
   if(item_index>m_prev_item_index_focus || item_index==WRONG_VALUE)
     {
      indexes[0]=(item_index==WRONG_VALUE || prev_item_index!=WRONG_VALUE)? prev_item_index : item_index;
      indexes[1]=item_index;
     }
//--- Se o cursor do mouse foi movido para cima
   else
     {
      indexes[0]=item_index;
      indexes[1]=prev_item_index;
     }
//--- Desenha o fundo das linhas
   for(int r=0; r<rows_total; r++)
     {
      //--- Calcula as coordenadas da borda superior e inferior da linha
      y1[r]=m_rows[indexes[r]].m_y+1;
      y2[r]=m_rows[indexes[r]].m_y2-1;
      //--- Determina o foco da linha em relação ao modo de realce
      bool is_item_focus=false;
      if(!m_lights_hover)
         is_item_focus=(indexes[r]==item_index && item_index!=WRONG_VALUE);
      else
         is_item_focus=(item_index==WRONG_VALUE)?(indexes[r]==prev_item_index) :(indexes[r]==item_index);
      //--- Desenha o fundo da linha
      m_table.FillRectangle(x1,y1[r],x2,y2[r],RowColorCurrent(indexes[r],is_item_focus));
     }
//--- Cor da grade
   uint clr=::ColorToARGB(m_grid_color);
//--- Desenha as bordas
   for(int r=0; r<rows_total; r++)
     {
      for(int c=0; c<columns_total; c++)
         m_table.Line(m_columns[c].m_x2,y1[r],m_columns[c].m_x2,y2[r],clr);
     }
//--- Desenha os ícones
   for(int r=0; r<rows_total; r++)
     {
      for(int c=0; c<m_columns_total; c++)
        {
         //--- Desenha o ícone, se (1) ele está presente nesta célula e (2) o texto desta coluna está alinhado à esquerda
         if(ImagesTotal(c,r)>0 && m_columns[c].m_text_align==ALIGN_LEFT)
            DrawImage(c,indexes[r]);
        }
     }
//--- Para calcular as coordenadas
   int x=0,y=0;
//--- Modo de alinhamento do texto
   uint text_align=0;
//--- Desenha o texto
   for(int c=0; c<m_columns_total; c++)
     {
      //--- Obtém (1) a coordenada X do texto e (2) o modo de alinhamento do texto
      x          =TextX(c);
      text_align =TextAlign(c,TA_TOP);
      //---
      for(int r=0; r<rows_total; r++)
        {
         //--- (1) Calcula a coordenada e (2) desenha o texto
         y=m_rows[indexes[r]].m_y+m_text_y_offset;
         m_table.TextOut(x,y,m_columns[c].m_rows[indexes[r]].m_short_text,TextColor(c,indexes[r]),text_align);
        }
     }
      }

Isso resulta no seguinte:

 Fig. 2. Demonstração do realce das linhas da tabela quando o mouse estiver em cima.

Fig. 2. Demonstração do realce das linhas da tabela quando o mouse estiver em cima. 

 

 

Métodos para redesenhar rapidamente as células da tabela

Foram consideradas os métodos para redesenhar rapidamente as linhas da tabela. Os métodos para redesenho rápido das células serão demonstrados agora. Por exemplo, se é necessário alterar o texto, sua cor ou ícone em qualquer célula da tabela, é conveniente redesenhar somente a célula, e não toda a tabela. O método privado CCanvasTable::RedrawCell() é utilizado para esta finalidade. Apenas o conteúdo da célula será redesenhado, enquanto que a sua estrutura não será atualizada. A cor de fundo é determinada levando em consideração o modo de realce, se ativado. Depois de determinar os valores e inicializar as variáveis ​​locais, o fundo, o ícone (se atribuído e se o texto é alinhado à esquerda) e o texto são desenhados na célula.

class CCanvasTable : public CElement
  {
private:
   //--- Redesenha a célula especificada da tabela
   void              RedrawCell(const int column_index,const int row_index);
  };
//+------------------------------------------------------------------+
//| Redesenha a célula especificada da tabela                        |
//+------------------------------------------------------------------+
void CCanvasTable::RedrawCell(const int column_index,const int row_index)
  {
//--- Coordenadas
   int x1=m_columns[column_index].m_x+1;
   int x2=m_columns[column_index].m_x2-1;
   int y1=m_rows[row_index].m_y+1;
   int y2=m_rows[row_index].m_y2-1;
//--- Para calcular as coordenadas
   int  x=0,y=0;
//--- Para verificar o foco
   bool is_row_focus=false;
//--- Se o modo de realce da linha está habilitado
   if(m_lights_hover)
     {
      //--- (1) Obtém a coordenada Y relativo ao cursor do mouse e (2) o foco sobre a linha da tabela especificada
      y=m_mouse.RelativeY(m_table);
      is_row_focus=(y>m_rows[row_index].m_y && y<=m_rows[row_index].m_y2);
     }
//--- Desenha o fundo da célula
   m_table.FillRectangle(x1,y1,x2,y2,RowColorCurrent(row_index,is_row_focus));
//--- Desenha o ícone, se (1) ele está presente nesta célula e (2) o texto desta coluna está alinhado à esquerda
   if(ImagesTotal(column_index,row_index)>0 && m_columns[column_index].m_text_align==ALIGN_LEFT)
      DrawImage(column_index,row_index);
//--- Obtém o modo de alinhamento do texto
   uint text_align=TextAlign(column_index,TA_TOP);
//--- Desenha o texto
   for(int c=0; c<m_columns_total; c++)
     {
      //--- Obtém a coordenada X do texto
      x=TextX(c);
      //--- Encerra o ciclo
      if(c==column_index)
         break;
     }
//--- (1) Calcula a coordenada Y, e (2) extrai o texto
   y=y1+m_text_y_offset-1;
   m_table.TextOut(x,y,m_columns[column_index].m_rows[row_index].m_short_text,TextColor(column_index,row_index),text_align);
      }

Agora, vamos considerar os métodos, que permitem mudar o texto, a cor do texto e o ícone (seleção daquelas que foram atribuídas) na célula. Os métodos públicos CCanvasTable::SetValue() e CCanvasTable::TextColor() devem ser usados ​​para definir o texto e a sua cor. Estes métodos são passados ​​para os índices da célula (coluna e linha) e o valor a ser definido. Para o método CCanvasTable::SetValue(), é um valor de string a ser exibido na célula. Aqui, a string passada, em sua versão completa e reduzida (se a string completa não couber no espaço da célula) são armazenados para os campos correspondentes da estrutura da tabela (CTCell). A cor do texto deve ser passada para o método CCanvasTable::TextColor(). Como o quarto parâmetro está em ambos os métodos, você pode especificar se for necessário redesenhar a célula imediatamente ou se ela será feita mais tarde chamando o método CCanvasTable::UpdateTable().

class CCanvasTable : public CElement
  {
private:
   //--- Define o valor para a célula da tabela especificada
   void              SetValue(const uint column_index,const uint row_index,const string value,const bool redraw=false);
   //--- Define a cor do texto para a célula da tabela especificada
   void              TextColor(const uint column_index,const uint row_index,const color clr,const bool redraw=false);
  };
//+------------------------------------------------------------------+
//| Preenche o array nos os índices específicos                      |
//+------------------------------------------------------------------+
void CCanvasTable::SetValue(const uint column_index,const uint row_index,const string value,const bool redraw=false)
  {
//--- Verifica se o tamanho do array não excedeu
   if(!CheckOutOfRange(column_index,row_index))
      return;
//--- Armazena o valor no array
   m_columns[column_index].m_rows[row_index].m_full_text=value;
//--- Ajusta e armazena o texto, se ele não se encaixar na célula
   m_columns[column_index].m_rows[row_index].m_short_text=CorrectingText(column_index,row_index);
//--- Redesenha a célula, se especificada
   if(redraw)
      RedrawCell(column_index,row_index);
  }
//+------------------------------------------------------------------+
//| Preencher o array de cor do texto                                |
//+------------------------------------------------------------------+
void CCanvasTable::TextColor(const uint column_index,const uint row_index,const color clr,const bool redraw=false)
  {
//--- Verifica se o tamanho do array não excedeu
   if(!CheckOutOfRange(column_index,row_index))
      return;
//--- Armazena a cor do texto no array comum
   m_columns[column_index].m_rows[row_index].m_text_color=clr;
//--- Redesenha a célula, se especificada
   if(redraw)
      RedrawCell(column_index,row_index);
      }

O ícone na célula pode ser alterado pelo método CCanvasTable::ChangeImage(). O índice do ícone para troca deve ser especificado como o terceiro parâmetro aqui. Como nos métodos descritos anteriormente para modificar as propriedades das células, é possível especificar se a célula é para ser redesenhada imediatamente ou mais tarde

class CCanvasTable : public CElement
  {
private:
   //--- Altera o ícone na célula especificada
   void              ChangeImage(const uint column_index,const uint row_index,const uint image_index,const bool redraw=false);
  };
//+------------------------------------------------------------------+
//| Altera o ícone na célula especificada                            |
//+------------------------------------------------------------------+
void CCanvasTable::ChangeImage(const uint column_index,const uint row_index,const uint image_index,const bool redraw=false)
  {
//--- Verifica se o tamanho do array não excedeu
   if(!CheckOutOfRange(column_index,row_index))
      return;
//--- Obtém o número de ícones da célula
   int images_total=ImagesTotal(column_index,row_index);
//--- Sai, se (1) não há ícones ou (2) fora do alcance
   if(images_total==WRONG_VALUE || image_index>=(uint)images_total)
      return;
//--- Sai, se o ícone especificado corresponde ao selecionado
   if(image_index==m_columns[column_index].m_rows[row_index].m_selected_image)
      return;
//--- Armazena o índice do ícone selecionado da célula
   m_columns[column_index].m_rows[row_index].m_selected_image=(int)image_index;
//--- Redesenha a célula, se especificada
   if(redraw)
      RedrawCell(column_index,row_index);
      }

Outro método público será necessário para redesenha a tabela inteira — CCanvasTable::UpdateTable(). Ele pode ser chamado em dois modos: 

  1. Quando é necessário ele atualizar a tabela para exibir as mudanças recentes feitas pelos métodos descritos acima.
  2. Quando é necessário redesenhar completamente a tabela, caso as mudanças foram feitas.

Por padrão, o único argumento do método está definido para false, o que indica a atualização sem redesenho. 

class CCanvasTable : public CElement
  {
private:
   //--- Atualizando a tabela
   void              UpdateTable(const bool redraw=false);
  };
//+------------------------------------------------------------------+
//| Atualizando a tabela                                             |
//+------------------------------------------------------------------+
void CCanvasTable::UpdateTable(const bool redraw=false)
  {
//--- Redesenha a tabela, se especificada
   if(redraw)
      DrawTable();
//--- Atualiza a tabela
   m_table.Update();
      }

Abaixo está o resultado do trabalho realizado:

 Fig. 3. Demonstração das novas características da tabela renderizada.

Fig. 3. Demonstração das novas características da tabela renderizada.


O Expert Advisor com a demonstração deste resultado pode ser baixado nos arquivos anexados deste artigo. Durante a execução do programa, os ícones em todas as células da tabela (5 colunas e 30 linhas) mudarão com uma frequência de 100 milissegundos. A imagem abaixo mostra a carga da CPU, sem a interação do usuário com a interface gráfica da aplicação MQL. A carga da CPU com a frequência de atualização de 100 milissegundos não excede 3%.

 Fig. 4. A carga da CPU durante a execução do aplicativo de teste MQL.

Fig. 4. A carga da CPU durante a execução do aplicativo de teste MQL. 

 

 

Aplicação para testar os controles

A versão atual da tabela renderizada já é "inteligente" o suficiente para criar as mesmas tabelas como na janela do Observador do Mercado (Market Watch), por exemplo. Vamos tentar demonstrar isso. Para o exemplo, crie uma tabela de 5 colunas e 25 linhas. Esses serão os 25 símbolos disponíveis no servidor MetaQuotes-Demo. Os dados da tabela serão o seguinte:

  • Symbol – Instrumentos financeiros (pares de moedas).
  • Bid – Oferta de compra.
  • Ask – Oferta de venda.
  • Spread (!) – diferença dentre a oferta de compra (Bid) e venda (Ask).
  • Time – o tempo da última cotação.

Vamos preparar os mesmos ícones para denotar as últimas mudanças no preço como na tabela da janela do Observador do Mercado. A primeira inicialização das células da tabela será feito imediatamente no método de criação do controle e que será realizado através da chamada do método auxiliar CProgram::InitializingTable() da classe personalizada. 

//+------------------------------------------------------------------+
//| Classe para a criação de uma aplicação                           |
//+------------------------------------------------------------------+
class CProgram : public CWndEvents
  {
private:
   //--- Inicializa a tabela
   void              InitializingTable(void);
  };
//+------------------------------------------------------------------+
//| Inicializa a tabela                                              |
//+------------------------------------------------------------------+
void CProgram::InitializingTable(void)
  {
//--- Array dos títulos do cabeçalho
   string text_headers[COLUMNS1_TOTAL]={"Symbol","Bid","Ask","!","Time"};
//--- Array de símbolos
   string text_array[25]=
     {
      "AUDUSD","GBPUSD","EURUSD","USDCAD","USDCHF","USDJPY","NZDUSD","USDSEK","USDHKD","USDMXN",
      "USDZAR","USDTRY","GBPAUD","AUDCAD","CADCHF","EURAUD","GBPCHF","GBPJPY","NZDJPY","AUDJPY",
      "EURJPY","EURCHF","EURGBP","AUDCHF","CHFJPY"
     };
//--- Array de ícones
   string image_array[3]=
     {
      "::Images\\EasyAndFastGUI\\Icons\\bmp16\\circle_gray.bmp",
      "::Images\\EasyAndFastGUI\\Icons\\bmp16\\arrow_up.bmp",
      "::Images\\EasyAndFastGUI\\Icons\\bmp16\\arrow_down.bmp"
     };
//---
   for(int c=0; c<COLUMNS1_TOTAL; c++)
     {
      //--- Define os títulos do cabeçalho
      m_canvas_table.SetHeaderText(c,text_headers[c]);
      //---
      for(int r=0; r<ROWS1_TOTAL; r++)
        {
         //--- Define os ícones
         m_canvas_table.SetImages(c,r,image_array);
         //--- Define os nomes do símbolo
         if(c<1)
            m_canvas_table.SetValue(c,r,text_array[r]);
         //--- Valor padrão para todas as células
         else
            m_canvas_table.SetValue(c,r,"-");
        }
     }
      }

Os valores dessas células da tabela serão atualizados a cada 16ms durante a execução do programa. Outro método auxiliar CProgram::UpdateTable() foi criado para este fim. Aqui, o programa deixa o método se for um dia do fim de semana (sábado ou domingo). Em seguida, um loop duplo itera sobre todas as colunas e linhas da tabela. Este loop duplo obtém os últimos dois ticks para cada símbolo e depois que as modificações nos preços forem analisadas, ele define os valores correspondentes. 

class CProgram : public CWndEvents
  {
private:
   //--- Inicializa a tabela
   void              InitializingTable(void);
  };
//+------------------------------------------------------------------+
//| Atualiza os valores da tabela                                    |
//+------------------------------------------------------------------+
void CProgram::UpdateTable(void)
  {
   MqlDateTime check_time;
   ::TimeToStruct(::TimeTradeServer(),check_time);
//--- Sai, se for um sábado ou domingo
   if(check_time.day_of_week==0 || check_time.day_of_week==6)
      return;
//---
   for(int c=0; c<m_canvas_table.ColumnsTotal(); c++)
     {
      for(int r=0; r<m_canvas_table.RowsTotal(); r++)
        {
         //--- O símbolo para obter os dados para
         string symbol=m_canvas_table.GetValue(0,r);
         //--- Obtém os dados dos últimos dois ticks
         MqlTick ticks[];
         if(::CopyTicks(symbol,ticks,COPY_TICKS_ALL,0,2)<2)
            continue;
         //--- Define o array como séries temporais
         ::ArraySetAsSeries(ticks,true);
         //--- Coluna de símbolos - Symbol. Determina a direção dos preços.
         if(c==0)
           {
            int index=0;
            //--- Se os preços não mudaram
            if(ticks[0].ask==ticks[1].ask && ticks[0].bid==ticks[1].bid)
               index=0;
            //--- Se o preço Bid mudou para cima
            else if(ticks[0].bid>ticks[1].bid)
               index=1;
            //--- Se o preço Bid mudou para baixo
            else if(ticks[0].bid<ticks[1].bid)
               index=2;
            //--- Ajusta o ícone correspondente
            m_canvas_table.ChangeImage(c,r,index,true);
           }
         else
           {
            //--- Coluna da diferença de preço - Spread (!)
            if(c==3)
              {
               //--- Obtém e define o tamanho do spread em pontos
               int spread=(int)::SymbolInfoInteger(symbol,SYMBOL_SPREAD);
               m_canvas_table.SetValue(c,r,string(spread),true);
               continue;
              }
            //--- Obtém o número de casas decimais
            int digit=(int)::SymbolInfoInteger(symbol,SYMBOL_DIGITS);
            //--- Coluna das ofertas de compra (Bid)
            if(c==1)
              {
               m_canvas_table.SetValue(c,r,::DoubleToString(ticks[0].bid,digit));
               //--- Se o preço mudou, define a cor correspondente a direção
               if(ticks[0].bid!=ticks[1].bid)
                  m_canvas_table.TextColor(c,r,(ticks[0].bid<ticks[1].bid)? clrRed : clrBlue,true);
               //---
               continue;
              }
            //--- Coluna das ofertas de venda (Ask)
            if(c==2)
              {
               m_canvas_table.SetValue(c,r,::DoubleToString(ticks[0].ask,digit));
               //--- Se o preço mudou, define a cor correspondente a direção
               if(ticks[0].ask!=ticks[1].ask)
                  m_canvas_table.TextColor(c,r,(ticks[0].ask<ticks[1].ask)? clrRed : clrBlue,true);
               //---
               continue;
              }
            //--- Coluna do última hora de chegada dos preços do símbolo
            if(c==4)
              {
               long   time     =::SymbolInfoInteger(symbol,SYMBOL_TIME);
               string time_msc =::IntegerToString(ticks[0].time_msc);
               int    length   =::StringLen(time_msc);
               string msc      =::StringSubstr(time_msc,length-3,3);
               string str      =::TimeToString(time,TIME_MINUTES|TIME_SECONDS)+"."+msc;
               //---
               color clr=clrBlack;
               //--- Se os preços não mudaram
               if(ticks[0].ask==ticks[1].ask && ticks[0].bid==ticks[1].bid)
                  clr=clrBlack;
               //--- Se o preço Bid mudou para cima
               else if(ticks[0].bid>ticks[1].bid)
                  clr=clrBlue;
               //--- Se o preço Bid mudou para baixo
               else if(ticks[0].bid<ticks[1].bid)
                  clr=clrRed;
               //--- Define o valor e a cor do texto
               m_canvas_table.SetValue(c,r,str);
               m_canvas_table.TextColor(c,r,clr,true);
               continue;
              }
           }
        }
     }
//--- Atualiza a tabela
   m_canvas_table.UpdateTable();
      }

O seguinte resultado é obtido:

Fig. 5. A comparação dos dados na janela Observador do Mercado e seu análogo personalizado.

Fig. 5. A comparação dos dados na janela Observador do Mercado e seu análogo personalizado. 


O aplicativo de teste apresentado no artigo pode ser baixado usando o link abaixo para estudá-lo ainda mais. 

 

Conclusão

A biblioteca para a criação de interfaces gráficas no atual estágio de desenvolvimento se parece com o esquema abaixo.

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

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


Abaixo, você pode baixar a versão mais recente da biblioteca e seus arquivos de teste.

Se você tiver dúvidas sobre a utilização do material a partir desses arquivos, você poderá consultar a descrição detalhada do desenvolvimento da biblioteca em um dos artigos da lista abaixo ou fazer sua pergunta nos comentários deste artigo. 


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

Arquivos anexados |
Tendência universal com GUI Tendência universal com GUI
No artigo, criaremos um indicador de tendência universal com base numa série de indicadores padrão. Será desenvolvida uma interface gráfica do usuário para selecionar o tipo de indicador e seus parâmetros. Exibiremos o indicador numa janela separada com fileiras de ícones coloridos.
Padrões disponíveis para negociação de cestas de moedas. Parte II Padrões disponíveis para negociação de cestas de moedas. Parte II
Continuação da conversa sobre padrões que podem ser detectados pelo trader ao operar pares de moedas. Esta parte descreve os padrões formados durante o uso combinado de indicadores de tendência. Como ferramentas de análise são usados indicadores construídos com base num índice de moeda.
Receitas MQL5 - sinais de negociação de pivô Receitas MQL5 - sinais de negociação de pivô
No artigo, é apresentado o processo de desenvolvimento e implementação de uma classe-robô de sinais com base em pivôs, isto é, níveis de reversão. Com base nesta classe é construída uma estratégia usando a Biblioteca padrão. São consideradas as possibilidades de desenvolver uma estratégia de pivôs adicionando filtros.
Interfaces Gráficas X: Novos recursos para a tabela Renderizada (build 9) Interfaces Gráficas X: Novos recursos para a tabela Renderizada (build 9)
Até agora, o tipo mais avançado de tabelas já desenvolvido em nossa biblioteca foi a CTable. Esta tabela é montada a partir de caixas de edição do tipo OBJ_EDIT, e seu posterior desenvolvimento tornou-se problemático. Portanto, em termos de capacidades máximas, é melhor desenvolver tabelas renderizadas do tipo CCanvasTable mesmo no atual estágio de desenvolvimento da biblioteca. Sua versão atual está completamente inerte, mas a partir deste artigo, nós vamos tentar corrigir esta situação.