English Русский 中文 Español Deutsch 日本語
Interfaces Gráficas VII: O Controle Guias (Capítulo 2)

Interfaces Gráficas VII: O Controle Guias (Capítulo 2)

MetaTrader 5Exemplos | 10 outubro 2016, 09:09
1 021 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.

Foi introduzido no primeiro capítulo da sétima parte três classes de controles para a criação de tabelas: Tabela com o rótulo de texto (CLabelsTable), tabela com a caixa de edição (CTable) e a tabela renderizada (CCanvasTable). Neste artigo (capítulo dois) nós vamos introduzir o controle Guias. Duas classes serão introduzidas para esse controle - a simples e com a funcionalidade estendida.

 


O controle Guias

As guias são utilizadas para controlar a exibição de conjuntos predefinidos do controle interface gráfica. Muitas vezes, as aplicações multi-funcionais exigem um grande número de controles para se encaixar em um espaço alocado limitado para a interface gráfica. As guias podem ser usadas ​​para agrupar os controles por categorias e exibir apenas o grupo que é necessário no momento. Isso torna a interface muito mais acessível e intuitiva para o usuário final. Na superfície, as guias são parecidas com um grupo de botões com rótulos (nome do grupo de controles). Ao mesmo tempo, apenas uma delas que poderá ser selecionada (ativa).

Vamos enumerar todos os componentes deste controle.

  1. Plano de fundo ou a área que encaixa o grupo de controles
  2. Guias

 Fig. 1. Componentes dos controle «Guias» .

Fig. 1. Componentes do controle Guias.

Vamos criar quatro modos para posicionar as guias em relação à área, onde outros controles serão colocados: superior, inferior, esquerda e direita. 

 


Desenvolvimento de uma Classe para a Criação do Controle Guias

Crie o arquivo Tabs.mqh e inclua-o no arquivo da biblioteca WndContainer.mqh:

//+------------------------------------------------------------------+
//|                                                 WndContainer.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Tabs.mqh"

A classe CTabs deve ser criada no arquivo Tabs.mqh. Nesta classe, assim como nas classes de outros controles, é necessário a criação dos métodos padrão, bem como os métodos para armazenar o ponteiro para o formulário, ao qual será anexado esse controle.

//+------------------------------------------------------------------+
//| Classe para a criação das guias                                  |
//+------------------------------------------------------------------+
class CTabs : public CElement
  {
private:
   //--- Ponteiro para o formulário ao qual o elemento está anexado
   CWindow          *m_wnd;
   //---
public:
                     CTabs(void);
                    ~CTabs(void);
   //--- (1) Armazena o ponteiro do formulário, (2) retorna os ponteiros para as barras de rolagem
   void              WindowPointer(CWindow &object)               { m_wnd=::GetPointer(object);      }
   //---
public:
   //--- Manipulador de eventos do gráfico
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- Timer
   virtual void      OnEventTimer(void);
   //--- Move o elemento
   virtual void      Moving(const int x,const int y);
   //--- (1) Exibe, (2) oculta, (3) reseta, (4) remove
   virtual void      Show(void);
   virtual void      Hide(void);
   virtual void      Reset(void);
   virtual void      Delete(void);
   //--- (1) Definir (2), resetar as prioridades para o clique esquerdo do mouse
   virtual void      SetZorders(void);
   virtual void      ResetZorders(void);
   //--- Reseta a cor
   virtual void      ResetColors(void) {}
  };
//+------------------------------------------------------------------+
//| Construtor                                                       |
//+------------------------------------------------------------------+
CTabs::CTabs(void)
  {
  }
//+------------------------------------------------------------------+
//| Destrutor                                                        |
//+------------------------------------------------------------------+
CTabs::~CTabs(void)
  {
  }

As propriedades das Guias podem ser divididas em únicas e comuns. Vamos enumerar as duas listas.

Propriedades únicas

  • Array de ponteiros para os controles ligados à guia
  • Texto
  • Largura

Crie a estrutura TElements para as propriedades únicas e declare um array dinâmico com este tipo: 

class CTabs : public CElement
  {
private:
   //--- Estrutura de propriedades e arrays de controles ligados a cada guia
   struct TElements
     {
      CElement         *elements[];
      string            m_text;
      int               m_width;
     };
   TElements         m_tab[];
  };

Propriedades comuns

  • Modo de posicionamento das guias
  • Cor de fundo da área
  • Tamanho das guias ao longo do eixo Y (altura)
  • Cores das guias em diferentes estados
  • Cor do texto da guia em diferentes estados
  • Cor das bordas da guia
  • Prioridades do clique do botão esquerdo do mouse

Para definir o modo de posicionamento da enumeração ENUM_TABS_POSITION deve ser adicionada ao arquivo Enums.mqh: 

//+------------------------------------------------------------------+
//| Enumeração das guias de posicionamento                           |
//+------------------------------------------------------------------+
enum ENUM_TABS_POSITION
  {
   TABS_TOP    =0,
   TABS_BOTTOM =1,
   TABS_LEFT   =2,
   TABS_RIGHT  =3
  };

A declaração dos campos e métodos para definir as propriedades são fornecidas no código a seguir: 

class CTabs : public CElement
  {
private:
   //--- Posicionamento das guias
   ENUM_TABS_POSITION m_position_mode;
   //--- Cor de fundo área comum
   int               m_area_color;
   //--- Tamanho das guias ao longo do eixo Y
   int               m_tab_y_size;
   //--- Cores das guias em diferentes estados
   color             m_tab_color;
   color             m_tab_color_hover;
   color             m_tab_color_selected;
   color             m_tab_color_array[];
   //--- Cor do texto da guia em diferentes estados
   color             m_tab_text_color;
   color             m_tab_text_color_selected;
   //--- Cor das bordas da guia
   color             m_tab_border_color;
   //--- Prioridades do clique do botão esquerdo do mouse
   int               m_zorder;
   int               m_tab_zorder;
   //---
public:
   //--- (1) Define as posições da guia (superior/inferior/esquerda/direita), (2) define o tamanho da guia ao longo do eixo Y
   void              PositionMode(const ENUM_TABS_POSITION mode)     { m_position_mode=mode;          }
   ENUM_TABS_POSITION PositionMode(void)                       const { return(m_position_mode);       }
   void              TabYSize(const int y_size)                      { m_tab_y_size=y_size;           }
   //--- Cor (1) de fundo comum, (2) cores das abas em diferentes estados, (3) a cor das bordas da guia
   void              AreaColor(const color clr)                      { m_area_color=clr;              }
   void              TabBackColor(const color clr)                   { m_tab_color=clr;               }
   void              TabBackColorHover(const color clr)              { m_tab_color_hover=clr;         }
   void              TabBackColorSelected(const color clr)           { m_tab_color_selected=clr;      }
   void              TabBorderColor(const color clr)                 { m_tab_border_color=clr;        }
   //--- Cor do texto da guia em diferentes estados
   void              TabTextColor(const color clr)                   { m_tab_text_color=clr;          }
   void              TabTextColorSelected(const color clr)           { m_tab_text_color_selected=clr; }
  };

Antes de criar um controle, é necessário adicionar o número necessário de guias com uma indicação do texto exibido e da largura. Vamos escrever o método CTabs::addTab() para isso. Os valores padrão dos argumentos do método são «» (string vazia) e 50 (largura)

class CTabs : public CElement
  {
public:
   //--- Adiciona uma guia
   void              AddTab(const string tab_text="",const int tab_width=50);
  };
//+------------------------------------------------------------------+
//| Adiciona uma guia                                                |
//+------------------------------------------------------------------+
void CTabs::AddTab(const string tab_text,const int tab_width)
  {
//--- Define o tamanho dos arrays das guias
   int array_size=::ArraySize(m_tabs);
   ::ArrayResize(m_tabs,array_size+1);
   ::ArrayResize(m_tab,array_size+1);
//--- Armazena as propriedades passadas
   m_tab[array_size].m_text  =tab_text;
   m_tab[array_size].m_width =tab_width;
//--- Armazena o número de guias
   m_tabs_total=array_size+1;
  }

Se uma determinada guia deve ser pré-selecionada ao carregar o aplicativo MQL ao gráfico, então, antes de criar o controle, é necessário especificar o seu índice usando o método CTabs::SelectedTab(). Ele também irá exigir o método privado CTabs::CheckTabIndex() para verificar e ajustar o índice da guia selecionada, no caso de exceder o seu tamanho. 

class CTabs : public CElement
  {
private:
   //--- Índice da guia selecionada
   int               m_selected_tab;
   //---
public:
   //--- (1) Armazena e (2) retorna o índice da guia selecionada
   void              SelectedTab(const int index)                    { m_selected_tab=index;          }
   int               SelectedTab(void)                         const { return(m_selected_tab);        }
   //---
private:
   //--- Verificação do índice da guia selecionada
   void              CheckTabIndex(void);
  };
//+------------------------------------------------------------------+
//| Verificação do índice da guia selecionada                        |
//+------------------------------------------------------------------+
void CTabs::CheckTabIndex(void)
  {
//--- Verifica se o tamanho do array não excedeu
   int array_size=::ArraySize(m_tab);
   if(m_selected_tab<0)
      m_selected_tab=0;
   if(m_selected_tab>=array_size)
      m_selected_tab=array_size-1;
  }

Para criar o controle, nós precisaremos de três métodos privados e um público

class CTabs : public CElement
  {
private:
   //--- Objetos para criar o elemento
   CRectLabel        m_main_area;
   CRectLabel        m_tabs_area;
   CEdit             m_tabs[];
   //---
public:
   //--- Métodos para criar as guias
   bool              CreateTabs(const long chart_id,const int subwin,const int x,const int y);
   //---
private:
   bool              CreateMainArea(void);
   bool              CreateTabsArea(void);
   bool              CreateButtons(void);
  };

Se nenhuma guia foi adicionada antes de colocar o controle, então, a chamada do método público CTabs::CreateTabs() irá parar de criar a interface gráfica enviará a seguinte mensagem para o log: 

//--- Se não houver uma guia no grupo, reporta
   if(m_tabs_total<1)
     {
      ::Print(__FUNCTION__," > Este método era para ser chamado, "
              "se um grupo conter pelo menos uma guia! Utilize o método CTabs::AddTab()");
      return(false);
     }

A determinação e o cálculo das coordenadas para os componentes dos objetos do controle também serão diferentes, dependendo do modo do posicionamento da guia selecionada. Esses cálculos exigem o método CTabs::SumWidthTabs(), que retorna a largura total de todas as guias. Ele irá retornar a largura da primeira guia nos modos de posicionamento da guia na esquerda (TABS_LEFT) e direita (TABS_RIGHT). Para a parte superior (TABS_TOP) e inferior (TABS_BOTTOM) do modos, é somado a largura de todas as abas. 

class CTabs : public CElement
  {
private:
   //--- Posicionamento das guias
   ENUM_TABS_POSITION m_position_mode;
   //---
private:
   //--- Tamanho de todas as guias
   int               SumWidthTabs(void);
  };
//+------------------------------------------------------------------+
//| Tamanho total de todas as abas                                   |
//+------------------------------------------------------------------+
int CTabs::SumWidthTabs(void)
  {
   int width=0;
//--- Se as guias estão posicionadas à direita ou à esquerda, retorna o tamanho da primeira guia
   if(m_position_mode==TABS_LEFT || m_position_mode==TABS_RIGHT)
      return(m_tab[0].m_width);
//--- Soma do tamanho de todas as guias
   for(int i=0; i<m_tabs_total; i++)
      width=width+m_tab[i].m_width;
//--- Com a consideração de uma sobreposição de pixels
   width=width-(m_tabs_total-1);
   return(width);
  }

O método CTabs::CreateMainArea() foi projetado para criar a área onde os grupos de controle serão localizados. O cálculo das coordenadas e tamanhos dos objetos são parecidos com isso (versão resumida do método): 

//+------------------------------------------------------------------+
//| Criar o fundo da área comum                                      |
//+------------------------------------------------------------------+
bool CTabs::CreateMainArea(void)
  {
//--- Elaborando o nome do objeto
   string name=CElement::ProgramName()+"_tabs_main_area_"+(string)CElement::Id();
//--- Coordenadas
   int x=0;
   int y=0;
//--- Tamanho
   int x_size=0;
   int y_size=0;
//--- Calcula as coordenadas e tamanhos relativos ao posicionamento das guias
   switch(m_position_mode)
     {
      case TABS_TOP :
         x      =CElement::X();
         y      =CElement::Y()+m_tab_y_size-1;
         x_size =CElement::XSize();
         y_size =CElement::YSize()-m_tab_y_size;
         break;
      case TABS_BOTTOM :
         x      =CElement::X();
         y      =CElement::Y();
         x_size =CElement::XSize();
         y_size =CElement::YSize()-m_tab_y_size;
         break;
      case TABS_RIGHT :
         x      =CElement::X();
         y      =CElement::Y();
         x_size =CElement::XSize()-SumWidthTabs()+1;
         y_size =CElement::YSize();
         break;
      case TABS_LEFT :
         x      =CElement::X()+SumWidthTabs()-1;
         y      =CElement::Y();
         x_size =CElement::XSize()-SumWidthTabs()+1;
         y_size =CElement::YSize();
         break;
     }
//--- Cria um objeto
   if(!m_main_area.Create(m_chart_id,name,m_subwin,x,y,x_size,y_size))
      return(false);
//--- Define as propriedades
//--- Margens da borda
//--- Armazena o tamanho
//--- Armazena as coordenadas
//--- Armazena o ponteiro de objeto
//...
   return(true);
  }

A seguir encontramos o cálculo das coordenadas e tamanhos do fundo para as guias, dependendo do modo de posicionamento indicado no método CTabs::CreateTabsArea(): 

//+------------------------------------------------------------------+
//| Criar o fundo da guia                                            |
//+------------------------------------------------------------------+
bool CTabs::CreateTabsArea(void)
  {
//--- Elaborando o nome do objeto
   string name=CElement::ProgramName()+"_tabs_area_"+(string)CElement::Id();
//--- Coordenadas
   int x=CElement::X();
   int y=CElement::Y();
//--- Tamanho
   int x_size=SumWidthTabs();
   int y_size=0;
//--- Calcula as coordenadas e tamanhos relativos ao posicionamento das guias
   if(m_position_mode==TABS_TOP || m_position_mode==TABS_BOTTOM)
     {
      y_size=m_tab_y_size;
     }
   else
     {
      y_size=m_tab_y_size*m_tabs_total-(m_tabs_total-1);
     }
//--- Ajusta as coordenadas para o posicionamento das guias na parte inferior e à direita
   if(m_position_mode==TABS_BOTTOM)
     {
      y=CElement::Y2()-m_tab_y_size-1;
     }
   else if(m_position_mode==TABS_RIGHT)
     {
      x=CElement::X2()-x_size;
     }
//--- Cria um objeto
   if(!m_tabs_area.Create(m_chart_id,name,m_subwin,x,y,x_size,y_size))
      return(false);
//--- Define as propriedades
//--- Margens da borda
//--- Armazena o tamanho
//--- Armazena as coordenadas
//--- Armazena o ponteiro de objeto
//...
   return(true);
  }


O método CTabs::CreateButtons() requer apenas o cálculo das coordenadas para a criação das guias. A largura é definida na classe personalizada do aplicativo antes que do controle ser criado. Caso contrário, o valor padrão (largura) é usado. Abaixo está uma versão resumida do método: 

//+------------------------------------------------------------------+
//| Cria as guias                                                    |
//+------------------------------------------------------------------+
bool CTabs::CreateButtons(void)
  {
//--- Coordenadas
   int x =CElement::X();
   int y =CElement::Y();
//--- Cálculo da coordena em relação ao posicionamento das guias
   if(m_position_mode==TABS_BOTTOM)
      y=CElement::Y2()-m_tab_y_size-1;
   else if(m_position_mode==TABS_RIGHT)
      x=CElement::X2()-SumWidthTabs();
//--- Verificação do índice da guia selecionada
   CheckTabIndex();
//--- Cria as abas
   for(int i=0; i<m_tabs_total; i++)
     {
      //--- Elaborando o nome do objeto
      string name=CElement::ProgramName()+"_tabs_edit_"+(string)i+"__"+(string)CElement::Id();
      //--- Cálculo das coordenadas em relação ao posicionamento das guias para cada guia individual
      if(m_position_mode==TABS_TOP || m_position_mode==TABS_BOTTOM)
         x=(i>0) ? x+m_tab[i-1].m_width-1 : CElement::X();
      else
         y=(i>0) ? y+m_tab_y_size-1 : CElement::Y();
      //--- Cria um objeto
      if(!m_tabs[i].Create(m_chart_id,name,m_subwin,x,y,m_tab[i].m_width,m_tab_y_size))
         return(false);
      //--- Define as propriedades
      //--- Margens da borda do painel
      //--- Coordenadas
      //--- Tamanho
      //--- Inicialização do array de gradiente
      //--- Armazena o ponteiro de objeto
     }
//---
   return(true);
  }

Para anexar qualquer controle para uma guia específica, nós vamos escrever o método CTabs::AddToElementsArray(). Ele tem dois argumentos: (1) índice da guia, que o controle deverá ser ligado e a (2) referência ao controle, um ponteiro que deve ser armazenado no array de controle da guia

class CTabs : public CElement
  {
public:
   //--- Adiciona o controle ao array da guia
   void              AddToElementsArray(const int tab_index,CElement &object);
  };
//+------------------------------------------------------------------+
//| Adiciona o controle para o array da guia especificada            |
//+------------------------------------------------------------------+
void CTabs::AddToElementsArray(const int tab_index,CElement &object)
  {
//--- Verifica se o tamanho do array não excedeu
   int array_size=::ArraySize(m_tab);
   if(array_size<1 || tab_index<0 || tab_index>=array_size)
      return;
//--- Adiciona o ponteiro do controle passado ao array da guia especificada
   int size=::ArraySize(m_tab[tab_index].elements);
   ::ArrayResize(m_tab[tab_index].elements,size+1);
   m_tab[tab_index].elements[size]=::GetPointer(object);
  }

Ao alternar as guias, é necessário ocultar os controles da guia anterior e exibir os controles da guia recém-selecionada. Para este efeito, vamos criar o método CTabs::ShowTabElements(). A verificação para a visibilidade do controle está no início do método. Se o controle está oculto, então, o programa sai do método. Em seguida, ele verifica o índice da guia ativa e ajusta ela, se necessário. Em seguida, ele verifica todas as guias em um loop e executa a tarefa principal do método.  

class CTabs : public CElement
  {
public:
   //--- Mostra somente os controles da guia selecionada
   void              ShowTabElements(void);
  };
//+------------------------------------------------------------------+
//| Mostra somente os controles da guia selecionada                  |
//+------------------------------------------------------------------+
void CTabs::ShowTabElements(void)
  {
//--- Sai, se as abas estão ocultas
   if(!CElement::IsVisible())
      return;
//--- Verificação do índice da guia selecionada
   CheckTabIndex();
//---
   for(int i=0; i<m_tabs_total; i++)
     {
      //--- Obtém o número de controles ligado à guia
      int tab_elements_total=::ArraySize(m_tab[i].elements);
      //--- Se esta guia é selecionada
      if(i==m_selected_tab)
        {
         //--- Exibe os controles da guia
         for(int j=0; j<tab_elements_total; j++)
            m_tab[i].elements[j].Show();
        }
      //--- Ocultar os controles das guias inativas
      else
        {
         for(int j=0; j<tab_elements_total; j++)
            m_tab[i].elements[j].Hide();
        }
     }
  }

O método CTabs::OnClickTab() será usado para manipular o acionamento de uma guia. Em primeiro lugar, o programa tem de passar por dois controles: (1) com base no nome do objeto pressionado e (2) com base no identificador do controle, obtido a partir do nome do objeto utilizando o método CTabs::IdFromObjectName(). Se as verificações são passadas, então o programa (1) encontra a guia pressionada em um loop, (2) armazena os índices e (3) define as cores correspondentes. No final do método CTabs::ShowTabElements(), apenas os controles da guia ativa são visíveis. 

class CTabs : public CElement
  {
private:
   //--- Manipulando o pressionamento da guia
   bool              OnClickTab(const string pressed_object);
   //--- Obtém o identificador do nome do objeto
   int               IdFromObjectName(const string object_name);
  };
//+------------------------------------------------------------------+
//| Pressionando uma guia em um grupo                                |
//+------------------------------------------------------------------+
bool CTabs::OnClickTab(const string clicked_object)
  {
//--- Sai se o pressionamento não foi na célula da tabela
   if(::StringFind(clicked_object,CElement::ProgramName()+"_tabs_edit_",0)<0)
      return(false);
//--- Obtém o identificador do nome do objeto
   int id=IdFromObjectName(clicked_object);
//--- Retorna, se o tipo definido não corresponder
   if(id!=CElement::Id())
      return(false);
//---
   for(int i=0; i<m_tabs_total; i++)
     {
      //--- Se essa guia é clicada
      if(m_tabs[i].Name()==clicked_object)
        {
         //--- Armazena o índice da guia selecionada
         SelectedTab(i);
         //--- Define as cores
         m_tabs[i].Color(m_tab_text_color_selected);
         m_tabs[i].BackColor(m_tab_color_selected);
        }
      else
        {
         //--- Define as cores para as guias inativas
         m_tabs[i].Color(m_tab_text_color);
         m_tabs[i].BackColor(m_tab_color);
        }
     }
//--- Mostra somente os controles da guia selecionada
   ShowTabElements();
   return(true);
  }

Neste caso, o código para o manipulador de evento principal CTabs::OnEvent() é como mostrado no código abaixo: 

//+------------------------------------------------------------------+
//| Manipulador de eventos do gráfico                                |
//+------------------------------------------------------------------+
void CTabs::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Manipulação do evento do movimento do cursor
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      //--- Sai se o elemento está oculto
      if(!CElement::IsVisible())
         return;
      //--- Coordenadas
      int x=(int)lparam;
      int y=(int)dparam;
      for(int i=0; i<m_tabs_total; i++)
         m_tabs[i].MouseFocus(x>m_tabs[i].X() && x<m_tabs[i].X2() && y>m_tabs[i].Y() && y<m_tabs[i].Y2());
      //---
      return;
     }
//--- Manipula o clique do botão esquerdo do mouse sobre o objeto
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- Pressionando uma guia
      if(OnClickTab(sparam))
         return;
     }
  }

Todos os métodos da classe CTabs classe foram considerados. Agora, vamos testar como tudo isso ira funcionar. 

 


Teste do Controle Guias

Para os testes, vamos usar o EA a partir da parte anterior da série e remover tudo, exceto o menu principal e a barra de estado de sua interface gráfica. Nós vamos fazer com que todos os modos de posicionamento da guia (superior/inferior/esquerda/direita) sejam fáceis de testar, bem como o seu tamanho (altura), que seja fácil de ajustar. Para este fim, adicione os parâmetros externos ao EA no arquivo Program.mqh com a classe personalizada:

//--- Parâmetros externos do Expert Advisor
input ENUM_TABS_POSITION TabsPosition =TABS_TOP; // Tabs Position
input                int TabsHeight   =20;       // Tabs Height

Em seguida, na classe personalizada (CProgram) do aplicativo, declare uma instância da classe CTabs e o método para a criação do controle Guias recuado dos pontos da borda do formulário: 

class CProgram : public CWndEvents
  {
private:
   //--- Guias
   CTabs             m_tabs;
   //---
private:
   //--- Guias
#define TABS1_GAP_X           (4)
#define TABS1_GAP_Y           (45)
   bool              CreateTabs(void);
  };

Haverá quatro guias no total. O texto exibido e a largura das guias podem ser modificadas ao inicializar os arrays, os valores de seus elementos são, em seguida, passado em um loop usando o método CTabs::addTab(). A altura e posicionamento das guias serão definidos pelos parâmetros externos. A segunda aba (índice 1) será selecionada por padrão (quando o programa for carregado ao gráfico pela primeira vez). O código completo do método CProgram::CreateTabs() pode ser visto abaixo: 

//+------------------------------------------------------------------+
//| Cria a área com as abas                                          |
//+------------------------------------------------------------------+
bool CProgram::CreateTabs(void)
  {
#define TABS1_TOTAL 4
//--- Passa o objeto do painel
   m_tabs.WindowPointer(m_window1);
//--- Coordenadas
   int x=m_window1.X()+TABS1_GAP_X;
   int y=m_window1.Y()+TABS1_GAP_Y;
//--- Arrays com texto e largura para as guias
   string tabs_text[]={"Tab 1","Tab 2","Tab 3","Tab 4"};
   int tabs_width[]={90,90,90,90};
//--- Define as propriedades antes da criação
   m_tabs.XSize(596);
   m_tabs.YSize(243);
   m_tabs.TabYSize(TabsHeight);
   m_tabs.PositionMode(TabsPosition);
   m_tabs.SelectedTab((m_tabs.SelectedTab()==WRONG_VALUE) ? 1 : m_tabs.SelectedTab());
   m_tabs.AreaColor(clrWhite);
   m_tabs.TabBackColor(C'225,225,225');
   m_tabs.TabBackColorHover(C'240,240,240');
   m_tabs.TabBackColorSelected(clrWhite);
   m_tabs.TabBorderColor(clrSilver);
   m_tabs.TabTextColor(clrGray);
   m_tabs.TabTextColorSelected(clrBlack);
//--- Adiciona as guias com as propriedades especificadas
   for(int i=0; i<TABS1_TOTAL; i++)
      m_tabs.AddTab(tabs_text[i],tabs_width[i]);
//--- Cria o controle
   if(!m_tabs.CreateTabs(m_chart_id,m_subwin,x,y))
      return(false);
//--- Adiciona o objeto ao array comum dos grupos de objetos
   CWndContainer::AddToElementsArray(0,m_tabs);
   return(true);
  }

O método deve ser chamado no método principal para criar a interface gráfica da aplicação (ver a versão resumida do método no código abaixo): 

//+------------------------------------------------------------------+
//| Cria um painel Expert                                            |
//+------------------------------------------------------------------+
bool CProgram::CreateExpertPanel(void)
  {
//--- Criação do formulário 1 para os controles
//--- Criação dos controles:
//    Menu principal
//--- Menus de contexto
//--- Guias
   if(!CreateTabs())
      return(false);
//--- Redesenha o gráfico
   m_chart.Redraw();
   return(true);
  }

Compile o programa e carregue-o ao gráfico. Os modos de posicionamento da guia será alterada sucessivamente nos parâmetros externos (veja as imagens abaixo):

 Fig. 2. Modo de posicionamento das guias - «Superior».

Fig. 2. Modo de posicionamento das guias - «Superior».

 Fig. 3. Modo de posicionamento das guias - «Inferior».

Fig. 3. Modo de posicionamento das guias - «Inferior».

 Fig. 4. Modo de posicionamento das guias - «Esquerda».

Fig. 4. Modo de posicionamento das guias - «Esquerda».

 Fig. 5. Modo de posicionamento das guias - «Direita».

Fig. 5. Modo de posicionamento das guias - «Direita».


Agora vamos testar como ele funciona com os grupos de controles ligados a cada guia. Para fazer isso, crie uma cópia separada do mesmo EA e exclua os parâmetros externos dele. Aqui, as abas serão posicionadas na parte superior (TABS_TOP) da área de trabalho. 

  1. Uma tabela com o rótulo de texto será anexada na primeira guia. 
  2. Uma tabela com a caixa de edição será anexada à segunda. 
  3. Uma tabela renderizada à terceira.
  4. O quarto - um grupo de controles, que contém:
    • quatro caixas de seleção;
    • quatro caixas de seleção com caixas de edição;
    • quatro caixas de combinação com as caixas de seleção;
    • uma linha de separação. 

Na classe personalizada (CProgram) do aplicativo de teste, declare as (1) instâncias desses controles, (2) os métodos para criá-los e (3) as margens das bordas d formulário (ver o código abaixo): 

class CProgram : public CWndEvents
  {
private:
   //--- Tabela com o rótulo de texto
   CLabelsTable      m_labels_table;
   //--- Tabela com a caixa de edição
   CTable            m_table;
   //--- Tabela renderizada
   CCanvasTable      m_canvas_table;
   //--- Caixas de seleção
   CCheckBox         m_checkbox1;
   CCheckBox         m_checkbox2;
   CCheckBox         m_checkbox3;
   CCheckBox         m_checkbox4;
   //--- Caixas de seleção com as caixas de edição
   CCheckBoxEdit     m_checkboxedit1;
   CCheckBoxEdit     m_checkboxedit2;
   CCheckBoxEdit     m_checkboxedit3;
   CCheckBoxEdit     m_checkboxedit4;
   //--- Caixas de combinação com as caixas de seleção
   CCheckComboBox    m_checkcombobox1;
   CCheckComboBox    m_checkcombobox2;
   CCheckComboBox    m_checkcombobox3;
   CCheckComboBox    m_checkcombobox4;
   //--- Linha de separação
   CSeparateLine     m_sep_line;
   //---
private:
   //--- Tabela com o rótulo de texto
#define TABLE1_GAP_X          (5)
#define TABLE1_GAP_Y          (65)
   bool              CreateLabelsTable(void);
   //--- Tabela com a caixa de edição
#define TABLE2_GAP_X          (5)
#define TABLE2_GAP_Y          (65)
   bool              CreateTable(void);
   //--- Tabela renderizada
#define TABLE3_GAP_X          (5)
#define TABLE3_GAP_Y          (65)
   bool              CreateCanvasTable(void);
   //--- Linha de separação
#define SEP_LINE_GAP_X        (300)
#define SEP_LINE_GAP_Y        (70)
   bool              CreateSepLine(void);
   //--- Caixas de seleção
#define CHECKBOX1_GAP_X       (18)
#define CHECKBOX1_GAP_Y       (75)
   bool              CreateCheckBox1(const string text);
#define CHECKBOX2_GAP_X       (18)
#define CHECKBOX2_GAP_Y       (175)
   bool              CreateCheckBox2(const string text);
#define CHECKBOX3_GAP_X       (315)
#define CHECKBOX3_GAP_Y       (75)
   bool              CreateCheckBox3(const string text);
#define CHECKBOX4_GAP_X       (315)
#define CHECKBOX4_GAP_Y       (175)
   bool              CreateCheckBox4(const string text);
   //--- Caixas de seleção com as caixas de edição
#define CHECKBOXEDIT1_GAP_X   (40)
#define CHECKBOXEDIT1_GAP_Y   (105)
   bool              CreateCheckBoxEdit1(const string text);
#define CHECKBOXEDIT2_GAP_X   (40)
#define CHECKBOXEDIT2_GAP_Y   (135)
   bool              CreateCheckBoxEdit2(const string text);
#define CHECKBOXEDIT3_GAP_X   (337)
#define CHECKBOXEDIT3_GAP_Y   (105)
   bool              CreateCheckBoxEdit3(const string text);
#define CHECKBOXEDIT4_GAP_X   (337)
#define CHECKBOXEDIT4_GAP_Y   (135)
   bool              CreateCheckBoxEdit4(const string text);
   //--- Caixas de combinação com as caixas de seleção
#define CHECKCOMBOBOX1_GAP_X  (40)
#define CHECKCOMBOBOX1_GAP_Y  (205)
   bool              CreateCheckComboBox1(const string text);
#define CHECKCOMBOBOX2_GAP_X  (40)
#define CHECKCOMBOBOX2_GAP_Y  (235)
   bool              CreateCheckComboBox2(const string text);
#define CHECKCOMBOBOX3_GAP_X  (337)
#define CHECKCOMBOBOX3_GAP_Y  (205)
   bool              CreateCheckComboBox3(const string text);
#define CHECKCOMBOBOX4_GAP_X  (337)
#define CHECKCOMBOBOX4_GAP_Y  (235)
   bool              CreateCheckComboBox4(const string text);
  };

Nos artigos anteriores, foi exibido repetidamente como criar os controles e anexá-los ao formulário. Portanto, o código de apenas um destes métodos será fornecido aqui para mostrar como anexar um controle a uma guia. O mais simples desses controles seria a linha de separação, que é suficiente como um exemplo. No código abaixo, a linha que chama o método CTabs::AddToElementsArray() é destacado em amarelo. O primeiro argumento é o índice da guia, na qual o controle deve ser anexado. Aqui, o índice é 3, ou seja, a quarta guia. O segundo argumento é o objecto do controle que deve ser ligado à guia especificada.

//+------------------------------------------------------------------+
//| Cria uma linha de separação                                      |
//+------------------------------------------------------------------+
bool CProgram::CreateSepLine(void)
  {
//--- Armazena o ponteiro da janela
   m_sep_line.WindowPointer(m_window1);
//--- Anexa à quarta página do primeiro grupo de guias
   m_tabs.AddToElementsArray(3,m_sep_line);
//--- Coordenadas  
   int x=m_window1.X()+SEP_LINE_GAP_X;
   int y=m_window1.Y()+SEP_LINE_GAP_Y;
//--- Tamanho
   int x_size=2;
   int y_size=210;
//--- Define as propriedades antes da criação
   m_sep_line.DarkColor(C'213,223,229');
   m_sep_line.LightColor(clrWhite);
   m_sep_line.TypeSepLine(V_SEP_LINE);
//--- Criação de um elemento
   if(!m_sep_line.CreateSeparateLine(m_chart_id,m_subwin,0,x,y,x_size,y_size))
      return(false);
//--- Adiciona o ponteiro do elemento para a base
   CWndContainer::AddToElementsArray(0,m_sep_line);
   return(true);
  }

Uma vez que a interface gráfica do aplicativo foi criada, o método CTabs::ShowTabElements() deve ser chamado para exibir apenas os controles das guias ativas (veja a versão resumida do método no código abaixo). Se não for feito isso, todos os controles de todas as guias serão exibidos. 

//+------------------------------------------------------------------+
//| Cria um painel Expert                                            |
//+------------------------------------------------------------------+
bool CProgram::CreateExpertPanel(void)
  {
//--- Criação do formulário 1 para os controles
//--- Criação dos controles:
//--- Menu principal
//--- Menus de contexto
//--- Barra de status
//--- Guias
//...
//--- Tabela com o rótulo de texto
   if(!CreateLabelsTable())
      return(false);
//--- Tabela com a caixa de edição
   if(!CreateTable())
      return(false);
//--- Cria a tabela renderizada
   if(!CreateCanvasTable())
      return(false);
//--- Linha de separação
   if(!CreateSepLine())
      return(false);
//--- Caixas de seleção
   if(!CreateCheckBox1("Checkbox 1"))
      return(false);
   if(!CreateCheckBox2("Checkbox 2"))
      return(false);
   if(!CreateCheckBox3("Checkbox 3"))
      return(false);
   if(!CreateCheckBox4("Checkbox 4"))
      return(false);
//--- Caixas de seleção com as caixas de edição
   if(!CreateCheckBoxEdit1("Checkbox Edit 1:"))
      return(false);
   if(!CreateCheckBoxEdit2("Checkbox Edit 2:"))
      return(false);
   if(!CreateCheckBoxEdit3("Checkbox Edit 3:"))
      return(false);
   if(!CreateCheckBoxEdit4("Checkbox Edit 4:"))
      return(false);
//--- Caixas de combinação com as caixas de seleção
   if(!CreateCheckComboBox1("CheckCombobox 1:"))
      return(false);
   if(!CreateCheckComboBox2("CheckCombobox 2:"))
      return(false);
   if(!CreateCheckComboBox3("CheckCombobox 3:"))
      return(false);
   if(!CreateCheckComboBox4("CheckCombobox 4:"))
      return(false);
//--- Exibe apenas os controles ativos da guia
   m_tabs.ShowTabElements();
//--- Redesenha o gráfico
   m_chart.Redraw();
   return(true);
  }

O resultado deve ser igual a imagem abaixo

 Fig. 6. Controles da primeira guia.

Fig. 6. Controles da primeira guia.

Fig. 7. Controles da segunda guia. 

Fig. 7. Controles da segunda guia.

 Fig. 8. Controles da terceira guia.

Fig. 8. Controles da terceira guia.

 Fig. 9. Controles da quarta guia.

Fig. 9. Controles da quarta guia.


Tudo está funcionando como pretendido. A sétima parte da série dedicada à biblioteca para criar as interfaces gráficas já pode ser concluída. Como um suplemento, você pode baixar outro código da classe (CIconTabs) para a criação das guias com funcionalidade estendida, que é fornecido nos anexos deste artigo. Ao contrário da classe CTabs, o controle do tipo CIconTabs pode ter ícones ajustáveis ​​para cada guia. Isso pode ajudar a tornar a interface gráfica mais amigável, se necessário. 

Os ícones e textos exibidos podem ser colocados com precisão em relação à borda de cada guia, usando os métodos especiais (ver no código abaixo): 

//+------------------------------------------------------------------+
//| Classe para a criação do ícone das guias                         |
//+------------------------------------------------------------------+
class CIconTabs : public CElement
  {
private:
   //--- Margens do rótulo
   int               m_icon_x_gap;
   int               m_icon_y_gap;
   //--- Margens do rótulo de texto
   int               m_label_x_gap;
   int               m_label_y_gap;
   //---
public:
   //--- Margens do rótulo
   void              IconXGap(const int x_gap)                       { m_icon_x_gap=x_gap;            }
   void              IconYGap(const int y_gap)                       { m_icon_y_gap=y_gap;            }
   //--- Margens do rótulo de texto
   void              LabelXGap(const int x_gap)                      { m_label_x_gap=x_gap;           }
   void              LabelYGap(const int y_gap)                      { m_label_y_gap=y_gap;           }
  };

Um exemplo da aparência do ícone do controle guias é exibido na imagem abaixo:

 Fig. 10. Controle «Ícone das Guias».

Fig. 10. Controle ícone das guias.

O código desse expert também pode ser baixado a partir dos anexos deste artigo. 

 

 


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. 11. Estrutura da biblioteca no atual estágio de desenvolvimento.

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

Os controles tabela e guias foram cobertos na sétima parte da série sobre a criação das interfaces gráficas nos terminais de negociação MetaTrader. Foi fornecido três classes (CLabelsTable, CTable e CCanvasTable) para a criação de tabelas e duas classes (CTabs e CIconTabs) para a criação das guias. 

A próxima parte (oitava) da série nós vamos estudar os seguintes controles.

  • Calendário estático e suspenso.
  • Lista hierárquica.
  • Navegador de arquivos.

Você pode encontrar e baixar o material da sétima parte da série nos arquivos anexados para testar o seu funcionamento. Se você tiver dúvidas sobre a utilização do material a partir desses arquivos, você poderá consultar a descrição detalhada do desenvolvimento da biblioteca em um dos artigos da lista abaixo ou fazer sua pergunta nos comentários deste artigo.

Lista de artigos (capítulos) da sétima parte:

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

Arquivos anexados |
Guia Prático MQL5 - Sinais de negociação de canais móveis Guia Prático MQL5 - Sinais de negociação de canais móveis
O artigo descreve o processo de desenvolvimento e implementação de uma classe para envio de sinais com base nos canais móveis. Cada versão do sinal é seguido por uma estratégia de negociação com os resultados dos testes. As classes da Biblioteca Padrão são utilizadas para criar classes derivadas.
Interfaces gráficas VII: O Controle Tabela (Capítulo 1) Interfaces gráficas VII: O Controle Tabela (Capítulo 1)
A sétima parte da série interfaces gráficas no MetaTrader lida com três tipos de tabelas: tabela com o rótulo de texto, tabela com a caixa de edição e a tabela renderizada. Outros controles importantes e frequentemente utilizados são as abas/guias que lhe permitem exibir/ocultar os grupos de outros controles e desenvolver uma interfaces mais compacta em suas aplicações MQL.
Trabalhando com cesta de moedas no mercado Forex Trabalhando com cesta de moedas no mercado Forex
O artigo descreve como os pares de moedas podem ser divididos em grupos (cestas), bem como a forma de obter dados sobre o seu status (por exemplo, compradas e vendidas) usando certos indicadores e como aplicar esses dados na negociação.
Como, na MetaTrader 5, desenvolver e depurar rapidamente sua estratégia de negociação Como, na MetaTrader 5, desenvolver e depurar rapidamente sua estratégia de negociação
Os sistemas automáticos de scalping são considerados não só o auge do trading algorítmico, mas também os mais difíceis na escrita do código. Neste artigo, nós mostraremos como -usando os recursos embutidos de depuração e teste visual- construir estratégias baseadas na análise de ticks entrantes. O desenvolvimento de regras de entrada e saída muitas vezes exige anos de negociação manual. Mas com a MetaTrader 5 você pode rapidamente verificar qualquer estratégia semelhante no histórico real.