English Русский 中文 Español Deutsch 日本語
preview
Melhore seus gráficos de negociação com uma GUI interativa baseada em MQL5 (Parte I): GUI móvel (I)

Melhore seus gráficos de negociação com uma GUI interativa baseada em MQL5 (Parte I): GUI móvel (I)

MetaTrader 5Negociação | 13 setembro 2023, 09:42
419 0
Kailash Bai Mina
Kailash Bai Mina

Introdução

Bem-vindo ao emocionante mundo das interfaces gráficas móveis em MQL5! Este guia está aqui para capacitá-lo com o conhecimento para criar interfaces gráficas dinâmicas e interativas que elevarão suas estratégias de negociação. Começaremos com o conceito básico de eventos gráficos – o mecanismo que controla a interatividade de nossa GUI. Com essa base, nós o guiaremos na criação de sua primeira GUI móvel.

Na próxima parte, avançaremos para a criação eficiente de várias GUI em um único gráfico (não apenas copiando e colando coisas), aprofundaremos na melhoria da nossa GUI adicionando e personalizando vários elementos, adaptando-a às suas necessidades exclusivas. Para aqueles que querem se aprofundar e criar uma interface móvel, criamos um guia simplificado que oferece etapas rápidas.

Ao final desta jornada, você terá adquirido uma habilidade valiosa na criação e manipulação de GUI móveis em MQL5, uma ferramenta poderosa para qualquer trader. Mesmo se você estiver com pressa, temos um guia rápido para aqueles que desejam pular diretamente na criação de sua GUI móvel.

Continuaremos da seguinte forma:


Decodificação de eventos gráficos: Blocos de construção de uma GUI móvel

No momento, o código do Expert Advisor se parece com isto (ou seja, ele é absolutamente básico):

Você pode estar se perguntando por que um EA e não um Indicador? Bem, a razão para isso é que o EA é mais fácil de entender para a maioria das pessoas, enquanto o Indicador pode confundir alguns, mas fique tranquilo, os mesmos passos podem ser seguidos no Indicador também.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
   {
//---
    
//---
    return(INIT_SUCCEEDED);
   }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
   {
//---

   }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
   {
//---

   }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
   {
//---
   }
//+------------------------------------------------------------------+

Agora, para entender melhor o OnChartEvent, vamos compará-lo com outra função predefinida que você já conhece a partir do EA básico acima com OnChartEvent().

Funções predefinidas Propósito
OnInit()
É executada na inicialização, ou seja, o EA é inicializado (anexado) no gráfico. Executada apenas uma vez
OnTick()
Iniciada com a entrada de um tick, ou seja, quando o símbolo no gráfico recebe um tick da corretora. Tick ​​significa atualização de preço
OnDeinit() É executada na desinicialização, ou seja, quando o EA é desinicializado (removido) do gráfico. Executada apenas uma vez

Da mesma forma, OnChartEvent() é uma função que é executada quando certos eventos ocorrem. Bem, sobre quais eventos estamos falando aqui?

Existem 9 eventos predefinidos (excluindo 2 personalizados):

  1. CHARTEVENT_KEYDOWN
  2. CHARTEVENT_MOUSE_MOVE 
  3. CHARTEVENT_OBJECT_CREATE 
  4. CHARTEVENT_OBJECT_CHANGE
  5. CHARTEVENT_OBJECT_DELETE 
  6. CHARTEVENT_CLICK 
  7. CHARTEVENT_OBJECT_CLICK 
  8. CHARTEVENT_OBJECT_DRAG 
  9. CHARTEVENT_OBJECT_ENDEDIT

Visão geral breve para aqueles que usaremos mais tarde no artigo:

  1. CHARTEVENT_KEYDOWN

    Quando a janela do gráfico está em foco (basta clicar em qualquer lugar na janela do gráfico para colocá-la em foco), a função OnChartEvent() é executada cada vez que qualquer tecla do teclado é pressionada.

    Mantê-la pressionada significará clicar na tecla novamente e novamente a uma taxa de 30 cliques por segundo.

    Quando uma tecla é pressionada, o que podemos fazer a respeito? Não muito por enquanto. É inútil até que saibamos qual tecla foi pressionada. Como conseguir isso? Bem, é aí que entram os parâmetros do OnChartEvent().

    De que parâmetros estamos falando? Existem 4 parâmetros que obtemos quando o OnChartEvent() é executado.

    1. id -> integer
    2. lparam -> long
    3. dparam -> double
    4. sparam -> string

    São simplesmente informações sobre os eventos para os quais o OnChartEvent() é chamado, e podemos usar esses dados dentro da função OnChartEvent().

    Por exemplo, no caso do evento CHARTEVENT_KEYDOWN,

    • id contém o próprio CHARTEVENT_KEYDOWN para que possamos determinar qual evento OnChartEvent() está sendo chamado e tratar outros parâmetros de acordo.
    • lparam contém o código da tecla pressionada.
    • dparam contém o número de pressionamentos de tecla gerados enquanto a tecla foi pressionada. Quando mantemos pressionada a tecla, o dparam faz 30 cliques por segundo. Este valor é sempre 1.
    • sparam contém uma máscara de bits. Em termos simples, o parâmetro descreve o estado da tecla, pressionada ou mantida pressionada, mostrando dois valores diferentes para uma tecla específica (veja o exemplo abaixo).


    Por exemplo, pressionamos/mantemos pressionada a tecla A no teclado, então OnChartEvent() será executado com

    • id =  CHARTEVENT_KEYDOWN
    • lparam = 65
    • dparam = 1
    • sparam = 30 para o primeiro clique e 16414 para cliques subsequentes contínuos enquanto mantém a tecla A a 30 cliques por segundo


    Agora que temos as informações, podemos usar algumas instruções "if" e fazer algo quando o usuário pressionar ou segurar a tecla A.



  2. CHARTEVENT_MOUSE_MOVE

    Primeiro, é necessário definir a propriedade booleana do gráfico CHART_EVENT_MOUSE_MOVE como True. Isso pode ser feito simplesmente usando uma única linha de código:

    //Set Chart property CHART_EVENT_MOUSE_DOWN to true 
    ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);

    Normalmente, é recomendável fazer isso no OnInit(). Depois disso, podemos usar o CHARTEVENT_MOUSE_MOVE.

    Sempre que o mouse for movido sobre o gráfico, o OnChartEvent() será executado com as seguintes informações nos parâmetros:

    • id =  CHARTEVENT_MOUSE_MOVE
    • lparam = coordenada X
    • dparam = coordenada Y
    • sparam = valor da máscara de bits que descreve o status dos botões do mouse

     A máscara de bits contém os seguintes valores -> 

    • Botão esquerdo do mouse -> 1
    • Botão direito do mouse -> 2
    • Botão do meio do mouse -> 16
    • Primeira tecla X do mouse -> 32
    • Segunda tecla X do mouse -> 64
    • Tecla Shift -> 4
    • Tecla de controle --> 8

    A janela do gráfico está no quarto quadrante, ou seja, a coordenada x (lparam) está à esquerda da janela do gráfico e a coordenada y (dparam) está no topo da janela do gráfico. Agora, com todas essas informações, estamos prontos para usar o CHARTEVENT_MOUSE_MOVE. Iremos usá-lo abaixo para tornar a GUI móvel.


  3. CHARTEVENT_CLICK

    Não requer nenhuma propriedade especial, podemos usá-lo diretamente. Ou seja, sempre que o mouse é clicado no gráfico, o OnChartEvent() é executado com os seguintes parâmetros:

    • id =  CHARTEVENT_CLICK
    • lparam = coordenada X
    • dparam = coordenada Y
    • sparam = "" ou seja, uma string vazia, o que significa que não contém nenhuma informação útil


    Agora que temos as informações acima, podemos usar algumas instruções "if" e fazer algo quando o usuário clicar em qualquer lugar no gráfico.


Acima, discutimos 3 eventos que iniciam a função OnChartEvent: CHARTEVENT_KEYDOWN, CHART_EVENT_MOUSE_MOVE e CHARTEVENT_CLICK.

Eu sei que, se você nunca usou a função OnChartEvent() antes, tudo isso pode parecer um pouco confuso. Bem, já estivemos lá, mas todos nós saímos dessa fase aprendendo mais e praticando mais. Vamos recorrer ao conhecimento acima para tornar a GUI móvel. Muito em breve você se sentirá mais confiante.

Leia a documentação ou comente abaixo se você precisar de detalhes sobre os outros eventos que iniciam o OnChartEvent().


Desenvolvimento de uma interface gráfica móvel: Guia passo-a-passo

Agora que todas as coisas chatas foram feitas, podemos nos concentrar no aprendizado real, ou seja, na aplicação da teoria a coisas reais, não apenas falar sobre elas.

Nesta seção, nosso objetivo é criar uma GUI muito simples, ou podemos dizer uma forma retangular vazia. Não fique desmotivado só porque não estamos criando uma GUI complexa e atraente; as coisas sempre começam a partir do básico e depois se vão tornando mais complexas.

Acha que consegue acompanhar? Experimente para descobrir, continue comigo até o final deste artigo e veja se pode dizer que tudo isso foi fácil. Vamos chamar esta GUI de painel (dashboard), pois em breve a transformaremos em um painel.

Primeiro, vamos criar uma forma retangular básica de 200x200 (XSize x YSize) com uma distância de 100 pixels da esquerda (XDistance) e 100 pixels do topo (YDistance) 

int OnInit()
   {
    //---
    //Set the name of the rectangle as "TestRectangle"
    string name = "TestRectangle";
    //Create a Rectangle Label Object at (time1, price1)=(0,0)
    ObjectCreate(0, name, OBJ_RECTANGLE_LABEL, 0, 0, 0);
    //Set XDistance to 100px i.e. Distance of Rectangle Label 100px from Left of the Chart Window
    ObjectSetInteger(0, name,OBJPROP_XDISTANCE, 100);
    //Set YDistance to 100px i.e. Distance of Rectangle Label 100px from Top of the Chart Window
    ObjectSetInteger(0, name,OBJPROP_YDISTANCE, 100);
    //Set XSize to 200px i.e. Width of Rectangle Label
    ObjectSetInteger(0, name,OBJPROP_XSIZE, 200);
    //Set YSize to 200px i.e. Height of Rectangle Label
    ObjectSetInteger(0, name,OBJPROP_YSIZE, 200);
    //---
    return(INIT_SUCCEEDED);
   }


Agora, quando executarmos o EA no gráfico, devemos ver nossa forma retangular criada:


Fig. 1. Forma retangular simples

Fig. 1. Forma retangular simples



Agora, se pudermos torná-la móvel com o arrasto do mouse, poderemos fazer muitas coisas, como mover uma barra de ferramentas avançada livremente na janela do gráfico, que pode criar EAs/Indicadores interativos muito bons. Uma das melhores aplicações para isso pode ser vista no Trade Assistant EA.

Então, como procedemos para torná-la móvel? Vamos criar um plano primeiro:

  • Condições antes do deslocamento:
    • O mouse deve estar no painel.

    • O botão esquerdo do mouse deve estar pressionado.

  • Se movermos o mouse com o botão esquerdo pressionado, o painel deverá se mover.

  • Mas a que distância? O painel deverá mover-se suavemente desde que ambas as condições sejam satisfeitas.

    Agora vamos codificá-lo passo a passo:

    Para a primeira condição, o mouse deve estar no painel, primeiro precisamos encontrar as coordenadas x e y da posição do mouse.


    É hora de aplicar a teoria. Para as coordenadas dos eixos x e y do mouse, precisamos usar o OnChartEvent().

    1. Definimos a propriedade do gráfico CHART_EVENT_MOUSE_MOVE como True

      Vamos colocar o código abaixo no OnInit() para torná-lo verdadeiro ao inicializar o EA:

      //Set Chart property CHART_EVENT_MOUSE_DOWN to true
      ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);

    2. Agora podemos obter as coordenadas do mouse no OnChartEvent().
      void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
         {
          //Verify the event that triggered the OnChartEvent was CHARTEVENT_MOUSE_MOVE because we only want to execute out code when that is the case
          if(id == CHARTEVENT_MOUSE_MOVE)
             {
              //Comment the X and Y Axes Coordinates
              Comment("X: ", lparam, "\nY: ", dparam);
             }
         }

      No OnChartEvent(), primeiro verificamos se o evento que acionou o OnChartEvent foi CHARTEVENT_MOUSE_MOVE usando um simples operador “if” que verifica se o id é igual a CHARTEVENT_MOUSE_MOVE, porque só queremos executar nosso código de comentário quando esse for o caso.

      Em seguida, anotamos as coordenadas dos eixos X e Y (eles aparecerão no canto superior esquerdo da janela do gráfico em branco com fonte pequena), assim: 

      Fig. 2. Coordenadas X e Y

      Fig. 2. Coordenadas X e Y

    Agora, para nossa lógica de descobrir se o mouse está no painel, veja a imagem abaixo: 

    Fig. 3. Visualização da fórmula

    Fig. 3. Visualização da fórmula





    Para saber se o mouse está sobre o painel,

    • X >= XDistance             --> X>=100
    • X <= XDIstance + XSize --> X<=300
    • Y >= YDistance             --> Y>=100
    • Y <= YDistance + YSize --> Y>=300

    Conversão em código:

    void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
      {
       //Verify the event that triggered the OnChartEvent was CHARTEVENT_MOUSE_MOVE because we only want to execute out code when that is the case
       if(id == CHARTEVENT_MOUSE_MOVE)
         {
          //define X, Y, XDistance, YDistance, XSize, YSize
          int X = (int)lparam;
          int Y = (int)dparam;
    
          string name = "TestRectangle";
          int XDistance = ObjectGetInteger(0, name, OBJPROP_XDISTANCE); //Should be 100 initially as we set it in OnInit()
          int YDistance = ObjectGetInteger(0, name, OBJPROP_YDISTANCE); //Should be 100 initially as we set it in OnInit()
          int XSize = ObjectGetInteger(0, name, OBJPROP_XSIZE); //Should be 200 initially as we set it in OnInit()
          int YSize = ObjectGetInteger(0, name, OBJPROP_YSIZE); //Should be 200 initially as we set it in OnInit()
          
          //Check Mouse on Dashboard condition
          if(X >= XDistance && X <= XDistance + XSize && Y >= YDistance && Y <= YDistance + YSize)
            {
             //Comment the X and Y Axes Coordinates and Mouse is on the dashboard
             Comment("X: ", lparam, "\nY: ", dparam, "\nMouse is on the Dashboard");
            }
          else
            {
             //Comment the X and Y Axes Coordinates and Mouse is not on the dashboard
             Comment("X: ", lparam, "\nY: ", dparam, "\nMouse is NOT on the Dashboard");
            }
    
         }
      }

    Definimos as variáveis X, Y, nome, XDistance, YDistance, XSize, YSize. Obtivemos X de lparam, Y de dparam, nome é apenas o nome de string que definimos acima, XDistance, YDistance, XSize, YSize usando a função ObjectGetInteger().

    Nosso objetivo aqui é fazer com que o painel se mova suavemente.

    Resultado:

    Fig. 4. Mouse sobre o painel

    Fig. 4. Mouse sobre o painel


    Como você pode ver, sempre que o mouse está sobre o painel, o comentário muda. Então nossa lógica funciona, e sabemos se o mouse está no painel ou não.

    Agora precisaremos do estado dos botões do mouse. Como lembramos, se clicarmos com o botão esquerdo, sparam será igual a 1. Vamos aproveitar isso.

    void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
      {
       //Verify the event that triggered the OnChartEvent was CHARTEVENT_MOUSE_MOVE because we only want to execute out code when that is the case
       if(id == CHARTEVENT_MOUSE_MOVE)
         {
          //define X, Y, XDistance, YDistance, XSize, YSize
          int X = (int)lparam;
          int Y = (int)dparam;
          int MouseState = (int)sparam;
    
          string name = "TestRectangle";
          int XDistance = ObjectGetInteger(0, name, OBJPROP_XDISTANCE); //Should be 100 initially as we set it in OnInit()
          int YDistance = ObjectGetInteger(0, name, OBJPROP_YDISTANCE); //Should be 100 initially as we set it in OnInit()
          int XSize = ObjectGetInteger(0, name, OBJPROP_XSIZE); //Should be 200 initially as we set it in OnInit()
          int YSize = ObjectGetInteger(0, name, OBJPROP_YSIZE); //Should be 200 initially as we set it in OnInit()
    
          //Check Dashboard move conditions
          if(X >= XDistance && X <= XDistance + XSize && Y >= YDistance && Y <= YDistance + YSize && MouseState == 1)
            {
             //Comment that the dashboard is ready to move
             Comment("Dashboard is ready to move.");
            }
          else
            {
             //Comment that the dashboard is not ready to move
             Comment("Dashboard is NOT ready to move.");
            }
          
         }
      }

    Adicionei

    int MouseState = (int)sparam; //To get the mouse state: 1 -> Mouse Left Button Down (You can check the other above)
    

    às variáveis, bem como condição

    if(MouseState == 1) // This insures that Mouse Left button in pressed

    à instrução “if”. Também mudei um pouco os comentários.

    Agora sempre que o botão esquerdo do mouse é pressionado no painel, obtemos o comentário “Dashboard is ready to move” (o painel está pronto para ser movido). Caso contrário, receberemos o comentário "Dashboar is NOT ready to move" (o painel NÃO está pronto para ser movido).

    Vamos ver isso em ação:

    Fig. 5. O painel está pronto para ser movido

    Fig. 5. O painel está pronto para ser movido


    Observe a alteração do comentário quando o botão esquerdo do mouse é mantido pressionado.


    Agora, com isso feito, estamos prontos para mover nosso painel. Então, como procedemos?

    Sabemos que o painel se moverá com nosso mouse, e como mudamos a posição do painel? Claro, com as propriedades XDistance e YDistance. Mas quanto devemos mudar XDistance e YDistance? Como o painel se move com o mouse, ele deve se mover na mesma medida que o mouse, certo?

    Bem, então, até que ponto nosso mouse se moveu? Ainda há muitas perguntas. Bem, vamos fazer um plano com base nessas perguntas.

    Plano: 

    • Descobrimos até que ponto o mouse se moveu depois de pressionar o botão esquerdo
    • Movemos o painel exatamente na mesma medida em que o mouse se move.
    • Faremos isso enquanto o botão esquerdo do mouse estiver pressionado e, depois disso, pararemos de mover o painel.

    Vamos dar um passo de cada vez.

    Sempre podemos obter a posição atual do mouse, certo? E se salvássemos a posição do mouse quando o botão esquerdo do mouse foi clicado pela primeira vez?

    E podemos saber se o botão esquerdo do mouse está pressionado a partir da variável MouseState que criamos para armazenar sparam.

    Abaixo está o código que detecta o primeiro clique esquerdo do mouse: 

    int previousMouseState = 0;
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
      {
       //Verify the event that triggered the OnChartEvent was CHARTEVENT_MOUSE_MOVE because we only want to execute out code when that is the case
       if(id == CHARTEVENT_MOUSE_MOVE)
         {
          int MouseState = (int)sparam;
          
          bool mblDownFirstTime = false;
          if(previousMouseState == 0 && MouseState == 1) {
             mblDownFirstTime = true;
          }
          
          previousMouseState = MouseState;
         }
      }
    //+------------------------------------------------------------------+

    Removi algumas linhas para facilitar a compreensão. Este método é bastante comumente usado na criação de funções de trailing para EA ou na detecção de uma nova barra, etc.

    Vamos analisar isso:

    1. int previousMouseState = 0;

      Declaramos uma variável int chamada previousMouseState no espaço global e a definimos como 0. Esta variável armazenará o valor MouseState da última vez que o evento CHARTEVENT_MOUSE_MOVE ocorreu. Como exatamente? Você descobrirá em breve.


    2. int previousMouseState = 0;

      Declaramos uma variável int chamada previousMouseState no espaço global e a definimos como 0. Esta variável armazenará o valor MouseState da última vez que o evento CHARTEVENT_MOUSE_MOVE ocorreu. Como exatamente? Tenha um pouco de paciência, você logo entenderá.

    3. int MouseState = (int)sparam;   
      bool mblDownFirstTime = false;
      if(previousMouseState == 0 && MouseState == 1) {
         mblDownFirstTime = true;
      }


      Primeiro, declaramos uma variável MouseState e a definimos como igual a sparam, que contém o estado do mouse. Em seguida, declaramos uma variável bool chamada mblDownFirstTime e definimos seu valor padrão como false.

      Em seguida, verificamos 2 condições: primeiro, previousMouseState deve ser igual a 0 (MLB solto, nenhum botão do mouse pressionado) e (&&) MouseState deve ser igual a 1 (MLB pressionado).

      Essa condição basicamente confirma ou nega se o MLB foi pressionado pela primeira vez ou não. Uma vez que sabemos que é a primeira vez, definimos mblDownFirstTime como true para que possamos usar essa variável posteriormente.


    Nossa primeira etapa está concluída, vamos seguir para as etapas 2 e 3.

    Agora, há muitas maneiras de prosseguir, mas para obter um movimento muito suave e sutil, abaixo estão os passos que seguiremos:

    1. Criamos uma variável global lógica movingState, que será definida como true assim que o usuário clicar com o botão esquerdo do mouse na barra de ferramentas, e declaramos MLB Down X, MLB Down Y, MLB Down XDistance, MLB Down YDistance (aqui, MLB Down significa que o botão esquerdo do mouse foi liberado primeiro). Isso será necessário para alterar a posição da barra de ferramentas.
    2. Enquanto movingState for true, atualizaremos a posição da barra de ferramentas de acordo com a alteração da posição do mouse em comparação com a posição original (quando o botão esquerdo do mouse foi pressionado pela primeira vez).
    3. Enquanto movingState for true, atualizaremos a posição da barra de ferramentas de acordo com a alteração da posição do mouse em comparação com a posição original (quando o botão esquerdo do mouse foi pressionado pela primeira vez).


    Novo código:

    int previousMouseState = 0;
    int mlbDownX = 0;
    int mlbDownY = 0;
    int mlbDownXDistance = 0;
    int mlbDownYDistance = 0;
    bool movingState = false;
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
      {
    //Verify the event that triggered the OnChartEvent was CHARTEVENT_MOUSE_MOVE because we only want to execute out code when that is the case
       if(id == CHARTEVENT_MOUSE_MOVE)
         {
          if(previousMouseState == 0 && MouseState == 1)
            {
             mlbDownX = X;
             mlbDownY = Y;
             mlbDownXDistance = XDistance;
             mlbDownYDistance = YDistance;
    
             if(X >= XDistance && X <= XDistance + XSize && Y >= YDistance && Y <= YDistance + YSize)
               {
                movingState = true;
               }
            }
    
          if(movingState)
            {
             ChartSetInteger(0, CHART_MOUSE_SCROLL, false);
             ObjectSetInteger(0, name, OBJPROP_XDISTANCE, mlbDownXDistance + X - mlbDownX);
             ObjectSetInteger(0, name, OBJPROP_YDISTANCE, mlbDownYDistance + Y - mlbDownY);
             ChartRedraw(0);
            }
    
          if(MouseState == 0)
            {
             movingState = false;
             ChartSetInteger(0, CHART_MOUSE_SCROLL, true);
            }
    
          previousMouseState = MouseState;
         }
      }
    //+------------------------------------------------------------------+

     Vamos simplificar isso:

    1. int mlbDownX = 0;
      int mlbDownY = 0;
      int mlbDownXDistance = 0;
      int mlbDownYDistance = 0;
      bool movingState = false;

      Criamos diversas variáveis ​​no escopo global. Já falamos sobre a previousMouseState além disso:

      • mlbDownX                -> Manter a coordenada X quando o botão esquerdo do mouse for pressionado pela primeira vez
      • mlbDownY                -> Manter a coordenada Y quando o botão esquerdo do mouse for pressionado pela primeira vez
      • mlbDownXDistance   -> Manter a propriedade XDistance da barra de ferramentas quando o botão esquerdo do mouse for clicado pela primeira vez
      • mlbDownYDistance   -> Manter a propriedade YDistance da barra de ferramentas quando o botão esquerdo do mouse for clicado pela primeira vez    
      • movingState             -> Manter como true se movermos o painel; caso contrário, false

      if(previousMouseState == 0 && MouseState == 1)
        {
         mlbDownX = X;
         mlbDownY = Y;
         mlbDownXDistance = XDistance;
         mlbDownYDistance = YDistance;
      
         if(X >= XDistance && X <= XDistance + XSize && Y >= YDistance && Y <= YDistance + YSize)
          {
           movingState = true;
          }
        }



      Primeiro, verificamos se é o primeiro clique do mouse; Nesse caso, atualizamos mlbDownX, mlbDownY, mlbDownXDistance, mlbDownYDistance para X, Y, XDistance e YDistance atuais, respectivamente. Nós os usaremos mais tarde.

      Em seguida, verificamos se o botão esquerdo do mouse estava pressionado no painel e, se estivesse, definimos movingState como true.

    2. if(movingState)
        {
         ChartSetInteger(0, CHART_MOUSE_SCROLL, false);
         ObjectSetInteger(0, name, OBJPROP_XDISTANCE, mlbDownXDistance + X - mlbDownX);
         ObjectSetInteger(0, name, OBJPROP_YDISTANCE, mlbDownYDistance + Y - mlbDownY);
         ChartRedraw(0);
        }

      E se movingState for true, modificamos XDistance e YDistance.

      X - mlbDownX // Change in Mouse X Position form the initial click
      and 
      Y - mlbDownY // Change in Mouse X Position form the initial click

      Essas são as alterações na posição do mouse a partir do clique inicial e as adicionamos a mlbDownXDistance e mlbDownYDistance para obter a nova posição do painel. Faz sentido se você pensar sobre isso.

      E também definimos a propriedade CHART_MOUSE_SCROLL como false para que o gráfico não se mova com nosso painel e, claro, redesenhamos o gráfico para obter um movimento muito suave.

    3. if(MouseState == 0)
        {
         movingState = false;
         ChartSetInteger(0, CHART_MOUSE_SCROLL, true);
        }

      Agora, assim que soltarmos o botão esquerdo do mouse, MouseState se tornará 0.

      Em seguida, definimos movingState como false e permitimos que o gráfico se mova novamente, definindo CHART_MOUSE_SCROLL como true novamente.



    Agora, com isso feito, nosso código está concluído. Dê um tapinha nas costas se você seguiu até agora.

    Nosso código completo fica assim:

    //+------------------------------------------------------------------+ //| Expert initialization function                                   | //+------------------------------------------------------------------+ int OnInit()   { //---    //Set the name of the rectangle as "TestRectangle"    string name = "TestRectangle";    //Create a Rectangle Label Object at (time1, price1)=(0,0)    ObjectCreate(0, name, OBJ_RECTANGLE_LABEL, 0, 0, 0);    //Set XDistance to 100px i.e. Distance of Rectangle Label 100px from Left of the Chart Window    ObjectSetInteger(0, name, OBJPROP_XDISTANCE, 100);    //Set YDistance to 100px i.e. Distance of Rectangle Label 100px from Top of the Chart Window    ObjectSetInteger(0, name, OBJPROP_YDISTANCE, 100);    //Set XSize to 200px i.e. Width of Rectangle Label    ObjectSetInteger(0, name, OBJPROP_XSIZE, 200);    //Set YSize to 200px i.e. Height of Rectangle Label    ObjectSetInteger(0, name, OBJPROP_YSIZE, 200); //Set Chart property CHART_EVENT_MOUSE_DOWN to true    ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true); //---    return(INIT_SUCCEEDED);   } //+------------------------------------------------------------------+ //| Expert deinitialization function                                 | //+------------------------------------------------------------------+ void OnDeinit(const int reason)   { //---   } //+------------------------------------------------------------------+ //| Expert tick function                                             | //+------------------------------------------------------------------+ void OnTick()   { //---   } //+------------------------------------------------------------------+ //Declare some global variable that will be used in the OnChartEvent() function int previousMouseState = 0; int mlbDownX = 0; int mlbDownY = 0; int mlbDownXDistance = 0; int mlbDownYDistance = 0; bool movingState = false; //+------------------------------------------------------------------+ //|                                                                  | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)   { //Verify the event that triggered the OnChartEvent was CHARTEVENT_MOUSE_MOVE because we only want to execute out code when that is the case    if(id == CHARTEVENT_MOUSE_MOVE)      {       //define X, Y, XDistance, YDistance, XSize, YSize       int X = (int)lparam;       int Y = (int)dparam;       int MouseState = (int)sparam;       string name = "TestRectangle";       int XDistance = (int)ObjectGetInteger(0, name, OBJPROP_XDISTANCE); //Should be 100 initially as we set it in OnInit()       int YDistance = (int)ObjectGetInteger(0, name, OBJPROP_YDISTANCE); //Should be 100 initially as we set it in OnInit()       int XSize = (int)ObjectGetInteger(0, name, OBJPROP_XSIZE); //Should be 200 initially as we set it in OnInit()       int YSize = (int)ObjectGetInteger(0, name, OBJPROP_YSIZE); //Should be 200 initially as we set it in OnInit()       if(previousMouseState == 0 && MouseState == 1) //Check if this was the MLB first click         {          mlbDownX = X; //Set mlbDownX (Variable that stores the initial MLB X location) equal to the current X          mlbDownY = Y; //Set mlbDownY (Variable that stores the initial MLB Y location) equal to the current Y          mlbDownXDistance = XDistance; //Set mlbDownXDistance (Variable that stores the initial XDistance i.e. Width of the dashboard) equal to the current XDistance          mlbDownYDistance = YDistance; //Set mlbDownYDistance (Variable that stores the initial YDistance i.e. Height of the dashboard) equal to the current YDistance          if(X >= XDistance && X <= XDistance + XSize && Y >= YDistance && Y <= YDistance + YSize) //Check if the click was on the dashboard            {             movingState = true; //If yes the set movingState to True            }         }       if(movingState)//if movingState is true, Update the Dashboard position         {          ChartSetInteger(0, CHART_MOUSE_SCROLL, false);//Restrict Chart to be moved by Mouse          ObjectSetInteger(0, name, OBJPROP_XDISTANCE, mlbDownXDistance + X - mlbDownX);//Update XDistance to: mlbDownXDistance + (X - mlbDownX)          ObjectSetInteger(0, name, OBJPROP_YDISTANCE, mlbDownYDistance + Y - mlbDownY);//Update YDistance to: mlbDownYDistance + (Y - mlbDownY)          ChartRedraw(0); //Redraw Chart         }       if(MouseState == 0)//Check if MLB is not pressed         {          movingState = false;//set movingState again to false          ChartSetInteger(0, CHART_MOUSE_SCROLL, true);//allow the cahrt to be moved again         }       previousMouseState = MouseState;//update the previousMouseState at the end so that we can use it next time and copare it with new value      }   } //+------------------------------------------------------------------+

    Agora, esse código simples faz o trabalho.

    Resultado:

    Fig. 6. Resultado final

    Fig. 6. Resultado final



    Conclusão

    Peço desculpas por deixar você em um suspense, pois ainda não criamos algo verdadeiramente único. Porém, dentro de alguns dias será publicada a próxima parte, na qual:

    • Criaremos várias interfaces gráficas em um gráfico (sem copiar e colar básico)
    • Melhoraremos nossa interface gráfica adicionando e customizando diversos elementos, adaptando-a às suas necessidades únicas. Para aqueles que querem se aprofundar e criar uma interface móvel, criamos um guia simplificado que oferece etapas rápidas.

    Ao final desta jornada, você terá adquirido uma habilidade valiosa na criação e manipulação de GUI móveis em MQL5, uma ferramenta poderosa para qualquer trader.


    Espero que tenha gostado e que tenha ajudado de alguma forma.

    Divirta-se codificando!



    Traduzido do Inglês pela MetaQuotes Ltd.
    Artigo original: https://www.mql5.com/en/articles/12751

    Arquivos anexados |
    Teoria das Categorias em MQL5 (Parte 10): Grupos monoides Teoria das Categorias em MQL5 (Parte 10): Grupos monoides
    Esse artigo é uma continuação da série sobre como implementar a teoria das categorias em MQL5. Nele, consideramos os grupos monoides como um meio de normalizar os conjuntos monoides e permitir uma comparação mais precisa em um espectro mais amplo de conjuntos monoides e tipos de dados.
    Teoria das Categorias (Parte 9): Ações dos monoides Teoria das Categorias (Parte 9): Ações dos monoides
    Esse artigo é a continuação da série sobre a implementação da teoria das categorias em MQL5. Nele são discutidas as ações de monoides como um meio de transformar os monoides descritos no artigo anterior para aumentar suas aplicações.
    Como se tornar um provedor de sinais bem-sucedido na MQL5.com Como se tornar um provedor de sinais bem-sucedido na MQL5.com
    O principal objetivo deste artigo é fornecer a você uma maneira simples e passo a passo de se tornar o melhor provedor de sinais na MQL5.com. A partir do meu conhecimento e experiência, explicarei o que é necessário para se tornar um provedor de sinais bem-sucedido, inclusive como encontrar, testar e otimizar uma boa estratégia. Além disso, darei dicas sobre como publicar seu sinal, escrever uma descrição convincente e promover e gerenciar de forma eficaz.
    Desenvolvendo um sistema de Replay (Parte 26): Projeto Expert Advisor — Classe C_Terminal Desenvolvendo um sistema de Replay (Parte 26): Projeto Expert Advisor — Classe C_Terminal
    Talvez já podemos começar a desenvolver um Expert Advisor a ser utilizado no replay / simulação. Mas não iremos criar qualquer coisa, este precisará ser algo um pouco mais bem elaborado. Mas não nos deixemos nos levar pelo grau de dificuldade neste primeiro momento. Temos de começar a fazer as coisas partindo de algum ponto. Caso contrário apenas iremos nos conformar, imaginando o qual difícil o desafio é, sem ao menos tentarmos de fato superar este obstáculo. Vida de programador de fato é isto: Encontrar um obstáculo e tentar superar ele, via estudo, testes e bastante pesquisa.