Работа с неактивным документом

Подавляющее большинство лисп-функций, показываемых на форумах и сайтах, работают с текущим документом. Как правило, этого достаточно. Но что делать, если надо обрабатывать несколько документов? Здесь я хотел бы рассмотреть некоторые вопросы, связанные именно с обработкой неактивного документа.

В текущем (активном) документе программисту доступны все возможности lisp'a - это и командные методы, и интерактивное формирование наборов примитивов, и ename-методы, и activex. Но стоит только заикнуться об обработке неактивного документа, как перечень возможных средств моментально сужается до состояния "ActiveX и практически больше ничего".

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

Первое желание - написать scr-файл (возможно, программно) по алгоритму: открыть файл, загрузить лисп, выполнить лисп, сохранить файл, открыть следующий и т.д.; и запустить его на выполнение. Чем плох подобный подход? Ну, хотя бы тем, что обрабатываемые файлы могут быть очень тяжелыми, времени на их открытие и регенерацию может уходить немеряно. Внутри файлов могут встретиться, например, прокси-объекты. Или будет запрос на поиск shx-файла (о том, как от этого запроса избавиться, поговорим позже). Или еще чего-то... Короче, придется сидеть рядом с компьютером и ждать, пока он не закончит выполнение скрипта. Не, это скучно.

Второе движение - выполнить программное открытие нужного dwg-файла, обработать его и закрыть. Все хорошо, да только вот проблема - такой подход практически никогда не работает. Открытие файла (хоть через команду _.open с установленной filedia = 0, хоть через vla-open) почти гарантированно приводит к активации только что открытого файла. Почему "почти"? Потому, что в некоторых условиях такого не происходит. Насколько я помню, этого не происходило в AutoCAD 2005 (чистом AutoCAD, а не входящем в состав, например, ADT). Но требуется-то разработать функцию, которая будет гарантированно корректно работать в любых условиях! Поэтому такой вариант тоже не годится.

Третий вариант - залезьть достаточно глубоко в дебри AutoCAD и открывать файл dwg без активизации. На нем я и хотел бы остановиться.

Скажу честно - идея не совсем моя, я только дорабатывал ее. Лично я считаю авторами и реализаторами идеи двух человек - Н.Н.Полещука и Fatty (он же fixo, он же Олег jr.). Суть метода состоит в том, чтобы обратиться к интерфейсу IAxDbDocument (или ObjectDBX) и с его помощью уже и обрабатывать dwg.

ObjectDBX появился не так уж и давно, и его регистрация в начале его существования отличалась от той, что используется в версиях последнего времени.

Поскольку вызов ObjectDBX напрямую зависит от версии AutoCAD, сначала определим версию:

1
2
3
4
5
6
7
(defun _lispru-acad-version ()
;|
*    Возвращает номер сборки AutoCAD'a. Для 2005 вернет 16.1, для 2006 - 16.2
* и т.д.
|;

(atof (getvar "acadver"))
) ;_ end of defun

После получения версии запущенного AutoCAD можно уже и пытаться вызвать ObjectDBX:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
(defun _lispru-odbx (/)
                    ;|
*    функция возвращает интерфейс IAxDbDocument (для работы с файлами DWG без
* их открытия). Если интерфейс не поддерживается, возвращает nil. Проверено
* на ACAD 2002, 2004, 2005, 2006, 2007, 2008, 2010
*    Автор - Fatty aka Олег jr. Моего только адаптация под общую систему и
* переименование
*    Параметры вызова:
* нет
*    Примеры вызова:
(_lispru-odbx)
|;

  (cond
    ((< (_lispru-acad-version) 15.06)
     (alert
       "ObjectDBX method not applicable\nin this AutoCAD version"
       ) ;_ end of KPBLC-MSG-ALERT
     nil
     )
    ((= (fix (_lispru-acad-version)) 15)
     (if (not (vl-registry-read
                "HKEY_CLASSES_ROOT\\ObjectDBX.AxDbDocument\\CLSID"
                ) ;_ end of vl-registry-read
              ) ;_ end of not
       (startapp "regsvr32.exe"
                 (strcat "/s "" (findfile "axdb15.dll") """)
                 ) ;_ end of startapp
       ) ;_ end of if
     (vla-getinterfaceobject
       (vlax-get-acad-object)
       "ObjectDBX.AxDbDocument"
       ) ;_ end of vla-getinterfaceobject
     )
    (t
     (vla-getinterfaceobject
       (vlax-get-acad-object)
       (strcat "ObjectDBX.AxDbDocument." (itoa (fix (_lispru-acad-version))))
       ) ;_ end of vla-getinterfaceobject
     )
    ) ;_ end of cond
  ) ;_ end of defun

Ну хорошо, сам интерфейс получен. А дальше-то что? А дальше вот что:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
_$ (setq odbx (_lispru-odbx))
#<VLA-OBJECT IAxDbDocument 000000003bcdea60>
_$ (vlax-dump-Object odbx t)
; IAxDbDocument: IAxDbDocument Interface
; Property values:
;   Application (RO) = Ошибка
;   Blocks (RO) = #<VLA-OBJECT IAcadBlocks 000000003bd3d6e8>
;   Database (RO) = #<VLA-OBJECT IAcadDatabase 000000003bb04488>
;   Dictionaries (RO) = #<VLA-OBJECT IAcadDictionaries 000000003bd3d928>
;   DimStyles (RO) = #<VLA-OBJECT IAcadDimStyles 000000003bd3d9b8>
;   ElevationModelSpace = 0.0
;   ElevationPaperSpace = 0.0
;   FileDependencies (RO) = #<VLA-OBJECT IAcadFileDependencies 000000003bdf6878>
;   Groups (RO) = #<VLA-OBJECT IAcadGroups 000000003bd3da48>
;   Layers (RO) = #<VLA-OBJECT IAcadLayers 000000003bd3dad8>
;   Layouts (RO) = #<VLA-OBJECT IAcadLayouts 000000003bd3db68>
;   Limits = (0.0 0.0 12.0 9.0)
;   Linetypes (RO) = #<VLA-OBJECT IAcadLineTypes 000000003bd3dbf8>
;   Materials (RO) = #<VLA-OBJECT IAcadMaterials 000000003bd3dc88>
;   ModelSpace (RO) = #<VLA-OBJECT IAcadModelSpace 000000003bb13408>
;   Name = ""
;   PaperSpace (RO) = #<VLA-OBJECT IAcadPaperSpace 000000003bb134a8>
;   PlotConfigurations (RO) = #<VLA-OBJECT IAcadPlotConfigurations 000000003bd3dd18>
;   Preferences (RO) = #<VLA-OBJECT IAcadDatabasePreferences 000000003bdf6838>
;   RegisteredApplications (RO) = #<VLA-OBJECT IAcadRegisteredApplications 000000003bd3dda8>
;   SectionManager (RO) = Ошибка
;   SummaryInfo (RO) = #<VLA-OBJECT IAcadSummaryInfo 000000003bce2f18>
;   TextStyles (RO) = #<VLA-OBJECT IAcadTextStyles 000000003bd3de38>
;   UserCoordinateSystems (RO) = #<VLA-OBJECT IAcadUCSs 000000003bd3dec8>
;   Viewports (RO) = #<VLA-OBJECT IAcadViewports 000000003bd3df58>
;   Views (RO) = #<VLA-OBJECT IAcadViews 000000003bd3dfe8>
; Methods supported:
;   CopyObjects (3)
;   DxfIn (2)
;   DxfOut (3)
;   HandleToObject (1)
;   ObjectIdToObject (1)
;   ObjectIdToObject32 (1)
;   Open (2)
;   Save ()
;   SaveAs (2)
T
_$

На данный момент наиболее интересны методы Open и SaveAs (а не Save, как это ни парадоксально). Пришло время поговорить о том, как работает ObjectDBX, какие у него ограничения и как их обходить.

Вопрос с открытием файла можно даже не поднимать:

1
(vla-open odbx filename)

А вот с обработкой и сохранением уже не все так очевидно.

  1. Не допускается применение интерактивных и командных методов. Только vla и ename (да и то, последнее только в случае крайней необходимости).
  2. ObjectDBX при открытии файла блокирует его. Доступ в файлу возможен только при условии монопольного к нему доступа. Если кто-то этот файл уже открыл (например, сосед), то ObjectDBX этот файл не то что не обработает, а даже не откроет.
  3. Несмотря на заявленную поддержку метода Save, этот метод не работает, приходится использовать SaveAs.
  4. Сохранение файла возможно только в текущей версии, без вариантов.
  5. Файл можно закрыть и без сохранения изменений. Для этого достаточно выполнить (vlax-release-object) к указателю на интерфейс.
  6. Для файла, открытого через ObjectDBX, невозможно добраться до состава внешних ссылок. С внешними ссылками внутри ObjectDBX вообще надо быть очень осторожными, постоянно проверяя тип ссылки - вставленная она или наложенная (бывают случаи, когда перечисляются все, независимо от уровня вложенности).
  7. С получением путей вставленных растров тоже могут быть проблемы.
  8. В файле, открытом через ObjectDBX, невозможно выполнить проверку целостности: vla-auditinfo работает только в текущем документе.

Несмотря на эти ограничения (кстати, возможно, я перечислил не все!), механизм достаточно удобен, особенно при аккуратном с ним обращении: можно получить список описаний блоков стороннего документа, а потом через CopyObjects скопировать их в текущий. Или наоборот. Можно привести сотни файлов в стандартный вид, не особо беспокоясь о потерях времени: при открытии файла до 95% времени тратится на его регенерацию. Можно... В общем, сделать можно много.

Осталось только одно: запомнить, что сохраняется файл только в текущей версии и только через метод SaveAs:

1
(vla-saveas odbx FileName)

Естественно, что, имея один указатель на ObjectDBX, можно открывать далеко не один и не два файла (хотя лично я такого не делаю. Я предпочту мириться с потерями памяти, но не с потерями работы: в версии 2005 и 2006, по-моему, использование одного указателя иногда приводило к странным зависаниям. То есть сейчас ситуация у меня такова: создал указатель - открыл файл - обработал файл - сохранил файл - "отрелизил" указатель - обнулил указатель и все сначала). В конце всеобщей обработки надо не забыть выполнить vlax-release-object, ну и, для гарантии, обнулить переменную:

1
(setq odbx nil)

Примеры использования, я думаю, будут. Но позже.

Ссылки для скачивания:
_lispru-acad-version.lsp
_lispru-odbx.lsp

Здесь описаны некоторые обнаруженные ограничения ObjectDBX.

Размещено в Код LISP, Новости, Функции LISP · Метки: ,



Комментарии

Есть 18 коммент. к “Работа с неактивным документом”
  1. TararykovDG пишет:

    Здравствуйте Алексей, подскажите пожалуйста, а возможно ли используя интерфейс IAxDbDocument (или ObjectDBX) при работе с неактивным документом, создать в этом документе какой-либо примитив. Если возможно, то подскажите как. У меня лично не получилось.

    (vla-AddCircle odbx (vlax-3D-point ‘(0 0 0)) 100)
    ; Ошибка: ActiveX Server возвратил ошибку: неизвестное имя: AddCircle

    Если посмотреть приведенный вами список доступных методов и свойств, полученных следующим образом:
    (vlax-dump-Object odbx t),
    То там есть всего лишь 8 доступных методов, и ни один из них не позволяет создать примитив в неактивном документе.

  2. Кулик Алексей aka kpblc пишет:

    Все верно. Сравните:
    (vla-AddCircle odbx (vlax-3D-point '(0 0 0)) 100)
    и
    (vla-AddCircle (vla-get-ModelSpace odbx) (vlax-3D-point '(0 0 0)) 100)
    Примитив не может быть создан в документе. Создаваемый примитив располагается в каком-то пространстве - модели, листа, блока...

  3. Кулик Алексей aka kpblc пишет:

    Кстати о примерах. Вот, не далее как сегодня нарисовалось: https://docs.google.com/Doc?docid=0AXNsAeY1XnTrZGY3cGJtcTlfNjNodG12ZHRkOQ&hl=ru

  4. Кулик Алексей aka kpblc пишет:

    Сегодня пришлось еще несколько времени потратить на использование ObjectDBX :)
    Несложно выяснить, что у ObjectDBX есть два интересных метода - DxfIn и DxfOut. С первым пока не воевал, а вот второй оказался, как бы сказать... В общем, это далеко не то же, что и SaveAs для активного документа :(
    DxfOut действительно создает dxf-файл, но вот формат этого dxf не выбрать и не назначить. Сохраняется только в "нативной" версии. Так что для нестандартных ситуаций, похоже, придется изобретать нестандартные решения...

  5. Do$ пишет:

    Алексей, большое спасибо! Наконец и мне пригодилось!

  6. Кулик Алексей aka kpblc пишет:

    Да не за что :) Я тут тоже с такой задачкой столкнулся - мама не горюй. Если появится гарантированно работающее решение, поделюсь ;)

  7. Кулик Алексей aka kpblc пишет:

    Хотелось бы сделать небольшое дополнение.
    Касается это возможности "открытия через ObjectDBX заблокированного файла". На данный момент я через vl-file-systime проверяю доступность файла на открытие (функция возвращает nil, если файл кем-то открыт). Если файл недоступен, то я его копирую в %temp% и открываю уже оттуда.
    Не очень изящно, зато работает. Пока, по крайней мере...

  8. Yuriy пишет:

    Приветствую, Алексей.
    Не подскажешь как правильно вызвать vla-ZoomExtents после создания объекта в неактивном документе

    (vla-ZoomExtents (vlax-get-acad-object)) - применился только в текущем открытом файле
    (vla-zoomextents (vla-get-application (vla-get-activedocument(vlax-get-acad-object)))) - применился только в текущем открытом файле

    (vla-ZoomExtents (vla-get-ModelSpace odbx)) ; = ActiveX Server returned the error: unknown name: ZoomExtents[cc lang="lisp"]

  9. Кулик Алексей aka kpblc пишет:

    По-моему, в неактивном документе это не сделать. Точно так же, как и не выполнить в неактивном документе vla-purgeall.
    А зачем зуммировать неактивный документ?

  10. Yuriy пишет:

    Проектировщики захотели автоматически обработывать dwg файлы, полученные из Ревита, для передачи смежникам, работающих в Автокаде.
    1.перекрасить все слои в нейтрально серый цвет
    2.переключиться в лист (layout)
    3.отключить все слои для каждого экрана (vplayer/freeze)
    4.вписать в чертеж в границы экрана (почему то приходя из Ревита чертеж в границы экрана не вписан)

    Lee Mac тоже пишет "... ZoomExtents method will only operate on the active drawing ..."
    https://www.theswamp.org/index.php?topic=47698.msg527066#msg527066 Reply #1

    похоже решать при помощи скрипта как в Reply #4

  11. Кулик Алексей aka kpblc пишет:

    >> Перекрасить все слои...
    Не проблема
    >> переключиться в лист (layout)
    Переключиться или активировать, чтобы он при открытии был на экране? И если переключиться, то на фига? Через vla-get-block и так можно добраться до описания блока листа и работать с ним как с обычным блоком
    >> отключить все слои для каждого экрана (vplayer/freeze)
    Не уверен, что подобное прокатит, но тем не менее: пройтись по всем примитивам описания блока листа, найти ВЭ, получить на них указатели и дальше с ними химичить.
    >> вписать в чертеж в границы экрана (почему то приходя из Ревита чертеж в границы экрана не вписан)
    В чертеж или в ВЭ? Разница немного принципиальная. Если второе (и ВЭ уже существует) - то надо смотреть его масштаб, менять координаты точки, на которую он указывает и т.д.
    Поскольку передо мной подобных задач никогда не ставилось, готовых решений у меня нет :(

  12. Yuriy пишет:

    Алексей, спасибо за ответы!
    С перекраской слоев я уже справился
    С темой работы с неактивным документом столкнулся впервые.
    Мне показалось, что это более правильный подход (чем через скрипты) если есть необходимость обработки множества чертежей.
    Постановку задачи буду еще уточнять...

  13. Евгения пишет:

    Алексей, вот хотелось бы уточнить, можно ли в неактивном документе использовать vl-cmdf? И если можно, то какая структура написания команды будет? Если я правильно понимаю, то (vl-cmdf "_НазваниеКоманды") - такая структура вызова команды будет работать только в текущем чертеже?

  14. Кулик Алексей aka kpblc пишет:

    Все правильно понимаете, vl-cmdf будет работать только в активном документе. Не думаю, что сработает даже конструкция типа vla-SendCommand

  15. Евгения пишет:

    На всякий случай проверила vla-SendCommand - не работает.

  16. Евгений пишет:

    Добрый день.
    Нужно получить доступ не к файлу .dwg, а к файлу .dxf.
    Попытки сделать что-то вроде
    (vla-open ObjectDBX "...\\123.dxf")
    выдают ошибку.
    Хотел попробовать метод
    DxfIn (2)
    Но LISP не понимает синтаксис (vla-DxfIn ...
    Информации о вызове данного метода в Autodesk AutoCAD 2015 : ActiveX Reference Guide я не нашёл.
    На других сайтах нашёл синтаксис только для C++.
    В общем - я в тупике. Как достать и обработать информацию из .dxf ?

  17. Кулик Алексей aka kpblc пишет:

    Евгений, ну если не работает метод "в лоб", пойдем в обход:

    1
    2
    3
    4
    5
    (setq odbx (_kpblc-odbx))
    (vlax-invoke-method odbx 'dxfin "d:\\drawing1.dxf")
    (vlax-dump-object (vla-get-modelspace odbx))
    ;;; ...
    (_kpblc-odbx-close odbx)

Трэкбэки

Узнайте, что другие говорят про эту заметку...
  1. [...] его? Частично этот вопрос я поднимал в статье Работа с неактивным документом, но там был вопрос именно с чтением уже существующего [...]



Поделитесь своим мнением


Я не робот.