Interfaces Gráficas X: Gestão avançada de listas e tabelas. Otimização do código (build 7)
Conteúdo
- Introdução
- Alterações no esquema da biblioteca e otimização do código
- Controlando uma barra de rolagem via programação
- Controlando listas via programação
- Otimização do código para a tabela do tipo CTable
- Controlando uma tabela do tipo CTable via programação
- Aplicação para testar os controles
- Conclusão
Introdução
A fim de obter uma melhor compreensão do propósito desta biblioteca, leia por favor o primeiro artigo: Interfaces Gráficas I: Preparação da Estrutura da Biblioteca (Capítulo 1). 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.
O código da biblioteca juntamente com uma otimização poderia torná-lo mais regularizado e, portanto, mais legível e compreensível para estudar. Além disso, nós vamos continuar a desenvolver os controles criados nos artigos anteriores: listas, tabelas e barras de rolagem. Vamos adicionar métodos que permitem gerenciar programaticamente as propriedades desses controles durante a execução da aplicação MQL.
Alterações no esquema da biblioteca e otimização do código
Otimizado parcialmente o código em todos os arquivos de biblioteca relacionados aos controles. Os casos com o código frequentemente repetidos foram colocados em métodos separados, e os próprios métodos foram movidos para uma classe separada.
Aqui está como ele foi feito. A classe CElement foi renomeada para CElementBase. Esta é a classe base para todos os controles da biblioteca. Agora, a próxima classe derivada é o nova classe CElement, que contém os métodos frequentemente repetido em todos os controles. Estas incluem:
- Método para armazenar o ponteiro de formulário, a qual o controle está ligado
- Verifica a disponibilidade do ponteiro do formulário
- Verifica o identificador do controle ativado
- Cálculo das coordenadas absolutas
- Cálculo das coordenadas relativas da borda do formulário
As classes CElementBase e CElement estão localizadas em arquivos diferentes, ElementBase.mqh e Element.mqh, respectivamente. Portanto, o arquivo ElementBase.mqh com a classe base está incluída no arquivo Element.mqh. Já que o tipo CWindows deve ser definido aqui, inclua o arquivoWindow.mqh também. Isto é apresentado no código a seguir:
//| Element.mqh |
//| Copyright 2016, MetaQuotes Software Corp. |
//| http://www.mql5.com |
//+------------------------------------------------------------------+
#include "ElementBase.mqh"
#include "Controls\Window.mqh"
//+------------------------------------------------------------------+
//| Classe para obter os parâmetros do mouse |
//+------------------------------------------------------------------+
class CElement : public CElementBase
{
protected:
//--- Ponteiro para o formulário ao qual o elemento está ligado
CWindow *m_wnd;
//---
public:
CElement(void);
~CElement(void);
//--- Armazena o ponteiro ao formulário
void WindowPointer(CWindow &object) { m_wnd=::GetPointer(object); }
//---
protected:
//--- Verifica se existe um ponteiro ao formulário
bool CheckWindowPointer(void);
//--- Verifica o identificador do controle ativado
bool CheckIdActivatedElement(void);
//--- Cálculo das coordenadas absolutas
int CalculateX(const int x_gap);
int CalculateY(const int y_gap);
//--- Cálculo das coordenadas relativas a partir da borda do formulário
int CalculateXGap(const int x);
int CalculateYGap(const int y);
};
//+------------------------------------------------------------------+
//| Construtor |
//+------------------------------------------------------------------+
CElement::CElement(void)
{
}
//+------------------------------------------------------------------+
//| Destrutor |
//+------------------------------------------------------------------+
CElement::~CElement(void)
{
}
Todos esses métodos e seu código eram frequentemente repetidos em todas as classes de controles. Movê-los para uma classe separada deixou o código em classes de controles significativamente mais compreensível e legível. O código de todos esses métodos é simples e literalmente se encaixa em uma única linha (veja o código abaixo). O posicionamento de controle em relação a um lado do formulário é considerado no cálculo das coordenadas.
//| Verifica o identificador do controle ativado |
//+------------------------------------------------------------------+
bool CElement::CheckIdActivatedElement(void)
{
return(m_wnd.IdActivatedElement()==CElementBase::Id());
}
//+------------------------------------------------------------------+
//| Calcula a coordenada X absoluta |
//+------------------------------------------------------------------+
int CElement::CalculateX(const int x_gap)
{
return((CElementBase::AnchorRightWindowSide())? m_wnd.X2()-x_gap : m_wnd.X()+x_gap);
}
//+------------------------------------------------------------------+
//| Calcula a coordenada Y absoluta |
//+------------------------------------------------------------------+
int CElement::CalculateY(const int y_gap)
{
return((CElementBase::AnchorBottomWindowSide())? m_wnd.Y2()-y_gap : m_wnd.Y()+y_gap);
}
//+------------------------------------------------------------------+
//| Calcula a coordenada X relativa da borda do formulário |
//+------------------------------------------------------------------+
int CElement::CalculateXGap(const int x)
{
return((CElementBase::AnchorRightWindowSide())? m_wnd.X2()-x : x-m_wnd.X());
}
//+------------------------------------------------------------------+
//| Calcula a coordenada Y relativa da borda do formulário |
//+------------------------------------------------------------------+
int CElement::CalculateYGap(const int y)
{
return((CElementBase::AnchorBottomWindowSide())? m_wnd.Y2()-y : y-m_wnd.Y());
}
Alguém pode perguntar: "Por que estes métodos não foram colocados na versão antiga da classe CElement? "Isso não era possível: quando estava incluindo o arquivo Window.mqh e tentássemos compilar, ocorria o erro de declaração sem o tipo, e como resultado - muitos outros erros relacionados:
Fig. 1. Mensagem na compilação que o tipo CElement estava faltando
As tentativas de contornar esta dificuldade, incluindo o Window.mqh depois do corpo da classe CElement, quando um objeto do tipo CWindow já havia sido declarado no corpo dessa classe, levava ao erro de compilação familiar do tipo especificado faltando.
Fig. 2. Mensagem na compilação que o tipo CWindow estava faltando
Por isso, decidiu-se criar uma classe intermediária herdada adicional para colocar o código e os métodos frequentemente repetidos para trabalhar com o ponteiro do formulário, ao qual os controles estão ligados. Parte do esquema da biblioteca quanto à relação entre o formulário e os controles aparentam a seguir:
Fig. 3. Parte do esquema da biblioteca quanto à relação entre o formulário e os controles.
Como pode ser visto a partir do esquema acima, a classe CWindow é derivada diretamente da classe CElementBase, já que a classe intermediária CElement é agora supérflua e inconveniente para o formulário. Todas as outras classes de controles são derivadas a partir da classe intermediária CElement.
Controlando uma barra de rolagem via programação
A necessidade de controlar programaticamente as barras de rolagem surgiram durante a aplicação da biblioteca. Para este objetivo, o método MovingThumb() foi implementado nas classes CScrollV e CScrollH, que podem ser utilizados para deslocar a barra de rolagem para a posição especificada.
A lista abaixo mostra o código apenas para a barra de rolagem vertical, como ela é virtualmente idêntica à barra de rolagem horizontal. O método tem um argumento, que tem como padrão WRONG_VALUE. Se o método é chamado sem especificar a posição (com o valor padrão), o polegar será movido para a última posição da lista. Isso é útil quando os itens são adicionados à lista, enquanto o programa está em execução, e também permite implementar o deslocamento automático da lista.
//| Classe para a gestão da barra de rolagem vertical |
//+------------------------------------------------------------------+
class CScrollV : public CScroll
{
public:
//--- Move o polegar para a posição especificada
void MovingThumb(const int pos=WRONG_VALUE);
};
//+------------------------------------------------------------------+
//| Move o polegar para a posição especificada |
//+------------------------------------------------------------------+
void CScrollV::MovingThumb(const int pos=WRONG_VALUE)
{
//--- Sai, se a barra de rolagem não é necessária
if(m_items_total<=m_visible_items_total)
return;
//--- Para verificar a posição do polegar
uint check_pos=0;
//--- Ajuste no caso do tamanho ser excedido
if(pos<0 || pos>m_items_total-m_visible_items_total)
check_pos=m_items_total-m_visible_items_total;
else
check_pos=pos;
//--- Armazena a posição do polegar
CScroll::CurrentPos(check_pos);
//--- Calcula e definir a coordenada Y do polegar da barra de rolagem
CalculateThumbY();
}
Controlando listas via programação
Uma série de métodos públicos para gerir as listas têm sido implementadas, que executam as seguintes ações:
- Reconstrução da lista
- Adicionando um item para o fim da lista
- Limpar a lista (excluindo todos os itens)
- Rolar a lista
Além disso, como parte da otimização do código da biblioteca, os métodos privados repetidos do código foram adicionados às classes da lista:
- Cálculo da coordenada Y do item
- Cálculo do tamanho dos itens
- Cálculo do tamanho da lista ao longo do eixo Y
Vamos dar uma olhada mais detalhadamente na estrutura desses métodos na classe CListView. Os métodos privados são apenas métodos auxiliares para o código repetido mais de uma vez em diferentes partes da classe. Eles ocupam apenas uma linha em cada método:
//| Classe para criar uma lista |
//+------------------------------------------------------------------+
class CListView : public CElement
{
private:
//--- Cálculo da coordenada Y do item
int CalculationItemY(const int item_index=0);
//--- Calculando a largura dos itens
int CalculationItemsWidth(void);
//--- Cálculo do tamanho da lista ao longo do eixo Y
int CalculationYSize(void);
//+------------------------------------------------------------------+
//| Cálculo da coordenada Y do item |
//+------------------------------------------------------------------+
int CListView::CalculationItemY(const int item_index=0)
{
return((item_index>0)? m_items[item_index-1].Y2()-1 : CElementBase::Y()+1);
}
//+------------------------------------------------------------------+
//| Cálculo da largura dos itens |
//+------------------------------------------------------------------+
int CListView::CalculationItemsWidth(void)
{
return((m_items_total>m_visible_items_total) ? CElementBase::XSize()-m_scrollv.ScrollWidth()-1 : CElementBase::XSize()-2);
}
//+------------------------------------------------------------------+
//| Cálculo do tamanho da lista ao longo do eixo Y |
//+------------------------------------------------------------------+
int CListView::CalculationYSize(void)
{
return(m_item_y_size*m_visible_items_total-(m_visible_items_total-1)+2);
}
A limpeza da lista fala por si só: todos os itens são removidos da lista. Para fazer isso, use o método CListView::Clear(). Aqui, as primitivas gráficas são removidas primeiro, o array de ponteiros para esses objetos é liberado e os valores padrão são definidos para determinados campos da classe. Depois disso, o tamanho da lista é definido como zero e os parâmetros de barra de rolagem são resetados. No final do método, é necessário adicionar o ponteiro ao fundo da lista para o array de ponteiros novamente, como se ele tivesse sido removido anteriormente pelo método CElementBase::FreeObjectsArray().
//| Classe para criar uma lista |
//+------------------------------------------------------------------+
class CListView : public CElement
{
public:
//--- Limpa a lista (apaga todos os itens)
void Clear(void);
};
//+------------------------------------------------------------------+
//| Limpa a lista (apaga todos os itens) |
//+------------------------------------------------------------------+
void CListView::Clear(void)
{
//--- Remove os itens de objetos
for(int r=0; r<m_visible_items_total; r++)
m_items[r].Delete();
//--- Limpa o array de ponteiros para os objetos
CElementBase::FreeObjectsArray();
//--- Define os valores padrão
m_selected_item_text ="";
m_selected_item_index =0;
//--- Define o tamanho zero à lista
ListSize(0);
//--- Reseta os valores de barra de rolagem
m_scrollv.Hide();
m_scrollv.MovingThumb(0);
m_scrollv.ChangeThumbSize(m_items_total,m_visible_items_total);
//--- Adiciona o fundo da lista ao array de ponteiros para os objetos do controle
CElementBase::AddToArray(m_area);
}
Para reconstruir a lista, use o método CListView::Rebuilding(). A reconstrução é uma situação em que é necessário re-criar a lista totalmente. Este método pode ser usado para alterar o número total de itens e o número de itens visíveis. Isto é, o tamanho da lista também mudará, se o número de itens visíveis é definido diferente do valor original.
A lista é limpa no início do método CListView::Rebuilding(). Em seguida, os valores dos argumentos transmitidos são usados para definir os novos tamanhos e para ajustar a altura da lista, se o número de itens visíveis mudar. Em seguida, os tamanhos dos objetos na barra de posicionamento são ajustados. Depois disso, a lista é criada, e se o número total de itens exceder a quantidade especificada de itens visíveis, uma barra de rolagem é exibida.
//| Classe para criar uma lista |
//+------------------------------------------------------------------+
class CListView : public CElement
{
public:
//--- Reconstrução da lista
void Rebuilding(const int items_total,const int visible_items_total);
};
//+------------------------------------------------------------------+
//| Reconstrução da lista |
//+------------------------------------------------------------------+
void CListView::Rebuilding(const int items_total,const int visible_items_total)
{
//--- Limpando a lista
Clear();
//--- Ajusta o tamanho de exibição da lista e sua parte visível
ListSize(items_total);
VisibleListSize(visible_items_total);
//--- Ajusta o tamanho da lista
int y_size=CalculationYSize();
if(y_size!=CElementBase::YSize())
{
m_area.YSize(y_size);
m_area.Y_Size(y_size);
CElementBase::YSize(y_size);
}
//--- Ajusta o tamanho da barra de rolagem
m_scrollv.ChangeThumbSize(m_items_total,m_visible_items_total);
m_scrollv.ChangeYSize(y_size);
//--- Cria a lista
CreateList();
//--- Mostra a barra de rolagem, se necessário
if(m_items_total>m_visible_items_total)
{
if(CElementBase::IsVisible())
m_scrollv.Show();
}
}
Um método separado CListView::CreateItem() foi implementado para a criação de um único item, como seu código será utilizado quando adicionar um item à lista no método CListView::AddItem() durante o tempo de execução, e não apenas ao criar uma lista inteira no ciclo dentro do método CListView::CreateList().
O método CListView::AddItem() recebe somente um argumento - o texto exibido do item. Ela é uma string vazia por padrão. O texto também pode ser adicionado depois de criar o item usando o método CListView::SetItemValue(). No início do método CListView::AddItem(), o conjunto de itens é incrementado por um elemento. Então, se a quantidade total de elementos no momento não for maior que o número de itens visíveis, isto significa que é necessário criar um objeto gráfico. Se o número da quantidade visível for excedida, então é necessário mostrar a barra de rolagem e ajustar o o tamanho do polegar, bem como ajustar a largura dos itens.
//| Classe para criar uma lista |
//+------------------------------------------------------------------+
class CListView : public CElement
{
public:
//--- Adiciona um item à lista
void AddItem(const string value="");
};
//+------------------------------------------------------------------+
//| Adiciona um item à lista |
//+------------------------------------------------------------------+
void CListView::AddItem(const string value="")
{
//--- Aumenta o tamanho do array por um elemento
int array_size=ItemsTotal();
m_items_total=array_size+1;
::ArrayResize(m_item_value,m_items_total);
m_item_value[array_size]=value;
//--- Se o número total de itens for maior do que os visíveis
if(m_items_total>m_visible_items_total)
{
//--- Ajusta o tamanho do polegar e exibe a barra de rolagem
m_scrollv.ChangeThumbSize(m_items_total,m_visible_items_total);
if(CElementBase::IsVisible())
m_scrollv.Show();
//--- Sai, se o array tem menos do que um elemento
if(m_visible_items_total<1)
return;
//--- Calcula a largura dos itens de exibição da lista
int width=CElementBase::XSize()-m_scrollv.ScrollWidth()-1;
if(width==m_items[0].XSize())
return;
//--- Define o novo tamanho para os itens da lista
for(int i=0; i<m_items_total && i<m_visible_items_total; i++)
{
m_items[i].XSize(width);
m_items[i].X_Size(width);
}
//---
return;
}
//--- Cálculo das coordenadas
int x=CElementBase::X()+1;
int y=CalculationItemY(array_size);
//--- Calcula a largura dos itens de exibição da lista
int width=CalculationItemsWidth();
//--- Cria o objeto
CreateItem(array_size,x,y,width);
//--- Destaca o item selecionado
HighlightSelectedItem();
//--- Armazena o texto do item selecionado
if(array_size==1)
m_selected_item_text=m_item_value[0];
}
O método CListView::Scrolling() é projetado para percorrer os itens da lista via programação. O número da posição na lista é tomada por um único argumento. O valor padrão é WRONG_VALUE, o que significa deslocar a lista para a última posição.
//| Classe para criar uma lista |
//+------------------------------------------------------------------+
class CListView : public CElement
{
public:
//--- Rolagem da lista
void Scrolling(const int pos=WRONG_VALUE);
};
//+------------------------------------------------------------------+
//| Rolagem da lista |
//+------------------------------------------------------------------+
void CListView::Scrolling(const int pos=WRONG_VALUE)
{
//--- Sai, se a barra de rolagem não é necessária
if(m_items_total<=m_visible_items_total)
return;
//--- Para determinar a posição do polegar
int index=0;
//--- Índice da última posição
int last_pos_index=m_items_total-m_visible_items_total;
//--- Ajuste no caso do intervalo ser excedido
if(pos<0)
index=last_pos_index;
else
index=(pos>last_pos_index)? last_pos_index : pos;
//--- Move o botão de rolagem
m_scrollv.MovingThumb(index);
//--- Move a lista
UpdateList(index);
}
Métodos semelhantes foram implementados para listas do tipo CCheckBoxList.
Otimização do código para a tabela do tipo CTable
O código da classe CTable também foi otimizada. Ela se tornou mais compacta e de fácil leitura, devido à adição de um número de métodos privados, que contém o código repetido com frequência. Esses métodos são os seguintes:
- Redimensionar as linhas do array
- Inicialização das células com valores padrão
- Cálculo do tamanho da tabela ao longo do eixo X
- Cálculo do tamanho da tabela ao longo do eixo Y
- Cálculo da coordenada X da célula
- Cálculo da coordenada Y da célula
- Cálculo da largura da coluna
- Alteração da largura das colunas
- Alteração do tamanho da tabela ao longo do eixo Y
//| Classe para a criação de uma tabela de caixa de edição |
//+------------------------------------------------------------------+
class CTable : public CElement
{
private:
//--- Redimensiona as linhas do array
void RowResize(const uint column_index,const uint new_size);
//--- Inicialização das células com valores padrão
void CellInitialize(const uint column_index,const int row_index=WRONG_VALUE);
//--- Cálculo do tamanho da tabela ao longo do eixo X
int CalculationXSize(void);
//--- Cálculo do tamanho da tabela ao longo do eixo Y
int CalculationYSize(void);
//--- Cálculo da coordenada X da célula
int CalculationCellX(const int column_index=0);
//--- Cálculo da coordenada Y da célula
int CalculationCellY(const int row_index=0);
//--- Cálculo da largura da coluna
int CalculationColumnWidth(const bool is_last_column=false);
//--- Alteração da largura das colunas
void ColumnsXResize(void);
//--- Alteração do tamanho da tabela ao longo do eixo Y
void YResize(void);
};
O método CTable::CalculationColumnWidth() é destinado para calcular a largura das colunas da tabela, possuindo apenas um argumento com o valor false. O valor padrão é usado para calcular a largura total de todas as colunas. Se o valor passado for true, então a largura para a última coluna será calculada. Neste caso, é usado a chamada de um método recursivo. A divisão no cálculo da largura total e a largura da última coluna é necessária, como no cálculo geral, a extremidade da direita da última coluna pode não coincidir com a borda da direita da tabela.
//| Cálculo da largura da coluna |
//+------------------------------------------------------------------+
int CTable::CalculationColumnWidth(const bool is_last_column=false)
{
int width=0;
//--- Verifica a presença de uma barra de rolagem vertical
bool is_scrollv=m_rows_total>m_visible_rows_total;
//---
if(!is_last_column)
{
if(m_visible_columns_total==1)
width=(is_scrollv)? m_x_size-m_scrollv.ScrollWidth() : width=m_x_size-2;
else
{
if(is_scrollv)
width=(m_x_size-m_scrollv.ScrollWidth())/int(m_visible_columns_total);
else
width=m_x_size/(int)m_visible_columns_total+1;
}
}
else
{
width=CalculationColumnWidth();
int last_column=(int)m_visible_columns_total-1;
int w=m_x_size-(width*last_column-last_column);
width=(is_scrollv) ? w-m_scrollv.ScrollWidth()-1 : w-2;
}
//---
return(width);
}
O método CTable::ColumnsXResize() é chamado quando uma tabela é criada ou quando a largura da tabela é alterada. Aqui o método CTable::CalculationColumnWidth() é chamado para o cálculo das larguras da coluna, que foi discutido acima. No final do processo, se a tabela está ordenada, é necessário ajustar a posição da seta de sinal da tabela ordenada.
//| Alteração da largura das colunas |
//+------------------------------------------------------------------+
void CTable::ColumnsXResize(void)
{
//--- Cálculo da largura das colunas
int width=CalculationColumnWidth();
//--- Colunas
for(uint c=0; c<m_columns_total && c<m_visible_columns_total; c++)
{
//--- Cálculo da coordenada X
int x=CalculationCellX(c);
//--- Ajusta a largura da última coluna
if(c+1>=m_visible_columns_total)
width=CalculationColumnWidth(true);
//--- Linhas
for(uint r=0; r<m_rows_total && r<m_visible_rows_total; r++)
{
//--- Coordenadas
m_columns[c].m_rows[r].X(x);
m_columns[c].m_rows[r].X_Distance(x);
//--- Largura
m_columns[c].m_rows[r].XSize(width);
m_columns[c].m_rows[r].X_Size(width);
//--- Margens a partir da borda do painel
m_columns[c].m_rows[r].XGap(CalculateXGap(x));
}
}
//--- Sai, se a tabela não está ordenada
if(m_is_sorted_column_index==WRONG_VALUE)
return;
//--- Desloca por um índice, se o modo de cabeçalho fixo está habilitado
int l=(m_fix_first_column) ? 1 : 0;
//--- Obtém as posições atuais dos polegares das barras de rolagem vertical e horizontal
int h=m_scrollh.CurrentPos()+l;
//--- Se não exceder o intervalo do array
if(m_is_sorted_column_index>=h && m_is_sorted_column_index<(int)m_visible_columns_total)
{
//--- Desloca a seta para a coluna de tabela ordenada
ShiftSortArrow(m_is_sorted_column_index);
}
}
Você pode facilmente estudar o código dos outros métodos privados, fornecidos na lista no início desta seção, já que eles não contêm qualquer coisa complexa, o que poderia causar perguntas.
Além dos métodos acima descritos, no âmbito da otimização, um método privado separado CTable::CreateCell() foi implementado para a criação das células da tabela. Outra adição útil para as tabelas do tipo CTable nesta atualização é a formatação automática do estilo da zebra. Anteriormente, se um usuário da biblioteca precisasse fazer uma tabela listrada para uma melhor compreensão do conjunto de dados, o método CTable::CellColor() tem de ser usado. Ou seja, é necessário atribuir cores para cada célula da tabela individualmente. Isso é inconveniente e demorado. Agora, para fazer a tabela listrada, basta chamar o método CTable::IsZebraFormatRows() antes de criar o controle, passando a segunda cor como um único argumento. O valor definido pelo método CTable::CellColor() para todas as células da tabela (padrão - branco) é usado como a primeira cor.
//| Classe para a criação de uma tabela de caixa de edição |
//+------------------------------------------------------------------+
class CTable : public CElement
{
private:
//--- Modo de colorir listrado estilo zebra da tabela
color m_is_zebra_format_rows;
//---
public:
//--- Modo de linhas de formatação no estilo zebra
void IsZebraFormatRows(const color clr) { m_is_zebra_format_rows=clr; }
};
Se a segunda cor para a formatação no estilo Zebra for especificada, então o método privado CTable::ZebraFormatRows() é chamado sempre que necessário.
//| Classe para a criação de uma tabela de caixa de edição |
//+------------------------------------------------------------------+
class CTable : public CElement
{
private:
//--- Formata a tabela no estilo zebra
void ZebraFormatRows(void);
};
//+------------------------------------------------------------------+
//| Formata a tabela no estilo zebra |
//+------------------------------------------------------------------+
void CTable::ZebraFormatRows(void)
{
//--- Sai, se o modo está desativado
if(m_is_zebra_format_rows==clrNONE)
return;
//--- A cor padrão
color clr=m_cell_color;
//---
for(uint c=0; c<m_columns_total; c++)
{
for(uint r=0; r<m_rows_total; r++)
{
if(m_fix_first_row)
{
if(r==0)
continue;
//---
clr=(r%2==0)? m_is_zebra_format_rows : m_cell_color;
}
else
clr=(r%2==0)? m_cell_color : m_is_zebra_format_rows;
//--- Armazena a cor de fundo das células no array comum
m_vcolumns[c].m_cell_color[r]=clr;
}
}
}
Controlando uma tabela do tipo CTable via programação
Nesta atualização da biblioteca, somente a tabela do tipo CTable recebe um controle programático. Vários métodos públicos foram implementadas para executar as seguintes ações:
- Reconstrução da tabela
- Adicionar uma coluna
- Adicionar uma linha
- Limpar a tabela (excluindo todas as colunas e linhas)
- Rolagem horizontal e vertical da tabela
//| Classe para a criação de uma tabela de caixa de edição |
//+------------------------------------------------------------------+
class CTable : public CElement
{
public:
//--- Reconstrução da tabela
void Rebuilding(const int columns_total,const int visible_columns_total,const int rows_total,const int visible_rows_total);
//--- Adiciona uma coluna à tabela
void AddColumn(void);
//--- Adiciona uma linha à tabela
void AddRow(void);
//--- Limpa a tabela (exclui todas as linhas e colunas)
void Clear(void);
//--- Rolagem da tabela: (1) vertical e (2) horizontal
void VerticalScrolling(const int pos=WRONG_VALUE);
void HorizontalScrolling(const int pos=WRONG_VALUE);
};
O método CTable::Clear() para limpar a tabela não será mencionada: é praticamente idêntico aquela nas listas, que foi mencionado nas seções anteriores deste artigo.
Para reconstruir a tabela, é necessário chamar o método CTable::Rebuilding(), em que o número total de colunas e linhas, bem como a sua quantidade visível deve ser passada como os argumentos. Aqui, no início do método, a tabela é apagada. Ou seja, todas as suas colunas e linhas são excluídas. Em seguida, as novas dimensões são definidas por array, com base nos valores dos parâmetros passados. As barras de deslocamento são ajustadas de acordo com o número atual total de linhas e colunas em relação à sua quantidade visível. Depois que todos os cálculos forem feitos, as células da tabela são criadas e, se necessário, as barras de rolagem são visíveis.
//| Reconstrução da tabela |
//+------------------------------------------------------------------+
void CTable::Rebuilding(const int columns_total,const int visible_columns_total,const int rows_total,const int visible_rows_total)
{
//--- Limpa a tabela
Clear();
//--- Ajusta o tamanho da tabela e sua parte visível
TableSize(columns_total,rows_total);
VisibleTableSize(visible_columns_total,visible_rows_total);
//--- Ajusta os tamanhos das barras de rolagem
m_scrollv.ChangeThumbSize(rows_total,visible_rows_total);
m_scrollh.ChangeThumbSize(columns_total,visible_columns_total);
//--- Verifica a presença de uma barra de rolagem vertical
bool is_scrollv=m_rows_total>m_visible_rows_total;
//--- Verifica a presença de uma barra de rolagem horizontal
bool is_scrollh=m_columns_total>m_visible_columns_total;
//--- Calcula o tamanho da tabela ao longo do eixo Y
int y_size=CalculationYSize();
//--- Redimensiona a barra de rolagem vertical
m_scrollv.ChangeYSize(y_size);
//--- Redimensiona a tabela
m_y_size=(is_scrollh)? y_size+m_scrollh.ScrollWidth()-1 : y_size;
m_area.YSize(m_y_size);
m_area.Y_Size(m_y_size);
//--- Ajusta a localização da barra de rolagem horizontal ao longo do eixo Y
m_scrollh.YDistance(CElementBase::Y2()-m_scrollh.ScrollWidth());
//--- Se uma barra de rolagem horizontal é necessária
if(is_scrollh)
{
//--- Define o tamanho de acordo com a presença de uma barra de rolagem vertical
if(!is_scrollv)
m_scrollh.ChangeXSize(m_x_size);
else
{
//--- Calcula e altera a largura da barra de rolagem horizontal
int x_size=m_area.XSize()-m_scrollh.ScrollWidth()+1;
m_scrollh.ChangeXSize(x_size);
}
}
//--- Cria as células da tabela
CreateCells();
//--- Mostra a barra de rolagem, se necessário
if(rows_total>visible_rows_total)
{
if(CElementBase::IsVisible())
m_scrollv.Show();
}
if(columns_total>visible_columns_total)
{
if(CElementBase::IsVisible())
m_scrollh.Show();
}
}
Os métodos para a adição de uma coluna CTable::AddColumn() e linha CTable::AddRow() são muito semelhantes nos seus algoritmos, por conseguinte, apenas um deles serão considerados aqui.
No início do método CTable::AddColumn(), o tamanho do array de linhas e colunas é definido naquela coluna. Em seguida, a inicialização das células com valores padrão para a coluna adicionada é realizada usando o método CTable::CellInitialize(). Depois disso, se o número total de colunas não for maior do que o valor especificado visível:
- As larguras das colunas são calculadas
- Um certo número de objetos gráficos (células da tabela) são criados para as colunas adicionadas
- Se necessário, a tabela é formatada no estilo zebra
- E no final do método, a tabela é atualizada
Se acontecer do número total de colunas e linhas for maior do que o valor especificado visível após o aumento das colunas e linhas do array, então é necessário mostrar uma barra de deslocamento horizontal e, assim, ajustar a altura da tabela. Depois disso, a tabela é formatada no estilo zebra e o programa deixa o método.
//| Adiciona uma coluna à tabela |
//+------------------------------------------------------------------+
void CTable::AddColumn(void)
{
//--- Aumenta o tamanho do array por um elemento
uint array_size=ColumnsTotal();
m_columns_total=array_size+1;
::ArrayResize(m_vcolumns,m_columns_total);
//--- Ajusta o tamanho das linhas do array
RowResize(array_size,m_rows_total);
//--- Inicializa os arrays com os valores padrão
CellInitialize(array_size);
//--- Se o número total de colunas for maior do que a quantidade visível
if(m_columns_total>m_visible_columns_total)
{
//--- Ajusta o tamanho da tabela ao longo do eixo Y
YResize();
//--- Se não houver nenhuma barra de rolagem vertical, faça a barra de rolagem horizontal ocupar toda a largura da tabela
if(m_rows_total<=m_visible_rows_total)
m_scrollh.ChangeXSize(m_x_size);
//--- Ajusta o tamanho do polegar e exibe a barra de rolagem
m_scrollh.ChangeThumbSize(m_columns_total,m_visible_columns_total);
//--- Mostra a barra de rolagem
if(CElementBase::IsVisible())
m_scrollh.Show();
//--- Formatação das linhas em estilo zebra
ZebraFormatRows();
//--- Atualiza a tabela
UpdateTable();
return;
}
//--- Cálculo da largura das colunas
int width=CalculationColumnWidth();
//--- Ajusta a largura da última coluna
if(m_columns_total>=m_visible_columns_total)
width=CalculationColumnWidth(true);
//--- Cálculo da coordenada X
int x=CalculationCellX(array_size);
//---
for(uint r=0; r<m_rows_total && r<m_visible_rows_total; r++)
{
//--- Cálculo da coordenada Y
int y=CalculationCellY(r);
//--- Criando o objeto
CreateCell(array_size,r,x,y,width);
//--- Define a cor correspondente ao cabeçalho
if(m_fix_first_row && r==0)
m_columns[array_size].m_rows[r].BackColor(m_headers_color);
}
//--- Formatação das linhas em estilo zebra
ZebraFormatRows();
//--- Atualiza a tabela
UpdateTable();
}
Os métodos CTable::VerticalScrolling() e CTable::HorizontalScrolling() para a rolagem da tabela são virtualmente idênticos aos discutidos na seção listas, portanto, seu código não será fornecido aqui. Você pode encontrá-los nos arquivos anexados a este artigo.
Agora, vamos criar um aplicativo MQL de teste, que demonstram os novos recursos das listas e tabelas do tipo CTable.
Aplicação para testar os controles
Para fins de teste, vamos criar um uma aplicação MQL semelhante, que permitiria ver de imediato o funcionamento dos métodos adicionados às classes das listas e tabelas do tipo CTable. Crie duas guias na interface gráfica desta aplicação. A primeira guia irá conter uma tabela do tipo CTable e controles para gerenciar as propriedades da tabela, localizada acima da tabela. Aqueles consistirá de dois botões e quatro caixas de edição numéricos:
- Botão «CLEAR TABLE» para limpar a tabela (excluindo todas as colunas e linhas)
- Botão «REBUILD TABLE» para reconstruir a tabela com base nos parâmetros especificados nas caixas de edição numéricos
- Caixa de edição «Rows total» para inserir o número total de linhas da tabela
- Caixa de edição «Columns total» para inserir o número total de colunas da tabela
- Caixa de edição «Visible rows total» para inserir o número de linhas da tabela visível
- Caixa de edição «Visible columns total» para inserir o número de colunas da tabela visível
A imagem abaixo mostra a sua aparência:
Fig. 4. Grupo de controles na primeira guia
A segunda guia irá conter duas listas (Ver a lista e lista de caixas de seleção). Para demonstrar a gestão programática das listas, os seguintes controles estarão presentes:
- Botão «CLEAR LISTS» para limpar as listas (excluindo todos os itens)
- Botão «REBUILD LISTS» para reconstruir as listas com base nos parâmetros especificados nas caixas de edição numéricos
- Caixa de edição «Items total» para inserir o número total de itens da lista
- Caixa de edição «Itens visíveis no total» para inserir o número de itens da lista visível
A imagem abaixo mostra os controles na segunda guia. Como um complemento, mais dois controles foram adicionados a ele: os controles calendário suspenso e Horário.
Fig. 5. Grupo de controles na segunda guia
Antes de prosseguir com a demonstração de características das listas e tabelas implementadas nesta atualização, vamos debruçar sobre uma outra adição, o que facilitará o trabalho do desenvolvedor MQL no timer de sua aplicação MQL. Esta é a classe CTimeCounter. Ela pode ser usada para gerenciar a atualização (redesenhar) com frequência para separar grupos de controles de interface gráfica nos intervalos de tempo especificados. A classe CTimeCounter contém apenas três campos e dois métodos (ver o código abaixo).
//| TimeCounter.mqh |
//| Copyright 2016, MetaQuotes Software Corp. |
//| http://www.mql5.com |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Contador de tempo |
//+------------------------------------------------------------------+
class CTimeCounter
{
private:
//--- Passo do contador
uint m_step;
//--- Intervalo de tempo
uint m_pause;
//--- Contador de tempo
uint m_time_counter;
//---
public:
CTimeCounter(void);
~CTimeCounter(void);
//--- Ajuste do passo e do intervalo de tempo
void SetParameters(const uint step,const uint pause);
//--- Verifica se o intervalo de tempo especificado terminou
bool CheckTimeCounter(void);
};
//+------------------------------------------------------------------+
//| Construtor |
//+------------------------------------------------------------------+
CTimeCounter::CTimeCounter(void) : m_step(16),
m_pause(1000),
m_time_counter(0)
{
}
//+------------------------------------------------------------------+
//| Destrutor |
//+------------------------------------------------------------------+
CTimeCounter::~CTimeCounter(void)
{
}
O método CTimeCounter::SetParameters() pode ser utilizado para definir e incrementar o contador de intervalo de tempo para uma pausa:
//| Ajuste do passo e intervalo de tempo |
//+------------------------------------------------------------------+
void CTimeCounter::SetParameters(const uint step,const uint pause)
{
m_step =step;
m_pause =pause;
}
O método CTimeCounter::CheckTimeCounter() é projetado para verificar se o intervalo de tempo especificado nos parâmetros de classe terminaram. Se o intervalo de tempo terminou, o método retorna true.
//| Verifica se o intervalo de tempo especificado terminou |
//+------------------------------------------------------------------+
bool CTimeCounter::CheckTimeCounter(void)
{
//--- Aumenta o contador, se o intervalo de tempo especificado não tiver decorrido
if(m_time_counter<m_pause)
{
m_time_counter+=m_step;
return(false);
}
//--- Zera o contador
m_time_counter=0;
return(true);
}
Antes de prosseguir, deve-se notar que a localização dos arquivos nas listas da biblioteca desenvolvidos também foram alterados. Apenas os arquivos que contêm as classes de controles estão localizados agora na pasta «MetaTrader 5\MQL5\Include\EasyAndFastGUI\Controls» Todos os outros arquivos foram movidos para a pasta raiz da biblioteca: «MetaTrader 5\MQL5\Include\EasyAndFastGUI». Portanto, para incluir a biblioteca em um arquivo de uma classe personalizada, é necessário especificar o caminho, como mostrado na lista abaixo. Também mostra como incluir um arquivo com a classe CTimeCounter (Irá ser utilizado em exemplos de teste).
//| Program.mqh |
//| Copyright 2016, MetaQuotes Software Corp. |
//| http://www.mql5.com |
//+------------------------------------------------------------------+
#include <EasyAndFastGUI\WndEvents.mqh>
#include <EasyAndFastGUI\TimeCounter.mqh>
Os parâmetros dos marcadores temporais serão definidos no construtor do contador personalizado:
//| Classe para a criação de uma aplicação |
//+------------------------------------------------------------------+
class CProgram : public CWndEvents
{
protected:
//--- Marcadores de tempo
CTimeCounter m_counter1; // para atualizar a barra de status
CTimeCounter m_counter2; // Para atualizar as listas e tabelas
};
//+------------------------------------------------------------------+
//| Construtor |
//+------------------------------------------------------------------+
CProgram::CProgram(void)
{
//--- Define os parâmetros de configuração para os contadores de tempo
m_counter1.SetParameters(16,500);
m_counter2.SetParameters(16,150);
}
A demonstração de adicionar itens a listas e adicionar colunas e linhas para a tabela após uma purga completa desses controles, será implementado um temporizador. Após o intervalo de tempo especificado, se o número de itens/linhas/colunas for menor que o especificado nas caixas de edição correspondentes, então, eles serão adicionados neste bloco (veja o código abaixo). Para demonstrar a gestão programática da barra de rolagem, os polegares das barras de rolagem serão transferidos para o fim das listas cada vez que um item for adicionado às listas.
//| Timer |
//+------------------------------------------------------------------+
void CProgram::OnTimerEvent(void)
{
CWndEvents::OnTimerEvent();
...
//--- Pausa entre as atualizações dos controles
if(m_counter2.CheckTimeCounter())
{
//--- Adicione uma linha à tabela, se a quantidade total for menor do que o especificado
if(m_table.RowsTotal()<m_spin_edit1.GetValue())
m_table.AddRow();
//--- Adiciona uma coluna à tabela, se o montante total for menor do que o especificado
if(m_table.ColumnsTotal()<m_spin_edit2.GetValue())
m_table.AddColumn();
//--- Adiciona um item à lista, se o montante total for menor do que o especificado
if(m_listview.ItemsTotal()<m_spin_edit5.GetValue())
{
m_listview.AddItem("SYMBOL "+string(m_listview.ItemsTotal()));
//--- Move o polegar da barra de rolagem para o final da lista
m_listview.Scrolling();
}
//--- Adiciona um item à lista de caixas de seleção, se o montante total é menor do que o especificado
if(m_checkbox_list.ItemsTotal()<m_spin_edit5.GetValue())
{
m_checkbox_list.AddItem("Checkbox "+string(m_checkbox_list.ItemsTotal()));
//--- Move o polegar da barra de rolagem para o final da lista
m_checkbox_list.Scrolling();
}
//--- Redesenha o gráfico
m_chart.Redraw();
}
}
A manipulação dos eventos de pressionamento do botão de limpeza e reconstrução das listas e a tabela de consulta são da seguinte forma:
//| Manipulador de eventos do gráfico |
//+------------------------------------------------------------------+
void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
{
//--- O evento de pressionamento do botão
if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON)
{
Print(__FUNCTION__," > id: ",id,"; lparam: ",lparam,"; dparam: ",dparam,"; sparam: ",sparam);
//--- Evento do primeiro botão
if(lparam==m_simple_button1.Id())
{
//--- Limpa a tabela
m_table.Clear();
return;
}
//--- Evento do segundo botão
if(lparam==m_simple_button2.Id())
{
//--- Reconstrói a tabela
m_table.Rebuilding((int)m_spin_edit3.GetValue(),(int)m_spin_edit4.GetValue(),
(int)m_spin_edit1.GetValue(),(int)m_spin_edit2.GetValue());
//--- Inicializa a tabela
InitializeTable();
//--- Atualiza a tabela para mostrar as mudanças
m_table.UpdateTable();
return;
}
//--- Evento do terceiro botão
if(lparam==m_simple_button3.Id())
{
//--- Limpar as listas
m_listview.Clear();
m_checkbox_list.Clear();
return;
}
//--- Evento do segundo botão
if(lparam==m_simple_button4.Id())
{
//--- Reconstrói as listas
m_checkbox_list.Rebuilding((int)m_spin_edit5.GetValue(),(int)m_spin_edit6.GetValue());
m_listview.Rebuilding((int)m_spin_edit5.GetValue(),(int)m_spin_edit6.GetValue());
//--- Escolhe o oitavo item na exibição da lista
m_listview.SelectItem(7);
//--- Preenchendo a exibição da lista com dados
int items_total=m_listview.ItemsTotal();
for(int i=0; i<items_total; i++)
m_listview.SetItemValue(i,"SYMBOL "+string(i));
//--- Preenche a lista de caixas de seleção com os dados, assinala cada segundo da caixa de seleção
items_total=m_checkbox_list.ItemsTotal();
for(int i=0; i<items_total; i++)
{
m_checkbox_list.SetItemValue(i,"Checkbox "+string(i));
m_checkbox_list.SetItemState(i,(i%2!=0)? true : false);
}
//---
return;
}
//---
return;
}
}
O aplicativo de teste apresentado no artigo pode ser baixado usando o link abaixo para estudá-lo ainda mais.
Conclusão
A esquemática da biblioteca para a criação das interfaces gráficas no atual estágio de desenvolvimento é parecido com a imagem abaixo:
Fig. 6. Estrutura da biblioteca no atual estágio de desenvolvimento.
Na próxima versão da biblioteca, os controles existentes serão melhorados e complementados com novas funcionalidades. 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/2943
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso