Тема: прерывание lisp программ по нажатию клавиши Esc
как организовать корректное прерывание программы, если пользователем нажата клавиша ESc. Т.е. вернуться к исходному состоянию чертежа до запуска программы.
Информационный портал для профессионалов в области САПР
Вы не вошли. Пожалуйста, войдите или зарегистрируйтесь.
Форумы CADUser → Программирование → LISP → прерывание lisp программ по нажатию клавиши Esc
Страницы 1
Чтобы отправить ответ, вы должны войти или зарегистрироваться
как организовать корректное прерывание программы, если пользователем нажата клавиша ESc. Т.е. вернуться к исходному состоянию чертежа до запуска программы.
Однажды я тоже пришёл сюда с таким же вопросом, но....!
Принцип:
1. делаешь функцию сохранения параметров
(defun STORE ()
(setq OLDSNAP (getvar "osmode"))
(setq OLDLR (getvar "clayer"))
(setvar "osmode" 0)
(setvar "cmdecho" 0)
) ;_ end of defun
2. делаешь функцию востановления параметров
(defun RESTORE ()
(if OLDLR (setvar "clayer" OLDLR))
(if OLDSNAP (setvar "osmode" OLDSNAP))
(setvar "cmdecho" 1)
(princ)
)
3. создаёшь свой обработчик ошибок
(defun NNN_ERROR_HANDLER (MSG)
(if
(or
(= MSG "завершить / выйти прервать")
(= MSG "quit / exit abort")
) ;_ end of or
(princ "Error!..................................")
(princ MSG)
) ;_ end of if
(command "_.UNDO" "_end")
(command "_.U")
(RESTORE)
(setq *ERROR* OLDERROR)
(princ)
) ;_ end of defun
4. В своей проге пишешь
(defun YOURPROG ()
(store)
(command "_.UNDO" "_begin")
(setq OLDERROR *ERROR*)
(setq *ERROR* NNN_ERROR_HANDLER)
.......
......
......
(command "_.UNDO" "_end")
(setq *ERROR* OLDERROR)
(restore)
)
5. Вот и всё
спасибо. все получилось.
А как сделать обработчик ошибок для
VLX приложения работающего в собственном
пространстве имён ?
Неужели никто не знает.
Дайте хоть такой ответ - незнаю.
А в чем собственно разница, VLX или исходный текст? Неужели предложенный вариант не работает?
Насколько я понимаю VLX приложение с отдельным пространством имён не знает функции *ERROR*.
Недаром есть функция vl-exit-with-error,
и если в своей программе
(которая будет работать в отдельном пространстве имён) написать (setq OLDERROR *ERROR*) ничего не выйдет.
Есть программа:
(defun c:a1 (/) … (b1) (b2) …) (defun b1 (/) … (c1) …) (defun b2 (/) …) (defun c1 (/) …) (defun *error* (/) …)
Так вот, когда ошибка\сбой происходят в команде а1 то функция *error* срабатывает, а когда ошибка\сбой происходят в функциях b1,b2,c1, то *error* не срабатывает.
На сколько возможно предотвратил появление ошибок IF-ами, WILE-ми на NIL и различными фильтрами, но от Esc это не спасает, что в принципе наверно тоже можно предусмотреть. Всё это усложняет код, да и кроме Esc бывют "чудеса техники"
Читал про vl-catch-all-appley – чувствую, что должна как-то помочь, а как не пойму?
(setq res (vl-catch-all-apply (function (lambda () ; здесь пишешь код точно так же, как и при создании любой функции через defun. ; В результате, будет создана безымянная функция (lambda) ; и тут же выполнена (vl-catch-all-apply). Результат последнего выражения окажется в res. ; При этом, в случае возникновения ошибки в переменную res будет помещен специальный объект, ; содержащий описание ошибки. ) ) ) ); ; Прочитать информацию об ошибке можно так: (if (vl-catch-all-error-p res) (setq msg (vl-catch-all-error-message res)));
Пстух
Спасибо, что откликнулся.
1) Функция Lambda - безымянна и одноразового применения, а у меня в программе их(функций) много, и к половине из них я обращаюсь неоднократно и из разных мест?
2)
Прочитать информацию об ошибке можно так:
(if (vl-catch-all-error-p res) (setq msg (vl-catch-all-error-message res)));
т.е. при возникновении ошибки программа не прерывается, а работает дальше, даёт возможность проверить была-ли ошибка, и почему она возникла, выполняет мои операторы по восстановлению системных параметров, и тихо, корректно завершает работу программы?
(defun _dwgru-error-catch (protected-function on-error-function / catch_error_result ) ;| *** Функция взята из книжной версии ruCAD'a без каких бы то ни было переделок, *** кроме переименования. * Оболочка отлова ошибок. * Параметры вызова: * protected-function - "защищаемая" функция * on-error-function - функция, выполняемая в случае ошибки |; (setq catch_error_result (vl-catch-all-apply protected-function)) (if (and (vl-catch-all-error-p catch_error_result) on-error-function ) ;_ end of and (apply on-error-function (list (vl-catch-all-error-message catch_error_result)) ) ;_ end of apply catch_error_result ) ;_ end of if ) ;_ end of defun
Пример вызова:
(defun test (/ res) (_dwgru-error-catch (function (lambda () (setq res (/ 5 0.)) ) ;_ end of lambda ) ;_ end of function '(lambda (x) (princ (strcat "\n ** Ошибка : " x))) ) ;_ end of _dwgru-error-catch res ) ;_ end of defun
Пусть имеется функция
(defun user-input (str) (getpoint str))
Данная функция приведет к сбою, если пользователь нажмет ESC во время запроса.
Чтобы этого не случилось, необходимо вызывать её через vl-catch-al-apply:
(setq res (vl-catch-all-apply 'user-input (list "Укажите точку: ")))
После чего анализировать res и принимать решение:
(cond ((vl-catch-all-error-p res); была нажата ESC: ; действия в ситуации, когда пользователь прервал ввод. ) (T; функция успешно выполнена и res содержит список с координатами точки. ; Использование полученного результата ) )
В блок vl-catch-all-apply с помощью lambda-функции можно упаковать вызов сразу нескольких функций, потенциально генерирующих ошибки так, как я указал в первом посте.
Спасибо!
Кулик Алексей aka kpblc - что подсказали
Пастух - что разжевали
Но, чуть-чуть не доходит, пожалуйста на моём примере покажите
;;;;;; (defun c:test (/ s oldosm t1 t2) (setq oldosm (getvar "osmode")) (setvar "osmode" 41) (initget "Д Н") (princ) (setq s (getkword "\n Будем рисовать? [Да/Нет] <Д>:")) (If (or (= s "Д") (= s "д") (= s nil)) (vvod) ) ) ;;;;;; (defun vvod (/) (while (and (setq t1 (getpoint "\nt1")) (setq t2 (getpoint "\nt2")) (ris) ) ) ) ;;;;;; (defun ris (t1 t2 /) (entmakex (list '(0 . "LWPOLYLINE") '(100 . "AcDbEntity") '(370 . 0) '(100 . "AcDbPolyline") '(90 . 4) '(70 . 1) (cons 10 t1) (cons 10 t2) ) ) ) ;;;;;; (defun *error* (msg) (princ msg) (setvar "OSMODE" oldosm) (princ) ) )
Для моего варианта получается нечто типа:
(defun c:test (/ _dwgru-error-catch will_draw pt1 pt2) (defun _dwgru-error-catch (protected-function on-error-function / catch_error_result ) ;| *** Функция взята из книжной версии ruCAD'a без каких бы то ни было переделок, *** кроме переименования. * Оболочка отлова ошибок. * Параметры вызова: * protected-function - "защищаемая" функция * on-error-function - функция, выполняемая в случае ошибки |; (setq catch_error_result (vl-catch-all-apply protected-function)) (if (and (vl-catch-all-error-p catch_error_result) on-error-function ) ;_ end of and (apply on-error-function (list (vl-catch-all-error-message catch_error_result)) ) ;_ end of apply catch_error_result ) ;_ end of if ) ;_ end of defun (_dwgru-error-catch (function (lambda () (initget "Да Нет Yes No _ Y N Y N") (setq will_draw (cond ((getkword "\nБудем рисовать [Да/Нет] <Да>? : ")) (t "Y") ) ;_ end of cond ) ;_ end of setq ) ;_ end of lambda ) ;_ end of function '(lambda (x) (princ "\nРисование отменено")) ) ;_ end of _dwgru-error-catch (if (= will_draw "Y") (progn (while (and ((lambda () (setq pt1 nil) (_dwgru-error-catch (function (lambda () (setq pt1 (getpoint "\nPt1 <Cancel> : ")) ) ;_ end of lambda ) ;_ end of function nil ) ;_ end of _dwgru-error-catch pt1 ) ;_ end of lambda ) ((lambda () (setq pt2 nil) (_dwgru-error-catch (function (lambda () (setq pt2 (getpoint pt1 "\nPt2 <Cancel> : ")) ) ;_ end of lambda ) ;_ end of function nil ) ;_ end of _dwgru-error-catch pt2 ) ;_ end of lambda ) ) ;_ end of and (entmakex (list '(0 . "LWPOLYLINE") '(100 . "AcDbEntity") '(370 . 0) '(100 . "AcDbPolyline") '(90 . 4) '(70 . 1) (cons 10 pt1) (cons 10 pt2) ) ;_ end of list ) ;_ end of entmakex ) ;_ end of while ) ;_ end of progn ) ;_ end of if (princ) ) ;_ end of defun
---
Добавлено:
Вариант без универсальности:
(defun c:test2 (/ will_draw pt1 pt2) (if (= "Y" (vl-catch-all-apply (function (lambda () (initget "Да Нет Yes No _ Y N Y N") (setq will_draw (cond ((getkword "\nБудем рисовать [Да/Нет] <Да>? : ")) (t "Y") ) ;_ end of cond ) ;_ end of setq ) ;_ end of lambda ) ;_ end of function ) ;_ end of vl-catch-all-apply ) ;_ end of = (while (and (= (type (setq pt1 (vl-catch-all-apply (function (lambda () (getpoint "\nPt1 <cancel> : ") ) ;_ end of lambda ) ;_ end of function ) ;_ end of vl-catch-all-apply ) ;_ end of setq ) ;_ end of type 'list ) ;_ end of = (= (type (setq pt2 (vl-catch-all-apply (function (lambda () (getpoint pt1 "\nPt2 <Cancel> : ") ) ;_ end of lambda ) ;_ end of function ) ;_ end of vl-catch-all-apply ) ;_ end of setq ) ;_ end of type 'list ) ;_ end of = ) ;_ end of and (entmakex (list '(0 . "LWPOLYLINE") '(100 . "AcDbEntity") '(370 . 0) '(100 . "AcDbPolyline") '(90 . 4) '(70 . 1) (cons 10 pt1) (cons 10 pt2) ) ;_ end of list ) ;_ end of entmakex ) ;_ end of while ) ;_ end of if ) ;_ end of defun
Необходимо внести изменения в c:test
(defun c:test (/ s oldosm t1 t2) (setq oldosm (getvar "osmode")) (setvar "osmode" 41) (initget "Д Н") (vl-catch-all-apply (function (lambda () (setq s (getkword "\n Будем рисовать? [Да/Нет] <Д>:")) (if (or (= s "Д") (= s "д") (= s nil)) (vvod)) ) ) ) (setvar "osmode" oldosm) ); end defun.
В данном случае даже не требуется анализировать результат, возвращаемый vl-catch-all-apply.
Поэтому я его не присваиваю никакой переменной.
А функцию *error* переопределять не требуется. Переопределение *error* - это прием из прошлой жизни, когда в AutoLISP не было vl-catch-all-apply. Эта функция (*error*) не перехватывает ошибку и не предовращает "вылет", а всего лишь пост-фактум выполняет заложенные в неё действия, позволяющие сгладить последствия вылета, например восстановить системные переменные.
Чтоб жизнь малиной не казалась
:D :D :D
Всю ночь разбирался, что да как работает в коде kpblcа (причём почему-то первого, а надо-то было сразу посмотреть "без универсальности"- почти всё понял(ваш стиль написания кода возможно очень высокого уровня, но трудно читаем для начинающих) , начал применять всё это дело к своему длинному, большому, запутанному (для меня) коду...
:D :D :D
И тут приходит и пишет Пастух - оказывается всё до ужаса просто и понятно, и в мой код надо добавить только в одном месте всего 3 строки.
Страницы 1
Чтобы отправить ответ, вы должны войти или зарегистрироваться
Форумы CADUser → Программирование → LISP → прерывание lisp программ по нажатию клавиши Esc
Форум работает на PunBB, при поддержке Informer Technologies, Inc