AutoCAD и реакторы

Известно, что в AutoCAD можно запрограммировать строго определенные действия, которые будут выполняться в ответ на какие-то действия CAD'a. Я не говорю об обычных пользовательских функциях и командах. Я говорю, например, о специфических реакциях на смену системных переменных, или клик мышкой, или выполнение штатной команды.

Такие вещи в lisp'е называются реакторами (в .NET, насколько я помню, аналогом становятся события). Каждый реактор знает свое событие и действие, которое он должен выполнять в ответ на это событие. Для создания, активизации или отключения реактора используются функции vlr-*.

Типы реакторов
Обозначение реактора Описание
1
:vlr-acdb-reactor
Реактор базы данных чертежа
1
:vlr-docmanager-reactor
Реактор документа
1
:vlr-linker-reactor
Реактор связи
1
:vlr-object-reactor
Реактор объекта
1
:vlr-editor-reactor
Реактор редактирования. Оставлен только для совместимости с предыдущими версиями, лучше не использовать
:vlr-editor-reactor заменен на несколько реакторов:
Замена :vlr-editor-reactor
Обозначение реактора Описание
1
:vlr-command-reactor
Реактор команды
1
:vlr-deepclone-reactor
Реактор клонирования объектов
1
:vlr-dwg-reactor
Реактор DWG
1
:vlr-dxf-reactor
Реактор DXF
1
:vlr-insert-reactor
Особый реактор на команду _.insert (ВСТАВИТЬ)
1
:vlr-lisp-reactor
Реактор на lisp-события
1
:vlr-miscellaneous-reactor
Другие события редактирования
1
:vlr-mouse-reactor
Реактор на действия мышью
1
:vlr-sysvar-reactor
Реактор на изменение системной переменной
1
:vlr-toolbar-reactor
Реактор изменения размера кнопок панелей инструментов
1
:vlr-undo-reactor
Особый реактор на команду _.undo (ОТМЕНИТЬ)
1
:vlr-wblock-reactor
Особый реактор на команду _.wblock (ПБЛОК)
1
:vlr-window-reactor
Реактор изменения формы и размеров окна AutoCAD
1
:vlr-xref-reactor
Реактор событий вставки и редактирования внешних ссылок

В общем и целом хотелось бы отметить несколько моментов:

  1. В реакторах не срабатывает обычный command или vl-cmdf. Если "с ножом к горлу" надо использовать командную строку (бывает такое), то следует использовать механизм vla-sendcommand для текущего документа
  2. Реакторы могут быть двух типов: постоянными и непостоянными. Понятно, что срабатывать и те, и другие будут только в том случае, если загружены соответствующие коды. Но вот какая штука - постоянные (persistent) реакторы прописываются в чертеже достаточно хитро.
    Представим себе ситуацию: на компьютере №1 есть соответствующий код, человек работает с постоянными реакторами. На этом компьютере создан dwg и отдан в руки пользователя, который сидит за девственно чистым компьютером №2. В результате AutoCAD может запросто заявить, что файл требует восстановления и проверки. После _.audit и нормального _.-purge записи о постоянных реакторах уничтожаются (Поправка: записи не обязательно уничтожаются. Бывает, что записи можно удалить, только уничтожив все реакторы). По закону подлости файл попадает обратно на компьютер №1. Реакторы понадобится опять прописывать, регистрировать и т.д.
    Учитывая, что неизвестно, с какими файлами приходится работать пользователю, подобные действия (прописывание, регистрация, активация и т.п.) для реакторов приходится все равно выполнять каждый раз. Так что особого смысла в использовании именно постоянных реакторов лично я не вижу.

Теперь попробуем создать элементарный реактор на команду:

1
2
3
4
5
6
7
8
9
(setq vlr_react (vlr-command-reactor
                  "Command reactor"
                  (list '(:vlr-commandwillstart . start-command))
                  ) ;_ end of vlr-command-reactor
      ) ;_ end of setq

(defun start-command (reactor execute-command)
  (princ (strcat "\n" (vlr-data reactor) " : control " (vl-princ-to-string execute-command)))
  ) ;_ end of defun

Загрузим код к AutoCAD и выполним пару команд. Лог получается таким:

1
2
3
4
5
6
7
8
9
10
11
12
Command: _LINE

Command reactor : control (LINE)
Specify first point:
Specify next point or [Undo]:
Specify next point or [Undo]:
Command: _PLINE

Command reactor : control (PLINE)
Specify start point:
Current line-width is 0.0000
Specify next point or [Arc/Halfwidth/Length/Undo/Width]:

А что будет, если мы случайно (или нет - неважно) загрузим код повторно?

1
2
3
4
5
6
Command: L
LINE
Command reactor : control (LINE)
Command reactor : control (LINE)Specify first point:
Specify next point or [Undo]:
Specify next point or [Undo]:

Обратите внимание: командный реактор сработал два раза! Получается, что командные реакторы надо каким-то образом "очищать" перед загрузкой (об этом чуть ниже).

Но здесь кроется еще один подводный камень: помимо контролируемых реакторов могут быть загружены сторонние коды, которые тоже используют командные реакторы. Что будет, если мы нарисуем два командных реактора на команду, к примеру, LINE: оба просто сигнализируют о срабатывании, но загружаются отдельными кодами.

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
;; Реактор 1

(setq vlr_react (vlr-command-reactor
                  "Command reactor"
                  (list '(:vlr-commandwillstart . start-command))
                  ) ;_ end of vlr-command-reactor
      ) ;_ end of setq

(defun start-command (reactor execute-command)
  (cond
    ((= (strcase (car execute-command)) "LINE")
     (princ "\nCommandReactor. Command : LINE; #1")
     )
    ) ;_ end of cond
  ) ;_ end of defun

;; Реактор 2

(setq vlr_react (vlr-command-reactor
                  "Command reactor"
                  (list '(:vlr-commandwillstart . start-command2))
                  ) ;_ end of vlr-command-reactor
      ) ;_ end of setq

(defun start-command2 (reactor execute-command)
  (cond
    ((= (strcase (car execute-command)) "LINE")
     (princ "\nCommandReactor. Command : LINE; #2")
     )
    ) ;_ end of cond
  ) ;_ end of defun

В командной строке получим:

1
2
3
4
Command: L
LINE
CommandReactor. Command : LINE; #1
CommandReactor. Command : LINE; #2Specify first point:

Реакторы срабатывают в той последовательности, в которой они были загружены в AutoCAD.

Вернемся к "очистке" реакторов. Можно удалить все реакторы либо строго определенные.

Для удаления всех реакторов служит функция vlr-removeall, которая вызывается по принципу

1
(vlr-remove-all [ТипРеактора])

Можно указать ТипРеактора (:vlr-command-reactor,:vlr-sysvar-reactor и т.п.) - и тогда код удалит все прописанные реакторы соответствующего типа. Я не уверен, коснется ли такая конструкция обработчиков событий, написанных на .NET - квалификации на создание подобных штук у меня не хватает :( Но лисповые конструкции точно будут удалены. Точнее, не удалены, а деактивированы.

Но! Если разрабатывается свое собственное приложение, которое, вполне возможно, будет работать совместно с дополнениями, неизвестно где и кем разработанными, то может потребоваться обрабатывать только строго определенные реакторы. Точнее, свои собственные реакторы - не затрагивая чужие. В таком случае начинаем использовать конструкцию

1
(vlr-remove [VLR-объект])

Здесь VLR-объект - это (в нашем случае) значения переменной vlr_react. Допустим, у нас код написан неверно: на одну и ту же переменную "засунуты" разные реакторы. vlr_react сначала ссылается на start-command, а потом на start-command2. Если сейчас мы выполним

1
(vlr-remove vlr_react)

то мы деактивируем только второй реактор. До первого будет просто не достучаться:

1
2
3
4
Command: l LINE
CommandReactor. Command : LINE; #1Specify first point:
Specify next point or [Undo]:
Specify next point or [Undo]:

Соответственно для безболезненного использования собственных реакторов необходимо использовать конструкцию типа:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(if vlr-cmd
  (progn
    (vlr-remove vlr-cmd)
    (setq vlr-cmd nil)
    ) ;_ end of progn
  ) ;_ end of if

(if (not vlr-cmd)
  (setq vlr-cmd (vlr-command-reactor "kpblc-command-reactor"
                                       '((:vlr-commandwillstart . command-start)
                                         (:vlr-commandended . command-end)
                                         (:vlr-commandcancelled . command-cancel)
                                         (:vlr-commandfailed . command-fail)
                                         )
                                       ) ;_ end of vlr-command-reactor
        ) ;_ end of setq
  ) ;_ end of if

Ну а после этого уже загружать соответствующие функции. Для реакторов системных переменных принципиально подход не меняется - достаточно заменить слово "command" на "sysvar" :)



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


Я не робот.