Программирование диалоговых окон на языке DCL
Составление списка вручную:
(setq layerList(list "0" "Layer1" " Layer2" " Layer3" " Layer4" " Layer5" "Layer6" " Layer7" " Layer8" " Layer9"))
То же самое для числа сторон многоугольника:
(setq numSides(list "4" "6" "8" "12" "16"))
Добавим эти строки в описание диалога DCL-файла.
;;;--- Add the layer names to the dialog box (start_list "layerlist" 3) (mapcar 'add_list layerList) (end_list) ;;;--- Add the number of sides to the dialog box (start_list "numsides" 3) (mapcar 'add_list numSides) (end_list)
После этого добавление основная LISP-программа должна выглядеть так:
(defun C:SAMPLE () ;;;--- Load the dcl file (setq dcl_id (load_dialog "SAMPLE.dcl")) ;;;--- Load the dialog definition if it is not already loaded (if (not (new_dialog "SAMPLE" dcl_id)) (progn (alert "The SAMPLE.DCL file could not be loaded!") (exit) ) ) (setq layerList(list "0" "Layer1" " Layer2" " Layer3" " Layer4" " Layer5" "Layer6" " Layer7" " Layer8" " Layer9")) (setq numSides(list "4" "6" "8" "12" "16")) ;;;--- Add the layer names to the dialog box (start_list "layerList" 3) (mapcar 'add_list layerList) (end_list) ;;;--- Add the number of sides to the dialog box (start_list "numsides" 3) (mapcar 'add_list numSides) (end_list) ;;;--- If an action event occurs, do this function (action_tile "cancel" "(setq ddiag 1)(done_dialog)") (action_tile "accept" "(setq ddiag 2)(saveVars)(done_dialog)") ;;;--- Display the dialog box (start_dialog) ;;;--- Unload the dialog box (unload_dialog dcl_id) ;;;--- If the cancel button was pressed - display message (if (= ddiag 1) (princ "\n \n ...SAMPLE Cancelled. \n ") ) ;;;--- If the "Okay" button was pressed (if (= ddiag 2) (princ "\n \n ...SAMPLE Complete!") ) ;;;--- Suppress the last echo for a clean exit (princ) )
Сохраните файлы, загрузите их и запустите команду Sample. На экране должно появиться диалоговое окно со списками (рис. 6.9):
Создадим сценарий отклика на события в окне в описании функции SaveVars LISP-файла. При выборе строки в списке функция чтения get_tile возвращает номер строки как строковую константу. На время отладки программы введем распечатки полученных значений переменных. В дальнейшем эти распечатки нужно убрать.
(defun saveVars() (setq radios(get_tile "radios")) ;;;--- Get the number of sides selected from the list (setq numStr(get_tile "numsides")) (if(= numStr "") (setq numSides nil) (setq numSides(nth (atoi numStr) numSides)) ) (princ numSides) (terpri) ;;;--- See if the user wants to save the settings (setq saveSet(atoi(get_tile "saveset"))) (princ saveSet) (terpri) ;;;--- Get the selected item from the layer list (setq sStr(get_tile "layerList")) ;;;--- If the index of the selected item is not "" then something was selected (if(/= sStr "") (progn ;;;--- Something is selected, so convert from string to integer (setq sIndex(atoi sStr)) ;;;--- And get the selected item from the list (setq layerName(nth sIndex layerList)) ) ;;;--- Else, nothing is selected (progn ;;;--- Set the index number to -1 (setq sIndex -1) ;;;--- And set the name of the selected item to nil (setq layerName nil) ) ) (princ layerName) (terpri) )
Если не выделить имя слоя в элементе list_box, то значение его будет nil. Иначе будет выдан примерно такой результат:
Command: sample 6 1 sStr5 sindex5 layerNameLayer5 ...SAMPLE Complete!
При вычерчивании окружности нужно сделать неработающим элемент "Number of Sides" popup_list. Для этого нужно запрограммировать события с радиокнопками таким образом:
;;;--- If an action event occurs, do this function (action_tile "drawcir" "( 1)") (action_tile "drawpol" "(toggleRadio 2)")
При выборе элемента drawcir вызывается функция toggleRadio 1, а при выборе элемента drawpol вызывается функция toggleRadio 2.
Создадим такую функцию с параметром (a) в AutoLisp-программе:
(defun toggleRadio(a) ;if circle is selected (if(= a 1) (mode_tile "numsides" 1) ;disable ;else (mode_tile "numsides" 0) ;enable ) )
По умолчанию в программе должна рисоваться окружность. Поэтому перед выражениями с функциями action_tile нужно добавить строку
(mode_tile "numsides" 1)
Таким образом, когда выделяем кнопку, чтобы нарисовать окружность, numSides popup_list недоступен. Когда же мы собираемся рисовать многоугольник, то элемент установки число сторон становится доступным.
А теперь можно изменить слой:
(setq oldLay(getvar "clayer")) (setvar "clayer" layerName)
Напишем также команды построения окружности и многоугольника
(if(= radios "drawcir") (progn (setq pt(getpoint "\n Center point: ")) (command "circle" pt pause) ) ;;;--- Else draw a polygon (progn (setq pt(getpoint "\n Center Point: ")) (command "polygon" numSides pt "C" pause) ) )
LISP-файл будет выглядеть следующим образом:
(defun C:SAMPLE () ;;;--- Load the dcl file (setq dcl_id (load_dialog "Sample.dcl")) ;;;--- Load the dialog definition if it is not already loaded (if (not (new_dialog "sample" dcl_id)) (progn (alert "The SAMPLE.DCL file could not be loaded!") (exit) ) ) (setq layerList (list "0" "Layer1" "Layer2" "Layer3" "Layer4" "Layer5" "Layer6" " Layer7" "Layer8" "Layer9" ) ) (setq numSides (list "4" "6" "8" "12" "16")) ;;;--- Add the layer names to the dialog box (start_list "layerList" 3) (mapcar 'add_list layerList) (end_list) ;;;--- Add the number of sides to the dialog box (start_list "numsides" 3) (mapcar 'add_list numSides) (end_list) (mode_tile "numsides" 1) ;;;--- If an action event occurs, do this function (action_tile "drawcir" "(toggleRadio 1)") (action_tile "drawpol" "(toggleRadio 2)") (action_tile "cancel" "(setq ddiag 1)(done_dialog)") (action_tile "accept" "(setq ddiag 2)(saveVars)(done_dialog)" ) ;;;--- Display the dialog box (start_dialog) ;;;--- Unload the dialog box (unload_dialog dcl_id) ;;;--- If the cancel button was pressed - display message (if (= ddiag 1) (princ "\n \n ...SAMPLE Cancelled. \n ") ) ;;;--- If the "Okay" button was pressed (if (= ddiag 2) (princ "\n \n ...SAMPLE Complete!") ) ;;;--- Suppress the last echo for a clean exit (princ) (if(= radios "drawcir") (progn (setq pt(getpoint "\n Center point: ")) (command "circle" pt pause) ) ;;;--- Else draw a polygon (progn (setq pt(getpoint "\n Center Point: ")) (command "polygon" numSides pt "" pause) ))) (defun saveVars () (setq radios (get_tile "radios")) ;;;--- Get the number of sides selected from the list (setq numStr (get_tile "numsides")) (if (= numStr "") (setq numSides nil) (setq numSides (nth (atoi numStr) numSides)) ) (princ numSides) (terpri) ;;;--- See if the user wants to save the settings (setq saveSet (atoi (get_tile "saveset"))) (princ saveSet) (terpri) ;;;--- Get the selected item from the layer list (setq sStr (get_tile "layerList")) (princ "sStr") (princ sStr) (terpri) ;;;--- If the index of the selected item is not "" then something was selected (if (/= sStr "") (progn ;;;--- Something is selected, so convert from string to integer (setq sIndex (atoi sStr)) (princ "sindex") (princ sIndex) (terpri) ;;;--- And get the selected item from the list (setq layerName (nth sIndex layerList)) (princ "layerName") (princ layerName) (terpri) ) ;;;--- Else, nothing is selected (progn ;;;--- Set the index number to -1 (setq sIndex -1) ;;;--- And set the name of the selected item to nil (setq layerName nil) ) ) (setq oldLay(getvar "clayer")) (setvar "clayer" layerName) ) (defun toggleRadio(a) ;if circle is selected (if(= a 1) (mode_tile "numsides" 1) ;disable ;else (mode_tile "numsides" 0) ;enable ) )
И наконец, займемся кодом сохранения установок по умолчанию. В элементах диалога все значения переменных интерпретируются как строки. В AutoCAD поддерживается 15 пользовательских системных переменных сохранения данных. USERR1 - USERR5 используют действительные числа. USERI1 - USERI5 используют целые числа. USERS1 - USERS5 будут сохранять наши данные как строки. Все эти величины доступны в файле чертежа каждый раз при его открытии.
Первой переменной у нас появляется RADIOS, и она уже имеет строковый тип (setvar "USERS1" radios).
Второй переменной является NUMSIDES - целое число сторон многоугольника. Но при чтении значения (value) элемента нам возвращается номер строки типа string: (setvar "USERS2" numStr).
Третья переменная SAVESET имеет тип integer. Поэтому ее нужно конвертировать (setvar "USERS3" (itoa saveSet)) .
Индекс четвертой переменной LAYERNAME сохраним в виде строки:
(setvar "USERS4" sSTR)
Ключевые термины
Язык DCL (Dialog Control Language) - специальный язык для создания диалоговых окон.
Директива - логически законченная единица программы диалога.
Элементы управления - части интерфейса диалогового окна, к которым имеет доступ пользователь (кнопки, текстовые поля, списки и пр.).
Функции DCL - встроенные функции языка для проведения действий и операций.
Переменные действия - глобальные переменные, в которых сохраняются некоторые дополнительные данные о состоянии диалогового окна и выполненных в нем действий пользователя. Переменные используются в LISP-выражениях, задаваемых в качестве второго аргумента функции action-tile.
Краткие итоги
Программы вычерчивания фигур должны иметь возможность изменять значения переменных. Это можно сделать либо в коде, либо по запросам в командной строке в процессе выполнения программы. Но удобнее всего выполнять установки в специальных диалоговых окнах. Технология создания таких окон с помощью языка DCL описана в данной лекции.
Вопросы
- Где нужно сохранять DCL-файлы?
- Через какой атрибут директивы устанавливается связь диалога с LISP-файлом?
- Что возвращает функция загрузки dcl-файла load_dialog?
- Каково назначение функции action_tile?
- Какие функции производят операции редактирования списков в элементах list_box и popup_list?
- Составьте алгоритм работы LISP-программы, которая запускает сценарий отклика на событие выделения радиокнопки в диалоговом окне.