preview
Do básico ao intermediário: Eventos em Objetos (IV)

Do básico ao intermediário: Eventos em Objetos (IV)

MetaTrader 5Exemplos |
27 0
CODE X
CODE X

Introdução

No artigo anterior Do básico ao intermediário: Eventos em Objetos (III), foi mostrado a primeira parte de algo ainda maior, que será visto aqui neste artigo. Onde iremos concluir o que foi iniciado no artigo anterior.

Como o objetivo destes artigos, é o de demonstrar como você meu caro leitor, pode controlar a forma como o MetaTrader 5 irá funcionar. Isto objetivando conseguir implementar algum tipo de aplicação. Espero que você esteja estudando e praticando o que tem sido mostrado aqui. Já que de maneira alguma, nenhum dos códigos vistos nestes meus artigos, devem ser encarados como aplicações finalizadas. Podendo assim vir a ser utilizadas sem os devidos cuidados e critérios.

Porém o que estamos vendo aqui, neste atual momento. Para muitos, poderia ser considerado um tipo de delírio da minha parte. Já que grande parte, não acredita que de fato, aplicações podem ter como objetivo, fazer o que estou mostrando que elas podem fazer. Porém a graça em se programar algo, não está em se fazer o obvio. Mas sim o de buscar novas possibilidades, dentro daquilo que poucos conseguem enxergar. Por conta disto, decidi, criar esta sequência. Que visa justamente mostrar, que você pode conseguir fazer quase tudo, desde que entenda certos conceitos. Então chegou a hora de tornar o que foi visto no artigo anterior, algo que de fato venha a fazer algum sentido.


Melhorando a interatividade

O que foi visto no artigo anterior, está longe de ter algum sentido lógico. isto se você apenas olhar a parte referente ao que tange ao código atual. E que foi deixado no anexo, para que você pudesse estudar e praticar. Isto afim de entender como o processo de interação poderia gerar, com muito pouco código sendo escrito.

Porém, aquele código, deixado no anexo do artigo anterior, ainda não atingiu a sua real maturidade. Ou seja, apesar de que ele já mostre algumas coisas que podemos implementar com um objetivo primário bastante curioso e de forma muito simples. Ele ainda não nos permite interagir com os objetos como desejamos fazer. Resumindo: Aquele código apenas gera o que é pode ser visto na animação 01, logo abaixo.

Animação 01

Apesar de parecer algo bobo, e até sem muito proposito. Esta animação, nos mostra o potencial que aquele código já contém. Nos permitindo criar um contorno sobre um objeto selecionado. No entanto, nosso objetivo não é apenas criar este contorno. Mas sim o de promover a mudança nas dimensões do objeto selecionado. Aqui é onde a coisa começa a ficar um tanto quanto confusa. Isto para aqueles que não vem praticando e estudando o que vem sendo mostrado nos meus artigos.

Nos meus artigos, até o presente momento, mostrei que podemos interagir com o MetaTrader 5, a fim de conseguir algum tipo de resultado. Porém, grande parte do trabalho era deixado nas mãos do MetaTrader 5. Sendo apenas uma parte feita por nós, durante a implementação do código. No entanto, aqui temos um pequeno problema. Este pode ser resumido da seguinte forma: O MetaTrader 5, consegue fazer boa parte do trabalho que precisa ser feito, a fim de que nossa aplicação, consiga controlar as dimensões de um objeto selecionado. Porém, e este é o ponto chave, não conseguiremos fazer isto sem algum trabalho extra. Digo isto, pois usando o código original, que é visto logo abaixo, na integra. NÃO TEREMOS UMA INTERAÇÃO 100% ADEQUADA. Já que o resultado da execução é visto na animação 01.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_Prefix  "Demo"
05. //+------------------------------------------------------------------+
06. #define macro_NameObject  def_Prefix + (string)(ObjectsTotal(0) + 1)
07. //+------------------------------------------------------------------+
08. struct st_BoxResize
09. {
10.     private :
11.         ushort  x, y, w, h;
12.         bool    isBack;
13.         string  szObjects[4];
14. //+----------------+
15.         void CreateEdge(const char what)
16.         {
17.             ushort p1 = x, p2 = y, p3 = w, p4 = h;
18. 
19.             switch (what)
20.             {
21.                 case 0: p4 = 1; break;
22.                 case 1: p3 = 1; break;
23.                 case 2: p2 = y + h; p4 = 1; break;
24.                 case 3: p1 = x + w; p3 = 1; break;
25.             }
26.             szObjects[what] = macro_NameObject;
27.             ObjectCreate(0, szObjects[what], OBJ_RECTANGLE_LABEL, 0, 0, 0);
28.             ObjectSetInteger(0, szObjects[what], OBJPROP_BGCOLOR, clrLime);
29.             ObjectSetInteger(0, szObjects[what], OBJPROP_XDISTANCE, p1);
30.             ObjectSetInteger(0, szObjects[what], OBJPROP_YDISTANCE, p2);
31.             ObjectSetInteger(0, szObjects[what], OBJPROP_XSIZE, p3);
32.             ObjectSetInteger(0, szObjects[what], OBJPROP_YSIZE, p4);
33.         }
34. //+----------------+
35.     public  :
36.         void CreateBoxResize(const string szArg, const uchar adjust = 2)
37.         {
38.             isBack = (bool)ObjectGetInteger(0, szArg, OBJPROP_BACK);
39.             ObjectSetInteger(0, szArg, OBJPROP_BACK, true);
40.             ObjectSetInteger(0, szArg, OBJPROP_SELECTED, false);
41.             x = (ushort)ObjectGetInteger(0, szArg, OBJPROP_XDISTANCE) - adjust;
42.             y = (ushort)ObjectGetInteger(0, szArg, OBJPROP_YDISTANCE) - adjust;
43.             w = (ushort)ObjectGetInteger(0, szArg, OBJPROP_XSIZE) + adjust;
44.             h = (ushort)ObjectGetInteger(0, szArg, OBJPROP_YSIZE) + adjust;
45.             for (uchar c = 0; c < 4; c++)
46.                 CreateEdge(c);
47.         }
48. //+----------------+
49.         void RemoveBoxResize(const string szArg)
50.         {
51.             ObjectSetInteger(0, szArg, OBJPROP_BACK, isBack);
52.             ObjectSetInteger(0, szArg, OBJPROP_SELECTED, false);
53.             ObjectsDeleteAll(0, def_Prefix);
54.         }
55. //+----------------+
56. }gl_RZ;
57. //+------------------------------------------------------------------+
58. int OnInit()
59. {
60.     IndicatorSetString(INDICATOR_SHORTNAME, def_Prefix);
61. 
62.     return INIT_SUCCEEDED;
63. };
64. //+------------------------------------------------------------------+
65. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
66. {
67.     return rates_total;
68. };
69. //+------------------------------------------------------------------+
70. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
71. {
72.     static string szNameObj = NULL;
73. 
74.     switch (id)
75.     {
76.         case CHARTEVENT_KEYDOWN         :
77.             if ((szNameObj != NULL) && (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)))
78.             {
79.                 gl_RZ.RemoveBoxResize(szNameObj);
80.                 szNameObj = NULL;
81.             }
82.             break;
83.         case CHARTEVENT_OBJECT_CLICK    :
84.             if (StringFind(sparam, def_Prefix) != INVALID_HANDLE) break;
85.             if (szNameObj != NULL) gl_RZ.RemoveBoxResize(szNameObj);
86.             if (szNameObj != sparam) gl_RZ.CreateBoxResize(szNameObj = sparam); else szNameObj = NULL;
87.             break;
88.         case CHARTEVENT_OBJECT_DRAG     :
89.             break;
90.     }
91.     ChartRedraw();
92. };
93. //+------------------------------------------------------------------+
94. void OnDeinit(const int reason)
95. {
96.     ObjectsDeleteAll(0, def_Prefix);
97.     ChartRedraw();
98. };
99. //+------------------------------------------------------------------+

