
Do básico ao intermediário: Eventos em Objetos (III)
Introdução
No artigo anterior Do básico ao intermediário: Eventos em Objetos (II), foi visto como seria possível implementar um tipo de aplicação, muito interessante e até mesmo bastante curiosa. Cujo objetivo seria o de tornar possível editar o texto de um objeto OBJ_LABEL diretamente no gráfico. Sem a necessidade de abrir a janela de propriedade de objetos e editar o texto a ser apresentado ali.
Apesar de ter sido algo muito interessante, e até mesmo bem bacana, isto olhando pelo ponto de vista prático e educacional. Podemos fazer algo ainda mais interessante do que aquilo que foi visto no artigo anterior. Como você, meu caro leitor, acredito que deve ter experimentado e até se questionado se haveria como estender aquela mesma aplicação. Isto a fim de que fosse possível atingir todo e qualquer objeto do tipo OBJ_LABEL, e não somente aquele que estaríamos criando pela nossa aplicação. Decidi, mostrar como poderíamos fazer isto. Já que pode vir a ser algo realmente interessante, justamente devido a uma outra questão que iremos abordar depois.
Como de praxe, vamos ao que seria o primeiro tópico deste artigo. Então hora de se afastar das distrações e focar em prestar atenção ao que será visto neste artigo. Pois aqui iremos tratar de diversas coisas bem legais.
Melhorando o que já era bom
Ok, no artigo anterior vimos ser possível fazer o que é mostrado na animação logo abaixo.
Animação 01
Portanto, é sim possível implementar um mecanismo para editar diretamente no gráfico o texto presente em um objeto do tipo OBJ_LABEL. Mas, se você tentou adicionar um novo OBJ_LABEL, de maneira manual. Viu que isto que é mostrado na animação 01 não seria possível. Mas porquê? O motivo são os filtros que estão sendo utilizados durante o tratamento dos eventos. De certa forma, a princípio, a simples remoção dos filtros já resolveria o problema. Isto a princípio. Já que se você tentar fazer isto, irá notar que a aplicação fica um tanto quanto maluca.
Por conta disto, vou mostrar de maneira rápida, como você deveria modificar o código fonte, visto no artigo anterior. De forma que pudesse utilizar o mecanismo visto na animação 01, para editar qualquer objeto do tipo OBJ_LABEL.
Pode parecer ser algo complicado, porém na verdade é bem mais simples do que possa parecer. Desde é claro, você venha a tomar certos cuidados durante a mudança do código. Como grande parte do que precisa ser feito, já foi explicado nos artigos anteriores. Vamos apenas focar no que realmente importa. E isto pode ser visto logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. int OnInit() 05. { 06. return INIT_SUCCEEDED; 07. }; 08. //+------------------------------------------------------------------+ 09. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 10. { 11. return rates_total; 12. }; 13. //+------------------------------------------------------------------+ 14. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 15. { 16. switch (id) 17. { 18. case CHARTEVENT_OBJECT_CLICK : 19. if ((ENUM_OBJECT)ObjectGetInteger(0, sparam, OBJPROP_TYPE) == OBJ_LABEL) Swap_ObjLabel_ObjEdit(sparam); 20. break; 21. case CHARTEVENT_OBJECT_ENDEDIT : 22. Swap_ObjLabel_ObjEdit(sparam); 23. break; 24. } 25. ChartRedraw(); 26. }; 27. //+------------------------------------------------------------------+ 28. void Swap_ObjLabel_ObjEdit(const string szName) 29. { 30. struct st_Mem 31. { 32. string font, text; 33. int x, y, w, h, fontSize; 34. color cor; 35. }mem; 36. ENUM_OBJECT type = (ENUM_OBJECT)ObjectGetInteger(0, szName, OBJPROP_TYPE) == OBJ_EDIT ? OBJ_LABEL : OBJ_EDIT; 37. 38. mem.font = ObjectGetString(0, szName, OBJPROP_FONT); 39. mem.x = (int)ObjectGetInteger(0, szName, OBJPROP_XDISTANCE); 40. mem.y = (int)ObjectGetInteger(0, szName, OBJPROP_YDISTANCE); 41. mem.w = (int)ObjectGetInteger(0, szName, OBJPROP_XSIZE); 42. mem.h = (int)ObjectGetInteger(0, szName, OBJPROP_YSIZE); 43. mem.fontSize = (int)ObjectGetInteger(0, szName, OBJPROP_FONTSIZE); 44. mem.cor = (color)ObjectGetInteger(0, szName, OBJPROP_COLOR); 45. mem.text = ObjectGetString(0, szName, OBJPROP_TEXT); 46. 47. ObjectDelete(0, szName); 48. ChartRedraw(); 49. ObjectCreate(0, szName, type, 0, 0, 0); 50. 51. ObjectSetInteger(0, szName, OBJPROP_SELECTABLE, (type != OBJ_EDIT ? true : false)); 52. ObjectSetInteger(0, szName, OBJPROP_XDISTANCE, mem.x); 53. ObjectSetInteger(0, szName, OBJPROP_YDISTANCE, mem.y); 54. ObjectSetInteger(0, szName, OBJPROP_XSIZE, mem.w); 55. ObjectSetInteger(0, szName, OBJPROP_YSIZE, mem.h); 56. ObjectSetInteger(0, szName, OBJPROP_COLOR, mem.cor); 57. ObjectSetInteger(0, szName, OBJPROP_FONTSIZE, mem.fontSize); 58. ObjectSetString(0, szName, OBJPROP_FONT, mem.font); 59. ObjectSetString(0, szName, OBJPROP_TEXT, mem.text); 60. if (type == OBJ_EDIT) 61. { 62. ObjectSetInteger(0, szName, OBJPROP_BGCOLOR, clrWhite); 63. ObjectSetInteger(0, szName, OBJPROP_READONLY, false); 64. } 65. } 66. //+------------------------------------------------------------------+
Código 01
Agora quero que você olhe este código 01 e me diga: Qual a parte complicada neste código? Bem, se você vem acompanhando esta seria de artigos, e também estudando e praticando o que vem sendo mostrado. Possivelmente a sua resposta será: Não tem nada de complicado aqui. É tudo muito simples e claro para mim. De fato, meu caro leitor, este código 01 é tão simples e fácil de entender que não vejo necessidade em explicar como ele funciona. Porém, temos uma pequena falha aqui. Nada de muito complicado. Mas antes de mostrar como corrigir a falha. Vamos ver o que acontece quando este código 01 estiver presente em um gráfico qualquer. Para isto, vamos ver usar uma pequena sequência de passos. Começando com a que podemos ver logo abaixo.
Animação 02
Nesta animação 02, apenas estamos adicionando o código 01 ao gráfico. Note que nada aconteceu. Já que o objetivo não é de fato criar algo, mas sim manipular algo. Logo depois vamos adicionar alguns objetos do tipo OBJ_LABEL. Para fazer isto, usamos o que é visto na imagem logo abaixo.
Imagem 01
Esta imagem mostra como você faz para adicionar objetos do tipo certo no gráfico. Adicione quantos desejar. No caso, irei adicionar apenas dois, já que o objetivo é mostrar justamente a falha que existe. Assim o gráfico fica como mostrado logo abaixo.
Imagem 02
Muito bem, agora vem a parte em que as coisas começam a fazer sentido. Uma vez que a aplicação esteja sendo executada no gráfico e temos algum objeto do tipo certo. Não poderemos mais fazer as coisas como eram feitas antes. Isto porque, ao clicar em um dos objetos do tipo OBJ_LABEL, ele irá se transformar em um objeto do tipo OBJ_EDIT. Nos permitindo assim, digitar diretamente o texto a ser mostrado. Isto sem precisar abrir a caixa de propriedades do objeto do tipo OBJ_LABEL.
Animação 03
Note que com base no que é visto na animação 03, realmente estamos conseguindo editar as coisas como foi prometido e demonstrado no artigo anterior. Só que agora podemos fazer isto com todo e qualquer objeto do tipo OBJ_LABEL. Mas não somente isto, podemos fazer bem mais coisas, só que se uma maneira um pouco diferente. Para isto, você precisa acessar a janela de lista de objetos. Assim você pode mudar algumas coisas, como cor, tipo de fonte, tamanho da fonte e também o nome do próprio objeto.
Porém, estas propriedades que estaremos manipulando assim, são propriedades extras. E de qualquer forma, tanto o objeto do tipo OBJ_EDIT quando o objeto do tipo OBJ_LABEL, irão partilhar das mesmas propriedades. Já que o procedimento da linha 28, visto no código 01, fará este trabalho de compartilhar as informações para nós.
Mas então que falha seria está da qual mencionei no início do tópico? Bem, meu caro leitor, não que seja uma falha terrível e assombrosa. É mais um problema de seleção e liberação. Quando usamos a aplicação vista no código 01, muitas das vezes, iremos desejar, que apenas e somente um único objeto do tipo OBJ_EDIT, esteja presente no gráfico. Isto porque, não faz muito sentido, em algumas situações existirem diversos objetos do tipo OBJ_EDIT presentes no gráfico. Mas devido a uma falha no processo de seleção e liberação, veja o que acontece.
Animação 04
Note que tínhamos originalmente dois objetos do tipo OBJ_LABEL e passamos a ter dois do tipo OBJ_EDIT. E isto pode ser corrigido. Não estou dizendo que é totalmente errado. Mas não faz sentido, que tenhamos uma situação como a vista na animação 04, se o objetivo é podermos simplesmente editar o texto de OBJ_LABEL diretamente no gráfico.
Corrigir este tipo de falha é muito fácil e direto. Tudo que precisamos fazer é mudar o código de tratamento de eventos, a fim de resolver esta questão. Para o nosso caso específico, bastará mudar o código como mostrado logo abaixo.
. . . 13. //+------------------------------------------------------------------+ 14. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 15. { 16. static string szOldEdit = ""; 17. 18. switch (id) 19. { 20. case CHARTEVENT_OBJECT_CLICK : 21. if ((ENUM_OBJECT)ObjectGetInteger(0, sparam, OBJPROP_TYPE) == OBJ_LABEL) 22. { 23. if (szOldEdit != "") Swap_ObjLabel_ObjEdit(szOldEdit); 24. Swap_ObjLabel_ObjEdit(szOldEdit = sparam); 25. } 26. break; 27. case CHARTEVENT_OBJECT_ENDEDIT : 28. Swap_ObjLabel_ObjEdit(sparam); 29. szOldEdit = ""; 30. break; 31. } 32. ChartRedraw(); 33. }; 34. //+------------------------------------------------------------------+ . . .
Código 02
Note o quanto a solução é simples de ser feita, sendo preciso mexer apenas nestes pontos mostrados no fragmento visto no código 02. E o resultado é pode ser observado, na animação 05, vista logo abaixo.
Animação 05
Ok, acho que consigo entender o quanto é importante praticar e estudar melhor as coisas. Antes de sairmos por aí dizendo algo que todos dizem apenas por parecer ser o correto. Mas estou com uma dúvida, com relação a esta aplicação, que você, mostrou tão gentilmente como produzir. Será que não existe uma forma de podermos manipular mais coisas, sem precisar abrir a janela onde podemos ver a lista de objetos? Digo isto, pois aparentemente não temos como manipular, a posição onde o objeto será mostrado. Uma vez com ele colocado e a aplicação estando sendo executada no gráfico. Fica praticamente impossível mover o objeto no gráfico.
Mas então, será que teria como resolvermos este tipo de situação? Já que isto me incomoda bastante. Apesar de achar bem legal, este tipo de manipulação que você mostrou como fazer.
Sim meu caro leitor, existe de fato formas de contornar isto. Por isto é importante que você entenda muito bem certas coisas. E sempre procure entender o conceito envolvido. E não necessariamente o código que foi utilizado. Isto por que, o código pode ser modificado e manipulado. Mas o conceito não.
Existem diversas formas diferentes de se fazer este tipo de coisa que você sugeriu. Que seria a possibilidade de manipular a posição do objeto, quando a aplicação vista aqui, esteja sendo executada no gráfico. Uma das mais simples, seria mudar a forma como o objeto do tipo OBJ_EDIT está trabalhando.
No artigo anterior, mencionei que para podermos editar o conteúdo de um objeto do tipo OBJ_EDIT. Precisamos que uma de suas propriedades tenha um determinado valor. Se esta mesma propriedade contiver um outro valor, a edição será bloqueada. Mas porque isto é importante para nós? A ideia não seria criar uma forma de mover o objeto no gráfico? Como esta informação sobre uma determinada propriedade, e o como ela afeta o objeto do tipo OBJ_EDIT, poderá nos ajudar a resolver a questão do movimento? Não estou conseguindo entender. Poderia explicar melhor isto?
Claro que posso explicar melhor onde quero chegar com isto. Se você se recorda, nestes dois últimos artigos, mostrei como eventos de objetos podem ser capturados por nosso código. Isto quando precisamos saber que está acontecendo. Como não queremos um sobre trabalho muito grande, a ponto de podem precisar dizer aos objetos como eles precisam ser movidos. Deixamos isto a cargo do MetaTrader 5 executar. Ok, esta é a parte fácil de entender. Talvez a parte complicada, e daí a necessidade de entender muito bem como os objetos funcionam. Vem do fato de que, quando um objeto do tipo OBJ_EDIT, não pode ser editado, devido ao fato de ele estar selecionado. Podemos jogar a responsabilidade de mover o objeto, para o MetaTrader 5.
E a parte mais difícil de entender. Isto para grande maioria das pessoas que estão começando a estudar programação. É que nosso código 01, mesmo depois de colocarmos o fragmento visto no código 02, estará transformando um objeto do tipo OBJ_LABEL em um objeto do tipo OBJ_EDIT. Isto para que editar o texto que será depois mostrado. Porém, o que acontece se você clicar novamente no objeto do tipo OBJ_EDIT? Com base no que estamos tratando no código, a resposta para isto é: NADA. Mas pense por um instante, quando terminamos de mover um objeto, o MetaTrader 5 irá disparar um evento. Este irá notificar nossa aplicação de que o objeto foi solto pelo usuário. E que ele se encontra em uma nova posição.
Assim, se usarmos um pouco de estratégia, podemos forçar o objeto do tipo OBJ_EDIT, a uma condição que irá permitir que o MetaTrader 5, controle a movimentação. Permitindo assim que o usuário, mova o objeto no gráfico. Quando ele for solto, liberamos a edição, ou podemos simplesmente, transformar o objeto do tipo OBJ_EDIT em um objeto do tipo OBJ_LABEL. Cabe a você, meu caro leitor, ver qual seria a alternativa que melhor se encaixa aquilo que você deseja fazer.
Talvez falando desta forma, isto que foi pensado, venha a parecer um tanto quanto complicado. Porém, quando tornamos as ideias em um código, entender as coisas se torna bem mais simples. No entanto, antes de fazermos isto, preciso mostrar uma coisa a você meu caro leitor. Será algo simples, mas que tornará bem mais claro o entendimento de um detalhe crucial para nós.
Quando eu disse no artigo anterior sobre o fato de precisarmos definir a propriedade OBJPROP_SELECTABLE, em um objeto do tipo OBJ_EDIT como sendo false. Não cheguei a mostrar porquê. Embora, em alguns casos podemos definir OBJPROP_SELECTABLE como true, e ainda assim ser possível editar o texto no objeto do tipo OBJ_EDIT. Mas mostrar um caso ou outro onde podemos fazer isto, definitivamente torna as coisas bem mais complicas. Para mostrar, na prática o que foi dito, veja o código logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. int OnInit() 05. { 06. return INIT_SUCCEEDED; 07. }; 08. //+------------------------------------------------------------------+ 09. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 10. { 11. return rates_total; 12. }; 13. //+------------------------------------------------------------------+ 14. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 15. { 16. static string szOldEdit = ""; 17. 18. switch (id) 19. { 20. case CHARTEVENT_OBJECT_CLICK : 21. if ((ENUM_OBJECT)ObjectGetInteger(0, sparam, OBJPROP_TYPE) == OBJ_LABEL) 22. { 23. if (szOldEdit != "") Swap_ObjLabel_ObjEdit(szOldEdit); 24. Swap_ObjLabel_ObjEdit(szOldEdit = sparam); 25. } 26. break; 27. case CHARTEVENT_OBJECT_ENDEDIT : 28. Swap_ObjLabel_ObjEdit(sparam); 29. szOldEdit = ""; 30. break; 31. } 32. ChartRedraw(); 33. }; 34. //+------------------------------------------------------------------+ 35. void Swap_ObjLabel_ObjEdit(const string szName, bool Selectable = true) 36. { 37. struct st_Mem 38. { 39. string font, text; 40. int x, y, w, h, fontSize; 41. color cor; 42. }mem; 43. ENUM_OBJECT type = (ENUM_OBJECT)ObjectGetInteger(0, szName, OBJPROP_TYPE) == OBJ_EDIT ? OBJ_LABEL : OBJ_EDIT; 44. 45. mem.font = ObjectGetString(0, szName, OBJPROP_FONT); 46. mem.x = (int)ObjectGetInteger(0, szName, OBJPROP_XDISTANCE); 47. mem.y = (int)ObjectGetInteger(0, szName, OBJPROP_YDISTANCE); 48. mem.w = (int)ObjectGetInteger(0, szName, OBJPROP_XSIZE); 49. mem.h = (int)ObjectGetInteger(0, szName, OBJPROP_YSIZE); 50. mem.fontSize = (int)ObjectGetInteger(0, szName, OBJPROP_FONTSIZE); 51. mem.cor = (color)ObjectGetInteger(0, szName, OBJPROP_COLOR); 52. mem.text = ObjectGetString(0, szName, OBJPROP_TEXT); 53. 54. ObjectDelete(0, szName); 55. ChartRedraw(); 56. ObjectCreate(0, szName, type, 0, 0, 0); 57. 58. ObjectSetInteger(0, szName, OBJPROP_SELECTABLE, Selectable); 59. ObjectSetInteger(0, szName, OBJPROP_XDISTANCE, mem.x); 60. ObjectSetInteger(0, szName, OBJPROP_YDISTANCE, mem.y); 61. ObjectSetInteger(0, szName, OBJPROP_XSIZE, mem.w); 62. ObjectSetInteger(0, szName, OBJPROP_YSIZE, mem.h); 63. ObjectSetInteger(0, szName, OBJPROP_COLOR, mem.cor); 64. ObjectSetInteger(0, szName, OBJPROP_FONTSIZE, mem.fontSize); 65. ObjectSetString(0, szName, OBJPROP_FONT, mem.font); 66. ObjectSetString(0, szName, OBJPROP_TEXT, mem.text); 67. if (type == OBJ_EDIT) 68. { 69. ObjectSetInteger(0, szName, OBJPROP_BGCOLOR, clrWhite); 70. ObjectSetInteger(0, szName, OBJPROP_READONLY, false); 71. } 72. } 73. //+------------------------------------------------------------------+
Código 03
Este código 03, seria o mesmo código 02, onde vimos apenas um fragmento do todo, só que sendo mostrado de forma mais integra desta vez. Porém, apesar de ser o mesmo código, aqui temos uma diferença. Esta é vista na linha 35. E este valor, mostrado no seguindo argumento é usado em apenas um único ponto. Ou seja, na linha 58. Agora preste atenção, pois isto pode vir a ser tornar um tanto quanto confuso dependendo do tipo de configuração que você esteja implementando no seu código. Da forma como está, este código 03 terá o seguinte comportamento, que pode ser visto na animação logo abaixo.
Animação 06
Parou. Espere um pouco aí. Este código não está funcionando como o que foi visto no código 02. Como assim? O simples fato de termos mudado uma única propriedade mudou o código desta forma? Isto é muito estranho. Mas será que não podemos permitir a edição ao mesmo tempo em que permitimos que o objeto possa ser selecionado? Existem casos que sim, meu caro leitor. Mas devido a algumas questões um tanto quanto chatas de explicar, já que são necessários entrar em detalhes sobre o funcionamento de algumas propriedades. Prefiro adotar um outro tipo de abordagem. Já que é mais simples explicar e demonstrar de forma que você não fique confuso.
Ok, então acredito que você conseguiu notar o seguinte fato. Se a propriedade OBJPROP_SELECTABLE estiver ligada, em um objeto do tipo OBJ_EDIT. Você deve pensar no fato de considerar não poder editar o texto diretamente no gráfico. Porém, será perfeitamente possível mover o objeto.
Então agora, tendo estes fatos em mente, podemos mudar o código 03 de forma a conseguir obter o tipo de comportamento que esperamos conseguir. Isto é feito, modificando o código como é mostrado logo abaixo.
. . . 18. switch (id) 19. { 20. case CHARTEVENT_OBJECT_CLICK : 21. { 22. ENUM_OBJECT type = (ENUM_OBJECT)ObjectGetInteger(0, sparam, OBJPROP_TYPE); 23. if (type == OBJ_EDIT) ObjectSetInteger(0, sparam, OBJPROP_SELECTABLE, false); 24. else if (type == OBJ_LABEL) 25. { 26. if (szOldEdit != "") Swap_ObjLabel_ObjEdit(szOldEdit); 27. Swap_ObjLabel_ObjEdit(szOldEdit = sparam); 28. ObjectSetInteger(0, sparam, OBJPROP_SELECTED, true); 29. } 30. } 31. break; . . .
Código 04
Aqui neste código 04, estamos dando foco apenas e somente a parte que realmente é necessária ser modificada no código 03. Ou seja, vemos apenas o fragmento que precisou ser modificado. Agora, quando utilizarmos este fragmento no código 03, ou o que seria o código 02. O resultado será o que você pode observar logo abaixo na animação 07.
Animação 07
Cara, mais que coisa mais maluca e sem noção é esta que está sendo implementada? Você não deve ser normal. Só pode, para pensar nesta forma de resolver a questão da movimentação e da edição do objeto do tipo OBJ_LABEL diretamente no gráfico. Isto é insano.
Mas tirando a parte insana, o que acabo de mostrar é ou não é algo divertido? Isto por que, basicamente sem quase nenhum tipo de trabalho, foi possível criar uma forma de editar diretamente um objeto do tipo OBJ_LABEL diretamente no gráfico. Claro que esta solução não é 100% perfeita. Isto porque você precisa utilizar a aplicação durante algum tempo para pegar o jeito da coisa. Já que se trata de uma sequência de operações que precisam ser efetuadas em determinados momento. Como pode ser observado na animação 07. Primeiro você clica em um objeto do tipo OBJ_LABEL. Então a linha 24 irá passar no teste e assim iremos converter um objeto do tipo OBJ_LABEL em um do tipo OBJ_EDIT. Mas como queremos poder mover ele, iremos na linha 28, ligar a seleção do objeto. Isto permitirá que possamos mover o objeto do tipo OBJ_EDIT no gráfico.
Assim que clicamos novamente, no mesmo objeto. Temos o teste da linha 23 desligando a possibilidade de seleção. Assim quando clicarmos novamente, o MetaTrader 5, não irá selecionar o objeto do tipo OBJ_EDIT. Mas sim irá selecionar o texto. Nos permitindo modificar o mesmo. Ficou legal, mas pode ser melhorado. Porém, como o objetivo, NÃO é o de criar uma aplicação, mas sim mostrar o que e como podemos fazer as coisas. Já me sinto satisfeito com o resultado apresentado. Podendo assim passar para um próximo tópico.
Controlando as dimensões diretamente no gráfico
Oque foi visto até este momento, posso dizer sem falsas modéstias, que foi algo bem divertido e interessante de ser feito. Mas nada se compara ao que iremos ver agora. Bem, no bem da verdade iremos começar a fazer. Já que neste momento, acredito que você, meu caro e estimado leitor, deve estar super empolgado para ver as possibilidades que passamos a ter em mãos. Isto por que, dependendo da sua criatividade e do quanto você consegue imaginar como as coisas podem se encaixar. Mais ou menos coisas passam a ser possíveis de serem feita. Pelo simples fato de que você passou a entender como o MetaTrader 5 e nossa aplicação interagem entre si, de maneira a conseguirmos produzir um certo tipo de resultado e de modelo de aplicação.
Uma das coisas que ao meu ver é muito divertida de ser feita, é a possibilidade de fazer com que o MetaTrader 5, seja utilizado, não para operações de mercado, como ele foi originalmente projetado para ser utilizado. Mas sim o de fazer com que ele venha a ser utilizado para gerar outros tipos de coisas. Como por exemplo um editor gráfico, editor de texto, ou até mesmo uma pequena plataforma de jogos simples. Como quebra-cabeças ou jogos clássicos. Estilo o que se via em antigos fliperamas.
Muitos talvez achem não ser possível fazer este tipo de coisa. Já que aparentemente não temos os mecanismos necessários para se produzir tais coisas. Porém, diferente do que muitos acreditam e pensam saber sobre o que podemos ou não fazer com o MetaTrader 5. Temos sim possibilidades de fazer este tipo de coisa na plataforma. Desde que você tenha imaginação e conhecimento adequado. Mas o principal é que você tenha os conceitos corretos e saiba como usá-los ao seu favor. Se isto ocorrer, você poderá conseguir construir muita coisa legal para ser executada no MetaTrader 5. E não somente ficar olhando gráficos de cotação, chatos e sem graça. Mas talvez você, até consiga elaborar uma forma um pouco mais divertida de plotar um gráfico de cotação. Usando para isto elementos 3D entre outras coisas.
Mas isto é algo que neste momento, muito provavelmente você não estará preparado para conseguir visualizar. Isto de forma a conseguir fazer tal coisa. Visto que até este momento, estamos apenas e somente no básico da coisa toda. E sim, diferente do que você possa estar imaginando. Ainda estamos no nível básico do que seria a programação em MQL5. Bem iniciante mesmo. E olha o quanto de coisa já conseguimos fazer, somente neste nível básico.
Ok, então agora vamos ver uma outra, que é bem fácil de ser feita. Apesar de ser um pouco complicada de entender, se você não estiver acompanhando os artigos, e os estudando. Já que iremos fazer uso de certas coisas que pode parecer um tanto quanto complicado. No entanto, não quero limitar a sua imaginação ao nível básico do que podemos fazer. Se podemos fazer algo, vamos fazer. Mesmo que demore um pouco para que você possa vir a entender de fato, o que está acontecendo e que tipo de conceito está sendo utilizado.
A ideia neste tópico é criar uma forma, ou maneira de mudar as dimensões de algum objeto no gráfico, usando para isto o mouse. Porém vamos tentar fazer isto, sem precisar fazer uso diretamente dos eventos de mouse. Parece um tanto quanto complicado, não é mesmo? Mas a ideia será o de continuar a utilizar o auxílio do MetaTrader 5 para administrar certos eventos e nós os iremos controlar de maneira a produzir um certo tipo de resultado.
Isto sim, parece ser algo muito, mas muito complicado de ser feito. Algo que somente algum maluco beleza, saído de algum sanatório, e doido para tornar outros igual a ele pensaria em fazer. Mas você irá ver que é tão simples de ser feito, quanto andar para frente. Talvez até mais simples que isto. Porém como o tema é bem logo e necessitará de mais de um artigo, para ser devidamente explicado e compreendido. Vamos começar pelo seguinte fato: Diferente do que aconteceu nos demais códigos, aqui iremos já permitir que você, ou qualquer outro usuário, possa manipular as dimensões de qualquer tipo de objeto.
Se bem que inicialmente não será qualquer tipo. Podendo este ter sido criado pela sua aplicação, ou tendo sido colocado pelo usuário no gráfico. Não importa, iremos trabalhar com todos da mesma maneira. Existem algumas limitações, mas iremos ver isto conforme o código for sendo implementado. Então para começar, vamos ter o seguinte código mostrado logo abaixo.
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. int OnInit() 09. { 10. IndicatorSetString(INDICATOR_SHORTNAME, def_Prefix); 11. 12. return INIT_SUCCEEDED; 13. }; 14. //+------------------------------------------------------------------+ 15. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 16. { 17. return rates_total; 18. }; 19. //+------------------------------------------------------------------+ 20. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 21. { 22. switch (id) 23. { 24. case CHARTEVENT_KEYDOWN : 25. break; 26. case CHARTEVENT_OBJECT_CLICK : 27. break; 28. case CHARTEVENT_OBJECT_DRAG : 29. break; 30. } 31. ChartRedraw(); 32. }; 33. //+------------------------------------------------------------------+ 34. void OnDeinit(const int reason) 35. { 36. ObjectsDeleteAll(0, def_Prefix); 37. ChartRedraw(); 38. }; 39. //+------------------------------------------------------------------+
Código 05
Este código 05 é nosso esqueleto base. Note que temos três eventos indicados para serem tratados neste código 05. Isto porque, não iremos criar uma aplicação fodástica (isto seria a união da palavra foda com a palavra fantástica). Aqui iremos apenas fazer algo simples e fácil de entender. Que será o de manipular as dimensões e talvez posição de um objeto qualquer. E isto diretamente no gráfico. Com relação a posição ainda estou pensando se vale ou não apena implementar ela.
Certo, você é o chefe aqui. Então qual é o nosso próximo passo? Bem, meu caro leitor, o próximo passo é usar o evento CHARTEVENT_OBJECT_CLICK, para capturar algumas informações sobre o objeto que iremos manipular. Basicamente iremos trabalhar somente esta informação, que é o nome do objeto. Já que esta é a única informação realmente importante para nós. Já que todo o restante podemos conseguir nos baseando nela. Assim agora o nosso foco, passa a ser o procedimento OnChartEvent. Isto para que eu não precise mostrar todo código novamente. Assim, as primeiras mudanças podem ser vistas logo abaixo.
. . . 19. //+------------------------------------------------------------------+ 20. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 21. { 22. static string szNameObj = NULL; 23. 24. switch (id) 25. { 26. case CHARTEVENT_KEYDOWN : 27. if ((szNameObj != NULL) && (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE))) szNameObj = NULL; 28. break; 29. case CHARTEVENT_OBJECT_CLICK : 30. if (StringFind(sparam, def_Prefix) != INVALID_HANDLE) break; 31. if (szNameObj != NULL); 32. if (szNameObj != sparam); else szNameObj = NULL; 33. break; 34. case CHARTEVENT_OBJECT_DRAG : 35. break; 36. } 37. ChartRedraw(); 38. }; 39. //+------------------------------------------------------------------+ . . .
Código 06
Ainda estamos sem saber o que será feito. Você pode dar uma adiantada no código por favor. Estou ficando um tanto quanto ansioso. Ok, meu caro leitor. Você é quem pediu. (RISOS). Mas antes de fazermos isto, veja a lógica embutida neste fragmento mostrado no código 06. Pois entender esta lógica é muito importante para ver o que será feito a seguir. Assim a próxima coisa a ser feita é mostrada no código logo abaixo.
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 07
Epá. Vai com calma, não precisa apelar. Bem, você pediu, aí está o resultado. Mas apesar de parecer muito complicado. Este código em si é muito simples e extremamente básico. Para entender ele, basta você observar com bastante calma como as coisas estão sendo feitas. De qualquer maneira, vou dar uma breve explicação do que está sendo feito aqui. Já que esta é a primeira parte do que iremos de fato fazer depois.
Na linha oito estamos criando uma estrutura, cujo objetivo será o de criar uma caixa entorno do objeto que estivermos selecionando. Agora preste atenção, pois isto daqui é importante meu caro leitor. O objeto que estaremos criando um contorno em torno dele, deve ter suas coordenadas sendo do tipo cartesianas. Coordenadas do tipo cotação ainda não são tratadas aqui. Como temos poucos objetos deste tipo, sendo implementados no MetaTrader 5. E destes apenas dois podem vir a ser de interesse, que são os objetos do tipo OBJ_TEXT e OBJ_BITMAP. Não vejo motivos para trabalhar neles ainda. Porém, os demais, são sim importantes para nós.
Observe que no tratador de eventos, OnChartEvent, estamos referenciando alguns procedimentos dentro da estrutura. Porém, a própria estrutura contém um procedimento interno e privativo a somente ela. Sendo este o responsável de fato em criar as linhas que irão contornar o objeto selecionado.
No caso estou utilizando uma cor bem cheguei. Esta é definida na linha 28. Se desejar, mude ela para outra que você ache mais adequada. Porém evite mexer nos outros pontos do código. Salvo o fato de que você esteja experimentando para entender como ele funciona. Muito bem, na linha 38, capturamos o fato de que o objeto selecionado está em primeiro plano ou se trata de um objeto em um plano de fundo. Isto é importante para nós, visto que de qualquer forma iremos jogar o objeto para o plano de fundo, e o desmarcar para que isto indique que ele não estará selecionado.
Mas porque estamos fazendo isto? O motivo é evitar que o objeto venha a interferir no que estaremos fazendo posteriormente. Lembre-se de que, quando um objeto está selecionado, podemos usar um ponto especifico dele para mover o mesmo na janela gráfica. Isto usando o próprio MetaTrader 5. No entanto, neste caso não queremos este tipo de comportamento. Já que iremos tomar as rédeas e controlar isto nós mesmos. Mas isto será visto depois. Antes preciso que você entenda muito bem, como este código funciona. E para isto, ele estará presente no anexo, assim como os demais vistos neste artigo.
Mas antes de fecharmos o artigo, que tão vermos o que este código 07 é capaz de fazer? Bem, isto é mostrado na animação logo abaixo. Mas antes veja os objetos que estão presentes no gráfico. Procure focar no nome dos objetos, pois isto é importante para entender, como o tratador de evento irá lidar com cada um destes objetos. Isto pode ser visto observando a imagem 03.
Imagem 03
Agora vamos a animação de demonstração.
Animação 08
Considerações finais
Neste artigo foi demonstrado como poderíamos lidar com objetos do tipo OBJ_LABEL junto com objetos do tipo OBJ_EDIT a fim de podermos editar um texto diretamente no gráfico. Além disto vimos como poderíamos adicionar uma lógica, capaz de nos permitir mover um objeto, mesmo que incialmente isto não fosse possível de ser feito. Já que quando fazíamos a troca entre um objeto do tipo OBJ_LABEL para um do tipo OBJ_EDIT, e vice versa. Acabávamos ficando em um beco sem saída para poder permitir a movimentação do objeto.
Também vimos o início do que será melhor explorado no próximo artigo. Mas como quero que você procure entender muito bem, o que foi feito aqui. Já que será necessário para o que será visto posteriormente. Decidi adiantar de maneira breve, algo que ao meu ver vai lhe dar diversas ideias de como controlar o MetaTrader 5, usando para isto o MQL5.
No demais, me despeço por enquanto, e nos vemos no próximo artigo. Então bons estudos e até breve.
Arquivo MQ5 | Descrição |
---|---|
Code 01 | Demonstração de evento em objeto |
Code 02 | Demonstração de evento em objeto |
Code 03 | Demonstração de evento em objeto |
Code 04 | Demonstração de evento em objeto |
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.





- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso