VISRETAIN – решение обновления слоев внешней ссылки.
В предыдущей части я рассказал о том, как ведут себя внешние ссылки (точнее, их слои) при visretain=1. Попробуем решить вопрос "неудаленных" слоев.
Прежде чем писать код, немного теории
Про реакторы я уже немного рассказывал здесь. Пришло время расширить собственные знания
Итак, среди всяких-разных реакторов есть отдельный тип реактора: реактор внешних ссылок (vlr-xref-reactor). Как обычно, в нем приличное количество событий, на которые он сможет среагировать. Перечень приводить не стану - он весьма длинный (подробнее см. в книге Н.Н.Полещука и П.В.Лоскутова "AutoLISP И VisualLISP в среде AutoCAD" (BHV, 2006). Кроме всех прочих, есть событие :vlr-xrefSubcommandReloadItem - событие, срабатывающее при обновлении ссылки. Отлично, в нем и пропишем нашу обработку!
Сначала нарисуем загрузчик реактора:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | (if *vlr-xref* (progn (setq *vlr-xref* nil) (vlr-remove-all :vlr-xref-reactor) ) ;_ end of progn ) ;_ end of if (if (not *vlr-xref*) (setq *vlr-xref* (vlr-xref-reactor "kpblc-xref-reactor" '((:vlr-xrefsubcommandreloaditem . _kpblc-vlr-xref-reloaditem) ) ) ;_ end of vlr-command-reactor ) ;_ end of setq ) ;_ end of if |
Теперь собственно прописываем функцию реакции:
1 2 3 4 5 | (defun _kpblc-vlr-xref-reloaditem (react datas) ;| * Функция реакции на факт перезагрузки внешней ссылки |; ) ;_ end of defun |
Первый параметр функции - указатель на вызвавший ее реактор. На данный момент не интересует совершенно. Второй параметр - это список из двух чисел:
Первое число принимать следующие значения:
0 - вызвана подкоманда
2 - ссылка обрабатывается ядром AutoCAD
3 - обработка ссылки ядром AutoCAD закончена
4 - подкоманда завершена
5 - подкоманда будет завершена
6 - подкоманда будет завершена аварийно
Второе число - это ID описания внешней ссылки либо 0.
Казалось бы, теперь становится все достаточно просто: анализируем состояние visretain (если переменная равна 0, то нам делать-то ничего не надо), состояние первого числа datas: если оно 3 (4 проверять смысла особого я не вижу), то выполняем что нам требуется:
1 2 3 4 5 | (defun _kpblc-vlr-xref-reloaditem (react datas) (if (and (= (getvar "visretain") 1) (= (car datas) 3)) ; Начинаем обработку ) ;_ end of if ) ;_ end of defun |
Но вот вопрос - а что за обработку выполнять будем? Можно, конечно, пройтись по всем слоям внешних ссылок и попробовать их посносить. Но можно, как говорится, выпендриться и обрабатывать слои только обновляемой ссылки. Чем и займусь.
Казалось бы, элементарная задача - преобразовать ID объекта в указатель на него! Но AutoCAD от 2009 до 2016 включительно (все версии x64) вываливали ошибку при попытке получения указателя на объект. Поэтому пришлось идти следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | (setq obj (car (vl-remove-if-not (function (lambda (x) (= (vla-get-objectid x) (cadr datas)) ) ;_ end of lambda ) ;_ end of function ((lambda (/ res) (vlax-for blk (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object))) (setq res (cons blk res)) ) ;_ end of vlax-for res ) ;_ end of lambda ) ) ;_ end of vl-remove-if-not ) ;_ end of car ) ;_ end of setq |
Только такой код позволил безошибочно получать указатель на описание внешней ссылки.
Теперь объединяем нашу проверку на visretain, (cad datas) и получение указателя на описание ссылки:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | (defun _kpblc-vlr-xref-reloaditem (react datas / obj) (if (and (= (getvar "visretain") 1) (= (car datas) 3) (setq obj (car (vl-remove-if-not (function (lambda (x) (= (vla-get-objectid x) (cadr datas)) ) ;_ end of lambda ) ;_ end of function ((lambda (/ res) (vlax-for blk (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object))) (setq res (cons blk res)) ) ;_ end of vlax-for res ) ;_ end of lambda ) ) ;_ end of vl-remove-if-not ) ;_ end of car ) ;_ end of setq ) ;_ end of and ; Начинаем обработку ) ;_ end of if ) ;_ end of defun |
Теперь уже внутри "обработки" проходим по слоям, имена которых начинаются с имени obj и содержат вертикальную черту, и пробуем их удалить:
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 | (defun _kpblc-vlr-xref-reloaditem (react datas / obj name) (if (and (= (getvar "visretain") 1) (= (car datas) 3) (setq obj (car (vl-remove-if-not (function (lambda (x) (= (vla-get-objectid x) (cadr datas)) ) ;_ end of lambda ) ;_ end of function ((lambda (/ res) (vlax-for blk (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object))) (setq res (cons blk res)) ) ;_ end of vlax-for res ) ;_ end of lambda ) ) ;_ end of vl-remove-if-not ) ;_ end of car ) ;_ end of setq ) ;_ end of and (progn ; Начинаем обработку (setq name (vla-get-name obj)) (vlax-for item (vla-get-layers (vla-get-activedocument (vlax-get-acad-object))) (vl-catch-all-apply (function (lambda () (vla-delete item) ) ;_ end of lambda ) ;_ end of function ) ;_ end of vl-catch-all-apply ) ;_ end of vlax-for ) ;_ end of progn ) ;_ end of if ) ;_ end of defun |
Естественно, если понадобится что-то более продвинутое (например, надо будет обновлять описания слоев), то потребуется использовать ObjectDBX для открытия файла ссылки, чтения оттуда всех данных и т.д. Но с задачей удаления слоев, отсутствующих во внешней ссылке, прекрасно справится и этот код.