Código 01

Mas porque você está dizendo que a interação não será 100% adequada? Para mim isto não faz sentido. Já que o mecanismo aparentemente está funcionando. De fato, meu caro leitor, o mecanismo está funcionando. Porém, existe um pequeno problema, e este é destacado na imagem logo abaixo.

Imagem 01

Talvez esta imagem 01 não está lhe dizendo nada. Mas estou destacando um ponto na mesma, justamente para chamar a sua atenção a um detalhe. A linha de contorno, NÃO TEM ELEMENTOS DE INTERAÇÃO, ou seja, não temos como dizer ao MetaTrader 5, ou ao usuário, como mover esta linha. Agora preste atenção. No código 01, a linha 84, serve justamente para impedir do usuário, poder selecionar qualquer objeto, cujo nome tenha o prefixo indicado na linha quatro do mesmo código. E o que isto quer nos dizer? Bem, isto explica o porquê de não conseguirmos selecionar o objeto OBJ_BUTTON. Já que este objeto, tem em seu nome, o dito prefixo mencionado. Isto pode ser observado na imagem 02 logo na sequência.

Imagem 02

Note que estou marcando em verde, justamente o prefixo, que nos impede de poder selecionar o referente objeto. Apesar de à primeira vista, isto não ter tanta relevância assim. Este simples detalhe, já é o suficiente para que o teste da linha 84, venha a nos impedir de selecionar. Ok, mas não precisamos selecionar as linhas em verde, que são usadas como contorno. Podemos fazer melhor, selecionando o próprio objeto do tipo OBJ_RECTANGLE_LABEL, e deixar com que o MetaTrader 5, faça todo o restante para nós. Sim, em parte isto é verdade. E para conseguir isto, seria necessário que uma simples mudança no código 01 acontecesse. Esta mudança é vista no fragmento de código logo abaixo.

                   .
                   .
                   .
034. //+----------------+
035.     public  :
036.         void CreateBoxResize(const string szArg, const uchar adjust = 2)
037.         {
038.             isBack = (bool)ObjectGetInteger(0, szArg, OBJPROP_BACK);
039.             ObjectSetInteger(0, szArg, OBJPROP_BACK, true);
040.             ObjectSetInteger(0, szArg, OBJPROP_SELECTED, false);
041.             x = (ushort)ObjectGetInteger(0, szArg, OBJPROP_XDISTANCE) - adjust;
042.             y = (ushort)ObjectGetInteger(0, szArg, OBJPROP_YDISTANCE) - adjust;
043.             w = (ushort)ObjectGetInteger(0, szArg, OBJPROP_XSIZE) + adjust;
044.             h = (ushort)ObjectGetInteger(0, szArg, OBJPROP_YSIZE) + adjust;
045.             for (uchar c = 0; c < 4; c++)
046.             {
047.                 CreateEdge(c);
048.                 ObjectSetInteger(0, szObjects[c], OBJPROP_WIDTH, 3);
049.                 ObjectSetInteger(0, szObjects[c], OBJPROP_SELECTABLE, true);
050.                 ObjectSetInteger(0, szObjects[c], OBJPROP_SELECTED, true);
051.             }
052.         }
053. //+----------------+
                   .
                   .
                   .

Código 02

O que este fragmento visto no código 02 irá produzir no código 01 é visto na animação 02 logo abaixo.

Animação 02

Muito bem, nesta animação 02, temos algo sendo mostrado a nós. Vamos observar isto com um pouco mais de calma. Isto é feito olhando a imagem 03.

Imagem 03

Este ponto que estou destacando nesta imagem 03, é justamente o ponto chave, que podemos usar para arrastar o objeto na janela. Isto fazendo uso do MetaTrader 5, sem que precisamos nos preocupar com absolutamente nada. Porém se você observar irá notar que temos apenas 3 pontos sendo visíveis. Onde está o quarto ponto? Bem, este é o nosso primeiro problema. O quarto ponto está junto com um outro ponto. Justamente neste local em que estou destacando na imagem 03. Mas se somente isto fosse o real problema tudo bem. Não haveria muito a fazer com relação a isto. No entanto, existe um problema um pouco mais chato do que este. E tal problema é mostrado na animação logo abaixo.

Animação 03

Agora não entendi. Por que isto que vemos na animação 03 seria um problema? Já que é algo que seria o padrão de funcionando do MetaTrader 5? Bem, meu caro leitor, de fato, acho que você não entendeu o que queremos fazer. Deixe-me tentar deixar isto um pouco mais claro para você. O que estamos tentando fazer, seria algo parecido como o que é visto na animação 03. Porém, sem que as linhas verdes venham a perder contato entre si. Ou seja, no momento em que o ponto de ancoragem for movido, toda linha ligada a ele também deverá ter um movimento correspondente. De forma que uma nova região seja delimitada. Mostrando assim, qual seria as novas dimensões do objeto selecionado. Este é o nosso objetivo, e não está coisa que pode ser vista na animação 03. Entendeu agora onde está o problema?

Ok, nestas horas, muitos já desistiriam e começariam a dizer: "Este tipo de coisa que você quer fazer é impossível de ser feita." Será que isto é mesmo verdade, meu caro leitor? Bem, pense um pouco. Sabemos como o MetaTrader 5 informa nossa aplicação sobre eventos que acontecem com objetos. Assim como também, já vimos como certas decisões de implementação podem influenciar no tipo de resultado que podemos conseguir. Então, pergunto:

Será que o que estou querendo fazer é algo impossível?

A resposta é não. Porém, a maior parte, por ter um conhecimento, experiência menor. Acaba acreditando que sim, já que como se pode ver na animação 03. As coisas não aconteceram como era esperado. Mas novamente devo ressaltar o seguinte ponto:

Não se apegue ao código. Procure sempre entender os conceitos envolvidos. E como eles foram utilizados.

E é justamente isto que programadores fazem. Eles pegam um problema e usando seu conhecimento, sobre conceitos de programação, encontram e elaboram uma solução para o problema. A forma como cada um irá elaborar a solução irá, e com toda a certeza variar, de programador para programador. No entanto, o resultado final é o que realmente importa. Por isto, diversas aplicações, podem ser mais ou menos interativas, sendo mais ou menos fácil de aprender como as utilizar. E no entanto, o resultado final, em todos os casos é o que realmente importa e nos interessa.

Ok, então vamos parar e pensar um pouco a este respeito. Sabemos que NÃO PODEMOS SELECIONAR nenhum dos objetos com um nome que tenha o prefixo definido no código. Isto justamente por conta da linha 84 do código 01. Mas sabemos, agora que não precisamos de fato selecionar tais objetos. Podemos durante a criação dos mesmos, dizer ao MetaTrader 5, que o objeto já se encontrará selecionado. Isto devido as linhas 49 e 50, vistos no fragmento do código 02. Que é justamente a modificação que implementamos no código 01.

Devido a este fato, conseguimos fazer com que o MetaTrader 5, desse ao usuário a possibilidade de mover objetos que antes não poderiam ser movidos, devido ao fato de que não poderíamos os selecionar. Não sei se você está conseguindo acompanhar a linha de raciocínio que estou tentando lhe mostrar, meu caro leitor. Mas, se não estiver, procure rever o que foi explicado nos artigos anteriores para entender onde estou querendo chegar. Pois isto que estou tentando mostrar, será justamente, o conceito que iremos adotar para conseguir alcançar nosso objetivo. Ou seja, redimensionar um objeto presente no gráfico, de forma totalmente interativa.

Tudo bem, vamos fazer o seguinte: Como grande parte de vocês, talvez não saibam como implantar o fragmento visto no código 02, ao código 01, a fim de conseguir o que é visto na animação 03. Irei criar um novo código, de forma que no anexo, você terá acesso ao oque seria o código 02 na integra. Assim você poderá estudar com calma, e aprender como implantar um código a outro já existente. Pois iremos fazer isto por muitas e muitas vezes. E saber como fazer isto, poderá lhe ajudar a estudar e praticar, coisas que de outra maneira não seria possível.

Ok, então, com base no que seria o código 01, com a modificação vista no fragmento de código 02. Vamos criar um novo código. E este pode ser visto logo abaixo.

                   .
                   .
                   .
013.         string  szObjects[6];
014. //+----------------+
015.         void CreateEdge(const char what)
016.         {
017.             ushort p1 = x, p2 = y, p3 = w, p4 = h;
018. 
019.             switch (what)
020.             {
021.                 case 0: p4 = 1; break;
022.                 case 1: p3 = 1; break;
023.                 case 2: p2 = y + h; p4 = 1; break;
024.                 case 3: p1 = x + w; p3 = 1; break;
025.                 case 5: p1 += p3; p2 += p4;
026.                 case 4: p3 = p4 = 1; break;
027.             }
                   .
                   .
                   .
035.         }
036. //+----------------+
037.     public  :
038.         void CreateBoxResize(const string szArg, const uchar adjust = 2)
039.         {
                   .
                   .
                   .
047.             for (uchar c = 0; c < szObjects.Size(); c++)
048.             {
049.                 CreateEdge(c);
050.                 if (c > 3)
051.                 {
052.                     ObjectSetInteger(0, szObjects[c], OBJPROP_WIDTH, 3);
053.                     ObjectSetInteger(0, szObjects[c], OBJPROP_SELECTABLE, true);
054.                     ObjectSetInteger(0, szObjects[c], OBJPROP_SELECTED, true);
055.                 }
056.             }
057.         }
058. //+----------------+
                   .
                   .
                   .

Código 03

