Как сделать отмену результата выполнения?
Разрабатываемые коды, как правило, что-то делают с файлом dwg: рисуют объекты, вносят изменения в таблицы файла и т.п. При этом могут меняться системные переменные, отрисовываться временные примитивы и т.п.
Все будет замечательно, если пользователь сделает все сразу и верно. А если нет? А если понадобится отменять выполненную команду?
Пользователю-то все просто: нажал один (подчеркиваю - один!) раз отмену, и его работа выполнена. "Команда была одна? Одна. Сколько надо отмен? По идее тоже одна",- так или примерно так рассуждает пользователь, и нельзя сказать, что он сильно неправ. При этом после выполненной отмены AutoCAD должен восстановить свое состояние. Такое поведение предсказуемо, и предсказуемость эта - одна из основ нормально работающей программы.
Рассмотрим элементарную задачу: нарисовать отрезок по 2 заранее известным точкам в указанном слое. Кстати, сразу заменить отрезку цвет и вес линии. Для большей наглядности работу будем выполнять командными методами. Опять же, для наглядности исключим всякие обработчики ошибок (подробнее здесь).
Точки: 0,0,0 - 100,10,0
Слой: имя слоя "Тест_123", цвет слоя - 1, вес линии - 0,25, тип линии - Continuous
Цвет отрезка: 2, вес линии отрезка - 0,50
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | (defun cmd (/ cecolor celweight) (command "_.-layer" "_make" "Тест_123" "_color" 1 "" "_lweight" 0.25 "" "" ) ;_ end of command (setq cecolor (getvar "cecolor") celweight (getvar "celweight") ) ;_ end of setq (setvar "cecolor" "2") (setvar "celweight" 50) (command "_.line" "_none" '(0. 0. 0.) "_none" '(100. 10. 0.) "") (setvar "cecolor" cecolor) (setvar "celweight" celweight) (princ) ) ;_ end of defun |
Казалось бы, все просто и логично: сначала создается, активируется и настраивается слой, потом устанавливаются системные переменные и выполняется собственно рисование. Попробуем выполнить этот код в новом файле (где слой всего один с именем "0", вес и цвет линии - по слою, тип линии - только Continuous / Непрерывный).
Все получилось просто отлично, отрезок нарисован, слой для него установлен верно и так далее. А вот теперь начнем отменять выполненные действия. В AutoCAD, естественно:
[Ctrl]+[z] - отрезок исчез, это хорошо. А цвет и слой вернулись обратно? Нет.
[Ctrl]+[z] - вернулись настройки слоев.
А если бы выполнялось рисование не 1 отрезка, а сотни? А если между рисованием отрезков надо было бы опять слои менять? Представьте себе, какой это будет адский труд - отменить выполнение всего одной команды.
Подобную проблему надо решать. Надо? - Решим.
Как раз для таких случаев существует понятие меток отмены. Ставится одна метка в начале, потом вторая - в конце. Все, что между ними, AutoCAD понимает как "одно неделимое целое". И отменяет "чохом".
Существует два способа установки этих самых меток: командный и "программисткий" (через ActiveX). Оба метода достаточно подробно описаны здесь. Повторяться не хотелось бы.
С метками (как, впрочем, и со всем, чего касается программирование) следует обращаться достаточно аккуратно. Количество меток начала и конца отмены должно совпадать, иначе AutoCAD может, что называется, "развалиться" на ровном месте.
То есть коды вида
1 2 3 4 5 6 7 8 | (defun cmd (/ cecolor celweight) (command "_.undo" "_begin") (command "_.-layer" "_make" "Тест_123" "_color" 1 "" "_lweight" 0.25 "" "" ) ;_ end of command (command "_.line" "_none" '(0. 0. 0.) "_none" '(100. 10. 0.) "") (princ) ) ;_ end of defun |
или
1 2 3 4 5 6 7 8 9 10 11 | (defun cmd (/ cecolor celweight) (command "_.-layer" "_make" "Тест_123" "_color" 1 "" "_lweight" 0.25 "" "" ) ;_ end of command (setq cecolor (getvar "cecolor") celweight (getvar "celweight") ) ;_ end of setq (command "_.line" "_none" '(0. 0. 0.) "_none" '(100. 10. 0.) "") (command "_.undo" "_end") (princ) ) ;_ end of defun |
неверны изначально. И подобных ситуаций надо избегать.
Лично я предпочитаю не использовать командные методы без крайней на то нужды (в силу разных причин - начиная с вопросов скорости выполнения и заканчивая проблемами переноса кода на BricsCAD, например). Поэтому дальнейшее тестирование будет идти с использованием либо entmake, entmakex, entmod и им подобных функций, либо с объектной моделью.
Итак, сделаем код, в котором будут установлены "вложенные" метки отмены (обращайте внимание на расположение vla-startundomark и vla-endundomark):
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 | (defun test (/ fun adoc model) (defun fun () (vla-startundomark adoc) ;;; Метка второго уровня, начало (vla-addtext model "Текст 2" (vlax-3d-point '(0. -5. 0.)) 2.5 ) ;_ end of vla-addtext (vla-endundomark adoc) ;; Метка второго уровня, конец ) ;_ end of defun (vl-load-com) (vla-startundomark (setq adoc (vla-get-activedocument (vlax-get-acad-object))) ) ;_ end of vla-startundomark ;;; Метка верхнего уровня, начало (setq model (vla-get-modelspace adoc) text1 (vla-addtext model "Текст 1" (vlax-3d-point '(0. 0. 0.)) 2.5 ) ;_ end of vla-addtext ) ;_ end of setq (fun) (vla-addtext model "Текст 3" (vlax-3d-point '(0. -10. 0.)) 2.5 ) ;_ end of vla-addtext (vla-endundomark adoc) ;; Метка верхнего уровня, конец (princ) ) ;_ end of defun |
Запустим код на выполнение. В принципе ничего сверхъестественного не происходит: в пространство модели активного документа добавляется 3 объекта текста. Но вот отмена выполнения уже не настолько очевидна.
Вызовем отмену. Удаляются объекты "Текст 3" и "Текст 2". То есть выполняется отмена вплоть до метки начала второго уровня (то есть отменяется и выполнение функции (fun)). Логично предположить, что AutoCAD находит код между последней vla-endundomark и ближайшей к ней по ходу выполнения vla-startundomark.
Если представить наш код в таком виде:
1 | {Начало1 {Начало2 Конец2} Конец1} |
То первая отмена будет выполняться так:
1 | {Начало2 Конец2} Конец1} |
И в результате мы получаем незавершенную марку Начало1. Как раз та ситуация, избегать которую мы договорились чуть выше.
Анализируя все вышесказанное, можно вывести рекомендацию: метки начала и отмены устанавливать только в функциях-"обертках" или командах, которые и будут вызывать пользователи. Установку меток отмены в функциях нижнего уровня, а также вложенных (локальных) функциях надо избегать.