Отмена обработки undo
При некоторых условиях необходимо, чтобы AutoCAD не записывал файл отмены каких-либо действий. Ну, например, возьмем полилинию с 50-60 тыс.вершин, разобьем ее и потом вызовем _.pedit (пример честно спер у А.Ривилиса ;)). В результате файл отмены (это который UNDO.AC$) достигнет невероятных размеров и AutoCAD просто откажется дальше жить. Можно придумать или вспомнить еще массу примеров, но лечить как-то надо...
На помощь приходит системная переменная UNDOCTL. Ну, точнее, не совсем приходит - переменная-то ReadOnly. Впрямую ее поменять нельзя.
Параметр хранится в виде битового кода, использующего сумму следующих значений.
0 Команда ОТМЕНИТЬ отключена 1 Команда ОТМЕНИТЬ включена 2 Можно отменить только одну команду 4 Включен параметр "Авто" 8 Группа в настоящий момент активна 16 Операции зумирования и панорамирования группируются в одно действие 32 Операции со свойствами слоев группируются в одно действие
Переменная не сохраняется и имеет значение по умолчанию 53. Нам же (ну, по крайней мере теоретически) достаточно снять все значения, кроме 16 и 32. Для этого достаточно выполнить последовательность команд:
1 | _undo _control _none |
А для восстановления
1 | _undo _all |
К сожалению, такой вариант означает невозможность впрямую обрабатывать undoctl внутри командных реакторов, ведь там невозможно выполнять команды. Единственный вариант "обхода" - это использование SendStringToExecute, но об этом как-нибудь в другой раз...
Небольшое уточнение. По поводу _PEDIT и бесконечно разрастающегося файла UNDO - этот факт был проверен в AutoCAD 2008 SP1. И такая ситуация возникала при установленной системной переменной PLINETYPE = 0, т.е. создаётся не облегченная, а 2D-полилиния. Проблема скорее все связана с многократным созданием/удалением вершин полилинии, которые в этом типе полилинии являются отдельными примитивами. Кстати, максимальный размер файла UNDO равен 2Гбайтам.
Добрый вечер! Подскажите, пожалуйста, есть ли какая-нибудь возможность изменения системной переменной UNDOCTL через лисп. Попробовала в командной в программе написать такую строку (command "_undo" "_control" "_none"), но это не поменяло значение системной переменной. Хотя если эту последовательность делать просто из командной строки - все прекрасно меняется.
Может так: (command "_.undo" "_none")
?
P.S.: Леша. Блог заменяет двойные кавычки на двойной апостроф.
Проверка:
(command "_.undo")
При копировании в vlide получаю только в третьем варианте не нормальные двойные кавычки, а преобразованные. Завтра попробую выяснить, в чем кроется источник проблемы. Спасибо
Ну тогда так:
2
3
(command "_UNDO" "_CONTROL" "_NONE")
)
Получилось, но не совсем как надо. Может я более подробно опишу свою проблему.
У меня есть программа, которая удаляет все одинаковые блоки из чертежа, в том числе блоки из блоков. Так вот мне нужно описать функцию отмены, если вдруг блок был удален не тот, который надо. Обычной командой UNDO отмену сделать не получается. Если из командной строки написать _undo _control _one, а затем _undo 1, то удаленные блоки восстанавливаются. Теперь пробую написать это в лиспе, а он не обрабатывает.
(if (= 1 (logand (getvar "UNDOCTL") 1))
(command "_UNDO" "_CONTROL" "_ONE")
)
(setq UndoMark (getvar "UNDOMARK"))
Последовательность такая: выбрала блок, программа все удалила, нажала правую кнопку мыши, выбрала отмену, далее ставлю в коде точку останова, и проверяю системную переменную. После кода, написанного выше - переменная остается UNDOCTL=53 и ничего не восстанавливается, если я дальше опять выбираю другой блок, все удаляется, правая кнопка мыши, выбор отмены и снова проверяю системную переменную, а она только теперь изменилась: UNDOCTL=51, но отмены все равно не произошло, т.к. после этого (command "_UNDO" "_CONTROL" "_ONE"), командное удаление (command "_undo" "1" "") - не работает
После того, как в коде было добавлено (vla-StartUndoMark adoc) .......(vla-EndUndoMark adoc), стала работать команда UNDO, но не программно, а нажатием на стандартную кнопку отмены.
Так обычный Ctrl + Z корректно отменяет метки vla-startundomark .. vla-endundomark. Если был удален блок "не тот", то увидит это только человек, человек же и отменит действие. Или я что-то не понимаю?
Все верно, человек может отменить, но для этого надо завершить программу удаления, а я бы хотела сделать это в коде. Может как-то описать в коде реакцию на UNDO или лучше другой способ?
Я не очень понимаю общую логику, поэтому совет может и не сработать:
2
3
4
5
1.1. startundo-удаление-endundo
1.2. Запрос: результат нормален? Если да, то переход на п.1. Если нет, послать в ком.строку ("_.undo" "1"), и на п.1
Продолжать, пока выбран блок
Может быть, общий алгоритм будет таким?
Алгоритм у меня именно такой.
При выборе пользуюсь командой ssget, т.к. есть возможность выбора рамкой. Все работает, если выбран один элемент или рамкой выбрано несколько элементов. Перестает работать отмена удаления только в том случае, если удаляется не один блок (штриховка или текст), а все подобные блоки (штриховки, тексты) из чертежа и из блоков. Т.е. получается при отмене должен восстановиться не один блок, а все подобные в чертеже.
Возможно нужен как раз таки для таких случаев другой алгоритм.
1. Выбор блока
2.Удаление всех блоков с таким именем
3.Запрос правильно ли удален блок? Если да, переход на п.1. А если нет - даже не могу представить что нужно сделать. Ведь если отмену делать стандартную, то она нажимается один раз и восстанавливаются все удаленные блоки сразу.
И вот еще вопрос, где конкретно нужно ставить startundo-endundo? непосредственно перед и после команды delete, или в момент проверки типа выбранного примитива, или может вообще после выбора примитива?
Сейчас еще немного поиграла с кодом и получилось следующее: я поставила startundo-endundo после проверки какой был выбран элемент на чертеже, и он хорошо отрабатывает из командной строки отмену. Но если я пытаюсь сделать отмену из лисп-программы, то он ругается:
" Команда: _DELETESELECT Неизвестная команда "DELETESELECT". Для вызова справки
нажмите F1."
Где DELETESELECT - это моя функция удаления выбранных элементов (defun C:DeleteSelect......)
Может ошибка как раз из-за подчеркивания? откуда оно может появляться?
Так, стоп. Блок - это блок. Штриховка - это штриховка. Предлагаю не вносить сумятицу и соблюдать терминологию
Как пример можете рассмотреть мой код по удалению маскировок из всех блоков текущего чертежа: http://autolisp.ru/wp-content/uploads/2013/11/erase-wipeout.lsp
P.S. Обычно я ставлю марки начала-конца отмены только на область, где вносятся изменения в чертеж.
P.P.S. Если (подчеркиваю - если!) я правильно понимаю, то обработка получается такая:
1. Запрос у пользователя типа примитива, который надо удалить (отрезок, штриховка, маскировка, вхождение блока...)
2. Если п.1 успешен (т.е. пользователь не нажал Esc), то:
2.1. Метка начала отмены
2.2. Разблокировать и разморозить слои (по желанию)
2.3. Проходим по всем описаниям блоков
2.4. Внутри каждого описания каждого блока находим примитив, аналогичный выданному в п.1., и удаляем его. Если п.2.2 не был выполнен, то перед удалением проверяем состояние слоя для примитива. Тут же будет "головняк" на предмет того, что делать, если примитив входит в блок, примитив находится на слое "0", а блок - на каком-то другом слое.
2.5. Восстанавливаем состояние слоев (если был п.2.2)
2.6. Метка конца отмены
3. При необходимости - возврат к п.1.
В результате по Ctrl+Z будет отменяться только последнее удаление.
Можно сделать по-другому, поместив отмену до запроса и после п.1. Тогда отменяться будут сразу все удаления.
На счет типа выбранных элементов - ведь Автокаду все равно, что отменять - будь то блок, штриховка, или что-то другое. Сложность только в том, что удаляется не один примитив, а несколько. И стандартная функция отмены восстанавливает сразу все удаленные одинаковые блоки.
В итоге, самая последняя строчка меня спасла
Я сделала двойное подтверждение удаления, сразу после того, как был выбран примитив, и уже после того, как он был удален. Правда после второго подтверждения приходится все равно завершать программу и отменять по Ctrl+Z.
Мне кажется, я даже знаю, почему не отменяется удаление. Когда в программе пошагово иду по коду, то в момент выполнения строки: (command "_undo" "1" ""), в командной строке пишет: "Неизвестная команда "DELETESELECT"." Не понятно совершенно почему он обращается к функции, которая в данный момент выполняется, и не понятно, почему она неизвестная, ведь она загружена в Автокаде? Может решение этого вопроса и позволит придти к нужному результату.
Евгения, AutoCAD отменяет операции, а не примитивы. В остальном - нужен код, потому что я уже запутался, что Вы хотите и в каком объеме.
Алексей, спасибо за подсказки, все получилось. С помощью кода получилось выполнять отмену удаления любого примитива любое количество раз!
Видимо главное было в правильном месте разместить startundo- endundo.
Благодаря помощи Админа, ошибка исправлена.