Agora observe o seguinte meu caro leitor, neste fragmento de código 03, temos uma simples mudança sendo feita. Porém, a mesma é bastante significativa. Note que na linha 13 mudamos a quantidade de elementos que existiriam no array. E no comando switch visto na linha 19, temos mais dois pontos de interesse. Que podem ser vistos nas linhas 25 e 26. Agora vamos dar uma breve pausa, para que eu possa lhe explicar uma outra coisa, que é muito importante.

No artigo Do básico ao intermediário: Comando SWITCH, existia uma imagem, onde foi mencionada a presença de algumas linhas vermelhas nela. Esta imagem pode ser revista logo abaixo.

Imagem 04

A questão aqui é justamente estas linhas vermelhas, vista na imagem 04. Aqui neste comando switch presente na linha 19, estas linhas vermelhas estão sendo utilizadas. E isto é providencial e desejável. Já que nos permite "vazar" parte do código para dentro de outro código. Rapaz, e eu que imaginei que as coisas não poderiam ficar ainda mais confusas. E me vem você dizendo que um código pode vazar para dentro de outro. E que isto é feito de maneira proposital. Acho que esta coisa de programação é muito mais complicada do que eu imaginava. Agora estou pensando em desistir e procurar outra coisa para fazer.

Calma meu caro leitor, não há motivos para você desistir agora. Justamente agora que tudo irá ficar ainda mais interessante, você vai desistir? Não, venha comigo entender o que seria este tal vazamento do qual estou mencionando.

Observe a sequência dos comandos case. Você irá notar que estão quase que alinhados, até que de repente a coisa muda, do três, para o cinco e depois para o quatro. Que coisa sinistra. Não tinha prestado atenção a isto, agora que você mencionou, parece mesmo estranho. Mas por que você está fazendo isto? Bem, o motivo, é que fiz isto de propósito. Porque? Justamente para lhe explicar a tal linha vermelha que pode ser vista na imagem 04.

Então preste atenção, apesar de ser algo simples, costuma bater forte em muito programador bom. E não apenas em iniciantes. Repare que de proposito, cada linha entre 21 e 26 no fragmento do código 03 existe no final da linha um comando break. Em todas menos em uma, que é a linha 25. Por que, disto? O motivo é que, quando o comando case da linha 25 testar positivo, a linha será executada. Porém, e é aqui onde mora a questão do vazamento, NÃO QUERO QUE A EXECUÇÃO TERMINE ALI. Quero que a rotina presente no próximo case seja executada sem que o comando case seja executado.

Virgem santa Maria. DEUS tenha misericórdia. Pois não entendi nada, do que você disse. Mas espere um pouco, deixe-me pensar com calma o que você disse. Até onde consigo entender, você está querendo que no momento em que a linha 25 seja testada, e a rotina do comando case seja executada. Também venha a ser executada a rotina do comando case da linha 26. É isto? Sim, meu caro leitor. Mas não entendi uma coisa. Com a rotina do comando case presente na linha 26, vai ser executada, sem que o comando case seja analisado? Isto não faz sentido.

É justamente neste ponto, em que acontece o vazamento. Note o seguinte, meu caro leitor. Por conta que o comando case sempre irá terminar em um comando break, como você pode notar na imagem 04. Caso NÃO TENHAMOS O COMANDO BREAK, o nosso comando case, irá entrar na rotina que estiver imediatamente abaixo, SEM PASSAR PELO TESTE DO PROXIMO CASE. E é aí onde a linha vermelha entra em ação.

Percebe, que quando estivermos criando o objeto de número quatro, estaremos colocando as dimensões dele como sendo iguais a um? E que, quando estivermos criando o objeto cinco, também iremos colocar as dimensões dele iguais a um? Porém, no caso do objeto cinco, estaremos mudando a posição inicial do mesmo. De forma que ele fique no canto inferior direito, em oposição ao objeto quatro que fica no canto superior esquerdo. E tudo isto é feito fazendo uso do vazamento que você pode observar neste comando switch da linha 19.

Porém note o seguinte: Na linha 50, testamos para verificar qual objeto estamos criando. Quando tivermos desenhado o quadrado que delimita o objeto selecionado. Passamos a desenhar os dois últimos objetos da mesma maneira que era feito pelo fragmento mostrado no código 02. No entanto, com um objetivo e resultado diferente. Como você pode observar na imagem logo abaixo.

Imagem 05

Note que agora no caso, temos dois pontos. Que é tudo que precisamos. Porém ainda não terminamos por aqui. Isto por que, apesar dos pontos terem sido criados. Veja o resultado de quando interagimos com estes mesmos pontos. Isto pode ser observado na animação logo abaixo.

Animação 04

Hum, interessante. De certa forma, agora ao meu entender temos algo relativamente promissor. E então qual seria o nosso próximo passo? Bem, meu caro leitor, as coisas nem sempre são assim tão simples. A questão aqui, não é exatamente o próximo passo a ser dado. A principal questão, e é isto que eu preciso de você, é que você entenda o que estamos fazendo aqui. Este tipo de manipulação que estou mostrando, precisa ser feito com algum cuidado e criando testes para assegurar que a coisa não irá sair do controle. Sem isto, você fica a mercê de alguma falha, acabar tornando seu dia, um dia muito desagradável.

Então novamente: Não se apegue ao código. Procure entender os conceitos que estão sendo aplicados. E tente criar uma solução que você consiga entender. Pois somente assim, você irá conseguir resolver os problemas que irão surgir durante o tempo de vida de sua aplicação.

Tendo dado novamente este alerta, que não canso de repetir, podemos ir para o que seria o próximo ponto. E neste caso teremos que mexer no código de tratamento de evento. Assim como em outra coisa que será vista depois. Mas primeiro o código de tratamentos de eventos. E este pode ser visto logo abaixo.

                   .
                   .
                   .
079. //+------------------------------------------------------------------+
080. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
081. {
082.     static string szNameObj = NULL;
083. 
084.     switch (id)
085.     {
086.         case CHARTEVENT_KEYDOWN         :
087.             if ((szNameObj != NULL) && (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)))
088.             {
089.                 gl_RZ.RemoveBoxResize(szNameObj);
090.                 szNameObj = NULL;
091.             }
092.             break;
093.         case CHARTEVENT_OBJECT_CLICK    :
094.             Print((ushort)lparam, " x ", (ushort)dparam, " ->> ", sparam);
095.             if (StringFind(sparam, def_Prefix) != INVALID_HANDLE) break;
096.             if (szNameObj != NULL) gl_RZ.RemoveBoxResize(szNameObj);
097.             if (szNameObj != sparam) gl_RZ.CreateBoxResize(szNameObj = sparam); else szNameObj = NULL;
098.             break;
099.         case CHARTEVENT_OBJECT_DRAG     :
100.             Print("CHARTEVENT_OBJECT_DRAG ->> ", sparam);
101.             break;
102.     }
103.     ChartRedraw();
104. };
105. //+------------------------------------------------------------------+
                   .
                   .
                   .

Código 04

Neste fragmento visto no código 04, estamos criando uma simples modificação para que eu possa lhe mostrar uma coisa, que ao meu ver é importante. Apesar de já ter sido vista em artigos anteriores. Porém é sempre bom reforçar certos conceitos, até que você os tenha de fato compreendido, meu caro leitor.

Observe que nas linhas 94 e 100, estamos dizendo para que algum tipo de mensagem seja impresso no terminal. Assim sendo, vamos ver o resultado do que seria uma primeira execução, digamos normal do código. Esta pode ser vista na animação logo abaixo.

Animação 05

Agora veja um comportamento com o qual queremos evitar que possa ocorrer. Este é mostrado na animação 06 logo abaixo.

Animação 06

Em ambas animações, tanto a 05 quanto a 06, temos o comportamento padrão do MetaTrader 5 em ação. Porém podemos conviver com o comportamento visto na animação 05, mas não com o comportamento visto na animação 06. Já que neste caso da animação 06, estamos selecionando ou não um objeto, com o qual a seleção não poderia ser feita pelo usuário. O mesmo poderia apenas e tão somente mover o objeto. Porém não poderia ligar e desligar o status de selecionado do objeto.

Então vamos resolver as coisas em passos. Sendo o primeiro passo este de inibir que o objeto venha a ter o status de seleção modificado, pelo simples fato de o usuário clicar nele. Isto pode ser feito, usando o código logo abaixo.

                   .
                   .
                   .
007. //+------------------------------------------------------------------+
008. struct st_BoxResize
009. {
                   .
                   .
                   .
036. //+----------------+
037.     public  :
                   .
                   .
                   .
065. //+----------------+
066.         void Block(const string szArg)
067.         {
068.             for (uchar c = 0; c < szObjects.Size(); c++)
069.                 if (szObjects[c] == szArg)
070.                     ObjectSetInteger(0, szObjects[c], OBJPROP_SELECTED, true);
071.         }
072. //+----------------+
073. }gl_RZ;
074. //+------------------------------------------------------------------+
                   .
                   .
                   .
086. //+------------------------------------------------------------------+
087. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
088. {
089.     static string szNameObj = NULL;
090. 
091.     switch (id)
092.     {
093.         case CHARTEVENT_KEYDOWN         :
094.             if ((szNameObj != NULL) && (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)))
095.             {
096.                 gl_RZ.RemoveBoxResize(szNameObj);
097.                 szNameObj = NULL;
098.             }
099.             break;
100.         case CHARTEVENT_OBJECT_CLICK    :
101.             Print((ushort)lparam, " x ", (ushort)dparam, " ->> ", sparam);
102.             if (StringFind(sparam, def_Prefix) != INVALID_HANDLE)
103.             {
104.                 gl_RZ.Block(sparam);
105.                 break;
106.             }
107.             if (szNameObj != NULL) gl_RZ.RemoveBoxResize(szNameObj);
108.             if (szNameObj != sparam) gl_RZ.CreateBoxResize(szNameObj = sparam); else szNameObj = NULL;
109.             break;
110.         case CHARTEVENT_OBJECT_DRAG     :
111.             Print("CHARTEVENT_OBJECT_DRAG ->> ", sparam);
112.             break;
113.     }
114.     ChartRedraw();
115. };
116. //+------------------------------------------------------------------+
                   .
                   .
                   .

Código 05

Note como a solução deste primeiro problema é muito fácil de implementar. Tudo que foi preciso fazer foi implementar um novo procedimento dentro da estrutura, e este pode ser observado na linha 66. Logo depois foi necessário adicionar uma chamada a este novo procedimento, e isto é feito na linha 104. O resultado? Bem, veja por si mesmo, na animação logo abaixo.

Animação 07

Agora vem a segunda parte da questão que precisamos resolver. Mas antes vou lembra que a solução que irei mostrar aqui, é voltada apenas para fins didáticos. Não necessariamente é a única que podemos implementar. No entanto, devido justamente aos fins se destina os artigos, estarei mostrando a solução mais simples de todas.

No entanto, nada lhe impede de implementar uma solução que seja bem mais adequada. Podendo inclusive ser uma solução do tipo onde cada movimento efetuado do mouse, a fim de mudar as dimensões, reflita imediatamente nas dimensões do objeto que estamos tentando redimensionar de maneira interativa. Talvez depois eu venha a mostrar como fazer isto. Já que é algo que a princípio também pode ser bastante educativo e até mesmo interessante em diversos casos, que você, meu caro leitor, possa vir a planejar implementar no futuro.

