Диалоговые окна dcl – зло? Или все же нет?

Редко какая программа обходится без взаимодействия с пользователем. Поскольку здесь разговор ведется именно о разработке для AutoCAD, уточняю: "редко какое пользовательское дополнение для AutoCAD обходится без взаимодействия с пользователем".

Можно запросить выбрать из контекстного меню опцию, а можно написать диалог. Вопрос: что выгоднее?

Ответа (по крайней мере однозначного ответа) лично я не знаю. Хотя для себя я выбрал правило: если запросов больше чем 3, надо задумываться о написании и вызове диалогового окна.

Диалоговые окна (далее - диалоги) бывают разные (модальные / немодальные, dcl / COM / VBA / .net, вложенные / одноуровневые и т.п.).

Модальные диалоги - это окна, "блокирующие" работу основного приложения, из которого они вызваны. Яркий пример - окно _.options. Или окно сохранения файла (по крайней мере пока). До версии 2008 подобным окном было окно управления слоями (как в 2009 дело обстоит, не помню; в 2010 это окно уже стало немодальным).

Немодальные диалоги соответственно оставляют приложение-"владелец" в режиме полноценной жизни (например, окно масштабов СПДС GraphiCS).

Это была теория (да знаю я, знаю, что это и так всем известно :) Ну дайте почувствовать себя немного умным :)).

Модальные диалоги намного более просты по реализации по сравнению с немодальными. Так, например, dcl-диалоги модальны (без вариантов). Диалоги, использующие технологию СОМ, сделать немодальными лично мне не удавалось (да там еще несколько достаточно неприятных моментов вылезло в процессе тестирования... В общем, я от них стараюсь отказываться. Хотя, конечно, разрабатывать их сплошное удовольствие). VBA из рассмотрения можно исключать - судьба этого придатка весьма и весьма туманна. Приложения на .NET и ObjectARX я разрабатывать не умею, но, судя по caduser.ru, и там реализация модальных окон на несколько порядков проще, чем немодальных.

Я хотел бы провести небольшое сравнение dcl vs COM

DCL COM
Описание диалога хранится в отдельном файле dcl. Логика работы - в lisp Описание диалога хранится в dll. Там же - описание работы элементов диалога
Среда разработки чрезвычайно недружелюбна Среда разработки - любая. Лишь бы умела компилировать регистрируемые в Windows dll
Крайне ограниченный набор типов элементов (контролов) диалога Помимо штатных контролов, можно подключать сторонние
Невозможно создать диалог с изменяемыми (программно или пользователем - неважно) размерами. Размеры диалога можно по мере надобности изменить.
Ни один из контролов не требует никаких дополнительных действий В системе пользователя приходится регистрировать использованные сторонние контролы. Кроме того, зачастую приходится учитывать различные версии и штатных (яркий пример - MSForms)
Если необходимо внести изменения в диалог (расставить контролы по-другому, изменить логику обработки и т.п.), достаточно, как правило, просто изменить один (!) dcl-файл и предоставить его пользователю Для внесения изменений приходится полностью перекомпилировать dll и предоставлять уже ее. Если при этом изменился набор сторонних контролов, приходится серьезно поломать голову над вопросом "как не загадить машину конечного пользователя кучей ненужного хлама"
dcl-файлы (поскольку это обычные текстовые файлы в кодировке Windows) можно создавать "на лету" во время выполнения lisp-кода dll-файлы обязательно должны быть зарегистрированы в системе. Без вариантов.

В силу разных причин (и прежде всего из-за необходимости регистрировать dll) я остановился на dcl-диалогах.

Сам по себе язык достаточно простой, но за этой простотой можно спрятать безумные вещи (например, отслеживать, в какой части графического контрола пользователь нажал на мышь. Или с помощью тех же графических элементов отобразить древовидное меню. Сам я такого не делал, и как сделать - представляю достаточно слабо. Но я знаю человека, который подобное реализовывал. Его код разбирать я не сунусь ни за какие коврижки). Безумствовать, пожалуй, не будем - лишнее, а вот динамически создать диалог попробуем... Заодно, может быть, и еще с парочкой моментов разберемся :)

Для примера упростим задачу: надо запустить команду, которая будет просто выдавать окно запроса с 4 элементами:

Пример dcl

Код диалога будет очень прост:

1
2
3
4
5
6
7
dlg:dialog{label="lispru dialog";
:toggle{key="check1";label="Вариант 1";}
:toggle{key="check2";label="Вариант 2";}
:radio_button{key="var";label="Сложить варианты";}
:radio_button{key="text";label="Сложить тексты";}
ok_cancel;
}

Что будет в результате - на данный момент несущественно. Важно следующее: предоставляя код lsp конечному пользователю, придется помимо lsp-файла обязательно предоставить и dcl. И молиться всем богам, чтобы пользователь правильно расположил файлы и ничего при этом не потерял. Или второй вариант - компилировать оба файла в vlx и отдавать уже скомпилированную сборку.

Скажу честно - второй подход лично мне не нравится. Мне вообще не нравится, когда предоставляется только vlx / fas без исходных кодов. В таком случае "подгрузить" эти (возможно, весьма полезные) коды, например, в BricsCAD нет никакой возможности.

Поэтому попробуем сделать так:

  • делать только lsp
  • компиляция в vlx/fas необязательна
  • конечному пользователю предоставляется только lsp, без каких бы то ни было дополнительных файлов

Выполнение всех трех пунктов реально. Можно создать этот диалог "на лету", в процессе выполнения. Поскольку dcl-файл - обычный текстовый файл, его можно создать, заполнить нужным содержанием и потом уже вызывать. Но тут вмешивается такая штука, как права пользователей. Далеко не все имеют права администратора, далеко не все. Поэтому файл надо создавать в каталоге, удовлетворяющему таким требованиям:

  • пользователь гарантированно имеет право на запись в этот каталог
  • приложение acad.exe гарантированно имеет доступ к этому каталогу

Этим требованиям на самом деле удовлетворяют 3 каталога, значения которых можно получить (в лиспе, разумеется) как:

1
2
3
4
5
6
;; Значение переменной окружения %temp%
(getenv "temp")
;; Значение переменной окружения %tmp%
(getenv "tmp")
;; Значение каталога временных файлов AutoCAD
(vla-get-tempfilepath (vla-get-files (vla-get-preferences (vlax-get-acad-object))))

Все эти три варианта равноценны (и, кстати, зачастую возвращают одинаковые результаты). Но лично я предпочитаю именно последний.

Суммируя все сказанное, можно сделать такой кусочек кода, создающий dcl-файл "на лету":

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
(vl-load-com)

(setq file   (strcat (vl-string-right-trim
           "\\"
           (vla-get-tempfilepath
       (vla-get-files
         (vla-get-preferences (vlax-get-acad-object))
         ) ;_ end of vla-get-files
       ) ;_ end of vla-get-tempfilepath
           ) ;_ end of vl-string-right-trim
         "\\dlg.dcl"
         ) ;_ end of strcat
      handle (open file "w")
      ) ;_ end of setq
(foreach item
        '("dlg:dialog{label=\"lispru dialog\";"
    ":toggle{key=\"check1\";label=\"Вариант 1\";}"
    ":toggle{key=\"check2\";label=\"Вариант 2\";}"
    ":radio_button{key=\"var\";label=\"Сложить варианты\";}"
    ":radio_button{key=\"text\";label=\"Сложить тексты\";}"
    "ok_cancel;" "}")
  (write-line item handle)
  ) ;_ end of foreach
(close handle)

Код, как видно, элементарен: сначала задается абсолютное имя файла, потом файл открывается на запись, в него построчно записывается код диалога, и файл закрывается. Теперь остается только вставить этот код в основную функцию и получить гарантированно работающее окно диалога:

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
38
39
40
41
(vl-load-com)

(defun lispru-dcl (/ file handle dcl_id dcl_res)

  (setq file   (strcat (vl-string-right-trim
       "\\"
       (vla-get-tempfilepath
         (vla-get-files
           (vla-get-preferences (vlax-get-acad-object))
           ) ;_ end of vla-get-files
         ) ;_ end of vla-get-tempfilepath
       ) ;_ end of vl-string-right-trim
           "\\dlg.dcl"
           ) ;_ end of strcat
  handle (open file "w")
  ) ;_ end of setq
  (foreach item
     '("dlg:dialog{label=\"lispru dialog\";"
       ":toggle{key=\"check1\";label=\"Вариант 1\";}"
       ":toggle{key=\"check2\";label=\"Вариант 2\";}"
       ":radio_button{key=\"var\";label=\"Сложить варианты\";}"
       ":radio_button{key=\"text\";label=\"Сложить тексты\";}"
       "ok_cancel;" "}")
    (write-line item handle)
    ) ;_ end of foreach
  (close handle)
  (setq dcl_id (load_dialog file))
  (new_dialog "dlg" dcl_id)
  ;; Устанавливаем значения
  (set_tile "check1" "1")
  (set_tile "check2" "1")
  (set_tile "var" "1")
  (action_tile "accept" "(done_dialog 1)")
  (action_tile "cancel" "(done_dialog 0)")
  (setq dlg_res (start_dialog))
  (unload_dialog dcl_id)
  (if (= dlg_res 1)
    (alert "Был нажат ОК, действуем дальше")
    (alert "Отмена")
    ) ;_ end of if
  ) ;_ end of defun

