Как правильно удалять объекты на графике

 

Господа, скажите, как вы удаляете объекты индикатора. При этом так, чтобы объекты созданные другими индикаторами или те что были вручную созданы оставались?


Я это делаю так, как посоветовали здесь https://forum.mql4.com/ru/12206:


void DeleteObjects()

{


int stop;

string name;

for(int n=ObjectsTotal()-1;n>=0;n--)

{

// получаем имя объекта

name = ObjectName(n);

// удаляем только те, имя которых начинается с префикса в переменной GOGP

if (StringFind(name, GOGP, 0)==0) ObjectDelete(name);

stop++;

if (stop > 100000) break;

}

Comment("stop=" + stop);

}


Однако, есть проблема, которая заключается в том, что фактически эта функция прерывается по таймауту, если есть объекты не принадлежащие индикатору по признаку префикса в имени объектов. Как все-таки сделать красиво, но чтобы не считать созданные объекты??



Сразу приведу мой начальный вариант, который более громоздкий, но все-таки работает (состоит из двух процедур):

// удаление объектов на графике, которые принадлежат данному индикатору.

void DeleteObjectsN()

{

// Для перехвата последующих ошибок удаления обнуляем предыдущие ошибки которые могли быть.

int err = GetLastError();

// Максимальное число попыток удаления объектов. Обычно делает до 20.

int MaxTryCount = 1000;

// Делаем цикличное удаление до полного отсутствия ошибок.

for (int i=0;i<MaxTryCount;i++)

{

DeleteObjects2();

err = GetLastError();

if (err == 0) break;

}

// очищаем коммент

//Comment("");

return(0);

}

// это удаляет объекты, но нужно делать много раз, 20 например...

// происходит какой-то глюк, хотя при следующем подходе все нормально удаляется.

void DeleteObjects2()

{

int obj_total=ObjectsTotal();

string name;

for(int n=0;n<obj_total;n++)

{

// получаем имя объекта

name = ObjectName(n); // здесь происходит ошибка 4202 ERR_OBJECT_DOES_NOT_EXIST,

// которая ведет за собой ошибку удаления

// удаляем только те, имя которых начинается с префикса в переменной GOGP

// и чтобы имя не пустое было, а то это влечет за собой следующую ошибку 4202 или 4204

if (StringFind(name, GOGP, 0)==0 && name!="") ObjectDelete(name);

}

}


 

Уберите

stop++;

if (stop > 100000) break; 
и все будет хорошо.
 

Я обычно пишу такой цикл в deinit:

for(i = ObjectsTotal() - 1; i >= 0; i--)
   {
   if (StringSubstr("полное имя найденного графического объекта", 0, "количество символов сначала строки") != "те самые символы, которые мы выбирали из строки имени объекта")
      continue;
      ObjectDelete("полное имя найденного графического объекта");
   }
При этом в индикаторе стараюсь создавать объекты с уникальными именами, чтобы при удалении объектов построенных индикатором не снести что-нибудь лишнее.


Проблем с таким кодом пока не возникало.

 

я просто делаю, почти также как и в вашем решении..

к имени каждого объекта добавляется имя кода (советника, индикатора)

string name = "bla-bla" + "_" + WindowExpertName();


и по вхождению этого имени объекты удаляются..

objectsDelete(WindowExpertName());


с помощью аналогичной (первый ваш кусок кода) функции ..

 

У меня когда-то возникла проблема присоединения нескольких копий одного индикатора с графикой, но с разн. параметрами, к одному инструменту.

Проблема заключалась как разделить названия объектов, как определить подокно, в ктр. менять объекты в работе или при изменении параметров. Короче, чтобы они не мешали друг дружке сосуществовать.

Понятно, что это крайняя ситуация, но ее решение уж точно позволит не заморачиваться с менее сложными, когда индикаторы разные (обычный случай).

Сделал маленькую библиотечку.

Общие переменные такие:

string NAME[]; // массив имен графических объектов
int NameCount; // счетчик графических объектов
string ShortName=""; // имя инидкатора для вывода в подокно
string _ID; // идентификатор (номер) копии индикатора
string _GV="Objects"; // имя гл.терм.переменной

Сначала, при инициализации индикатора нужно получить через гл.терм.переменную его уникальный ID. Функция GetID() в библиотеке. _ID=GetID() в init().

// генерарация идентификатора индикатора
string GetID() {
   int ID=GlobalVariableGet(_GV); // получить существующий идентификатор индикатора
   ID++; // новый номер
   GlobalVariableSet(_GV,ID); // новый номер в гл.пер; (если гл.пер. нет, то создается) 
   
   return(DoubleToStr(ID,0)); // возврат идентификатора индикатора
  }

Далее, имя объекта создается как ID + [необ.имя] + порядковый номер объекта. Ф-я Name(). Т.е. string ObjName=Name() всегда будет уникальным. Имена ф-я сохраняет в массив string NAME[].

string Name(string obj="") {
   NameCount++; // счетчик граф. объектов
   string _Name=_ID+": "+obj+NameCount; // имя объекта = ID индикатора + имя объекта + № гр.объекта
   ArrayResize(NAME,NameCount); // увеличение массива граф. объектов на 1
   NAME[NameCount-1]=_Name; // занесение имени объекта в массив

   return(_Name); // возврат уникального имени объекта
  }