De qualquer maneira, vamos ao que seria a solução mais simples de todas. Ela basicamente envolve dois passos. Sendo o primeiro o que podemos ver sendo implementado no fragmento logo abaixo.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #define def_Prefix  "Demo"
005. //+------------------------------------------------------------------+
006. #define macro_NameObject  def_Prefix + (string)(ObjectsTotal(0) + 1)
007. //+------------------------------------------------------------------+
008. enum E_Elment   {
009.                     TOP,
010.                     LEFT,
011.                     BOTTOM,
012.                     RIGHT,
013.                     TL,
014.                     BR
015.                 };
016. //+------------------------------------------------------------------+
017. struct st_BoxResize
018. {
019.     private :
020.         ushort  x, y, w, h;
021.         bool    isBack;
022.         string  szObjects[6];
023. //+----------------+
024.         void SetPosition(const E_Elment what)
025.         {
026.             ushort p1 = x, p2 = y, p3 = w, p4 = h;
027. 
028.             switch (what)
029.             {
030.                 case TOP: p4 = 1; break;
031.                 case LEFT: p3 = 1; break;
032.                 case BOTTOM: p2 = y + h; p4 = 1; break;
033.                 case RIGHT: p1 = x + w; p3 = 1; break;
034.                 case BR: p1 += p3; p2 += p4;
035.                 case TL: p3 = p4 = 1; break;
036.             }
037.             ObjectSetInteger(0, szObjects[what], OBJPROP_XDISTANCE, p1);
038.             ObjectSetInteger(0, szObjects[what], OBJPROP_YDISTANCE, p2);
039.             ObjectSetInteger(0, szObjects[what], OBJPROP_XSIZE, p3);
040.             ObjectSetInteger(0, szObjects[what], OBJPROP_YSIZE, p4);
041.         }
042. //+----------------+
043.         void CreateEdge(const E_Elment what)
044.         {
045.             szObjects[what] = macro_NameObject;
046.             ObjectCreate(0, szObjects[what], OBJ_RECTANGLE_LABEL, 0, 0, 0);
047.             ObjectSetInteger(0, szObjects[what], OBJPROP_BGCOLOR, clrLime);
048.             SetPosition(what);
049.         }
050. //+----------------+
051.     public  :
                   .
                   .
                   .
072. //+----------------+
073.         void UpdateBoxSize(const string szArg)
074.         {
075.             for (E_Elment c = 0; c < (E_Elment)szObjects.Size(); c++)
076.                 if (szObjects[c] == szArg)
077.                 {
078.                     x = (ushort)ObjectGetInteger(0, szObjects[TL], OBJPROP_XDISTANCE);
079.                     y = (ushort)ObjectGetInteger(0, szObjects[TL], OBJPROP_YDISTANCE);
080.                     w = (ushort)ObjectGetInteger(0, szObjects[BR], OBJPROP_XDISTANCE) - x;
081.                     h = (ushort)ObjectGetInteger(0, szObjects[BR], OBJPROP_YDISTANCE) - y;
082.                     for (E_Elment i = 0; i < (E_Elment)4; i++)
083.                         SetPosition(i);
084.                 }
085.         }
086. //+----------------+
                   .
                   .
                   .
100. //+----------------+
101. }gl_RZ;
102. //+------------------------------------------------------------------+
                   .
                   .
                   .
114. //+------------------------------------------------------------------+
115. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
116. {
117.     static string szNameObj = NULL;
118. 
119.     switch (id)
120.     {
                   .
                   .
                   .
137.         case CHARTEVENT_OBJECT_DRAG     :
138.             if (StringFind(sparam, def_Prefix) != INVALID_HANDLE)
139.                 gl_RZ.UpdateBoxSize(sparam);
140.             break;
141.     }
142.     ChartRedraw();
143. };
144. //+------------------------------------------------------------------+
                   .
                   .
                   .

Código 06

Sei que este código 06 se encontra bastante fragmentado. Mas isto é proposital, devido ao fato de que aqui, quero dar ênfase somente as partes que realmente precisaram ser implementadas. Note que para simplificar diversas coisas, acabei optando em criar uma enumeração na linha oito. Esta por sua vez irá facilitar a implementação destes dois últimos passos que estaremos dando. Também dividi a parte da construção dos objetos, que criam a caixa de limite em dois blocos. Um é o procedimento SetPosition, que você pode observar na linha 24. Enquanto o outro, é a criação propriamente dita dos lados da caixa delimitadora. No caso, este procedimento se encontra na linha 43.

O fato importante aqui, é justamente o surgimento deste procedimento UpdateBoxSize, visto na linha 73. Este procedimento, basicamente irá reconstruir a caixa delimitadora em uma nova posição. Isto quando o evento CHARTEVENT_OBJECT_DRAG, disparar a chamada vista na linha 139.

De qualquer jeito, no final, o objetivo a ser conseguido pode ser visto logo abaixo.

Animação 08

Neste ponto, em que o código se encontra, visto o resultado da animação 08 é um ponto chave. Aqui você pode mudar a forma como o resultado da animação 08 é conseguido. Mas o importante é que ele seja conseguido da maneira como é mostrado nesta animação 08. Porém, além deste objetivo, temos nisto que é a animação 08, algo que podemos mudar conforme vier a se tornar necessário ou desejado por cada programador em particular. Podemos fechar o processo de redimensionamento aqui, no que seria esta animação 08, ou podemos fazer diferente. Que é justamente o caminho no qual tomaremos. E o motivo é simples: Manter a didática o mais próximo possível daquilo que acredito ser o ideal.

Sendo assim, o próximo passo a ser tomado é visto no código logo abaixo. Porém, neste caso irei deixar o código sendo mostrado na íntegra, para que você consiga vislumbrar toda a beleza que pode ser criada com um nível de programação muito singelo e básico. Mas que faz uso correto de diversos conceitos e ensinamentos mostrados até este momento. Apenas admire e curta o resultado. Você merece isto meu caro leitor, isto depois de tanto esperar e ficar imaginando como seria a solução que eu daria ao problema de redimensionar interativamente um objeto no gráfico.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #define def_Prefix  "Demo"
005. //+------------------------------------------------------------------+
006. #define macro_NameObject  def_Prefix + (string)(ObjectsTotal(0) + 1)
007. //+------------------------------------------------------------------+
008. enum E_Elment   {
009.                     TOP,
010.                     LEFT,
011.                     BOTTOM,
012.                     RIGHT,
013.                     TL,
014.                     BR
015.                 };
016. //+------------------------------------------------------------------+
017. struct st_BoxResize
018. {
019.     private :
020.         ushort  x, y, w, h;
021.         bool    isBack;
022.         string  szObjects[6];
023. //+----------------+
024.         void SetPosition(const E_Elment what)
025.         {
026.             ushort p1 = x, p2 = y, p3 = w, p4 = h;
027. 
028.             switch (what)
029.             {
030.                 case TOP: p4 = 1; break;
031.                 case LEFT: p3 = 1; break;
032.                 case BOTTOM: p2 = y + h; p4 = 1; break;
033.                 case RIGHT: p1 = x + w; p3 = 1; break;
034.                 case BR: p1 += p3; p2 += p4;
035.                 case TL: p3 = p4 = 1; break;
036.             }
037.             ObjectSetInteger(0, szObjects[what], OBJPROP_XDISTANCE, p1);
038.             ObjectSetInteger(0, szObjects[what], OBJPROP_YDISTANCE, p2);
039.             ObjectSetInteger(0, szObjects[what], OBJPROP_XSIZE, p3);
040.             ObjectSetInteger(0, szObjects[what], OBJPROP_YSIZE, p4);
041.         }
042. //+----------------+
043.         void CreateEdge(const E_Elment what)
044.         {
045.             szObjects[what] = macro_NameObject;
046.             ObjectCreate(0, szObjects[what], OBJ_RECTANGLE_LABEL, 0, 0, 0);
047.             ObjectSetInteger(0, szObjects[what], OBJPROP_BGCOLOR, clrLime);
048.             SetPosition(what);
049.         }
050. //+----------------+
051.     public  :
052.         void CreateBoxResize(const string szArg, const uchar adjust = 2)
053.         {
054.             isBack = (bool)ObjectGetInteger(0, szArg, OBJPROP_BACK);
055.             ObjectSetInteger(0, szArg, OBJPROP_BACK, true);
056.             ObjectSetInteger(0, szArg, OBJPROP_SELECTED, false);
057.             x = (ushort)ObjectGetInteger(0, szArg, OBJPROP_XDISTANCE) - adjust;
058.             y = (ushort)ObjectGetInteger(0, szArg, OBJPROP_YDISTANCE) - adjust;
059.             w = (ushort)ObjectGetInteger(0, szArg, OBJPROP_XSIZE) + adjust;
060.             h = (ushort)ObjectGetInteger(0, szArg, OBJPROP_YSIZE) + adjust;
061.             for (E_Elment c = 0; c < (E_Elment)szObjects.Size(); c++)
062.             {
063.                 CreateEdge(c);
064.                 if ((c == TL) || (c == BR))
065.                 {
066.                     ObjectSetInteger(0, szObjects[c], OBJPROP_WIDTH, 3);
067.                     ObjectSetInteger(0, szObjects[c], OBJPROP_SELECTABLE, true);
068.                     ObjectSetInteger(0, szObjects[c], OBJPROP_SELECTED, true);
069.                 }
070.             }
071.         }
072. //+----------------+
073.         void UpdateBoxSize(const string szArg)
074.         {
075.             for (E_Elment c = 0; c < (E_Elment)szObjects.Size(); c++)
076.                 if (szObjects[c] == szArg)
077.                 {
078.                     x = (ushort)ObjectGetInteger(0, szObjects[TL], OBJPROP_XDISTANCE);
079.                     y = (ushort)ObjectGetInteger(0, szObjects[TL], OBJPROP_YDISTANCE);
080.                     w = (ushort)ObjectGetInteger(0, szObjects[BR], OBJPROP_XDISTANCE) - x;
081.                     h = (ushort)ObjectGetInteger(0, szObjects[BR], OBJPROP_YDISTANCE) - y;
082.                     for (E_Elment i = 0; i < (E_Elment)4; i++)
083.                         SetPosition(i);
084.                 }
085.         }
086. //+----------------+
087.         void RemoveBoxResize(const string szArg, const bool update)
088.         {
089.             ObjectSetInteger(0, szArg, OBJPROP_BACK, isBack);
090.             ObjectSetInteger(0, szArg, OBJPROP_SELECTED, false);
091.             if (update)
092.             {
093.                 ObjectSetInteger(0, szArg, OBJPROP_XDISTANCE, x);
094.                 ObjectSetInteger(0, szArg, OBJPROP_YDISTANCE, y);
095.                 ObjectSetInteger(0, szArg, OBJPROP_XSIZE, w);
096.                 ObjectSetInteger(0, szArg, OBJPROP_YSIZE, h);
097.             }
098.             ObjectsDeleteAll(0, def_Prefix);
099.         }
100. //+----------------+
101.         void Block(const string szArg)
102.         {
103.             for (uchar c = 0; c < szObjects.Size(); c++)
104.                 if (szObjects[c] == szArg)
105.                     ObjectSetInteger(0, szObjects[c], OBJPROP_SELECTED, true);
106.         }
107. //+----------------+
108. }gl_RZ;
109. //+------------------------------------------------------------------+
110. int OnInit()
111. {
112.     IndicatorSetString(INDICATOR_SHORTNAME, def_Prefix);
113. 
114.     return INIT_SUCCEEDED;
115. };
116. //+------------------------------------------------------------------+
117. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
118. {
119.     return rates_total;
120. };
121. //+------------------------------------------------------------------+
122. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
123. {
124.     static string szNameObj = NULL;
125. 
126.     switch (id)
127.     {
128.         case CHARTEVENT_KEYDOWN         :
129.             if ((szNameObj != NULL) && (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE)))
130.             {
131.                 gl_RZ.RemoveBoxResize(szNameObj, false);
132.                 szNameObj = NULL;
133.             }
134.             break;
135.         case CHARTEVENT_OBJECT_CLICK    :
136.             if (StringFind(sparam, def_Prefix) != INVALID_HANDLE)
137.             {
138.                 gl_RZ.Block(sparam);
139.                 break;
140.             }
141.             if (szNameObj != NULL) gl_RZ.RemoveBoxResize(szNameObj, true);
142.             if (szNameObj != sparam) gl_RZ.CreateBoxResize(szNameObj = sparam); else szNameObj = NULL;
143.             break;
144.         case CHARTEVENT_OBJECT_DRAG     :
145.             if (StringFind(sparam, def_Prefix) != INVALID_HANDLE)
146.                 gl_RZ.UpdateBoxSize(sparam);
147.             break;
148.     }
149.     ChartRedraw();
150. };
151. //+------------------------------------------------------------------+
152. void OnDeinit(const int reason)
153. {
154.     ObjectsDeleteAll(0, def_Prefix);
155.     ChartRedraw();
156. };
157. //+------------------------------------------------------------------+

