Тема: LISP. Общее. Загрузка файла. Вызовы функций

Здесь: https://www.caduser.ru/forum/topic19684.html возник вопрос о загрузке функций. Все нижесказанное не претендует на оригинальность и не является истиной в последней инстанции. Маленькое уточнение - работает на ура.
Немаленькое уточнение - идеи слизаны с "САПР на базе..."
Итак, начнем.
Для начала создадим папку, в которой будут храниться наши функции и программы - например, c:\adds.lsp. Дальше копия FAQ:

Запускаем AutoCAD, _options -> Files -> Support files search -> Добавить эту папку.

Каждый код, который публикуется здесь, сохраняется в отдельном файле. Например, опубликована функция (что уж ходить далеко за примером, kpblc-extlen - см. ссылку выше). Выделили код, как описано в FAQ, сохранили под именем kpblc-extlen.lsp в папке c:\adds.lsp.
Примечание: желательно сохранять функцию в файле с таким же именем, т.е. для функции (func_deg2rad) использовать имя "func_deg2rad.lsp".
Для примера сделаем еще одну функцию, но с запросом в ком.строке значения удлинения линии - kpblc-extlen-with-quest:

(defun kpblc-extlen-with-quest(/ qu_pluslen)
  (setq qu_pluslen (getreal "\nВведите значение удлинения : "))
  (kpblc-extlen qu-pluslen)
);_defun

и сохраняем его в файле kpblc-extlen-with-quest.lsp
В той же папке делаем лисп примерно следующего вида:

(defun load-apps( / app_list app_counter)
  (vl-load-com)
  (setq app_list (list
       "kpblc-extlen"
       "kpblc-extlen-with-quest"
       )
    );_setq
  (foreach app_counter app_list
    (load app_counter)
    );_foreach
  (if (not *kpblc-activedoc*)
    (setq *kpblc-activedoc* (vla-get-activedocument (vlax-get-acad-object)))
    );_if
  );_defun

Этот файл можно постоянно наращивать, добавляя (или удаляя) строки из списка app_list. Для макросов меню делаем следующее:

^C^C(if (not *kpblc-activedoc*) (load "load-apps"));function_name;

При условии изменения списка app_list можно просто перегрузить этот лисп-файл, и новые функции станут доступны для использования.
В случае работы одновременно с несколькими документами лисп можно перегружать с использованием команды

(vl-load-all "load-apps")

Лично я использую функцию (load ...), т.к. подавляющее большинство функций я пишу без префикса "с:". Если постоянно используется префикс "с:" в объявлениях функций, можно использовать (autoload ...)
Надеюсь, никого не обидел.

Re: LISP. Общее. Загрузка файла. Вызовы функций

Тут еще надо подумать. Честно скажу, достаточно сложно описано. А чем хороша "C:ИМЯ"? Ясно, чем - вызывается повторно нажатием "Enter", "Space" ("Пробел") или правой кнопки мыши.

Re: LISP. Общее. Загрузка файла. Вызовы функций

Дело в том, что при обращении к таким функциям из-под лиспа (у меня практически все такое) приходится писать не (kpblc-extlen), а (c:kpblc-extlen). И, кроме того, в передачей параметров через меню у меня что-то с c:* не срослось.

Re: LISP. Общее. Загрузка файла. Вызовы функций

kpblc пишет:

Лично я использую функцию (load ...)

А я, в случае разовой загрузки просто перетаскиваю файл из проводника в окно акада... Загружается без проблем!
Для новичков совет проще некуда - лишь бы был лисп-файл.

Re: LISP. Общее. Загрузка файла. Вызовы функций

> Евгений Елпанов
По вкусу ИМХО. Мне проще сделать appload и вперед ;)
Кстати, совсем упустил из виду!
В конце файла load-apps.lsp надо добавить строку

(load-apss)

Либо, как вариант, переименовать функцию в s::startup - тогда она будет выполняться при каждой загрузке файла.

Re: LISP. Общее. Загрузка файла. Вызовы функций

> kpblc
Тут мне недавно подсказал ЯR , как делать объявление функции без префикса "с:"

(defun test ()
;....
)
(vl-load-com)
(vlax-remove-cmd "test")
(vlax-add-cmd "test" 'test)

Re: LISP. Общее. Загрузка файла. Вызовы функций

Так что, считаем, что с загрузкой и запуском все ясно, особенно новичкам, и выдаем только коды?

Re: LISP. Общее. Загрузка файла. Вызовы функций

Теперь по теме.
Так как я ленивый человек, а лень как известно является двигателем прогреса, то я прочитав книгу Полищука вынес маленькую идею:
В некоторой переменной хранится путь к моей библиотеке лиспов, при этом библиотека организована с подпапками(разбита ещё на разделы).
Потом я загружаю свою функцию(lisp) поиска лисп-файлов(включая в любые вложения в подпапки) в этой папке. Я получаю список файлов и загружаю их при помощи mapcar.
Обычно прога и переменная у меня хранится в настройках моего меню, в файле mnl, но возможно создать стартовый лисп файл и его автоматом загружать стандартными средствами акада.
Таким образом пользователю достаточно подшить лиспик автозагрузки и присвоить значение некоторой переменной путь к папке (например "D:\\AutoCad_ru_Lisp" ) и в эту папку просто сохранять лиспфайлы и даже их оформлять на разделы(делать вложенные папки), а прога автоматом их будет загружать при запуске и останется только настроить кнопочки или вводить команду в комстроке.
К сожалению на работе кода лиспа у меня нет но будучи дома заброшу. Но смысл надеюсь понятен. Прошу расмотреть и этот вариант загрузки - нужен/ненужен

Re: LISP. Общее. Загрузка файла. Вызовы функций

Однозначно - нужен! Мой подход работать может только для одноуровневых папок (т.е. никаких вложений). Код весьма интересен.

Re: LISP. Общее. Загрузка файла. Вызовы функций

Послушайте, вы говорите на языке профи, а для профи вообще этот раздел не нужен, IMHO. А вообще-то... Вот у меня есть папка AKD, в которой есть папки AKD\B (пиктограммы BMP), AKD\D (блоки DWG), AKD\L (файлы LSP и DCL), AKD\M (файлы меню), AKD\S (слайды SLD и SLB).
Разве большой труд прописать эти папки в путях доступа? Папка AKD скопирована на полсотни компьютеров, причем диски могут не совпадать.
Зачем мне возиться с дополнительными программами поиска лиспов (а как же остальные файлы), если я спокойненько при установке AutoCAD'а прописал эти 5 папок в путях доступа и забыл о них нафиг. А сама папка AKD занимает около 60 Мб и если все файлы вывернуть в 1 одну папку, то выйдет более 2000 файлов. А в графическом меню, в которых надо указывать, где находится слайд, а где блок? Не, ваш метод хорош, так сказать, "для себя", для профи. Новичку этого не надо, IMHO.

Re: LISP. Общее. Загрузка файла. Вызовы функций

> Владимир Громов
Это хорошо, пока количество папок в support files search не превышает некий предел (то ли 12 штук, то ли 16 - не помню) - после этого кад может и не найти файлы в новых папках. А таких подпапок у меня может быть штук 20-30 (и это минимум!)
По поводу слайдов: их же можно объединять в библиотеки (у Геннадия Поспелова на сайте есть отличная программка, выполняющая эти задачи - Slide Manager называется). Исходные слайды тогда можно убирать.
поиск блоков (в качестве предложения, не проверял) - можно попробовать через (findfile) найти определенный файл, который, во-первых, прописан в путях поддержки, а, во-вторых, лежит в "корне" для блока. Например, ищем файл "blocks.def". Находим и получаем путь "c:\adds.lsp" в каталоге c:\adds.lsp\blocks лежат блоки, в каталоге c:\adds.lsp\slides - слайды. В таком случае можно написать функцию вставки блока из каталога c:\adds.lsp\blocks? указывая в качестве параметра только имя блока. Если будет время и возможность, накатаю такое (только в готовые программы кидать не буду).
Есть еще один вариант, который вообще убирает все эти проблемы. В VisualLISP IDE можно сделать проект, в котором включены все файлы (и плевать, где они физически находятся), а потом его скомпилировать в fas-файл. Но это имхо только для законченных систем.
> ZZZ : код все равно, плиз, опубликуй
---
ИМХО

Re: LISP. Общее. Загрузка файла. Вызовы функций

> ZZZ
Абсолютно с вами согласен!
У меня в автозагрузке вообще пусто - каждый раз гружу по необходимости. Единственно прописал

(vl-load-com)

Каждый раз выполняю разные задачи, а "чистый" акад работает шустрее...

Re: LISP. Общее. Загрузка файла. Вызовы функций

> ZZZ
У меня в автозгрузке вообще ничего нет, кроме загрузки системных файлов. У меня не 20 файлов, а 200, задействованных в разных меню. Но поскольку AutoCAD'у известно с самого начала, где находятся мои файлы, то мне и достаточно написать в пункте меню или в макросе кнопки:

^C^C^P(if (null C:BI_LINE) (load "bi_line")) BI_LINE

Здесь, как вы видете, не указан путь вообще, выполняется проверка на наличие в памяти функции, и если ее нет, то загружается файл и вызывается сама функция. Да вы это все сами знаете. Я этим пользуюсь лет 10. Мне достаточно только записать соответствующий файл в соответсвующую папку и модифицировать меню, что неизбежно и - все. А в путях доступа дополнительные 5 пунктов - это мелочь, у меня бывало и 10 и все работало.

Re: LISP. Общее. Загрузка файла. Вызовы функций

Вот хоть бы какой нибудь начинающий пользователь написал бы сюда, что он понял из всего этого диалога. Или поставьте себя на их место - вот вы работаете в AutoCAD'е год с кепкой, книжек нет, AutoLisp'а вы знаете, а тут вам гутарят про MAPCAR...

Re: LISP. Общее. Загрузка файла. Вызовы функций

Вот, например, фрагмент меню для раздела проекта "Слаботочная сигнализация":

 [->Слабые токи]
   [Подсчет и сортировка блоков]^C^C^P(load "sp_block")
   [Подсчет и сортировка блоков на слоях CC_]^C^C^P(load "ss_block")
   [Подсчет суммарной длины кабелей]^C^C^P(load "sp_kbl")
   [Трассировка линий связи]^C^C^P(load "trassa")
   [--]
   [->Блоки]
   [Палатная сигнализация]^C^C^P(if (not C:ПАЛАТА) (load "ddpalata")) ПАЛАТА
   [Сети Радио]^C^C^P(if (not C:РАДИО) (load "ddradio")) РАДИО
   [Часы]^C^C^P(if (not C:ЧАСЫ) (load "ddclock")) ЧАСЫ
   [Телефонная сеть]^C^C^P(if (not C:ТЕЛЕФОН) (load "ddtelefon")) ТЕЛЕФОН
   [Пожарная сигнализация]^C^C^P(if (not C:ПОЖАР) (load "ddpogar")) ПОЖАР
   [Охранная сигнализация]^C^C^P(if (not C:ОХРАНА) (load "ddohrana")) ОХРАНА
   [Контроль доступа]^C^C^P(if (not C:КОНТРОЛЬ) (load "ddkontrol")) КОНТРОЛЬ
   [Сети ТВ]^C^C^P(if (not C:ТВ) (load "ddtv")) ТВ
   [<-<-Локальные сети]^C^C^P(if (not C:ЛВС) (load "ddlvs")) ЛВС

Там, где записана только "load...", функция определена через (apply '(lambda ()...
Никаких путей и меню одинаковое у всех.

Re: LISP. Общее. Загрузка файла. Вызовы функций

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

Re: LISP. Общее. Загрузка файла. Вызовы функций

Я, начинающий юзер. Начинаю работать с LISP; VBA приложениями.
Ничего не понял. Что за команды? Что за "соответствующие папки"?
В Windows XP куда- то все проваливается. Хрен найдешь. Иконки рисую каждый раз как начинаю работать:(  Или Владимир пишет: "Никаких путей и меню одинаковое у всех.". Что это?

Re: LISP. Общее. Загрузка файла. Вызовы функций

Pancake, не могу удержаться... Я тут упорно проталкиваю мысль о ПРОСТЕЙШЕМ и БЫСТРЕЙШЕМ способе загрузки и выполнении конкретной программы, которую пользователь выберет когда-нибудь в этом разделе, и чтобы описание этого способа можно было бы отправить в FAQ. Потом пользователь заматереет и сам захочет усовершенствовать этот способ, тогда он с новым интересом прочитает то, что здесь написано. Мне и самому все это интересно и полезно. Но сейчас эта тема напоминает историю с пылесосом, описанную где-то на этой конференции, и получается, что Forma как раз и спрашивает про пылесос, а ему разъясняют про танк Т-82.

Re: LISP. Общее. Загрузка файла. Вызовы функций

Ок. самый простой - складывать все файлы в одну папку и делать как Вы описываете.
---
ИМХО

Re: LISP. Общее. Загрузка файла. Вызовы функций

> Владимир Громов
Куда уж проще...
Один раз прописываем одну программу указав одну папку и ВСЕ! После достаточно положить файл программы в папочку и все заработает.
> kpblc
Зачем же в одну папку... можно и вложенные папки использовать, чтоб понятнее было какие программы для каких целей.

Re: LISP. Общее. Загрузка файла. Вызовы функций

kpblc пишет:

Дело в том, что при обращении к таким функциям из-под лиспа (у меня практически все такое) приходится писать не (kpblc-extlen), а (c:kpblc-extlen). И, кроме того, в передачей параметров через меню у меня что-то с c:* не срослось.

Если читаете книгу "САПР на базе AutoCAD...", то внимательнее. Там все расписано.
Функции типа (C:XXX) имитируют команды Автокада. Чтобы бестолковые буржуи могли набрать в командной строке. Плюс повтор нажатием Enter.
Но в такие функции нельзя передавать параметры. И это их главный недостаток. Но никто же не заставляет так писать. Пишите обычные функции, передавайте в них аргументы и используйте одну функцию для тысяч "команд", привязанных к меню.
Называть свои "команды" ТЕЛЕФОН, ОХРАНА и т.п. можно, пока их немного. А как только начнете делать программы для пяти сотен "электрических" рисунков, то и слов не хватит, и запомнить их будет невозможно. Все равно программа будет привязываться к меню или кнопке. А значит и ее осмысленное имя значения не имеет, не надо использовать префикс C:, и все команды можно заменить на одну единственную.
Проверка типа (if (not C:ТЕЛЕФОН) (load "ddtelefon")) дает сомнительную экономию на сотых долях секунды загрузки. Зато, если вы отрабатываете программу, и она изменена, то ее придется загружать заново как-то вручную.
Вообще надо разделять свои ЛИСПы на библиотеки функций, которые загружаются один раз, и на прикладные программы, максимально использующие библиотеки. Прикладные программы - загружать из меню. И не заставлять пользователя что-то набирать после загрузки. Программа должна загрузиться и сама запуститься.
Конструкция
(mapcar 'load
(z-files-in-directory (strcat *z_root_dir* "Lisp") "*.lsp" t)
)
может и не применяться. Лучше сделать проект своей библиотеки в Visual Lisp и компилировать кучу своих LSP в один FAS. Это удобнее и компилированные работают намного быстрее.

Re: LISP. Общее. Загрузка файла. Вызовы функций

> ShaggyDoc
Со своими программами все проще... Есть множество путей и компилировать свои программы в один проект несомненно лучше, но если обсуждать  FAQ для пользователей ни разу не сталкивавшихся с програмированием, необходимо найти простое и универсальное решение, которое будет доступно любому заинтересовавшемуся готовой програмкой...
Предлагать таким пользователям компилировать программы или даже перекомпилировать для добавления новых функций - помоему это слишком!
PS. А за книгу спасибо!
Нашел в ней много нового и интересного, хотя в своих программах использую только ваши приемы - чтоб использовать ваши функции нужно принять на вооружение большую часть ваших библиотечных функций. Я пока к этому не готов.

Re: LISP. Общее. Загрузка файла. Вызовы функций

Подниму-ка по новой тему ;) Учитывая предложения, получаем:

;|=======================================================================================
*    Выполняет загрузку всех файлов системы. За компанию создает ссылку на активный
* документ.
*    Поскольку основная область работы - ACAD LT + LT Extender, применение fas-файлов
* невозможно (ограничение LT Extender)
*    Дополнительно: файл сохранить как kpblc-loader.lsp и прописать его в автозагрузку.
*    Возможно применение иных имен, надо будет заменить строки
    filename          (findfile "kpblc-loader.lsp")
<...>
    (if (/= (vl-filename-base file_counter) "kpblc-loader")
на Ваше имя автозагрузчика
Также заменить слово "kpblc" на более привычное Вам.
=======================================================================================|;
(defun kpblc-loader( / files_list filename)
  (vl-load-com)
  (setq *kpblc-activedoc*   (vla-get-activedocument (vlax-get-acad-object))
    *kpblc-acad-object* (vlax-get-acad-object)
    *kpblc-model*       (vla-get-modelspace *kpblc-activedoc*)
    *kpblc-layout*      (vla-get-layouts    *kpblc-activedoc*)
    filename          (findfile "kpblc-loader.lsp")
    files_list (z-files-in-directory
             (vl-filename-directory filename)
             "*.lsp"
             T)
    );_setq
  ;;(mapcar 'load files_list)
  (foreach file_counter files_list
    (if (/= (vl-filename-base file_counter) "kpblc-loader")
      (load file_counter)
      );_if
    )
  )
;|=======================================================================================
*    функция z-files-in-directory возвращает список файлов находящаяся в заданной
* директории
*    Автор : ZZZ
*    Источник : https://www.caduser.ru/forum/topic19699.html
*
*    Параметры:
*        directory    путь к папке например "D:\\Мои документы\\ZEF\\Lisp"
*        pattern        шаблон например "*.lsp"
*        nested        искать в вложенных папках: t (да) или nil (нет)
* Пример вызова:
(z-files-in-directory "D:\\Мои документы\\ZEF\\Lisp" t)
(z-files-in-directory (vl-directory-files (strcat (vl-filename-directory (findfile "kpblc-loader.lsp")))) "*.lsp" T)
=======================================================================================|;
(defun z-files-in-directory (directory pattern nested /)
  (if nested
    (apply 'append
       (append (list (mapcar '(lambda (f) (strcat directory "\\" f))
                 (vl-directory-files directory pattern 1)
                 ) ;_ mapcar
             ) ;_ list
           (mapcar '(lambda (d)
                  (z-files-in-directory
                (strcat directory "\\" d)
                pattern
                nested
                ) ;_ z-files-in-directory
                  ) ;_ lambda
               (cddr (vl-directory-files directory nil -1))
               ) ;_ mapcar
           ) ;_ append
       ) ;_ append
    (mapcar '(lambda (f) (strcat directory "\\" f))
        (vl-directory-files directory pattern 1)
              ) ;_ mapcar
    ) ;_ if
  ) ;_ defun
(kpblc-loader)

Прошу ногами не бить - прога работает, бОльшего от нее (пока?) не требуется.

Re: LISP. Общее. Загрузка файла. Вызовы функций

> ShaggyDoc
Это не просто команды "ТЕЛЕФОН" и т.д. - это лисп-программы загрузки графических меню, в котором уже по 10 или более слайдов для вставки блоков для конкретного раздела слаботочной сигнализации, и уже там каждый блок имеет свое имя. Это так, для справки...

Re: LISP. Общее. Загрузка файла. Вызовы функций

; Проверка и задание переменной *z_root_dir* путь к папке автозагрузки
(if (or  (not (getenv "*z_root_dir*"))
  (= (getenv "*z_root_dir*") "")
  ) ;_ or
  (setenv "*z_root_dir*" (BrowseFolder))
)
; Обнуление переменной *z_root_dir* для задания нового пути к папке
;(setenv  "*z_root_dir*" "")
;|=======================================================================================
*    функция z-files-in-directory возвращает список файлов находящаяся в заданной
* директории
*    Автор : Зуенко Виталий (ZZZ)
*  Параметры:
*    directory  путь к папке например "D:\\Мои документы\\ZEF\\Lisp"
*    pattern    шаблон например "*.lsp" или список '("*.dwg" "*.dxf")
*    nested    искать в вложенных папках: t (да) или nil (нет)
* Пример вызова:
(z-files-in-directory "D:\\Мои документы\\ZEF\\Lisp" "*.lsp" t)
(z-files-in-directory "D:\\Мои документы\\ZEF\\Lisp" '("*.lsp" "*.fas") t)
=======================================================================================|;
(defun z-files-in-directory (directory pattern nested /)
  (if (not (listp pattern))
    (setq pattern (list pattern))
    ) ;_ if
  (if nested
    (apply
      'append
      (append
  (mapcar  '(lambda (_pattern)
       (mapcar '(lambda (f) (strcat directory "\\" f))
         (vl-directory-files directory _pattern 1)
         ) ;_ list
       ) ;_ lambda
    pattern
    ) ;_ mapcar
  (mapcar
    '(lambda (d)
       (z-files-in-directory
         (strcat directory "\\" d)
         pattern
         nested
         ) ;_ z-files-in-directory
       ) ;_ lambda
    (vl-remove
      "."
      (vl-remove ".." (vl-directory-files directory nil -1))
      ) ;_ vl-remove
    ) ;_ mapcar
  ) ;_ append
      ) ;_ append
    (apply
      'append
      (mapcar '(lambda (_pattern)
     (mapcar '(lambda (f) (strcat directory "\\" f))
       (vl-directory-files directory _pattern 1)
       ) ;_ list
     ) ;_ lambda
        pattern
        ) ;_ mapcar
      ) ;_ apply
    ) ;_ if
  ) ;_ defun
;(BrowseFolder)
;http://discussion.autodesk.com
(defun BrowseFolder ( / ShlObj Folder FldObj OutVal)
  (vl-load-com)
  (setq
    ShlObj (vla-getInterfaceObject
       (vlax-get-acad-object)
       "Shell.Application"
     )
    Folder (vlax-invoke-method ShlObj 'BrowseForFolder 0 "" 0)
  )
  (vlax-release-object ShlObj)
  (if Folder
    (progn
      (setq
  FldObj (vlax-get-property Folder 'Self)
  OutVal (vlax-get-property FldObj 'Path)
      )
      (vlax-release-object Folder)
      (vlax-release-object FldObj)
      OutVal
    )
  )
)
;загрузка библиотеки лиспов
(mapcar  'load
  (z-files-in-directory (getenv "*z_root_dir*") '("*.lsp" "*.fas" "*.vlx") t)
  ) ;_ mapcar

Таким образом Вы оформляете это всё (код приведён выше) в один лисп файл.
Этот лисп загружаете в автозагрузку, тут есть куча вариантов, например команда _appload потом в чемодан Contents.
В итоге при запуске када все лисп файлы находящееся в данной папке будут загружены. Остаётся только запускать проги - писать в командной строке название команды или прописать команды на кнопки и меню. Новый провереный лисп можно просто забрасывать в папку для автозагрузки лиспов.
Чтобы изменить путь к папке автозагрузки лиспов, достаточно будет в комстроке набрать (vl-bb-set '*z_root_dir* nil) и перезапустить акад.
P.S.
Благодарность:
1) Евгению Елпанову за предоставление функции BrowseFolder , пусть даже не им написанную, но гдето опубликованную и им найденную;
2) kpblc за поддержку;
3) {Smirnoff} (Fantomas) за находение ошибки в функции z-files-in-directory поиска в корневых каталогах и предоставлении идеи поиска по нескольким расширениям файлов;
4) Пелещук Николай и Зуев Сергей (ShaggyDoc) за хорошие книги.