Я специально не прорабатывал полную логику работы диалога (так, например, можно было для toggle написать специальный обработчик, который блокировал бы, к примеру, кнопку ОК и / или radio_button; или при выборе одного из значений radio_button выдавать различный результат в том же самом alert). Задача состояла не в этом.

Обратите внимание - конечный пользователь получает один (!) лисп, который по мере надобности создает файл диалога и корректно его отображает. Уже не надо беспокоиться о том, чтобы dcl-файл лежал в строго определенном месте.

Размещено в Код LISP, Новости, Функции LISP · Метки: ,



Комментарии

Есть 12 коммент. к “Диалоговые окна dcl – зло? Или все же нет?”
  1. mr_runa пишет:

    Есть полезная функция для создания имени и пути временного файла vl-filename-mktemp
    Для dcl-файла можно использовать шаблон

    1
    (vl-filename-mktemp "" "" ".dcl")

    Есть примечание "все файлы с именами, сгенерироваными функцией vl-filename-mktemp во время сеанса работы в среде Visual LISP, автоматически удаляются при выходе из Visual LISP" Что значит это примечание я не знаю. Думаю лучше по окончании работы приложения удалять все созданные dcl-файлы.

  2. Кулик Алексей aka kpblc пишет:

    Дело в том, что имя этого временного файла назначается "от балды". И либо потом надо предусматривать его принудительное удаление (а я что-то не сильно уверен в успешности такого подхода), либо махнуть на все рукой и оставлять в %temp% безумное количество мусора.
    Мне вариант предопределенного имени файла показался более жизненным, т.к. это позволяет (при минимуме фантазии) как минимум отделить файлы собственных диалогов от "всего остального". Хотя, конечно, вариант "на любителя" :)
    P.S. Да, "при выходе из VisualLISP". А если файл скомпилирован в fas / vlx? Где там будет выход? А раз выхода нет, так и удаляться не факт что будет.

  3. Do$ пишет:

    Алексей, vl-filename-mktemp возвращет имя созданного файла (с полным указанием пути к нему, vl-filename-mktemp также позволяет задавать префикс для имен создаваемых временных файлов). Я сохраняю его в переменной, и когда файл больше не нужен, удаляю его с помощью vl-file-delete. У меня учетка ограниченная - проблем никаких не возникало.

  4. Do$ пишет:

    "Невозможно создать диалог с изменяемыми (программно или пользователем – неважно) размерами."
    С созданием файла DCL "на лету", почему бы и нет? Вычислил - необходимые размеры в коде - преобразовал в строки - сформировал DCL с нужными размерами диалога :) Или что-то другое имелось в виду?

  5. Кулик Алексей aka kpblc пишет:

    Имелось в виду прежде всего изменение размеров окна пользователем (ну, по аналогии с окном открытия файла). Но и программное решение - тоже "не гуд" ИМХО. Может быть выбрана настолько замороченная схема Windows, что стандартные средства ее обработают нормально, а вот собственные вычисления не дадут приемлемого результата.
    Я с подобным на VB6 сталкивался...

  6. trir пишет:

    А я на VBA делал Немодальное окно, там в АкАд'е есть специальный компонент - кидаешь его на форму и АкАд приглядывает за твоим окошком...

  7. Кулик Алексей aka kpblc пишет:

    trir, вообще-то разговор был не об этом...

  8. Maksim пишет:

    После загрузки лиспа и вызова функции автокад выдает ошибку "Не удается открыть файл C:\TEMP\dld.dcl -- error 0"

  9. Кулик Алексей aka kpblc пишет:

    Исправил написание кода, спасибо.

  10. Максим пишет:

    Добрый день! В 2021 появилась ошибка с error 0. Я так понимаю, это посвящены вот эти статьи и это вопрос безопасности?
    https://autolisp.ru/2013/10/08/cant-check-dcl/
    https://autolisp.ru/2021/04/11/lispsys-and-dcl-on-the-fly/

  11. Кулик Алексей aka kpblc пишет:

    С чего вдруг? Ну даже если забыть, что поиск в гугле "AutoCAD 2021 error 0" показывает в лучшем случае ошибки лицензирования.

Трэкбэки

Узнайте, что другие говорят про эту заметку...
  1. […] диалоги “на лету” (пример можно посмотреть здесь). Но иногда бывает, что dcl достаточно сложен, и хочется […]



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


Я не робот.