Código 07

Neste caso não irei explicar o que está acontecendo neste código 07. Quero que você procure estudar e assim compreender como este código, aparentemente sem muito sentido. E que para muitos, parece algo saído da cabeça de algum maluco beleza. Consegue fazer o que é mostrado nas animações logo abaixo. Primeiro o redimensionamento padrão.

Animação 09

Agora um redimensionamento, com um outro que será cancelado na sequência.

Animação 10


Considerações finais

Programar, não é algo chato, difícil e tão pouco cansativo. Na verdade, é algo agradável, empolgante e divertido. O grande detalhe é: Você está conseguindo entender o que deseja fazer? Consegue entender conceitos básicos e aplicar os mesmos em um conjunto lógico de instruções? Onde você diz a máquina o que fazer, quando fazer e por que fazer? Ou você apenas fica digitando um monte de coisas totalmente sem sentido esperando que a máquina consiga compreender o que você deseja que seja feito?

Muita gente acha que programar é somente para grandes e talentosos profissionais. Digo que pode até vir a ser este o caso. Mas nada impede que você, meu caro e estimado leitor. Entusiasta de programação consiga pensar em uma forma de resolver um problema. Colocar isto em um pedaço de papel. Analisar quais os conceitos que precisam ser adotados e depois implementar isto em forma de código.

Talento é para pouco. Mas empenho, dedicação, estudo e disciplina é para aqueles que realmente sabem onde querem chegar e procuram meios de conseguir atingir seus objetivos.

Pense nisto. E aproveite o conteúdo que está sendo disponibilizado para estudar e praticar. Não deixe para amanhã o que poderia ter sido feito ontem. Mas nunca é tarde para começar e se dedicar a algo que você sinta prazer em fazer.

Talvez no próximo artigo, eu venha a mostrar outras coisas, que podemos implementar a fim de melhorar ainda mais, este código visto aqui, e que estará disponível no anexo. Tudo vai depender, se o material a ser mostrado irá ou não agregar, algo ao que foi visto aqui. De qualquer maneira, não espere que eu venha mostrar como melhor as coisas. Procure você mesmo, uma solução, e se não conseguir implementar as coisas logo de início. Procure estudar o que já foi visto até aqui. Pois tem muito material para ser assimilado, e isto somente vem com a prática e com o tempo.

Arquivo MQ5Descrição
Code 01 Demonstração de evento em objeto
Code 02  Demonstração de evento em objeto 
Arquivos anexados |
Anexo.zip (2.94 KB)
Simulação de mercado: Position View (II) Simulação de mercado: Position View (II)
Neste artigo, mostrarei de maneira o mais simples e prática possível. Como você poderá usar um indicador como sendo uma forma de observar posições que estejam abertas. Isto junto ao servidor de negociação. Estou fazendo isto, desta forma e ao poucos, justamente para mostrar, que você não precisa necessariamente, colocar tais coisas em um Expert Advisor. Muitos de vocês, já devem estar bastante acostumados em fazer isto. Seja por um motivo, seja por outro qualquer. Mas a verdade é que isto é pura bobagem, já que conforme formos avançando nesta implementação, ficará claro, que você poderá criar, ou implementar diversos tipos diferentes de indicadores, para tão propósito.
Simulação de mercado: Position View (I) Simulação de mercado: Position View (I)
O conteúdo, que veremos a partir de agora, é muito mais complicado em termos de teorias e conceitos. Tentarei deixar o conteúdo o mais simples quanto for possível fazer. A parte referente a programação em si. É até bastante simples e direta. Mas se você não compreender toda a teórica, que está debaixo dos panos. Ficará completamente sem meios para poder melhorar, ou mesmo adaptar o sistema de replay/simulador. A algo diferente do que irei mostrar. Meu intuito não é que você simplesmente compile e use o código que estou mostrando. Quero que você aprenda, entenda e se possível, possa criar algo ainda melhor.
Ganhe Vantagem em Qualquer Mercado (Parte IV): Índices de Volatilidade do Euro e do Ouro da CBOE Ganhe Vantagem em Qualquer Mercado (Parte IV): Índices de Volatilidade do Euro e do Ouro da CBOE
Vamos analisar dados alternativos selecionados pela Chicago Board Of Options Exchange (CBOE) para melhorar a precisão de nossas redes neurais profundas ao prever o símbolo XAUEUR.
Como funções de cem anos atrás podem atualizar suas estratégias de trading Como funções de cem anos atrás podem atualizar suas estratégias de trading
Neste artigo, vamos falar sobre as funções de Rademacher e Walsh. Vamos explorar formas de aplicar essas funções na análise de séries temporais financeiras, além de considerar diferentes maneiras de usá-las no trading.