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 для открытия файла ссылки, чтения оттуда всех данных и т.д. Но с задачей удаления слоев, отсутствующих во внешней ссылке, прекрасно справится и этот код.



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


Я не робот.