Тема: Создание слоя в реакторе и помещение в него объектов

По порядку. Задача: при вставке блока в чертеж необходимо поместить его атрибуты, в зависимомости от их названия, в разные слои.
Попутно возникает вопрос: как засечь вставку блока? Я делал это в DatabaseReactor'e, в методе objectAppended, приводя пришедший в параметрах объект к AcDbSequenceEnd. Считая, что к этому моменту блок целиком вставлен, я начинаю его обработку. Правильно?
Теперь собственно проблема. Если нужный слой уже существует, то атрибут помещается в него без проблем. А вот если сначала создать слой, а затем поместить в него атрибут, возникает access violation где-то в автокаде, после выхода из objectAppended.
Очень хотелось бы услышать ваши соображения. Спасибо.

Re: Создание слоя в реакторе и помещение в него объектов

Попробуй перед внесением изменений в документ вызвать

acDocManager->lockDocument(m_pDoc, AcAp::kWrite);

После внесения изменений не забудь:

acDocManager->unlockDocument(m_pDoc);

Re: Создание слоя в реакторе и помещение в него объектов

Перед изменениями делаю

AcAxDocLock docLock( acdbHostApplicationServices()->workingDatabase(), AcAxDocLock::kNormal);

Вроде это тоже самое. Не помогает :(

Re: Создание слоя в реакторе и помещение в него объектов

> Ведмедь
Вроде работает, хотя правильнее было бы менять слой атрибутов в commandEnded(), а не в objectAppended()

// -----------------------------------------------------------------------------
void DbReactor::objectAppended(const AcDbDatabase * dwg, const AcDbObject * dbObj)
{
//-----------------------------------------------------------------
//   AcDbObjectIdArray aAttIds; - Массив id's для аттрибутов блока
//   Его нужно вставить в описание класса
//-----------------------------------------------------------------
  AcDbDatabaseReactor::objectAppended (dwg, dbObj) ;
  Acad::ErrorStatus es;
  const char *layerName = "MY_LAYER"; // Имя слоя для атрибутов
  AcDbBlockReference *pBref   = AcDbBlockReference::cast(dbObj);
  AcDbAttribute      *pAttrib = AcDbAttribute::cast(dbObj);
  AcDbSequenceEnd    *pBend   = AcDbSequenceEnd::cast(dbObj);
  if (pBref) {
    aAttIds.setLogicalLength(0); // Очищаем список ID's атрибутов блока
  } else if (pAttrib) {
    aAttIds.append(pAttrib->objectId()); // Добавляем ID атрибута в массив
  } else if (pBend) {
    AcDbLayerTableRecord *pLayer=NULL;
    AcDbObjectId layerId;
    AcDbLayerTablePointer lt(const_cast<AcDbDatabase *>(dwg)->layerTableId(),AcDb::kForRead);
    if (lt.openStatus() == Acad::eOk) {
      if (lt->getAt(layerName,layerId) != Acad::eOk) {
        pLayer = new AcDbLayerTableRecord();
        pLayer->setName(layerName);
        if ((es=lt->upgradeOpen()) == Acad::eOk) {
          lt->add(layerId,pLayer);
          pLayer->close();
        } else {
          acutPrintf("\nlt->upgradeOpen()=%s",acadErrorStatusText(es)) ;
          delete pLayer;
          return;
        }
      }
    }
    for (int i=0; i < aAttIds.length(); i++) {
      AcDbObjectPointer<AcDbAttribute> pAtt(aAttIds[i],AcDb::kForWrite);
      if ((es = pAtt.openStatus())==Acad::eOk) {
        if ((es = pAtt->setLayer(layerId)) != Acad::eOk) {
          acutPrintf("\npAtt->setLayer(layerId)=%s",acadErrorStatusText(es)) ;
        }
      } else {
        acutPrintf("\npAtt.openStatus()=%s",acadErrorStatusText(es)) ;
      }
    }
  }
}

Re: Создание слоя в реакторе и помещение в него объектов

> Александр Ривилис
Спасибо за пример. Я уж было обрадовался, что действительно работает, но оказалось - чудес не бывает, работает не для всех блоков :( Если интересно, могу выслать неработающий блок.
Попутно к вам появился вопрос. Я, например, использовал AcDbBlockReference::attributeIterator, чтобы перебрать атрибуты блока. А вы - массив. Для этого есть какие-то причины или просто сила привычки?

Re: Создание слоя в реакторе и помещение в него объектов

> Ведмедь
Причина очень простая - в этом месте состояние AutoCAD'а такое, что использовать attributeIterator не следует - база в неустойчивом состоянии. Если обратили внимание для добавленного слоя objectAppended приходит уже после objectAppended для AcDbSequenceEnd. Поэтому я и предложил изменение слоя вынести в другой реактор, например в commandEnded(). Это будет значительно безопаснее. Кроме того если имя слоя фиксировано, то почему бы не добавить его в таблицу слоев заранее?

Re: Создание слоя в реакторе и помещение в него объектов

> Ведмедь
Не работает - это разваливает AutoCAD или не меняет слой атрибутам? Кроме того если есть блок в блоке, то (не проверял) в objectAppended будет не одна AcDbSequenceEnd. Так что логика усложняется.

Re: Создание слоя в реакторе и помещение в него объектов

Разваливает AutoCAD - AV после выхода из objectAppended. Блоков в блоке нет.
Имя слоя фиксировано, и можно, конечно, создать слои заранее. Но тогда нет гарантии, что слой не окажется удален в момент вставки блока.
В любом случае, спасибо за участие. Я уже, в общем-то, почти смирился с мыслью перенести это в commandEnded :\

Re: Создание слоя в реакторе и помещение в него объектов

> Ведмедь
Пришли блок. Будет время - посмотрю.

Re: Создание слоя в реакторе и помещение в него объектов

> Ведмедь
А вставить проверку и создание слоя в commandStart() слабо? :)

Re: Создание слоя в реакторе и помещение в него объектов

Ххы. Не слабо. Только проверку чего? Как узнать будет в ходе команды вставлен блок или нет? Ведь не гарантируется, что блоки будут вставляться только insert'ом. Тогда уж действительно лучше в objectAppended запоминать вставленные блоки, а в commandEnded обрабатывать.

Re: Создание слоя в реакторе и помещение в него объектов

> Ведмедь
А какая разница? Просто в любом случае (для любой команды) проверяешь в commandStarted()есть ли слой и если его нет - создаешь. Думаю, что потери производительности не будет, зато слой всегда на месте. Кроме того можно еще такую же проверку вставить в lispStarted(), ну и т.д. А вообще - тебе решать.