Do básico ao intermediário: Objetos e sub janelas (III)
Introdução
No artigo anterior Do básico ao intermediário: Objetos e sub janelas (II), vimos como poderíamos lidar de maneira o mais simples e prática possível com a questão de algum objeto colocado por nosso código no gráfico, fosse removido. Seja de forma intencional, seja por erro do operador ou usuário. No entanto, aquela forma de tratar este tipo de situação, pode acabar gerando um certo desagrado por parte de alguns usuários. Justamente por conta de que para ele, o fato de remover algum objeto do gráfico, não deveria fazer com que toda a aplicação, fosse também removida.
Objetos e sub janelas (III)
Apesar do foco aqui, em tese ser o tratamento da integridade de uma sub janela e de objetos colocados nela. O que será visto, se estende muito além deste tópico. Podendo inclusive ser utilizado, quando você não estiver fazendo de fato uso de sub janelas. Mas apenas querendo que um ou outro objeto, permaneça no gráfico, enquanto a aplicação continuar presente ali. Por este motivo, vamos começar com algo mais simples, e depois estenderemos o conceito, para um caso onde precisarmos de sub janelas.
Muito bem, até no artigo anterior, criamos um tipo de conceito voltado a lhe ajudar a criar aplicações bem complexas com um mínimo de esforço. Isto por que, aqui no MetaTrader 5, indicadores são a base de toda uma gama de coisas que no final culmina no que muitos conhecem como Expert Advisores. Ainda não começamos a falar dos tais Expert Advisores, justamente por conta de que sem os indicadores, tais códigos, que seriam os Expert Advisores, não servem para muito coisa. No entanto, tendo uma base bem solida e construída de maneira adequada e correta e entendendo o que podemos ou não fazer dentro das possibilidades dos indicadores. Construir e projetar um Expert Advisor, se torna uma das tarefas mais simples e fáceis de serem feitas.
Porém, sem que esta base tenha sido construída antes. Qualquer coisa ligada aos Expert Advisores se tornará confusa e muito sem sentido. Apesar de você poder achar que tudo isto é pura perda de tempo, entender como lidar com situações diferentes, é mais importante do que, sair criando códigos a esmo. E no final, você acabará em uma situação na qual não conseguirá resolver, justamente por não ter paciência, ou entendimento sobre como planejar o que pretende implementar.
Ok, com base no que já foi visto, temos a seguinte situação: conseguimos criar um indicador, que pode ou não ser usado em uma sub janela, apenas pelo simples fato de o integrarmos a um segundo indicador. Sendo este primeiro, um indicador que antes era o principal, passando a ser um indicador auxiliar. Portanto, não precisamos necessariamente fazer todo o trabalho de uma só fez. Podemos dividir ele em pequenas etapas, que são mais simples e fáceis de lidar, e somente depois unir tudo, em um bloco maior e mais complexo.
Com base neste princípio, podemos pressupor a seguinte condição: O código visto logo abaixo, pode ser perfeitamente analisado, implementado conforme necessitamos, e depois integrado a um sistema bem maior.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property description "DEMO Indicator" 05. //+------------------------------------------------------------------+ 06. #include "include\C_MiniChart.mqh" 07. //+------------------------------------------------------------------+ 08. #property indicator_chart_window 09. #property indicator_height 150 10. #property indicator_plots 0 11. //+------------------------------------------------------------------+ 12. C_MiniChart *gl_MiniChart; 13. //+------------------------------------------------------------------+ 14. int OnInit(void) 15. { 16. IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName); 17. ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, 0, true); 18. gl_MiniChart = new C_MiniChart("Mini Chart"); 19. 20. return INIT_SUCCEEDED; 21. }; 22. //+------------------------------------------------------------------+ 23. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 24. { 25. return rates_total; 26. }; 27. //+------------------------------------------------------------------+ 28. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 29. { 30. switch(id) 31. { 32. case CHARTEVENT_OBJECT_DELETE: 33. if(gl_MiniChart == sparam) 34. { 35. ChartIndicatorDelete(0, ChartWindowFind(0, def_ShortName), def_ShortName); 36. OnDeinit(REASON_INITFAILED); 37. } 38. break; 39. } 40. }; 41. //+------------------------------------------------------------------+ 42. void OnDeinit(const int reason) 43. { 44. delete gl_MiniChart; 45. ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, 0, false); 46. }; 47. //+------------------------------------------------------------------+ 48. //+------------------------------------------------------------------+ 49.
Código 01
Se você executar este código 01, e remover o objeto que ele criou. Irá perceber que o próprio código também será removido do gráfico. Isto foi visto e explicado no artigo anterior. Portanto, não vejo necessidade de entrar em mais detalhes de como este código 01 funciona. No entanto, aqui vamos tratar esta mesma situação de maneira diferente. Ou seja, ao invés de simplesmente remover o próprio indicador, ou outro código qualquer do gráfico, em que ele se encontra. Iremos fazer com que o objeto removido, volte a ser apresentado. Criando assim, uma mudança completa e total de paradigma, de como as coisas podem ser feitas.
Para começar, quero que você entenda o seguinte: quanto mais você desejar que as coisas voltem a ser como estavam antes de tudo ser desmontado. Maior será seu trabalho, para garantir que todas as informações necessárias estejam de fato disponíveis. Isto quando for necessário recriar algo que foi destruído. Em alguns casos, podemos fazer uso de simples estruturas de dados. Enquanto em outros casos, será necessário utilizar um pequeno banco de dados. Felizmente, para ambas as situações, o MQL5 tem a capacidade e todos os mecanismos que precisamos para efetuar as devidas análises e correções. A única questão será: Quanto esforço você está disposto a colocar na solução do problema?
Bem, pensando desta forma, precisaremos começar de algum ponto. E este ponto, é o local onde o menor esforço será utilizado. Quando digo menor esforço, estou me referindo ao fato de que, toda e qualquer modificação ou personalização que um usuário, ou operador tenha feito, será completamente perdida. Forçando assim com que todas as modificações ou personalizações precisarão ser refeitas pelo usuário.
Está tomada de decisão, apesar de parecer um tanto quanto drástica, ainda assim é melhor que a alternativa vista no artigo anterior. Ou que está presente neste código 01. Então para conseguir com que este código 01, ao invés de ser removido do gráfico, caso o objeto que ele criou, seja removido, precisaremos modificar ele de maneira bem sutil. Porém, que mudará completamente o comportamento do código. Esta modificação é vista logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property description "DEMO Indicator" 05. //+------------------------------------------------------------------+ 06. #include "include\C_MiniChart.mqh" 07. //+------------------------------------------------------------------+ 08. #property indicator_chart_window 09. #property indicator_height 150 10. #property indicator_plots 0 11. //+------------------------------------------------------------------+ 12. C_MiniChart *gl_MiniChart; 13. //+------------------------------------------------------------------+ 14. void Initilize(void) 15. { 16. gl_MiniChart = new C_MiniChart("Mini Chart"); 17. ChartRedraw(); 18. } 19. //+------------------------------------------------------------------+ 20. int OnInit(void) 21. { 22. IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName); 23. ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, 0, true); 24. Initilize(); 25. 26. return INIT_SUCCEEDED; 27. }; 28. //+------------------------------------------------------------------+ 29. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 30. { 31. return rates_total; 32. }; 33. //+------------------------------------------------------------------+ 34. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 35. { 36. switch(id) 37. { 38. case CHARTEVENT_OBJECT_DELETE: 39. if(gl_MiniChart == sparam) 40. Initilize(); 41. break; 42. } 43. }; 44. //+------------------------------------------------------------------+ 45. void OnDeinit(const int reason) 46. { 47. delete gl_MiniChart; 48. ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, 0, false); 49. }; 50. //+------------------------------------------------------------------+ 51. //+------------------------------------------------------------------+ 52.
Código 02
Este código 02 corrige parte do nosso problema. E admitindo que você, vem estudando os artigos desta sequência, não creio que você tenha dificuldades em entender o que estamos fazendo aqui. Apenas olhando este código 02 e o analisando, você logo percebe o objetivo a ser alcançado. Mas este código 02, apesar de funcionar, contém algumas pequenas falhas. Para entender isto, preciso que você primeiro veja, como grande parte dos testes são feitos. A maioria dos programadores iniciantes, fazem simplesmente o que é visto na animação logo abaixo.

Animação 01
O que não está errado. Porém, é considerado um teste incompleto, e pode gerar conclusões erradas. No entanto, o objetivo seria justamente testar se o objeto está ou não sendo recriado. E como deu para ver, sim, ele está sendo recriado. Assim você considera que o resultado é satisfatório e segue em frente. Entretanto, ao procurar observar mais atentamente, tudo que está ocorrendo, você se depara com o que podemos ver na animação logo abaixo.

Animação 02
Perceba o seguinte: O que está sendo mostrado aqui na animação 02, somente está ocorrendo, por conta que o terminal está aberto. Muitas vezes, grande parte dos iniciantes não mantém o terminal aberto. Desta forma, diversos avisos, ou mesmo algum alerta pode passar desapercebido por um longo período. E quando você vai tentar resolver, já não conseguirá, justamente por conta de que o código já pode estar complexo demais para que todas as falhas sejam encontradas. Mas como esta animação 02, ficou um tanto quanto rápida. Vamos ver a parte que nos interessa em destaque na imagem logo abaixo.

Imagem 01
Mas como assim? De onde esta falha surgiu? Não vejo nada de errado no código 02. Já que basicamente apenas fizemos uma simples alteração no que seria o código 01. Não entendo por que de esta falha ter sido apresentada. Bem, meu caro leitor, está falha demonstra um dos motivos pelo qual o MQL5, NÃO CONTER OPERAÇÕES COM PONTEIROS. Ponteiros são coisas maravilhosas, mas quando mal compreendidos, se tornam uma pedra em nosso sapato. Nestes códigos vistos acima, temos um ponteiro sendo declarado na linha doze. Porém, ele somente é inicializado quando usamos o operador new. Agora preste atenção, se, e somente si, a inicialização acontecer uma única vez, que seria no momento em que o tratador OnInit fosse executado. A tal mensagem vista, em destaque, na imagem 01, NÃO SERIA APRESENTADA.
Porém, toda via e, entretanto, no código 02, o ponteiro poderá ser inicializado mais de uma vez. Hã! Como assim? Sempre achei que uma variável não poderia fazer uso de duas posições diferentes na memória. E de fato, elas não podem fazer isto. O problema é que quando a linha vinte e quatro é executada, neste código 02, temos a primeira inicialização do ponteiro. Porém, se o usuário deletar o objeto do gráfico, teremos uma segunda inicialização do mesmo ponteiro. Isto por conta da linha quarenta. Justamente por conta desta segunda inicialização, alguma coisa não será de fato removida da memória, quando a linha quarenta e sete for executada. Por conta disto, que a tal mensagem vista em destaque na imagem 01 nos é apresentada. Demonstrando que algo está errado aqui.
Mas como podemos contornar este tipo de problema? Basicamente, existem duas soluções. A primeira seria a de não utilizar ponteiros em seus códigos. Porém, isto limita bastante as coisas, podendo inclusive tornar seu código menos claro, conforme ele for ficando cada vez maior. A segunda solução é garantir que antes de ser inicializado, um ponteiro esteja livre de qualquer informação anterior. Como está, a meu ver, esta seria a melhor das soluções. Assim adotaremos ela como resposta a fim de resolver a mensagem em destaque na imagem 01. No entanto, existe um segundo problema, neste código 02. Este segundo problema, de certa forma é menos prejudicial ao sistema. Porém, dependendo do que esteja sendo implementado, pode ser que ele nos cause dores de cabeça. E o tal problema está dentro do procedimento OnDeinit. Agora pare e pense um pouco. Na linha quarenta e sete, deste código 02, estamos removendo o objeto do gráfico. Certo? Até aí, nada de errado.
No entanto, perceba que ao fazermos esta remoção, ainda estamos pedindo para que o MetaTrader 5, nos informe, caso algum objeto seja removido do gráfico. Por conta disto, o MetaTrader 5 irá colocar na fila de eventos, um que indique que algum objeto foi removido do gráfico. Portanto, se o código demorar um pouco mais para sair, teremos a execução da linha quarenta. Forçando assim que o objeto, destruído, pelo fato do código está sendo encerrado, volte a ser recriado. O que em muitas situações pode não ocorrer, mas em outras poderá acontecer. Então precisamos corrigir isto, antes de as coisas começarem a sair completamente do nosso controle. Feita esta analise, podemos modificar o código 02, para o código visto logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property description "DEMO Indicator" 05. //+------------------------------------------------------------------+ 06. #include "include\C_MiniChart.mqh" 07. //+------------------------------------------------------------------+ 08. #property indicator_chart_window 09. #property indicator_height 150 10. #property indicator_plots 0 11. //+------------------------------------------------------------------+ 12. C_MiniChart *gl_MiniChart; 13. //+------------------------------------------------------------------+ 14. void Initilize(void) 15. { 16. delete gl_MiniChart; 17. gl_MiniChart = new C_MiniChart("Mini Chart"); 18. ChartRedraw(); 19. } 20. //+------------------------------------------------------------------+ 21. int OnInit(void) 22. { 23. IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName); 24. ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, 0, true); 25. Initilize(); 26. 27. return INIT_SUCCEEDED; 28. }; 29. //+------------------------------------------------------------------+ 30. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 31. { 32. return rates_total; 33. }; 34. //+------------------------------------------------------------------+ 35. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 36. { 37. switch(id) 38. { 39. case CHARTEVENT_OBJECT_DELETE: 40. if(gl_MiniChart == sparam) 41. Initilize(); 42. break; 43. } 44. }; 45. //+------------------------------------------------------------------+ 46. void OnDeinit(const int reason) 47. { 48. ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, 0, false); 49. delete gl_MiniChart; 50. }; 51. //+------------------------------------------------------------------+
Código 03
Estas simples mudanças feitas aqui no código 03, irá garantir que não teremos aqueles problemas mencionados acima. Agora vamos ver como o código lidará com a remoção do objeto do gráfico. Isto pode ser visualizado na animação logo abaixo.

Animação 03
Simplesmente perfeito. Funcionou maravilhosamente bem. Sem nenhuma falha aparente. Agora perceba que mesmo após mover o objeto na tela, o mesmo foi recolocado no ponto inicial. Isto é o tal custo baseado no mínimo de esforço, do qual menciono no início do artigo. Já que ao fazermos isto, decidimos ignorar toda e qualquer configuração, ou personalização que poderia ter sido feita. Forçando assim o usuário a refazer tudo deste o início. Bem, este seria um custo baixo, já que o indicador, continua ativo no gráfico, diferente do que era feito antes. Onde simplesmente removíamos o indicador do gráfico.
De qualquer maneira, podemos melhorar um pouco isto. Tornando a experiência do usuário, ou operador, um tanto quanto menos desagradável. Mas ao decidirmos fazer isto, entramos na questão de que, quanto mais coisas desejarmos recriar, de forma que o usuário não perceba a diferença, antes e após ter removido o objeto do gráfico. Mais esforço precisará ser empregado. Assim sendo, vamos aumentar só um pouco o esforço que será feito. Já que o objetivo aqui, é puramente didático.
Ok, agora vem a parte das decisões a serem tomadas. Como quero deixar as coisas bem simples, e, ao mesmo tempo, lhe dar uma ideia de como fazer uma recriação mais completa. Vamos fazer o seguinte: Iremos armazenar a posição e as dimensões do objeto gráfico. E apesar de fazermos somente isto, você notará que o trabalho necessário para conseguir fazer com que tudo funcione, será bem maior do que o mostrado até aqui. Para começar, precisaremos modificar o código original da classe. O original é visto logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #include "defines.mqh" 05. //+------------------------------------------------------------------+ 06. class C_MiniChart 07. { 08. private : 09. //+----------------+ 10. string m_szNameObject; 11. int m_Sub; 12. //+----------------+ 13. void SetInteger(ENUM_OBJECT_PROPERTY_INTEGER prop, int value) { ObjectSetInteger(0, m_szNameObject, prop, value); } 14. //+----------------+ 15. int WhatSubWin(void) 16. { 17. for(int c0 = 0; c0 < (int)(ChartGetInteger(0, CHART_WINDOWS_TOTAL)); c0++) 18. for(int c1 = (ChartIndicatorsTotal(0, c0) - 1); c1 >= 0; c1--) 19. if(ChartIndicatorName(0, c0, c1) == def_ShortName) 20. return c0; 21. return INVALID_HANDLE; 22. } 23. //+----------------+ 24. public : 25. //+----------------+ 26. C_MiniChart(string szName) 27. { 28. if((m_Sub = WhatSubWin()) == INVALID_HANDLE) 29. return; 30. ObjectCreate(0, m_szNameObject = def_ShortName + "#" + szName + "#" + (string)ObjectsTotal(0, -1, -1), OBJ_CHART, m_Sub, 0, 0); 31. ObjectSetString(0, m_szNameObject, OBJPROP_SYMBOL, _Symbol); 32. SetInteger(OBJPROP_XDISTANCE, 10); 33. SetInteger(OBJPROP_YDISTANCE, 0); 34. SetInteger(OBJPROP_XSIZE, 300); 35. SetInteger(OBJPROP_YSIZE, indicator_height); 36. SetInteger(OBJPROP_PERIOD, PERIOD_M15); 37. SetInteger(OBJPROP_DATE_SCALE, false); 38. SetInteger(OBJPROP_PRICE_SCALE, false); 39. SetInteger(OBJPROP_SELECTABLE, true); 40. SetInteger(OBJPROP_SELECTED, false); 41. } 42. //+----------------+ 43. ~C_MiniChart() 44. { 45. ObjectDelete(0, m_szNameObject); 46. ChartRedraw(); 47. } 48. //+----------------+ 49. bool operator==(const string szArg) { return (szArg == m_szNameObject); } 50. //+----------------+ 51. }; 52. //+------------------------------------------------------------------+
Código 04
Muito bem, como quero deixar você experimentar isto, sem necessitar digitar estes códigos. No anexo, a versão modificada estará com outro nome. Apenas para facilitar a identificação e também lhe permitir testar as coisas de modo local. Legal, então vamos começar criando um mecanismo para armazenar o sistema de posicionamento do objeto. Depois você entenderá, porque este mecanismo precisa ser pensado uma coisa separada do restante do código. Desta forma, modificando o código da classe visto acima, temos o que é visto logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #include "defines.mqh" 05. //+------------------------------------------------------------------+ 06. class C_MiniChart 07. { 08. private : 09. //+----------------+ 10. string m_szNameObject; 11. int m_Sub; 12. //+----------------+ 13. struct st_Memory 14. { 15. short x, y, w, h; 16. bool selectable; 17. } m_Memory; 18. //+----------------+ 19. void SetInteger(ENUM_OBJECT_PROPERTY_INTEGER prop, int value) { ObjectSetInteger(0, m_szNameObject, prop, value); } 20. //+----------------+ 21. int WhatSubWin(void) 22. { 23. for(int c0 = 0; c0 < (short)(ChartGetInteger(0, CHART_WINDOWS_TOTAL)); c0++) 24. for(int c1 = (ChartIndicatorsTotal(0, c0) - 1); c1 >= 0; c1--) 25. if(ChartIndicatorName(0, c0, c1) == def_ShortName) 26. return c0; 27. return INVALID_HANDLE; 28. } 29. //+----------------+ 30. public : 31. //+----------------+ 32. C_MiniChart(string szName) 33. { 34. if((m_Sub = WhatSubWin()) == INVALID_HANDLE) 35. return; 36. m_Memory.x = 10; 37. m_Memory.y = 0; 38. m_Memory.w = 300; 39. m_Memory.h = indicator_height; 40. m_Memory.selectable = true; 41. m_szNameObject = def_ShortName + "#" + szName + "#" + (string)ObjectsTotal(0, -1, -1); 42. RestoreMemory(); 43. ObjectSetString(0, m_szNameObject, OBJPROP_SYMBOL, _Symbol); 44. SetInteger(OBJPROP_PERIOD, PERIOD_M15); 45. SetInteger(OBJPROP_DATE_SCALE, false); 46. SetInteger(OBJPROP_PRICE_SCALE, false); 47. SetInteger(OBJPROP_SELECTED, false); 48. } 49. //+----------------+ 50. ~C_MiniChart() 51. { 52. ObjectDelete(0, m_szNameObject); 53. ChartRedraw(); 54. } 55. //+----------------+ 56. void RestoreMemory(void) 57. { 58. if(ObjectFind(0, m_szNameObject) < 0) 59. ObjectCreate(0, m_szNameObject, OBJ_CHART, m_Sub, 0, 0); 60. SetInteger(OBJPROP_XDISTANCE, m_Memory.x); 61. SetInteger(OBJPROP_YDISTANCE, m_Memory.y); 62. SetInteger(OBJPROP_XSIZE, m_Memory.w); 63. SetInteger(OBJPROP_YSIZE, m_Memory.h); 64. SetInteger(OBJPROP_SELECTABLE, m_Memory.selectable); 65. } 66. //+----------------+ 67. void StoreMemory(void) 68. { 69. m_Memory.x = (short) ObjectGetInteger(0, m_szNameObject, OBJPROP_XDISTANCE); 70. m_Memory.y = (short) ObjectGetInteger(0, m_szNameObject, OBJPROP_YDISTANCE); 71. m_Memory.w = (short) ObjectGetInteger(0, m_szNameObject, OBJPROP_XSIZE); 72. m_Memory.h = (short) ObjectGetInteger(0, m_szNameObject, OBJPROP_YSIZE); 73. m_Memory.selectable = (bool) ObjectGetInteger(0, m_szNameObject, OBJPROP_SELECTABLE); 74. } 75. //+----------------+ 76. bool operator==(const string szArg) { return (szArg == m_szNameObject); } 77. //+----------------+ 78. }; 79. //+------------------------------------------------------------------+
Código 05
Certo, agora temos um código realmente muito doido em nossas mãos. Isto por que, este código 05, tem o mesmo funcionamento que o código 04. No entanto, ele tem um pequeno bloco de memória interna sendo criado para propósito local. E isto faz com que este código 05 consiga reestabelecer as configurações que porventura forem destruídas.Isto caso o usuário remova o objeto do gráfico, e o indicador ainda permaneça ativo no gráfico. Se esta condição específica ocorrer, o indicador irá conseguir, usando dados presentes na memória do objeto, recriar o objeto dentro daquelas possibilidades que foram armazenadas de antemão.
Então, se você ficou interessado nisto que acabei de dizer. Preste atenção para entender como este mecanismo aqui funciona. Pois apesar de ser muito simples, ele exige um certo cuidado, no momento em que você for utilizar ele em algum código.
Como mencionei, precisamos de uma memória a parte. Esta está sendo criada na linha treze deste código 05. Mas você pode estar se perguntando: Bem, esta memória não está a parte. Ela está dentro da classe que cria o objeto. Isto não irá gerar algum tipo de problema depois? Não se você entender como as coisas de fato ocorrem quando o código é executado. Se você, já achar que conhece e entende o que está fazendo, muito provavelmente você fará besteira. Mas se realmente entender o que será explicado, tudo funcionará perfeitamente bem.
Com esta estrutura de memória tendo sido criada, precisaremos inicializar a mesma, e isto é feito entre da linha trinta e cinco até a linha trinta e nove. Note que tais linhas estão dentro do constructor da classe. Fazer isto neste momento é muito importante. Da mesma forma que também é importante que o nome do objeto seja definido neste constructor. Agora perceba que NÃO ESTAMOS CRIANDO O OBJETO NO CONSTRUCTOR. Isto por que, se a criação fosse feita aqui, teríamos dificuldades em saber, quando e onde o objeto realmente foi criado.
Desta forma, uma vez que temos a estrutura de memória inicializada, podemos chamar o procedimento que irá de fato criar o objeto, ou melhor dizendo, irá recolocar o objeto no gráfico. Isto será executado pelo procedimento implementado na linha cinquenta e cinco. Este procedimento, faz exatamente o que o constructor do código 04 fazia. Com uma única exceção, que no caso é a linha cinquenta e sete, onde verificamos se o objeto que queremos recolocar no gráfico se encontra ou não presente na lista de objetos. Mas por que fazer este teste? Bem, o motivo é que, dependendo das circunstâncias, pode ser que algo tenha ocorrido e que algum objeto tenha assumido o posto do que foi removido. Mas por hora, não se preocupe com este fato, já que isto será assunto para outro artigo futuro. Então vamos continuar a entender o que está acontecendo.
Uma vez que os dados tenham sido restaurados, durante a execução do constructor da classe, o objeto já estará funcional e terá todas as configurações vistas anteriormente. Mas antes de passarmos para o código principal, vamos ver um último procedimento que foi adicionado a classe. Este é visto na linha sessenta e cinco. O objetivo deste procedimento é muito simples. Ele irá capturar as informações que desejamos restaurar e as armazenar na memória local, como forma de preservar, mesmo que de forma momentânea as configurações do objeto. Este é o código mais básico e simples que podemos implementar visando restaurar um objeto deletado pelo usuário. Agora vamos ver como o código principal precisou ser modificado, pois de fato, ele não é igual ao código 03. Mas se parece bastante, como você pode observar logo abaixo. Já que o código é visto na íntegra.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property description "DEMO Indicator" 05. //+------------------------------------------------------------------+ 06. #include "include\C_MiniChart_2.mqh" 07. //+------------------------------------------------------------------+ 08. #property indicator_chart_window 09. #property indicator_height 150 10. #property indicator_plots 0 11. //+------------------------------------------------------------------+ 12. C_MiniChart *gl_MiniChart; 13. //+------------------------------------------------------------------+ 14. void Initilize(void) 15. { 16. if(gl_MiniChart == NULL) 17. gl_MiniChart = new C_MiniChart("Mini Chart"); 18. else(*gl_MiniChart).RestoreMemory(); 19. ChartRedraw(); 20. } 21. //+------------------------------------------------------------------+ 22. int OnInit(void) 23. { 24. IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName); 25. ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, 0, true); 26. gl_MiniChart = NULL; 27. Initilize(); 28. 29. return INIT_SUCCEEDED; 30. }; 31. //+------------------------------------------------------------------+ 32. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 33. { 34. return rates_total; 35. }; 36. //+------------------------------------------------------------------+ 37. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 38. { 39. switch(id) 40. { 41. case CHARTEVENT_OBJECT_DELETE: 42. if(gl_MiniChart == sparam) 43. Initilize(); 44. break; 45. case CHARTEVENT_OBJECT_DRAG: 46. if(gl_MiniChart == sparam) 47. (*gl_MiniChart).StoreMemory(); 48. break; 49. } 50. }; 51. //+------------------------------------------------------------------+ 52. void OnDeinit(const int reason) 53. { 54. ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, 0, false); 55. delete gl_MiniChart; 56. }; 57. //+------------------------------------------------------------------+
Código 06
Este código 06, é realmente algo muito belo, mas está faltando um detalhe nele. Porém, antes de corrigir este detalhe que está faltando, quero que você olhe, admire este código, pois ele consegue, trazer algo do mundo dos mortos. Na verdade, a coisa volta meio que deformada, mas isto se deve ao fato de que não estamos memorizando todas as propriedades do objeto, mas apenas alguns deles. Por isto, para entender este código 06, você primeiro precisa entender os demais. Incluindo o que foi explicado em outros artigos nesta mesma sequência.
Principalmente o artigo Do básico ao intermediário: Eventos em Objetos (IV), pois neste artigo, explico como trabalhar com eventos simples em objetos igualmente simples. Claro que talvez você precise ver os artigos anteriores. Mas esta decisão deixo a seu critério.
Detalhe importante: Ponteiros, NÃO PODEM receber valores discretos diretamente. Eles sempre e para todo o sempre, precisam apontar para algum local na memória.
No entanto, apesar deste detalhe, o valor NULL pode sim ser atribuído a um ponteiro, fazendo com que o identifiquemos como sendo um ponteiro inválido. Esta situação é muito, mas muito importante para nós. Isto por que, diferente do que antes era feito no procedimento Initilize, agora NÃO IREMOS destruir o ponteiro, caso o procedimento receba uma segunda chamada. Que sempre ocorrerá quando o objeto for deletado do gráfico. Novamente, é preciso entender os códigos anteriores para entender como este código 06 funciona.
Assim, na linha dezesseis, efetuamos um teste, cujo objetivo é justamente o de descobrir se o ponteiro é valido ou não. Como na primeira chamada ele está sendo declarado como inválido, o teste passará e uma posição da memória será atribuído ao ponteiro, pelo uso do operador new. Agora vem a parte confusa, para muitos. O que acontece quando a linha quarenta e um chamar novamente este procedimento da linha quatorze? Bem, neste caso, o objeto terá sido removido do gráfico. Porém, o ponteiro onde os dados do objeto se encontravam, não terá sido eliminado ainda da memória. Isto somente acontecerá quando o operador delete for executado. Coisa que somente acontece na linha cinquenta e dois.
Agora perceba, que como a linha cinquenta e um, remove a notificação de evento de um objeto sendo removido do gráfico. O MetaTrader 5, não mais irá disparar, um evento que fará com que a linha quarenta e um seja novamente executada. Este tipo de coisa é muito importante ser feita nesta exata ordem. Caso contrário, quando o teste da linha dezesseis voltasse a ser executado, teríamos um ponteiro invalido, e um novo seria criado. Mas como isto não ocorrerá, durante a execução normal do código. Quando a linha quarenta e um executar, o teste da linha dezesseis fará com que a linha dezessete seja executada. E esta linha faz com que os dados do objeto sejam restaurados. Contudo, como o objeto, já não está mais presente no gráfico, a classe irá forçar que um novo objeto, com o mesmo nome seja recriado, como pode ser visto na linha cinquenta e sete do código 05.

Animação 04
Nesta animação 04, podemos ver o princípio básico sendo aplicado. Note que no momento em que removemos o objeto do gráfico, um novo objeto é criado. Porém, as características e propriedades salvas são restauradas. As demais são substituídas por valores padrão do objeto.

Animação 05
Porém, e isto é importante, se após modificar as propriedades, você mover o objeto, as configurações serão salvas, como mostrado na animação logo na sequência.

Animação 06
Hum, interessante. Mas será que não existem meios de modificarmos as propriedades, e não perdermos os novos valores, sem precisar mover o objeto no gráfico? Sim, meu amigo, existe uma forma de fazer isto. Mas antes de mostrar isto, vamos a uma última animação. Que seria justamente a que mostra o que acontece quando a aplicação que mantém o objeto no gráfico é removida. Esta animação é vista abaixo.

Animação 07
Note que em nenhuma das animações foi constatado nenhum tipo de relato, indicando algum problema no código. O que é um ótimo sinal. Então vamos ver como resolver a questão vista na animação 05, onde após modificar as propriedades, elas foram perdidas, justamente por conta de que não foram salvas em memória. Para resolver esta questão, precisamos adicionar uma simples linha de código. E esta visa fazer a captura justamente do evento que o MetaTrader 5 envia, nos notificando que alguma das propriedades do objeto foram modificadas. Como o código praticamente se mantém igual, podemos focar apenas no fragmento que realmente precisa ser modificado. Este é visto logo abaixo.
. . . 35. //+------------------------------------------------------------------+ 36. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 37. { 38. switch(id) 39. { 40. case CHARTEVENT_OBJECT_DELETE: 41. if(gl_MiniChart == sparam) 42. Initilize(); 43. break; 44. case CHARTEVENT_OBJECT_CHANGE: 45. case CHARTEVENT_OBJECT_DRAG: 46. if(gl_MiniChart == sparam) 47. (*gl_MiniChart).StoreMemory(); 48. break; 49. } 50. }; 51. //+------------------------------------------------------------------+ . . .
Fragmento 01
E é isto, meu caro leitor. Tudo que precisamos fazer, para corrigir o que é visto na animação 05, é adicionar esta linha quarenta e três que podemos ver no fragmento 01. Com isto, quando o MetaTrader 5, enviar uma notificação informando que as propriedades do objeto foram modificas, nosso código conseguirá interceptar esta notificação e garantir que a memória seja atualizada. Assim, mesmo após modificar as propriedades, o objeto seja deletado, mesmo sem ter sido movido, nossa aplicação conseguirá reviver o objeto, exatamente como ele foi salvo. Lembrando que para restabelecer um objeto que seja idêntico ao que existia antes, precisaremos memorizar um número bem maior de propriedades. Mas como aqui quero apenas e tão somente demonstrar como poderíamos estar fazendo isto. Já me dou por satisfeito com este resultado alcançado.
Considerações finais
Apesar de não termos levado o indicador mostrado neste artigo para ser usado em uma sub janela, via integração com algum outro indicador, como foi feito no artigo anterior. O que foi visto e explicado aqui, é e pode ser considerado, um ponto-chave para entender como faremos para resolver a segunda parte de outra questão. Que é justamente o de garantir que o indicador, presente em uma sub janela, e que esteja integrado a outro indicador, não seja removido, simplesmente por conta de que algum dos objetos criados e mantidos por ele, tenha sido removido do gráfico.
Apesar de que, ligar o que foi visto aqui, como o que havia sido visto no artigo anterior, ser algo aparentemente simples e fácil de ser feito. Existe um pequeno macete, por assim dizer, e isto para não termos de fato um comportamento diferente daquele que seria o esperado. Já que uma coisa é remover e repor um objeto no gráfico. E outra completamente diferente, é o de repor um indicador inteiro, que por ventura seja removido do gráfico. Ambas situações exigem uma abordagem um pouco diferente.
| Arquivo MQ5 | Descrição |
|---|---|
| Indicators\Code 01 | Demonstração básica |
| Indicators\Code 02 | Demonstração básica |
| Indicators\Code 03 | Demonstração básica |
| Indicators\Code 04 | Demonstração básica |
| Indicators\Code 05 | Demonstração básica |
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.
Desenvolvimento de um sistema personalizado de detecção do regime de mercado em MQL5 (Parte 2): Expert Advisor
Otimização por Comunidade de Cientistas - Community of Scientist Optimization (CoSO): Prática
Do iniciante ao especialista: criação de um EA animado para notícias em MQL5 (VI): Estratégia de trading pós-notícia
Do iniciante ao especialista: criação de um EA animado para notícias em MQL5 (VI): estratégia de ordens pendentes para trading baseado em notícias
- 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