При создании объекта нужно знать, в каком подокне это делать. Для этого существует ф-я GetWinNumber(). Неважно сколько одинаковых или разных индикаторов, использующих графику, прикреплено - нужный номер найдется без ошибок, т.к. ф-я сначала подставляет в короткое имя ID индикатора, потом узнает по этому имени номер подокна, а потом восстанавливает оригинальное короткое имя.

// выяснение текущего номера подокна индикатора
int GetWinNumber()
  {
   IndicatorShortName(_ID); // подмена: имя индикатора = идентификатору (номеру) копии индикатора
   int win=WindowFind(_ID); // считан номер подокна с именем идентификатора (номера) копии индикатора
   IndicatorShortName(ShortName); // восстановлено имя индикатора

   return(win); // возврат номера подокна
  }

Для удаления созданных индикатором объектов используется ф-я DelObjects(). Используется в deinit() и при пересчете (когда IndicatorCounted() сбрасывается в 0), когда произошла подкачка баров - чтоб графика не сбилась.

// уничтожение объектов
void DelObjects() {
   for(int i=0; i<NameCount; i++) ObjectDelete(NAME[i]); // Стереть созданные индикатором объекты
   ArrayResize(NAME,1); NameCount=0; // сброс массива и счетчика
  }

При удалении индикатора с графика нужно освободить его ID в гл.терм.переменной. Для это есть ф-я DeinitObjects(). Вызывается из deinit().

// осовобождение идентификатора
void DeinitObjects() {
   int ID=GlobalVariableGet(_GV); // получить существующий идентификатор (номер) копии индикатора
   ID--; // уменьшить номер
   if(ID==0) {GlobalVariableDel(_GV); return;} // если индикаторов,исп.объекты больше нет,удалить гл.пер.
   GlobalVariableSet(_GV,ID); // новый номер в гл.пер.
  }
В аттаче эта библиотечка и пример ее использования. В примере графически рисуется МАшка с пиковыми значениями по High/Low. Можете сколько угодно сделать подокон с этим инидкатором, потом индивидуально для каждого менять параметры, уничтожать окна - мешать они друг другу не будут.
Файлы:
 
А... забыл примерчик.
Файлы:
 
string Сессия = "";

...

int init()
{
   Сессия = MathRand();

...


int deinit()
{
   Clear();

...


string objname = "Абырвалг " + Сессия;

...


void Clear()
{
   int obj_total = ObjectsTotal();
   for(int obj = obj_total - 1; obj >= 0; obj--)
   {  
      string objname = ObjectName(obj);
      if (StringFind(objname, "Абырвалг " + Сессия) >= 0)
         ObjectDelete(objname);
   }
}
Если я правильно тебя понял. Понималкка отключалась... С прраздником всех... Уфх, отметился...
 
Svinozavr >>:

У меня когда-то возникла проблема присоединения нескольких копий одного индикатора с графикой, но с разн. параметрами, к одному инструменту.

Проблема заключалась как разделить названия объектов, как определить подокно, в ктр. менять объекты в работе или при изменении параметров. Короче, чтобы они не мешали друг дружке сосуществовать.

Понятно, что это крайняя ситуация, но ее решение уж точно позволит не заморачиваться с менее сложными, когда индикаторы разные (обычный случай).

Сделал маленькую библиотечку.

Общие переменные такие:

Сначала, при инициализации индикатора нужно получить через гл.терм.переменную его уникальный ID. Функция GetID() в библиотеке. _ID=GetID() в init().

Далее, имя объекта создается как ID + [необ.имя] + порядковый номер объекта. Ф-я Name(). Т.е. string ObjName=Name() всегда будет уникальным. Имена ф-я сохраняет в массив string NAME[].

При создании объекта нужно знать, в каком подокне это делать. Для этого существует ф-я GetWinNumber(). Неважно сколько одинаковых или разных индикаторов, использующих графику, прикреплено - нужный номер найдется без ошибок, т.к. ф-я сначала подставляет в короткое имя ID индикатора, потом узнает по этому имени номер подокна, а потом восстанавливает оригинальное короткое имя.

Для удаления созданных индикатором объектов используется ф-я DelObjects(). Используется в deinit() и при пересчете (когда IndicatorCounted() сбрасывается в 0), когда произошла подкачка баров - чтоб графика не сбилась.

При удалении индикатора с графика нужно освободить его ID в гл.терм.переменной. Для это есть ф-я DeinitObjects(). Вызывается из deinit().

В аттаче эта библиотечка и пример ее использования. В примере графически рисуется МАшка с пиковыми значениями по High/Low. Можете сколько угодно сделать подокон с этим инидкатором, потом индивидуально для каждого менять параметры, уничтожать окна - мешать они друг другу не будут.

Суппер. Ну свинтозавр ты мегамонстр :) Учел всё. Думаю твое решение исчерпывающее. Спасибо.

С новым годом всех :)

 
Икк... Конкурс зрительских симпатий выиграл пандеромоторолет Свинозавра... Новоиспеченный пандеромоторолет`чик готов к испытаниям... Ик...
 

Всех с НГ.

Все-таки конфликт имен возможен. Чтобы он гарантированного не возникал, просто не используйте ф-ю DeinitObjects(). Тогда, правда, гл.терм.переменная будет увеличиваться каждый раз при присоединении нового индикатора с графикой, но не будет уменьшаться при его удалении. Но это - фигня. Вряд ли кому-нить удастся достигнуть предела по double (гл.терм переменные имеют тип double) - это до 1.7 * e308.)))

 
Я плакалъ. Просто возьмите мой код и перестаньте создавать себе проблемы на пустом месте.
Причина обращения: