MQL5 para iniciantes: Proteção antivandalismo de objetos gráficos

Dina Paches | 28 março, 2016

Vandalismo é uma forma depravada e destrutiva do comportamento humano,
quando os objetos de arte, cultura, pública e de propriedade
privada são destruídos ou profanados.

Wikipedia

Conteúdo:


1. Introdução

Uma das vantagens da linguagem de programação MQL5 são as funções padrões existentes MQL5, onde você pode formar códigos para completar várias tarefas e alcançar objetivos diferentes quando se usa o terminal de negociação MetaTrader 5.

Este artigo, escrito numa linguagem simples e contendo exemplos fáceis, considera duas variantes de programas a implementação da resposta a ações aos objetos gráficos do painel de controle que são apagados ou alterados. Nós vamos mostrar a você como garantir que não existam objetos sem dono presentes no gráfico, após o programa ser excluído, onde o programa pode ter perdido o controle, porque alguém ou algo os renomeou.

Exemplo de um painel de controle antes e depois das propriedades dos objetos serem alteradas manualmente

Fig. 1. Exemplo da aparência de um painel de controle antes e depois das propriedades dos objetos serem alteradas manualmente

Opções para construir as ações de resposta à interferência externa no código, descritas neste artigo, podem não serem redundantes aos casos em que, por exemplo, um programa de terceiros lançados no gráfico e não destinados diretamente a sua limpeza, usam uma função de exclusão de objetos ( objetos DeleteAll() ou uma que você mesmo criar), operando pelos parâmetros definidos nela como:

Essas opções também são relevantes, quando é aconselhável, incluindo a operação correta do programa, providenciar ações para remoção acidental ou intencional de objetos de seu painel de controle ou alteração manual das suas propriedades no código.

Este artigo também pode ser útil para aqueles que começaram a aprender o evento de manipulação na função OnChartEvent().

Deixe-me avisá-lo de imediato que este artigo não abrange a criação de códigos rígidos e reacionários, cujos objetos foram modificados/removidos sem autorização. O principal objetivo dos programas do terminal é resolver as questões que os traders possam ter, portanto a interferência de guerra de robôs não é aceitável aqui.

Para aqueles que preferem ações revolucionárias, gostaria de fazer uma analogia antes de considerar este movimento. Imagine que um faxineiro tenha quebrado o computador de alguém com seu esfregão e pintado toda a sua mesa, acreditando que tenha feito algo lindo. Em resposta o dono dos objetos danificados joga pela janela os equipamentos, os móveis e todos os presentes no escritório juntamente com o faxineiro, esta ação será considerada como inadequada. Será improvável que uma pessoa de comportamento agressivo se beneficie com este tipo de situação.

Antes de prosseguir com exemplos de duas (entre outras) possíveis formas de reagir ao vandalismo em relação aos objetos, eu acredito que também é útil mencionar uma das proteções dos objetos primários existentes nas linguagens de programação MQL5/MQL4.


2. Um dos objetos primários de proteção no MQL5/MQL4

O acesso para criar programas de propriedades do objeto seria mais aberto, sem essa proteção.

Deixe-me explicar o que eu quis dizer. Para fornecer a proteção primária a objetos que são deletados ou modificados através de suas propriedades - nome, descrição, cores, etc., um recurso OBJPROP_HIDDEN está presente e pode ser usado de forma explícita. Ele configura ou remove um banimento, exibindo o nome de um objeto gráfico na lista de objetos a partir do menu terminal: Gráficos -> Objetos -> Lista de objetos. Por padrão, o banimento é configurado para exibição de objetos nos eventos do calendário, histórico de negociação e também objetos criados com programas MQL5.

O banimento explícito (não definido por padrão) pode ter o seguinte esquema no código:

ObjectSetInteger(chart_id,name,OBJPROP_HIDDEN,true);

Onde:

A aplicação prática deste esquema e coerência no código pode ser encontrada na Documentação. Ao clicar em qualquer link na lista fornecida dos tipos de objetos, você pode visualizar os exemplos de códigos prontos com funções para criar objetos.

Objetos que são banidos por exibir o nome no gráfico na lista de objeto, definidos explicitamente ou por padrão, podem ser visto pressionando o botão Todos, onde são exibidos todos os objetos neste gráfico. Ele atua como uma proteção preliminar de objetos gráficos a partir da intervenção manual em suas configurações através da Lista de objetos.

Lista de objetos no gráfico antes de pressionar o botão "Todos"

Fig. 2. Lista de objetos no gráfico antes de pressionar o botão "Todos"

Lista de objetos no gráfico depois de pressionar o botão "todos"

Fig. 3. Lista de objetos no gráfico depois de pressionar o botão "Todos"

O fato é que os objetos gráficos não podem ser completamente escondidos no gráfico, mesmo se você torná-los invisíveis, é mais uma vantagem do que uma desvantagem. Você pode rapidamente visualizar, alterar ou copiar algo nas propriedades dos objetos através da lista, sem verificar o gráfico inteiro que pode conter vários objetos e barras de anos anteriores. Além disso, os objetos na lista podem ser classificados pelo tipo, nome e alguns outros parâmetros.

Uma das muitas outras vantagens dos terminais de negociação MetaTrader 5 e MetaTrader 4 é a simplicidade em que você mesmo pode escrever assistentes automatizados para estes terminais e utilizando vários assistentes criados por outros. Mas existem as pessoas que nunca erram? Claro que não, o nível de preparação para escrever programas pode ser diferente, assim são as fronteiras éticas internas. A linguagem de programação, assim como as pessoas, pode mudar e melhorar com o tempo.

Portanto, o fato é que você pode garantir a exatidão de formar objetos gráficos com um programa a qualquer momento, é uma vantagem definitiva. Você pode encontrar rapidamente os objetos exigidos na lista e ver suas propriedades, em vez de olhar para eles durante todo o gráfico. Além disso, você pode ter certeza que o gráfico não possui quaisquer objetos por engano ou deliberadamente escondidos, incluindo os que são gerados por erros no código.

Também é muito provável que, exibindo a lista de todos os objetos e selecionando alguns deles para serem deletados, há uma chance de destruir acidentalmente os objetos do painel de controle que devem estar no gráfico nesse ponto.

Antes de prosseguir para as variantes de ações de resposta do programa, podemos tirar proveito das linguagens de programação MQL5 e MQL4 mencionados anteriormente no artigo. Refiro-me a capacidade em formar códigos que usam métodos diferentes para resolver várias tarefas.


3. Estratégia para criar variantes fornecidas aqui

Isso pode ajudá-lo, se necessário, para criar suas próprias soluções no código e salvá-lo de perder tempo na procura de soluções usando rotas erradas ou complicadas.

Para evitar soar muito formal e acadêmico, vou dizer-lhe sobre a estratégia descrita aqui do jeito que naturalmente me veio a cabeça, os códigos mencionados estão anexados no final deste artigo. Para a sua operação você precisa salvar o arquivo ObjectCreateAndSet, localizado na base de código, na pasta do seu terminal Include. Ele contém funções para a criação de objetos e para alterar suas propriedades com as verificações necessárias. Este arquivo não é necessário a operação do indicador anexa, test_types_of_chart_events.


3.1. Ações e considerações antes de decidir sobre a aplicação prática do código

De acordo com a documentação, atualmente você pode obter e processar informações sobre nove tipos de eventos necessários a seus próprios fins e tarefas, excluindo mudanças no gráfico e eventos de outros usuários, por meio da função OnChartEvent(). Alguns deles notificam sobre a remoção, mudança e criação de objetos gráficos.

Primeiramente, eu queria saber como implementar o seguinte no código:

Ambos têm de ser aplicados simultaneamente com a remoção de objetos que são "alienados" do gráfico devido a alguém ou algo que os tenham renomeados, e sem loops extras de manipulação de eventos.

A primeira ideia que surgiu foi sobre o total das revisões do teste automático do programa de sua propriedade, quando a informação sobre cada caso de remoção ou modificação de objetos é recebida, afinal não parece ser a melhor e mais prática solução disponível. Então eu pensei que era porque o total de verificações automáticas poderiam implicar em um monte de ações desnecessárias e nem sempre justificadas.

Por exemplo, durante o evento de manipulação de ações com objetos, o nome do objeto gráfico, no qual é realizado qualquer ação, é comparado com os nomes sendo monitorados pelo programa. Então em qualquer tipo de evento no programa, as informações sobre ele não vão apenas aos objetos gráficos do programa, mas a todos os objetos no gráfico. O nome do objeto tem o tipo string, e dados em sequência são processados por mais tempo do que outros tipos. O gráfico pode ter vários objetos criados manualmente ou usando outros programas que podem ter um monte de diversas ações aplicadas a eles, formando assim um grande número de eventos.

Abaixo está um esquema de aproximação para a verificação da correspondência dos nomes de objetos, quando a função de envio de notificações sobre eventos de remoção de objetos gráficos está ativada. O esquema é fornecido sem manipulação prévia para diminuir a quantidade de comparações extra, que são aplicadas quando se trabalha com vários objetos gráficos:

if(id==CHARTEVENT_OBJECT_DELETE)
        {
         string deletedChartObject=sparam;
         //---
         for(int i=QUANT_OBJ-1;i>=0;i--)
           {
            if(StringCompare(deletedChartObject,nameObj[i],true)==0)
              {
            /*existe um certo código com ações, se o nome de um assunto que
            chegou durante o evento tem acompanhado o
            nome do objeto do programa completamente*/
               return;
              }
           }
        }

Além disso, a opção "revisão" (com base nos eventos ou periódico) não resolve o problema de renomear os objetos por alguém ou alguma coisa, e, eventualmente, o programa pode perder o controle, deixando objetos "sem dono" para trás.

É por isso que eu pensei em usar e detalhar o sistema "flag". Similar ao alarme, onde uma flag de semáforo igual a 0, por exemplo, significa que ao alterar e excluir os objetos do código, deve ser tratado pelo programa como uma interferência externa não autorizada. O valor da flag altera as ações pessoais de modificação e apaga os objetos fornecidos pelo código, de modo que os eventos de exclusão de mudança não são mais percebidos como uma "invasão externa".

Os eventos de gráfico formam uma fila com sua posterior manipulação no OnChartEvent(), pois o sistema de desencadeamento das flags cria os objetos de "auto recuperação" no código, onde tais casos não foram particularmente claros naquele momento. Também, para evitar simplesmente os processamentos extras e repetidos de loops gerados, quando a manipulação dos eventos de eliminação deste tipo entra em ação.

Por exemplo, um painel constituído por apenas alguns objetos e saída de informações ativado com Print() no código de manipulação de eventos de exclusão dos objetos deste painel. Ao notificar sobre estes eventos, um painel pode facilmente preencher o arquivo log com várias notas dentro de segundos, durante a auto recuperação dos objetos, enquanto fica piscando no gráfico a partir da recriação contínua. E isto acontece apesar da seleção preliminar pelo nome, pois a flag do semáforo e auto recuperação são aplicadas utilizando o seguinte método (ou equivalente):

else  if(id==CHARTEVENT_OBJECT_DELETE)
     {
     if(flagAntivandal==0)
        {
            string deletedChartObject=sparam;
            //---
            for(int i=QUANT_OBJ-1;i>=0;i--)
              {
               //--- ações quando nomes se combinam por completo:
               if(StringCompare(deletedChartObject,nameObj[i],true)==0)
                 {
                  //--- imprime na aba "Experts" do terminal:
                  TEST_PRINT(deletedChartObject);
                  //--- desativa o "alarme" para o tempo de execução das operações dos programas "amigáveis"
                  flagAntivandal=-1;
                  //--- deleta os objetos restantes:
                  ObjectsDeleteAll(0,prefixObj,0,-1);
                  //--- recria painel
                  PanelCreate();
                 //--- redesenha gráfico 
                  ChartRedraw();
                 //--- re-ativa a flag de "proteção" de objetos:
                  flagAntivandal=0;
                  return;
                 }
              }
         return;
        }
      return;
     }//else  if(id==CHARTEVENT_OBJECT_DELETE)

Remoção e criação de objetos vai ser em loop com tal código escrito, formando um fluxo de notificações de eventos. E a remoção de outros painéis de objetos para posterior recuperação pode ser necessária, por exemplo, se a cobertura de outros objetos (botões, campos de entrada etc) são acidentalmente ou intencionalmente eliminados e precisam ser recriados com os que não se sobrepõem a outros objetos.

Se o seu computador não tem memória suficiente, então eu não recomendo usar o código mencionado acima. Se isso acontecer, então você pode testá-lo sozinho, substituindo-o completamente com a manipulação de eventos de exclusão e alterando o código de ensaio anexado no test_count_click_2.

Eu não verifiquei a capacidade máxima para o preenchimento de um arquivo log como resultado do uso de uma flag errada, porque eu fui forçado a desligar os terminais em 2-3 segundos após o início dos eventos de uma falsa "auto recuperação" no máximo, enquanto observava um "piscar" dos objetos e rápidos do programa, com contínuas notas na guia "Expert" do terminal. Eu apaguei um aumento considerável de arquivo log e esvaziei a Lixeira imediatamente.

Além disso, a solução dos casos de objetos restantes do código permaneceu desconhecido. Estava faltando uma "âncora" que poderia ajudar nesta situação.

É por isso que eu decidi refrescar minha memória com informações sobre tipos de eventos gráficos retirados da Documentação, e olhei para a descrição da função ObjectSetString(), onde diz que depois de renomear um objeto, dois eventos são formado simultaneamente:

Depois de ler a seção proriedades dos Objetos, eu decidi que minha âncora seria o tempo de criação dos objetos, que pode ser determinado usando a propriedade OBJPROP_CREATETIME.

Por exemplo, como você pode ver a partir do script test_get_objprop_createtime anexado, o momento da criação do objeto é igual a hora local em um computador onde o terminais rodam:

O momento da criação do objeto é igual a hora local num computador no momento da criação de um objeto.

Fig. 4. O momento da criação do objeto é igual a hora local num computador no momento da criação de um objeto.

Um script de teste cria um botão no gráfico, determina o tempo de sua criação e imprime via Print() nas mensagens da aba Experts. Ele define e imprime vários tipos de tempo na mesma guia do terminal: a hora local do computador, o tempo atual estimado do servidor de negociação, o tempo do último servidor conhecido no momento da última cotação, hora GMT. Em seguida, ele "congela" por 10 segundos e exclui o botão que criou a partir do gráfico, encerrando a sua operação.

O próximo passo incluiu a preparação de um plano de ação para uma formulação mais clara de soluções durante a criação de código subsequente. Essas soluções finais devem ser versátil o suficiente, de modo que no futuro não haja necessidade de escrever ações de recuperação separadas por "cada botão". Eles também deve considerar o princípio da coexistência pacífica no gráfico.


O plano de ação consistiu no seguinte:

3.1.1. Verificar quais os eventos e em que ordem eles são exibidos a um programa de terceiros usado como um observador independente, e por um programa cujos objetos são afetados externamente:

Ao mesmo tempo é necessário exibir os tempos da criação de objetos criados e por conveniência anotar os resultados obtidos como tabelas.

Assim, o seguinte programa foi necessário para implementar o primeiro ponto do plano:

3.1.2. Um indicador assistente de programação que atua como um observador externo e descreve os eventos que ocorrem no gráfico.

Eu escolho um indicador que possa operar no gráfico com um Expert Advisor ao mesmo tempo.

Eu não vou discutir esse assistente em profundidade, o seu código completo é anexado com os arquivos test_types_of_chart_events. No entanto, uma vez que possa ser útil para você no futuro, ao trabalhar ou se familiarizar com eventos, vou mencionar aqui o seguinte:

Fig. 5. Propriedades externas personalizadas de um teste do indicador de observação

O indicador tem notificações sobre eventos que não estão diretamente ligadas às ações com objetos, desativadas por padrão. Ele possui as notificações ativadas/desativadas para cinco dos nove tipos de eventos padrão que estão operando.

3.1.3. Outros assistentes participantes nos experimentos:


3.2. Resultados e experimentos efetuados

Os resultados são apresentados na tabela abaixo. O programa, cujos objetos estão sujeitos a ações, "visualiza" notificações padrões sobre os eventos da mesma forma que outros programas fazem no gráfico, a razão pela qual é apenas fornecida uma tabela . É baseado em ações com os objetos "Button" e "Edit object".

Ação com objeto id lparam dparam sparam Nome do evento notificando sobre as ações realizadas "manualmente" Tempo de criação do objeto ** Nome do evento notificando sobre as ações executadas por programas "amigáveis" e de terceiros
Tempo de criação do objeto **
Criação de novo objeto (não por renomeação) 7
0 0 nome do objeto CHARTEVENT_OBJECT_CREATE hora local no computador onde o terminal é executado CHARTEVENT_OBJECT_CREATE hora local no computador onde o terminal é executado
Mudar o nome de objeto (objeto com o nome anterior é excluído e um novo objeto é criado ao mesmo tempo)
6

7

8
0

0

0
0

0

0
nome do objeto,
nome do objeto,
nome do objeto
CHARTEVENT_OBJECT_DELETE

CHARTEVENT_OBJECT_CREATE

CHARTEVENT_OBJECT_CHANGE
tempo de criação do novo objeto é igual ao tempo de criação do objeto com o nome anterior sem avisos
tempo de criação do novo objeto é igual ao tempo de criação do objeto com o nome anterior
Alterar o fundo do objeto 8 0 0 nome do objeto СHARTEVENT_OBJECT_CHANGE nenhuma alteração sem avisos nenhuma alteração
Alterar as coordenadas do objeto no eixo X
8 0 0 nome do objeto СHARTEVENT_OBJECT_CHANGE nenhuma alteração sem avisos nenhuma alteração
Excluindo objeto 6 0 0 nome do objeto CHARTEVENT_OBJECT_DELETE *** CHARTEVENT_OBJECT_DELETE ***

 

Tabela 1. Fração do que um programa amigável e de terceiros pode "ver" a partir das notificações sobre eventos de criação, alteração e remoção de objetos no gráfico *


Notas:

* Enquanto uma notificação de evento é tratada no programa, outras notificações que aparecem durante ou após o manuseio, formam uma fila para serem manipuladas neste programa.

** Tenho notado que, se um gráfico contém objetos gráficos que não são recriados pelo programa após o terminal ser reiniciado, então após um reinício, a função OBJPROP CreateTime retorna o tempo de criação desses objetos igual a 1970.01.01 00:00:00. Assim, o tempo anterior é "resetado". Isso também acontece com os objetos do gráfico que são colocados manualmente. Quando você alternar períodos de tempo, o tempo de criação de "objetos não é reposto.

*** Se você tentar determinar o tempo de criação do objeto usando OBJPROP_CREATETIME durante a notificação sobre o objeto, excluindo eventos, então uma vez que o objeto já foi excluído, a mensagem de erro 4203 ( identificador incorreto de uma propriedade de objeto gráfico) será exibida.

Se as alterações de propriedade foram executadas programaticamente, não será mostrado essas notificações. Tal como o resto, obviamente isto é feito por uma razão. Porque um único gráfico do terminal pode operar com uma série de programas, incluindo os que executam várias ações com um número de objetos gráficos, podendo ter muitos gráficos abertos com estes programas. Assim suponho que as notificações para programas onde a manipulação de eventos é aplicada, cada mudança programática de propriedades de qualquer objeto gráfico levaria a um aumento considerável de consumo dos recursos.

Para evitar restringir os usuários com um conjunto padrão de tipos de notificação de eventos, os desenvolvedores nos deram oportunidade de criar notificações pessoais em quaisquer eventos específicos individuais no código. A função EventChartCustom() é usada para este finalidade e gera notificações sobre os eventos personalizados específicos a um gráfico ou todos do terminal.

De acordo com os resultados da experiência foram propostas algumas soluções versáteis.

Elas certamente não podem ser aplicadas a qualquer cenário possível. No entanto, eu acredito que são capazes de executar as suas tarefas para painéis de controle de objetos, e também, podem ser úteis ao desenvolvimento das suas próprias opções.

Estas soluções são baseadas em:

O que se segue é comum para estas soluções. Independentemente do que for necessário para organizar a "proteção" de um ou todos os objetos de um painel de controle, da implementação de variantes da auto recuperação de objetos e/ou de programas de saída automática a partir do gráfico, você precisa incluir no código:

A variável para limpar o gráfico a partir dos objetos renomeados, usada para ser objeto do programa, deve ter as seguintes informações:

Além disso, é importante implementar ações de "segurança" no código.

Se você só construir um programa de "saída automática" a partir do gráfico, desencadeará alterações não autorizadas/exclusão de objetos no código e, em seguida, dependendo do conjunto de tarefas, uma única variável pode ser suficiente para uma flag com valores, por exemplo:
if(flagAntivandal==-1){return;}
IndicatorSetString(INDICATOR_SHORTNAME,short_name);
  • ExpertRemovе() para um Expert Advisor. Se você não estiver familiarizado com esta função ainda, então por favor, verifique a documentação para obter uma descrição e exemplo fornecido lá para sua informação.

Pelo menos duas flags serão necessárias, se você está planejando criar o objeto da "auto recuperação" após a interferência não autorizada no código, onde os objetos serão excluídos e novos criados em seu lugar. Um deles é igual à flag anterior nas suas funções. A segunda é necessária para evitar o loop do código a partir da possivel recepção de eventos de exclusão durante a ações de recuperação dos objetos. As ações serão um pouco mais complicadas aqui. A segunda fag é adaptativa e serve para um auto-ajuste automático para alcançar bom funcionamento do "alarme" e manipulação dos eventos de remoção de objetos. Ela "descarrega" a manipulação de eventos desnecessários, estarei olhando para ela mais adiante no exemplo 4.2.

Agora, tendo dito tudo isso, vamos passar para os exemplos.


4. Esquemas de variantes de execução durante a modificação ou remoção de objetos de programas não autorizados

Exemplo de implementação simultânea de uma "auto recuperação" no que diz respeito a alguns objetos e uma "saída automática" do gráfico quando altera/exclui outros objetos dentro do código fornecidos no capítulo 5 deste artigo. Descreve o esquema de código do programa operacional que também pode se tornar seu assistente, se necessário.

Vamos progredir de questões simples para complexas, utilizando o exemplo de outro código de teste que cria um painel sobre o gráfico para calcular cliques do mouse sobre seus objetos, "Sim" e "Não".

Este painel de código de teste anexo é semelhante aos da linguagem de negociação dos terminais

Fig. 6. Painel de código de teste anexo é semelhante aos da linguagem de negociação dos terminais

Este é um código de teste simples que facilita o acompanhamento dos esquemas de operação (descritos abaixo) nos dois tipos de ação:

O teste inicial do código do indicador, com os esquemas descritos abaixo não inclusos, está anexado ao artigo com o nome test_count_click_0. Durante a execução dos dois esquemas, duas variedades deste código de teste foram criadas com uma consideração das adições feitas. É por isso que você pode ver a operação de cada esquema imediatamente e individualmente como um código completo, e também testar a sua operação no gráfico removendo ou excluindo objetos desses códigos.

As linhas adicionais para a saída Print() estão incluídas nos códigos anexos, onde este esquema é aplicado, para que você possa visualizar a ordem de manipulação com métodos implementados ao alterar ou remover objetos. Abaixo estão as linhas de código sem auxilio dos comandos de saída Print().


4.1. "Saída automática" de programa do gráfico com a remoção dos objetos modificados

A versão completa do código de teste está aanexado com o nome test_count_click_1.

4.1.1. Criação do arquivo com este esquema no código:

O arquivo test_count_click_0 está salvo com o nome test_count_click_1. Isso pode ser feito pela abertura do arquivo anexado contendo o número 0 no título na MetaEditor, em seguida escolha Arquivo -> Salvar como... no menu superior do MetaEditor.

4.1.2. Variáveis declaradas além de todas as funções no âmbito da visibilidade a todas as partes dos programas, ou seja, até o bloco da função OnInit():

O nome indicadores em #define é trocado por uma substituição interna posterior:

#define NAME_INDICATOR             "count_click_1: "

a variável é adicionada a flag semáforo:

int flagAntivandal;
Seguido por um array com dimensão igual à quantidade de objetos "protegidos" para armazenar os tempos de criação dos objetos:
datetime timeCreateObj[QUANT_OBJ];

Uma array de texto para armazenar duas mensagens de aviso no caso dos indicadores de "saída automática" do gráfico serem adicionados. Um para a remoção bem sucedida no gráfico, o segundo - se ocorrer um erro durante esta operação. O array é declarado no âmbito de visibilidade para todas as partes do programa, uma vez que a saída destas mensagens é fornecida em dois idiomas (inglês e russo), dependendo do idioma do terminal.

string textDelInd[2];

Além disso, as mensagens armazenadas nesse array podem ser úteis, fornecendo a saída do indicador no gráfico, devido à recepção da razão de desinicialização REASON_INITFAILED no OnInit()

4.1.3. Na função LanguageTerminal(), responsável por exibir as mensagens dos textos dependendo do idioma terminais de negociação, temos o seguinte:

Os textos das mensagens são adicionados em ambos os programas de "saída automática", bem ou mal sucedidos, a partir do gráfico.

void LanguageTerminal()
  {
   string language_t=NULL;
   language_t=TerminalInfoString(TERMINAL_LANGUAGE);
//---
   if(language_t=="Russian")
     {
      textObj[3]="Да: ";
      textObj[4]="Нет: ";
      textObj[5]="Всего: ";
      //---
      StringConcatenate(textDelInd[0],"Удалось удалиться индикатору: \"",
                        prefixObj,"\". Код ошибки: ");
      StringConcatenate(textDelInd[1],"Удалился с графика индикатор: \"",
                        prefixObj,"\".");
     }
   else
     {
      textObj[3]="Yes: ";
      textObj[4]="No: ";
      textObj[5]="All: ";
      //---
      StringConcatenate(textDelInd[0],"Failed to delete indicator: \"",
                        prefixObj,"\". Error code: ");
      StringConcatenate(textDelInd[1],
                        "Withdrew from chart indicator: \"",
                        prefixObj,"\".");
     }
//---
   return;
  }

4.1.4. Na função PanelCreate() são criados os objetos do painel de controle:

Depois de criar objetos, no momento da sua criação, é determinado e armazenado no array. Numa versão simples isto pode se parecer mais ou menos assim:

void PanelCreate(const long chart_ID=0,const int sub_window=0)
  {
   for(int i=NUMBER_ALL;i>=0;i--)
     {
      //--- campos para mostrar o número de cliques:
      if(ObjectFind(chart_ID,nameObj[i])<0)
        {
         EditCreate(chart_ID,nameObj[i],sub_window,X_DISTANCE+WIDTH,
                    Y_DISTANCE+(HEIGHT)*(i),WIDTH,HEIGHT,
                    textObj[i],"Arial",FONT_SIZE,"\n",ALIGN_RIGHT,true,
                    CORNER_PANEL,clrText[i],CLR_PANEL,CLR_BORDER);
         //--- determinar e armazenar o tempo de criação do objeto:
         CreateTimeGet(chart_ID,nameObj[i],timeCreateObj[i]);
        }
     }
//---
   int correct=NUMBER_ALL+1;
//---
   for(int i=QUANT_OBJ-1;i>=correct;i--)
     {
      //--- campos com notas explicativas:
      if(ObjectFind(chart_ID,nameObj[i])<0)
        {
         EditCreate(chart_ID,nameObj[i],sub_window,X_DISTANCE+(WIDTH*2),
                    Y_DISTANCE+(HEIGHT)*(i-correct),WIDTH,HEIGHT,
                    textObj[i],"Arial",FONT_SIZE,"\n",ALIGN_LEFT,true,
                    CORNER_PANEL,clrText[i-correct],CLR_PANEL,CLR_BORDER);
         //--- determinar e armazenar o tempo de criação do objeto:
         CreateTimeGet(chart_ID,nameObj[i],timeCreateObj[i]);
        }
     }
   return;
  }

4.1.5. A variante da função para determinar e armazenar o momento da criação do objeto numa variável:

bool CreateTimeGet(const long chart_ID,
                   const string &name,
                   datetime &value
                   )
  {
   datetime res=0;
   if(!ObjectGetInteger(chart_ID,name,OBJPROP_CREATETIME,0,res))
     {
      Print(LINE_NUMBER,__FUNCTION__,", Error = ",
            GetLastError(),", name: ",name);
      return(false);
     }
   value=res;
   return(true);
  }

No caso, o programa está no gráfico durante a reinicialização de emergência do terminal, por exemplo: reinicialização por congelamento, foi adicionada uma função de revisão para a criação de tempo dos objetos:

bool RevisionCreateTime(int quant,datetime &time_create[])
  {
   datetime t=0;
   for(int i=quant-1;i>=0;i--)
     {
      t=time_create[i];
      if(t==0)
        {
         Print(LINE_NUMBER,__FUNCTION__,", Error create time: ",
               TimeToString(t,TIME_DATE|TIME_SECONDS));
         return(false);
        }
     }
//---
   return(true);
  }

Esta chamada da função é colocada na OnInit() após a PanelCreate().

Parto do princípio de que, se após o encerramento da emergência no terminal e seu reinício subsequente, essa função retorna false, então antes que aconteça, uma mensagem de erro 4102 a partir da função CreateTimeGet(), indicando que o gráfico não responde, pode aparecer na aba "Experts" do terminal.

4.1.6. No bloco da função OnInit():

4.1.6.1. Anterior a função para a criação de painéis de controle, a flag do semáforo adicionada é inicializada com o valor -1. Isso significa que o alarme é desativado no tempo de execução de ações "amigáveis" com objetos. Em particular, para evitar decepções quando o período de tempo do gráfico for alterado.

flagAntivandal=-1;

4.1.6.2. O nome abreviado do indicador é definido, se ainda não foi, será necessário a remoção forçada do programa do gráfico.

No exemplo de código de teste fornecido, o nome abreviado do indicador foi definido anteriormente e igual ao prefixo dos objetos que consiste no nome dos indicadores, símbolo e período do gráfico onde será definido:

//--- criar prefixo para nomes de objetos de código e nome abreviado do indicador:
   StringConcatenate(prefixObj,NAME_INDICATOR,Symbol(),"_",EnumToString(Period()));
//--- define o nome abreviado do indicador:
   IndicatorSetString(INDICATOR_SHORTNAME,prefixObj);

4.1.6.3. Nós habilitamos o alarme de "proteção", isto após a função de criação do painel de controle e revisão do array com o tempo de criação dos objetos:

//--- cria um painel:
   PanelCreate();
/*revisão do array com o tempo de criação dos objetos, e que encerra 
o programa caso algo dê errado:*/
   if(!RevisionCreateTime(QUANT_OBJ,timeCreateObj))
     {return(REASON_INITFAILED);}
/*vamos escolher a flag para a posição de resposta
aos eventos de remoção e alteração de objetos:*/
   flagAntivandal=0;


4.1.6.4. Ativamos a notificação sobre eventos de objetos removidos do gráfico, se ele está desativado nas propriedades:

//--- ativar a notificação sobre eventos de exclusão de objetos gráficos
 bool res=true;
 ChartEventObjectDeleteGet(res);
 if(res!=true)
 {ChartEventObjectDeleteSet(true);}


Nota: ChartEventObjectDeleteGet() e ChartEventObjectDeleteSet() são funções prontas da página com exemplos de como trabalhar com gráficos na Documentação.

4.1.6.5. Antes de sair do OnInit(), vamos definir a frequência do timer em segundos, a fim de verificar se as notificações sobre eventos de exclusão de objetos serão desativadas nas propriedades do gráfico durante o funcionamento deste programa:

EventSetTimer(5);

4.1.7. No bloco da função OnDeinit():

Nós especificamos que o "alarme" deve ser desativado:

flagAntivandal=-1;

Nós especificamos o término de geração de eventos a partir do timer:

EventKillTimer();

A exclusão do indicador se o código da razão de desinicialização REASON_INITFAILED vem do OnInit():

//--- exclusão do indicador se o REASON_INITFAILED veio do OnInit().
   if(reason==REASON_INITFAILED)
     {
      ChIndicatorDelete(prefixObj,textDelInd[0],textDelInd[1],0,0);
     }

4.1.8. No bloco da função OnTimer():

Usando copiar e colar, replicamos as linhas de código a partir do ponto 4.1.6.4 para ativar as notificações sobre eventos de exclusão dos objetos no gráfico com um temporizador, no caso deles serem desativados por outro programa.

4.1.9. Criando ações na funçao OnChartEvent():

4.1.9.1. Se a manipilação é fornecida apenas para "medidas de segurança", em caso de alteração e exclusão de objetos, então a manipulação desses eventos pode ser combinado num único bloco.

4.1.9.2. Então a primeira coisa é verificar a flag, caso o "alarme" seja ativado ao receber uma notificação sobre o evento de mudança/exclusão do objeto. Se o valor da bandeira é igual a -1 (o "alarme" está desativado), então ele sai do bloco da manipulação de eventos.

4.1.9.3. Quando o valor da flag for igual a 0 (o "alarme" está ativado), você pode definir primeiro uma verificação para combinar o nome do objeto obtido com um prefixo dos objetos de programas na notificação. A função StringFind() é utilizado para esta finalidade, por isso não há necessidade de interagir todos os arrays dos nomes dos objetos de programas com cada notificação sobre as mudanças e remoção dos objetos do gráfico. Neste caso, um prefixo definido em OnInit() não é abreviado, devido ao fato que o filtro é melhor. A formação de prefixo é fornecida em p.4.1.6.2.

Se não houver nenhuma correspondência de prefixo, ocorre a saída do manipulador de eventos do bloco.

4.1.9.4. Se houver uma correspondência pelo prefixo, somente então a notificação de eventos recebida é verificada para uma combinação completa de um nome com os objetos dos programas "protegidos". Quando os nomes coincidem totalmente, o valor da flag de proteção é alterado para -1 (desativado), a fim de evitar a criação de loops extras, quando um programa é excluído do gráfico.

4.1.9.5. Se um objeto está "sem dono", por ter sido renomeado, a busca de objetos com o mesmo tempo de criação igual ao com notificação é executado, com mais uma remoção, se for encontrado.

4.1.9.6. Só então o programa é removido a partir do gráfico.

Ações descritas criadas no código:

else  if(id==CHARTEVENT_OBJECT_DELETE || id==CHARTEVENT_OBJECT_CHANGE)
     {
      if(flagAntivandal==-1)//ignore o evento, se o "alarme estiver desativado"
        return;
      else if(flagAntivandal==0)//se o alarme estiver ativado,
        {
         //--- olhar por uma correspondência pelo prefixo:
         findPrefixObject=StringFind(sparam,prefixObj);
         if(findPrefixObject==0)//se houver uma correspondência pelo prefixo
           {
            string chartObject=sparam;
            findPrefixObject=-1;
            //---
            for(int i=QUANT_OBJ-1;i>=0;i--)
              {
               //--- ações quando nomes se combinam por completo:
               if(StringCompare(chartObject,nameObj[i],true)==0)
                 {
                  //--- desativar o alarme para evitar looping nas operações de remoção:
                  flagAntivandal=-1;
/*remover objetos cujo tempo de criação é igual ao tempo de criação de objetos excluídos ou
 alterados:*/
                  ObDelCreateTimeExcept0(timeCreateObj[i],0,0,OBJ_EDIT);
                  //--- remove indicador do gráfico:
                  ChIndicatorDelete(prefixObj,textDelInd[0],textDelInd[1],0,0);
                  //---
                  ChartRedraw();
                  return;
                 }
              }//for(int i=QUANT_OBJ-1;i>=0;i--)
            return;
           }//if(findPrefixObject==0)
         return;
        }//if(flagAntivandal==0)
      return;
     }//if(id==CHARTEVENT_OBJECT_DELETE || id==CHARTEVENT_OBJECT_CHANGE)  

4.1.10. A função ObDelCreateTimeExcept0() para a remoção de objetos no momento de sua criação:

O esquema da funções para a exclusão de objetos pelo seu tempo de criação pode ser parecido com este

void ObDelCreateTimeExcept0(datetime &create_time,// hora da criação
                            long chart_ID=0,// identificador dos gráficos
                            int sub_window=-1,// número da subjanela
                            ENUM_OBJECT type=-1)// -1 = todos os tipos de objetos
  {
/*sair da função, se o tempo do objeto = 0(1970.01.01 00:00:00),
 por isso não há necessidade de excluir objetos manualmente ou através de objetos criados por programação, 
 cujo tempo de criação é zerado após 
 O terminal ser reiniciado, a partir do gráfico:*/
   if(create_time==0){return;}
   int quant_obj=0;
   quant_obj=ObjectsTotal(chart_ID,sub_window,type);
//---
   if(quant_obj>0)
     {
      datetime create_time_x=0;
      string obj_name=NULL;
      //---
      for(int i=quant_obj-1;i>=0;i--)
        {
         obj_name=ObjectName(chart_ID,i,sub_window,type);
         create_time_x=(datetime)ObjectGetInteger(chart_ID,obj_name,
                        OBJPROP_CREATETIME);
         //---
         if(create_time_x==create_time)
           {ObDelete(chart_ID,obj_name);}
        }
     }
   return;
  }
//+------------------------------------------------------------------+


Ele tem um filtro no interior, por isso se os dados de criação do objeto "protegido" for igual a 0 (que significa: 1970.01.01 00:00:00), então o manipulador de eventos sobre ele deve ser descontinuado. Isso é necessário para evitar a exclusão de objetos criados manualmente ou através de programação, cujo tempo de criação é zerado após o terminal ser reiniciado, a partir do gráfico.

4.1.11. A função ObDelete() no código acima:

É tirado da biblioteca ObjectCreateAndSet e tem uma saída de erro fornecida quando os objetos são deletados:

bool ObDelete(long chart_ID,string name)
  {
   if(ObjectFind(chart_ID,name)>-1)
     {
      ResetLastError();
      if(!ObjectDelete(chart_ID,name))
        {
         Print(LINE_NUMBER,__FUNCTION__,", Error Code = ",
               GetLastError(),", name: ",name);
         return(false);
        }
     }
   return(true);
  }

4.1.12. Função para apagar um indicador:

//+------------------------------------------------------------------+
//| Deletando um indicador do gráfico                                |
//+------------------------------------------------------------------+
void ChIndicatorDelete(const string short_name,//nome abreviado do indicador
                       const string &text_0,//texto quando há um erro ao excluir um indicador
                       const string &text_1,//texto quando um indicador é excluído com sucesso
                       const long  chart_id=0,// identificador do gráfico
                       const int   sub_window=-1// número da subjanela
                       )
  {
   bool res=ChartIndicatorDelete(chart_id,sub_window,short_name);
//---
   if(!res){Print(LINE_NUMBER,text_0,GetLastError());}
   else Print(LINE_NUMBER,text_1);
  }
//+------------------------------------------------------------------+

onde LINE_NUMBER é um texto geral no início das mensagens que contém o número da linha de código definido em #define:

#define LINE_NUMBER    "Line: ",__LINE__,", "

Agora vamos para o próximo esquema.


4.2. "Auto recuperação" dos objetos do programa

Criar a auto recuperação dos objetos requer uma atenção mais séria no arranjo e mudança das flags de valores para evitar um loop de funcionamento do programa.

O esquema apresentado baseia-se numa modificação do código anterior test_count_click_1. O código completo pode ser examinado e testado, você pode encontrá-lo no arquivo anexado chamado test_count_click_2.

4.2.1. Criando um arquivo com este esquema no código:

O arquivo test_count_click_1 está salvo com o nome test_count_click_2.

4.2.2. Variáveis declaradas estão além de todas as funções no âmbito da visibilidade a todas as partes dos programas, ou seja, antes do bloco da função OnInit():

O nome dos indicadores é substituído em #define para uma substituição interna posterior:

#define NAME_INDICATOR             "count_click_2: "

Outra variável é adicionada, desta vez para uma flag de adaptação auxiliar, além da simples flag principal descrita anteriormente:

int flagAntivandal, flagResetEventDelete;

A variável também é criada para as mensagens de texto antes de realizar as operações de recuperação:

string textRestoring;

4.2.3. A função LanguageTerminal() é responsável pela exibição das mensagens dos textos, dependendo do idioma nos terminais de negociação:

Ele contém uma mensagem exibida antes das operações de recuperação, no caso de remoção não autorizada da mudança dos objetos no painel:

void LanguageTerminal()
  {
   string language_t=NULL;
   language_t=TerminalInfoString(TERMINAL_LANGUAGE);
//---
   if(language_t=="Russian")
     {
      textObj[3]="Да: ";
      textObj[4]="Нет: ";
      textObj[5]="Всего: ";
      //---
      textRestoring="Уведомление о страховом случае: объект ";
      //---
      StringConcatenate(textDelInd[0],"Удалось удалиться индикатору: \"",
                        prefixObj,"\". Код ошибки: ");
      StringConcatenate(textDelInd[1],"Удалился с графика индикатор: \"",
                        prefixObj,"\".");
     }
   else
     {
      textObj[3]="Yes: ";
      textObj[4]="No: ";
      textObj[5]="All: ";
      //---
      textRestoring="Notification of the insured event: object ";
      //---
      StringConcatenate(textDelInd[0],"Failed to delete indicator: \"",
                        prefixObj,"\". Error code: ");
      StringConcatenate(textDelInd[1],
                        "Withdrew from chart indicator: \"",
                        prefixObj,"\".");
     }
//---
   return;
  }

Eu usei esse texto assumindo que as opções consideradas agem como uma rede de segurança em caso de eventos indesejáveis.

4.2.4. No bloco da função OnInit():

Nós inicializamos ambos as flags com um valor -1 (o alarme é completamente desativado) antes da função de criação do objeto:

flagAntivandal = flagResetEventDelete = -1;

Após o bloco de criação de objetos do painel e uma revisão do array de tempo para a criação de objetos - inicialização da flag com o valor 0 (ativando o "alarme"):

//--- cria um painel:
   PanelCreate();
/*revisão do array com o tempo de criação dos objetos, e que encerra 
o programa caso algo dê errado:*/
   if(!RevisionCreateTime(QUANT_OBJ,timeCreateObj))
     {return(REASON_INITFAILED);}
/*define flags antivandalismo na posição de resposta a 
eventos de exclusão e alteração de objetos:*/
   flagAntivandal=flagResetEventDelete=0;

4.2.5. Vamos continuar com a função OnChartEvent():

Vamos mudar o bloco abaixo, já existente, da manipulação das notificações sobre eventos de alteração e exclusão dos objetos. No novo bloco, as ações seguem o esquema que também é comum para manipular eventos de remoção e mudança dos objetos:

  • Antes de mais nada, verifique se a "proteção" está habilitada. Se não, então a saída é realizada a partir do evento de manipulação do bloco.
  • Então, se a flag auxiliar responsável por redefinir os eventos de exclusão extras é maior do que 0, as notificações recebidas sobre os eventos de exclusão alteram os objetos dos painéis que são resetadas até a flag igualar a 0.
  • Somente quando as flags principais e auxiliares são iguais a zero, as ações de recuperação são possíveis em caso de alteração ou remoção dos objetos não autorizados.
else  if(id==CHARTEVENT_OBJECT_DELETE || id==CHARTEVENT_OBJECT_CHANGE)
     {
      if(flagAntivandal==-1){return;}
      else if(flagResetEventDelete>0)
        {
         findPrefixObject=StringFind(sparam,prefixObj);
         if(findPrefixObject==0)
           {
            findPrefixObject=-1;
/*redefinindo valores dos eventos de "counter flag" para prevenção subsequente 
de um loop da operação de código a partir da fila dos eventos construídos de remoção de objetos
que aparecem como resultado de ações de recuperação:*/
            flagResetEventDelete=flagResetEventDelete-1;
            //---
            return;
           }
         return;
        }
      else if(flagAntivandal==0 && flagResetEventDelete==0)
        {
         //--- examine uma combinação pelo prefixo:
         findPrefixObject=StringFind(sparam,prefixObj);
         if(findPrefixObject==0)//se houver uma correspondência pelo prefixo
           {
            string chartObject=sparam;//o nome do objeto no evento
            findPrefixObject=-1;
/*verificando se um objeto é "protegido" e tende a recuperar 
            medidas, se for:*/
            RestoringObjArrayAll(chartObject,prefixObj,flagAntivandal,
                                     flagResetEventDelete,QUANT_OBJ,nameObj,
                                     timeCreateObj);
            return;
           }//if(findPrefixObject==0)
         return;
        }//else if(flagAntivandal==0 && flagResetEventDelete==0)
      return;
     }//else  if(id==CHARTEVENT_OBJECT_DELETE || id==CHARTEVENT_OBJECT_CHANGE)

Principais atividades de recuperação são criadas na função RestoringObjArrayAll(). Várias de tais funções podem ser aplicadas simultaneamente em bloco para diferentes grupos de objetos. Você pode encontrar um exemplo relevante no quinto capítulo deste artigo. O nome do objeto por evento de exclusão ou alteração de quedas no âmbito desta função após uma verificação, se ela corresponde aos objetos "protegidos".

4.2.6. A função RestoringObjArrayAll().

Ele contém as seguintes ações:

  • Se a confirmação para uma combinação completa pelo nome mostra que o objeto de evento é "protegido", então:
    • a principal flag de "alarme" obtém valor -1 (o "alarme" está desativado para o tempo das ações "amigáveis");
    • no caso de um evento de exclusão de objeto, uma flag auxiliar é definida igual ao montante global de objetos "protegidas" menos um, porque se o evento de exclusão de objetos é manipulado, isso significa que pelo menos neste momento um objeto foi excluído.
  • Após essas ações preliminares os objetos protegidos remanescentes são excluídos pelo prefixo usando a função ObDeletePrefixFlag() mostrada abaixo, enquanto os objetos excluídos são calculados. Com base neste cálculo, o valor da flag auxiliar flagResetEventDelete é corrigida, se necessário.

E uma vez que durante a exclusão de objetos, os eventos de exclusão aparecem, eles não vão ser posteriormente manipulados até que o valor da flag auxiliar não seja redefinida para 0, como são tratados nesta parte do código:

else if(flagResetEventDelete>0)
        {
         findPrefixObject=StringFind(sparam,prefixObj);
         if(findPrefixObject==0)
           {
            findPrefixObject=-1;
/*redefinindo valores dos eventos de "counter flag" para prevenção subsequente  
de um loop da operação de código a partir da fila dos eventos construídos de remoção de objetos
que aparecem como resultado de ações de recuperação:*/
            flagResetEventDelete=flagResetEventDelete-1;
            //---
            return;
           }
         return;
        }


//+---------------------------------------------------------------------------------------+
//|Função da "auto recuperação" de objetos na OnChartEvent()                              |
//+---------------------------------------------------------------------------------------+
//|string sparam = nome do objeto transmitido para OnChartEvent()                         |
//|string prefix_obj = prefixo comum de objetos protegidos                                |
//|int &flag_antivandal = flag para ativar/desativar o alarme                             |
//|int &flag_reset = flag para resetar eventos                                            |
//|int quant_obj = quantidade total de objetos no array                                   |
//|"protegido"                                                                            |
//|string &name[] = array dos nomes dos objetos "protegidos"                              |
//|datetime &time_create[] = array de tempo de criação desses objetos                     |
//|int quant_obj_all=-1 quantidade total de objetos recuperáveis                          |
//|do programa, >= quant_obj (se -1, então iguala a quant_obj)                            |
//|const long chart_ID = 0 identificador de gráficos                                      |
//|const int sub_window = 0 índice da janela                                              |
//|int start_pos=0 posição nos nomes dos objetos para começar a procurar o prefixo com    |
//+---------------------------------------------------------------------------------------+
int RestoringObjArrayAll(const string sparam,
                         const string prefix_obj,
                         int &flag_antivandal,
                         int &flag_reset,
                         const int quant_obj,
                         string &name[],
                         datetime &time_create[],
                         int quant_obj_all=-1,
                         const long chart_ID=0,
                         const int sub_window=0,
                         const int start_pos=0
                         )
  {
//--- aceita os resultados aqui:
   int res=-1;
/*Verificação para correção da entrada da quantidade de objetos
   dos parâmetros externos da função. O fato é que se parâmetros externos errados 
   desta função foram inseridos durante a criação do código,
   serão conhecidos somente após o evento "alarme".
   É por isso que você precisa de antemão ter a certeza de
   configurar os parâmetros externos corretos para esta função ao criar o código.*/
   if(quant_obj<=0)//Se a quantidade especificada de objetos protegidos <= 0
     {
      Print(LINE_NUMBER,__FUNCTION__,
            ", Error Code. Valor errado: quant_obj =",quant_obj);
      return(res);
     }
   else   if(quant_obj>quant_obj_all && quant_obj_all!=-1)
     {
      Print(LINE_NUMBER,__FUNCTION__,
            ", Error Code. Não é o valor correto: quant_obj_all");
      return(res);
     }
   else if(quant_obj_all==-1){quant_obj_all=quant_obj;}
//--- variável para um valor de comparação do nome completo de um objeto:
   int comparison=-1;
/*Loop para verificar o nome do objeto obtido a partir de eventos com nomes 
 no array dos objetos protegidos:*/
   for(int i=quant_obj-1;i>=0;i--)
     {
      comparison=StringCompare(sparam,name[i],true);
      //--- ações quando nomes se combinam por completo:
      if(comparison==0)
        {
         comparison=-1;
         res=1;
         //--- notificação sobre a ocorrência de caso "seguro":
         Print(LINE_NUMBER,textRestoring,sparam);
/*enquanto o programa restaura objetos, configuramos a flag 
para não reagir à eventos de exclusão dos objetos:*/
         flag_antivandal=-1;
/*Valor inicial do "counter flag" dos eventos que não são mais necessários 
para a manipulação dos eventos futuros. Para a prevenção consequente 
de loop do código a partir da fila de construção dos eventos de exclusão dos 
objetos como resultado de ações de recuperação:*/
         flag_reset=quant_obj_all-1;
         //--- removendo objetos restantes protegidos de um array, se for o caso
         ObDeletePrefixFlag(flag_reset,prefix_obj,chart_ID,sub_window,start_pos);
         //--- redesenhar o gráfico:
         ChartRedraw();
         //--- a remoção de objetos renomeados, se houver:
         ObDelCreateTimeExcept0(time_create[i],chart_ID,sub_window);
         //--- recriação de objetos:
         PanelCreate();
         //--- redesenho final do gráfico:
         ChartRedraw();
         //--- habilitando o alarme:
         flag_antivandal=0;
         //---
         return(res);
        }
     }//for(int i=QUANT_OBJECTS-1;i>=0;i--)
   return(res);
  }

Nota: A função tem a seleção pelo tipo de objeto ausente por uma razão. Durante a "auto recuperação" dos objetos, a intenção de diminuir a quantidade de manipulação pelo estreitamento do círculo de manipulação por tipo de objeto pode levar a um efeito oposto, assim a auto recuperação de todos os objetos de painéis recriados e excluídos seria de diferentes tipos. Portanto, do meu ponto de vista, o melhor é não aplicar a estratégia de estreitar o círculo de manipulação por tipos específicos de objetos, se no ponto de interferência não autorizado, os objetos de painel na tabela consistem em vários tipos. E assumindo que pode haver aqueles que vão ignorar este aviso, a opção é removida da função acima.

Você pode visualizar os efeitos indesejáveis ao escrever e usar a seleção dos tipos de objetos durante a "auto recuperação" no exemplo do quinto capítulo do artigo, onde objetos de diferentes tipos estão presentes no painel. Você só tem que iniciar o Print() nos blocos de código para manipular eventos e recuperar com antecedência.

Devo acrescentar também, que você pode facilmente poupar recursos, reduzindo a quantidade de manipuladores no código por meio das restrições de entrada por tipo de objeto na variante da "saída automática" do programa a partir do gráfico, como descrito acima.

4.2.7. A função ObDeletePrefixFlag() destina-se a remover objetos pelo prefixo e, se necessário, corrigir o valor da flag auxiliar:

Eu confiei no esquema de um código usado no livro de Sergei Kovalev's, e que mais tarde foi mencionado no fórum MQL4 por Artem Trishkin e pelo usuário 7777877, desconhecido para mim. Eu não prestei muita atenção, é por isso que estou grato à aqueles que tocaram no assunto no fórum, uma vez que a base do esquema é bastante universal e tem sido de grande ajuda para mim em muitas tarefas diferentes.

Basicamente, esta variedade é aplicada aqui:

//+------------------------------------------------------------------+
//| int &flag_reset = flag para repor eventos de objetos de exclusão |
//| string prefix_obj = prefixo geral nos nomes dos objetos          |
//| long  chart_ID = identificador dos gráficos                      | 
//| int   sub_window=-1 = índice da janela (-1 = todos)              |
//| int start_pos=-1 = inicia a posição de um prefixo geral          | 
//| substring no nome do objeto                                      |
//+------------------------------------------------------------------+
int ObDeletePrefixFlag(int &flag_reset,
                       string prefix_obj,
                       const long chart_ID=0,
                       const int sub_window=0,
                       int start_pos=0)

  {
   int quant_obj=ObjectsTotal(chart_ID,sub_window,-1);
   if(quant_obj>0)
     {
      int count=0;
      int prefix_len=StringLen(prefix_obj);
      string find_substr=NULL,obj_name=NULL;
//---
      for(int i=quant_obj-1;i>=0;i--)
        {
         obj_name=ObjectName(chart_ID,i,sub_window,-1);
         find_substr=StringSubstr(obj_name,start_pos,prefix_len);

         if(StringCompare(find_substr,prefix_obj,true)==0)
           {
            count=count+1;
            ObDelete(chart_ID,obj_name);
           }
        }
      if(count>0 && flag_reset<count)
        {flag_reset=count;}
     }
   return(flag_reset);
  }

A operação do código pode ser verificada de acordo com:
  • execução do código de teste ligado no gráfico;
  • clicando nos objetos pertencentes ao "Yes" e "No", aumentando os valores a partir do zero a outro valor;
  • e depois tentando mudar estes objetos de painel através do diálogo de propriedades ou excluindo-os.
Após a execução das ações de recuperação automática, o painel terá números "clicados" mais vezes do que quando não estavam no momento da mudança e remoção de seus objetos, uma vez que a quantidade de cliques é salvo na array de dados, usado durante a criação do painel.

No entanto, diversas respostas a vários objectos podem ser criadas.

Nós apresentamos a parte principal do procedimento, exceto em alguns casos especiais, tais como:

Agora vamos avançar para um exemplo de implementação prática, onde o programa responde de ambos os sentidos a exclusão e mudança dos seus objetos.


5. Exemplo de implementação de duas opções de resposta num programa

A seleção de um indicador não é aleatória, onde a implementação de duas opções das reações de respostas a ações não autorizadas seriam fornecidas passo a passo:

E, se necessário, no futuro poderá ser estendido pela expansão da lista dos valores exibidos.

Um código completo é ligado sob o nome id_name_object. Aqui vou descrever:

Finalidade do indicador fornecido: ao clicar em qualquer objeto gráfico, mostra o nome do objeto clicado, seu tempo de criação e o seu tipo (além de si próprio). Se necessário, você pode copiar estes valores dos campos onde são exibidos. Durante uma tentativa de alterar ou remover os valores exibidos no painel, sua restauração será fornecida. Isso vale separadamente a partir das opções consideradas no artigo e como um complemento a elas.

Além disso, existem as seguintes características:

O aparecimento de um painel de controle dependendo do idioma do terminal

Fig. 7. O aparecimento de um painel de controle dependendo do idioma do terminal

Mapa para arrays e uma variável para armazenar os nomes criados no painel de objetos

Fig. 8. Mapa para arrays e uma variável para armazenar os nomes criados no painel de objetos


Nome
Tipo de objeto
Finalidade
Onde o texto está armazenado
Fonte
Cor do texto *
Fundo * Cor da borda *
Tempo de criação é armazenado
nameRectLabel
OBJ_RECTANGLE_LABEL
Telas
---
--- ---
CLR_PANEL clrNONE
timeCreateRectLabel
nameButton[0]
OBJ_BUTTON
Botão para a remoção de identificador a partir do gráfico
textButtUchar[0]**
"Wingdings"
CLR_TEXT
CLR_PANEL clrNONE
timeCreateButton[0]
nameButton[1] OBJ_BUTTON
Botão para o painel de "minimização"
textButtUchar[1]**
"Wingdings"
CLR_TEXT
CLR_PANEL clrNONE
timeCreateButton[1]
nameButton[2] OBJ_BUTTON
Botão para o painel de "maximização" depois de "minimizá-lo"
textButtUchar[2]**
"Wingdings"
CLR_TEXT
CLR_PANEL clrNONE
timeCreateButton[2]
nameEdit0[0]
OBJ_EDIT
"Nome do objeto:" etiqueta
textEdit0[0]
"Arial"
CLR_TEXT_BACK
CLR_PANEL CLR_BORDER
timeCreateEdit0[0]
nameEdit0[1]
OBJ_EDIT
"Tempo de criação:" etiqueta textEdit0[1] "Arial"
CLR_TEXT_BACK
CLR_PANEL CLR_BORDER
timeCreateEdit0[1]
nameEdit0[2]
OBJ_EDIT
"Tipo do objeto:" etiqueta textEdit0[2] "Arial"
CLR_TEXT_BACK
CLR_PANEL CLR_BORDER
timeCreateEdit0[2]
nameEdit1[0]
OBJ_EDIT
Campo para exibição do nome do objeto textEdit1[0]
"Arial"
CLR_TEXT
CLR_PANEL CLR_BORDER
timeCreateEdit1[0]
nameEdit1[1] OBJ_EDIT
Campo para a exibição de tempo da criação textEdit1[1] "Arial"
CLR_TEXT
CLR_PANEL CLR_BORDER
timeCreateEdit1[1]
nameEdit1[2] OBJ_EDIT
Campo para a exibição do tipo de objeto
textEdit1[2] "Arial"
CLR_TEXT
CLR_PANEL CLR_BORDER
timeCreateEdit1[2]

 

Table 2. Mapa para objetos do painel

* Colors set with #define:

#define CLR_PANEL                  clrSilver//contexto geral do painel
#define CLR_TEXT                   C'39,39,39'//cor principal do texto
#define CLR_TEXT_BACK              C'150,150,150' //cor do texto sombreado 
#define CLR_BORDER                 clrLavender//cor dos quadros

** Symbols "Wingdings":

uchar textButtUchar[3]={251,225,226};


5.1. Variáveis externas:

O tamanho do painel é calculado com base no tamanho de um botão. Portanto, a largura e a altura do tamanho do botão e da fonte são exibidas nas propriedades personalizadas externas. Isto permite aumentar o painel e textos exibidos sem fazer ajustes no próprio código.

Propriedades personalizadas externas de um indicador

Fig. 9. Propriedades personalizadas externas de um indicador


5.2. Variáveis para flags declaradas além de qualquer função no âmbito da visibilidade para todas as partes do programa, isto é, até o bloco da função OnInit().

Duas flags são declaradas:

int flagClick, flagResetEventDelete;

flagClick é uma flag que define o rastreamento das notificações aos cliques nos botões do painel. Seus valores são:

0
Acompanha as notificações sobre eventos de cliques nos botões de "minimização" do painel e remoção de identificador a partir do gráfico, ou seja, este valor é fornecido quando a parte principal do painel está no gráfico.
-1
As ações de "minimização" são definida antes, ou pelo contrário, a "maximização" do painel no gráfico, e também, antes da ação de exclusão de identificador a partir do gráfico, após o clique no botão correspondente.
1 Quando monitora as notificações sobre eventos clicando no botão "maximizar".

 

Também age como a principal flag "anti vandalismo" (foi nomeada flagAntivandal nos códigos anteriores deste artigo), no caso de modificação não autorizada ou remoção de objetos. O valor 0 habilita o "alarme" da parte principal do painel 1 — do botão "maximizar", e -1 desativa o "alarme" durante as ações em curso, se for o caso.

Se você deseja criar ums flag principal "anti vandalismo" adaptativa por valor, já que uma flag auxiliar reseta a manipulação de eventos no código, então será necessário uma variável separada para esta finalidade.

flagResetEventDelete é uma flag usada para reiniciar a manipulação de eventos desnecessários com valores de adaptação, ao implementar a opção "auto recuperação" para objetos alterados ou excluídos.

Outras variáveis declaradas nesta área podem ser vistas no código anexado.


5.3. No bloco da função OnInit():

int OnInit()
  {
   flagClick=flagResetEventDelete=-1;
/*array para campos de texto do painel onde 
   os valores dos objetos gráficos clicados serão consequentemente exibidos:*/
   for(int i=QUANT_OBJ_EDIT-1;i>=0;i--){textEdit1[i]=" ";}
//+------------------------------------------------------------------+
/*O nome abreviado do indicador:*/
   StringConcatenate(prefixObj,INDICATOR_NAME,"_",Symbol(),"_",
                     EnumToString(Period()));
   IndicatorSetString(INDICATOR_SHORTNAME,prefixObj);
//--- criando o nome do objeto
   NameObjectPanel(nameRectLabel,prefixObj,"rl");
   NameObjectPanel(QUANT_OBJ_BUTTON,nameButton,prefixObj,"but");
   NameObjectPanel(QUANT_OBJ_EDIT,nameEdit0,prefixObj,"ed0");
   NameObjectPanel(QUANT_OBJ_EDIT,nameEdit1,prefixObj,"ed1");
//+------------------------------------------------------------------+
/*Textos do Indicador, depende do idioma do terminal de negociação:*/
   LanguageTerminal();
//+------------------------------------------------------------------+
/*restrições da largura e altura de um botão:*/
   if(objWidthHeight>=20 && objWidthHeight<=300)
     {obj0WidthHeight=objWidthHeight;}
   else if(objWidthHeight>300){obj0WidthHeight=300;}
   else {obj0WidthHeight=20;}
//--- a criação de um painel de controle no gráfico
   PanelCreate();
/*revisão dos arrays com o tempo de criação dos objetos e finalização do programa
se algo de errado acontece:
(Apenas dois dos três botões estão presentes na parte principal do painel,
portanto, o seu valor durante a revisão é um a menos)*/
   if(!RevisionCreateTime(QUANT_OBJ_BUTTON-1,timeCreateButton))
     {return(REASON_INITFAILED);}
   if(!RevisionCreateTime(QUANT_OBJ_EDIT,timeCreateEdit0))
     {return(REASON_INITFAILED);}
   if(!RevisionCreateTime(QUANT_OBJ_EDIT,timeCreateEdit1))
     {return(REASON_INITFAILED);}
//---
   flagClick=flagResetEventDelete=0;
//--- incluindo as notificações sobre eventos de remoção dos objetos gráficos:
   bool res;
   ChartEventObjectDeleteGet(res);
   if(res!=true)
     {ChartEventObjectDeleteSet(true);}
//--- criar timer
   EventSetTimer(8);
//---
   return(INIT_SUCCEEDED);
  }

5.4. Na função PanelCreate():

Os objetos do painel estão divididos em diferentes grupos de nomes, a fim de simplificar a mudança de sua aparência no futuro. O painel em si é colocado no canto superior direito do gráfico. Assim, a distâncias em pixels ao longo do eixo X e Y determinam a posição dos pontos superior esquerdo dos objetos em relação ao canto superior direito do gráfico.

Os casos de mudanças de nomes não autorizados dos objetos no momento de sua criação serão definidos e registrados nos arrays previstos para esta finalidade. Uma variável separada é fornecida para a tela do painel.

void PanelCreate(const long chart_ID=0,//ID do gráfico 
                 const int sub_window=0)// número da subjanela
  {
//--- largura da tela do painel:
   int widthPanel=(obj0WidthHeight*11)+(CONTROLS_GAP_XY*2);
//--- altura da tela do painel:
   int heightPanel=(obj0WidthHeight*6)+(CONTROLS_GAP_XY*8);
//--- a distância ao longo do eixo х:
   int x_dist=X_DISTANCE+widthPanel;
//---
   if(ObjectFind(chart_ID,nameRectLabel)<0)//telas
     {
      RectLabelCreate(chart_ID,nameRectLabel,sub_window,x_dist,
                      Y_DISTANCE,widthPanel,heightPanel,"\n",CLR_PANEL,
                      BORDER_RAISED,CORNER_PANEL,clrNONE,STYLE_SOLID,1,
                      false,false,true);
      //--- determina e registra o tempo de criação de objeto:
      CreateTimeGet(chart_ID,nameRectLabel,timeCreateRectLabel);
     }
//---
   x_dist=X_DISTANCE+CONTROLS_GAP_XY;
   int y_dist=Y_DISTANCE+CONTROLS_GAP_XY;
//---
   for(int i=QUANT_OBJ_BUTTON-2;i>=0;i--)
     {
      //--- botões para excluir e minimizar o indicador:
      if(ObjectFind(chart_ID,nameButton[i])<0)
        {
         ButtonCreate(chart_ID,nameButton[i],sub_window,
                      x_dist+(obj0WidthHeight*(i+1)),y_dist,obj0WidthHeight,
                      obj0WidthHeight,CORNER_PANEL,
                      CharToString(textButtUchar[i]),"\n","Wingdings",
                      font_sz+1,CLR_TEXT,CLR_PANEL,clrNONE);
         //--- determina e registra o tempo de criação de objeto:
         CreateTimeGet(chart_ID,nameButton[i],timeCreateButton[i]);
        }
     }
//---
   x_dist=X_DISTANCE+widthPanel-CONTROLS_GAP_XY;
   int y_dist_plus=(CONTROLS_GAP_XY*2)+(obj0WidthHeight*2);
//---
   for(int i=QUANT_OBJ_EDIT-1;i>=0;i--)
     {
      //--- nomes das propriedades do objeto:
      if(ObjectFind(chart_ID,nameEdit0[i])<0)
        {
         EditCreate(chart_ID,nameEdit0[i],sub_window,x_dist,
                    y_dist+(y_dist_plus*i),obj0WidthHeight*8,
                    obj0WidthHeight,textEdit0[i],"Arial",font_sz,
                    "\n",ALIGN_CENTER,true,CORNER_RIGHT_UPPER,
                    CLR_TEXT_BACK,CLR_PANEL,CLR_BORDER);
         //--- determina e registra o tempo de criação de objeto:
         CreateTimeGet(chart_ID,nameEdit0[i],timeCreateEdit0[i]);
        }
     }
//---
   y_dist=Y_DISTANCE+obj0WidthHeight+(CONTROLS_GAP_XY*2);
//---
   for(int i=QUANT_OBJ_EDIT-1;i>=0;i--)
     {
      //--- para a exibição de textos de valores de propriedade dos objetos:
      if(ObjectFind(chart_ID,nameEdit1[i])<0)
        {
         EditCreate(chart_ID,nameEdit1[i],sub_window,x_dist,
                    y_dist+(y_dist_plus*i),obj0WidthHeight*11,
                    obj0WidthHeight,textEdit1[i],"Arial",font_sz,
                    "\n",ALIGN_LEFT,true,CORNER_RIGHT_UPPER,
                    CLR_TEXT,CLR_PANEL,CLR_BORDER);
         //--- determina e registra o tempo de criação de objeto:
         CreateTimeGet(chart_ID,nameEdit1[i],timeCreateEdit1[i]);
        }
     }
   return;
  }

5.5. No OnDeinit():

"Desabilitar" a flag principal quando deletar o indicador no gráfico, excluindo os objetos pelo prefixo, parando a geração de eventos a partir do timer e remoção do indicador no gráfico, se uma razão de desinicialização, REASON_INITFAILED, for recebida do OnInit():

void OnDeinit(const int reason)
  {
   flagClick=-1;
   ObjectsDeleteAll(0,prefixObj,0,-1);
   EventKillTimer();
//--- exclusão do indicador se o REASON_INITFAILED foi recebida do OnInit.
   if(reason==REASON_INITFAILED)
     {
      ChIndicatorDelete(prefixObj,textDelInd[0],textDelInd[1],0,0);
     }
//---
   ChartRedraw();
  }

5.6. No OnTimer():

Verificação ocasionais pelo tempo definido no OnInit(), se a notificação sobre eventos de exclusão de objetos gráficos está incluída nas propriedades do gráfico:

void OnTimer()
  {
//--- ativar uma notificação sobre eventos de exclusão de objetos gráficos
   bool res;
   ChartEventObjectDeleteGet(res);
   if(res!=true)
     {ChartEventObjectDeleteSet(true);}
  }


5.7. A função OnChartEvent():

5.7.1. No bloco de manipulação das notificações sobre eventos dos objetos clicando com o mouse:
Na flag de semáforo flagClick igual a 0, então:
  • Primeiro, as verificações são realizadas para averiguar se foi um clique sobre nos botões da parte principal do painel.
  • Se foi um clique no botão de minimização do painel, então a flag flagClick obtém o valor anterior -1 para ações futuras.
  • Se um botão de "minimizar" foi clicado, então a parte principal do painel é minimizado e o botão "maximizar" é criado no canto superior direito do gráfico.
  • A flagClick é obtida com o valor igual a 1 depois disso, o que implica em arrastar os cliques no painel do botão "maximizar" e sua modificação não será autorizada ou removida.
  • Se fosse um clique no botão para remover o indicador no gráfico, então a flag flagClick também exibe o valor -1, antes de qualquer nova ação. Somente então é feita uma tentativa para remover o indicador a partir do gráfico.
  • Se não fosse um clique nos botões do painel de controle, mas em vez disso, sobre o outro objeto de gráfico, exceto para estes objetos do indicador, então o seu nome, a hora de criação e o tipo são determinados e exibidos neste valor de objeto no painel.

Se a flag semáforo, flagClick , e igual a 1, então clicando no botão "maximizar":

  • Esta flag é obtida com o valor -1, antes de outras ações.
  • O botão é removido e o painel é criado.
  • Depois que a flag flagClick é obtida com o valor 0. A flag auxiliar flagResetEventDelete, responsável por resetar os eventos de exclusão do botão "maximizar", obtém o valor 1.
if(id==CHARTEVENT_OBJECT_CLICK)//id = 1
     {
      if(flagClick==0)//se o painel de controle de parte principal está no gráfico
        {
         string clickedChartObject=sparam;
         findPrefixObject=StringFind(clickedChartObject,prefixObj);
         //---
         if(findPrefixObject==0)
           {
            findPrefixObject=-1;
            //---
            for(int i=1;i>=0;i--)
              {
               if(StringCompare(clickedChartObject,nameButton[i],true)==0)
                 {
                  switch(i)
                    {
                     //--- botão "minimizar":
                     case 1:flagClick=-1;
                     MinimizeTable(nameButton,2,textButtUchar,2,
                                   timeCreateButton,obj0WidthHeight);
                     flagClick=1;return;
                     //--- botão para excluir um indicador no gráfico:
                     case 0:flagClick=-1;
                     ChIndicatorDelete(prefixObj,textDelInd[0],textDelInd[1],0,0);
                     return;
                     default:return;
                    }
                 }
              }
           }
         else//se o clique não foi nos botões do painel de controle,
           {
            SetValuesTable(clickedChartObject);//vamos definir o valor do objeto
            return;
           }
        }
      //--- se um clique foi no painel "maximizar":
      else if(flagClick==1)
        {
         findPrefixObject=StringFind(sparam,prefixObj);
         if(findPrefixObject==0)
           {
            string clickedChartObject=sparam;
            findPrefixObject=-1;
            //--- botão do painel "maximizar":
            if(StringCompare(clickedChartObject,nameButton[2],true)==0)
              {
               flagClick=-1;
               //--- ações principais ao pressionar o botão "maximizar" no painel
               ToExpandTheTable(clickedChartObject);
               //---
               flagClick=0;
               //--- redefine o evento para deletar este botão:
               flagResetEventDelete=1;
               //---
               return;
              }
           }
         return;
        }
      return;
     }//if(id==CHARTEVENT_OBJECT_CLICK)

5.7.2. No bloco de manipulação das notificações sobre finalização da edição de texto no objeto gráfico Edit:
Quando existem valores de objetos gráficos no campo deste painel, eles se tornam disponíveis para copiar a partir desses campos. Portanto, se os valores exibidos pelo indicador foram alterados ou excluídos ao copiar acidentalmente/intencionalmente nesses campos, então o texto anterior é restaurado:
else  if(id==CHARTEVENT_OBJECT_ENDEDIT)//id = 3
     {
      findPrefixObject=StringFind(sparam,prefixObj);
      if(findPrefixObject==0)
        {
         string endEditChartObject=sparam;
         findPrefixObject=-1;
         int comparison=-1;
         //---
         for(int i=QUANT_OBJ_EDIT-1;i>=0;i--)
           {
            if(StringCompare(endEditChartObject,nameEdit1[i],true)==0)
              {
               string textNew=ObjectGetString(0,endEditChartObject,OBJPROP_TEXT);
               comparison=StringCompare(textEdit1[i],textNew,true);
               //---
               if(comparison!=0)
                 {
                  ObSetString(0,nameEdit1[i],OBJPROP_TEXT,textEdit1[i]);
                 }
               //---
               ChartRedraw();
               return;
              }
           }//for(int i=0;i<QUANT_OBJ_EDIT;i++)
         return;
        }//if(findPrefixObject==0)
      return;
     }//if(id==CHARTEVENT_OBJECT_ENDEDIT)

5.7.3. No bloco para manipular notificações sobre a exclusão ou remoção de objetos:
  • Se a flag flagClick é igual a -1, então ao sair do evento ocorre a manipulação do bloco, uma vez que o seu valor é obtido na flag durante as ações "amigáveis".
  • Se a primeira verificação é concluída e revelou que a flag auxiliar, flagResetEventDelete, para eventos resetados excede 0 e o objeto de evento refere-se ao painel de objetos, então os eventos são redefinidos e excluídos do bloco de manipulação.
  • Se as flags flagClick e a flagResetEventDelete são iguais a 0, então quando o nome do objeto do evento corresponde a quaisquer objetos "protegidos", ações de recuperação de parte principal do painel são realizadas usando a função, já conhecida, RestoringObjArrayAll() e as funções de acompanhamento.
  • Se a flag principal flagClick é igual a 1 no evento, então quando o nome do objeto pelo evento combina com o botão "maximizar" do painel, o indicador é removido a partir do gráfico.

else  if(id==CHARTEVENT_OBJECT_DELETE || id==CHARTEVENT_OBJECT_CHANGE)
     {
      if(flagClick==-1)return;
      else if(flagResetEventDelete>0)
        {
         findPrefixObject=StringFind(sparam,prefixObj);
         if(findPrefixObject==0)
           {
            findPrefixObject=-1;
            flagResetEventDelete=flagResetEventDelete-1;
            //---
            return;
           }
         return;
        }
      //--- verificar se o nosso objeto é excluído/removido:
      else if(flagClick==0 && flagResetEventDelete==0)
        {
         findPrefixObject=StringFind(sparam,prefixObj);
         //---
         if(findPrefixObject==0)
           {
            string chartObject=sparam;
            findPrefixObject=-1;
            int res=-1;
            //--- ações se o objeto é a tela do painel de controle:
            res=RestoringObjOneAll(chartObject,prefixObj,flagClick,
                                   flagResetEventDelete,nameRectLabel,
                                   timeCreateRectLabel,QUANT_OBJ_ALL);
            //---
            if(res==1){return;}
            //--- ações se o objeto é o botão do painel de controle:
            res=RestoringObjArrayAll(chartObject,prefixObj,flagClick,
                                     flagResetEventDelete,QUANT_OBJ_BUTTON,
                                     nameButton,timeCreateButton,QUANT_OBJ_ALL);
            //---
            if(res==1){return;}
            //--- ações se o objeto for uma nota (Edit0) no painel de controle:
            res=RestoringObjArrayAll(chartObject,prefixObj,flagClick,
                                     flagResetEventDelete,QUANT_OBJ_EDIT,
                                     nameEdit0,timeCreateEdit0,QUANT_OBJ_ALL);
            //---
            if(res==1){return;}
            //--- ações se o objeto for uma nota (Edit1) no painel de controle:
            res=RestoringObjArrayAll(chartObject,prefixObj,flagClick,
                                     flagResetEventDelete,QUANT_OBJ_EDIT,
                                     nameEdit1,timeCreateEdit1,QUANT_OBJ_ALL);
            //---
            return;
           }
        }
      else if(flagClick==1)//se o botão do painel de minimizar for alterado ou excluído
        {
         string clickedChartObject=sparam;
         if(StringCompare(clickedChartObject,nameButton[2],true)==0)
           {
            flagClick=-1;
            //--- removendo o indicador do gráfico:
            ChIndicatorDelete(prefixObj,textDelInd[0],textDelInd[1],0,0);
            return;
           }
         return;
        }
      return;
     }//else  if(id==CHARTEVENT_OBJECT_DELETE || id==CHARTEVENT_OBJECT_CHANGE)

O novo recurso neste bloco é a função RestoringObjOneAll() aplicada quando da tela do painel é alterada ou eliminada. É similar a função RestoringObjArrayAll() presente aqui e usada anteriormente.
//+-----------------------------------------------------------------------+
//|string sparam = nome do objeto transmitido para OnChartEvent()         |
//|string prefix_obj = prefixo comum dos objetos protegidos               |
//|int &flag_antivandal = flag para habilitar/desbilitar o alarme         |
//|int &flag_reset = flag para resetar eventos                            |
//|string name = nome do objeto                                           |
//|datetime time_create = tempo de criação do objeto                      |
//|int quant_obj_all = número total de objetos recuperáveis               |
//|of the program, >= quant_obj (se -1, então são iguais a quant_obj)     |
//|const long chart_ID = 0 identificador de gráfico                       |
//|const int sub_window = 0 índice da janela                              |
//|int start_pos=0 a posição no nome de objetos para procurar por prefixo |
//+-----------------------------------------------------------------------+
int RestoringObjOneAll(const string sparam,
                       const string prefix_obj,
                       int &flag_antivandal,
                       int &flag_reset,
                       string name,
                       datetime time_create,
                       const int quant_obj_all,
                       const long chart_ID=0,
                       const int sub_window=0,
                       int start_pos=0
                       )
  {
   int res=-1;
   int comparison=-1;
//---
   comparison=StringCompare(sparam,name,true);
//--- ações quando nomes se combinam por completo:
   if(comparison==0)
     {
      res=1;
      comparison=-1;
      //--- notificação sobre a ocorrência de caso "seguro":
      Print(LINE_NUMBER,textRestoring,sparam);
      //---
      flag_antivandal=-1;
      flag_reset=quant_obj_all-1;
      //---
      ObDeletePrefixFlag(flag_reset,prefix_obj,chart_ID,sub_window,start_pos);
      //---
      ChartRedraw();
      //---
      ObDelCreateTimeExcept0(time_create,chart_ID,sub_window,-1);
      //---
      PanelCreate();
      //---
      ChartRedraw();
      //---
      flag_antivandal=0;
      //---
      return(res);
     }
   return(res);
  }

Um código completo do indicador está anexo com o nome: id_name_object.


6. Conclusão

Perceba que os esquemas mais fáceis e suas variações, com base no sistema de flags mencionados neste artigo, podem ser adicionados ao código pronto em relação a um ou muitos objetos, se houver tal necessidade ou possibilidade. Não há nenhuma necessidade de fazer alterações consideráveis nas partes do código que são responsáveis pela operação principal do programa.

Se necessário, outras ações relevantes podem ser providenciadas no código para casos como:

Por exemplo, você pode implementar algumas verificações adicionais durante as ações "de rotina" no painel, bem como controles de revisão seletivas ou totais sobre os dados importantes exibidos no painel do timer, se for justificável em relação ao código.

No Expert Advisor, por exemplo, você pode organizar uma animação durante alteração ou remoção de objetos gráficos, com ou sem exibição de mensagens não autorizadas, se não for causar uma interferência significativa com o desempenho das atividades de negociação.

Exemplificando, ao exibir mensagens você pode proporcionar a possibilidade da seleção, pressionando o respectivo botão: excluindo o programa do gráfico ou fazendo uma tentativa para restaurar os objetos danificados. Você também pode colocar um contador para exibir um aviso nas situações de remoção não autorizada ou na alteração dos objetos: "Esta é a "Xª" interferência não autorizada nos objetos do programa. Após o "X°" caso, o programa será excluido do gráfico. Por favor, procure o possível motivo desta situação".

Baseado nas grandes oportunidades da linguagem de programação MQL5 e, certamente, com sua mente inquisitiva e cheia de conhecimentos, você poderá criar várias opções, incluindo as que não foram abordadas neste artigo, se necessário.