Автоматизированное формирование меню надстройки для nanoCAD

Разработчики программных решений для nanoCAD, имея потребность создать для своего плагина меню (здесь и далее под словом «меню» будем понимать вкладки и кнопки в ленточном и/или классическом интерфейсе программы), сталкиваются со следующими проблемами:

  1. Не понятно, какие файлы создавать и что в них прописывать. Существующие примеры .cfg/.cuix файлов (которые можно найти в папках nanoCAD или попробовать сделать вручную через создание «кнопок» в настройках nanoCAD) содержат в себе очень много не очень понятного текста. Консолидированной информации по этому поводу ни для nanoCAD, ни для других CAD-платформ нами найдено не было.

  2. Для создания каждой отдельной «кнопки» в меню необходимо прописать достаточно большое количество текста. Хочется иметь какое-то средство автоматизированной генерации обозначенных файлов.

Актуальность этих проблем поднялась в нашем Telegram-чате nanoCAD API.

Обе проблемы постараемся решить в этой статье.

Важно отметить, что в статье будет рассмотрен минимальный рабочий вариант разработки меню с нашей точки зрения. Представленное решение не позиционируется, как идеальное, и мы готовы выслушать комментарии с дополнением. Также обратим внимание, что описываемый подход используется во всех плагинах для nanoCAD, разрабатываемых командой TBS. В качестве «боевого» примера будет использоваться плагин TBS Plus, функционал которого на момент написания статьи содержит больше 70 команд (и соответствующее количество «кнопок» в меню).

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

Часть 1. Ручное формирование файлов меню надстройки для nanoCAD

Итак, первое, с чего мы должны начать — создание конфиг файла: создаём обычный текстовый файл с любым именем и расширением ».cfg». У нас это будет «Test.cfg».

ВАЖНО — кодировка файла — «UTF-8 with BOM». Замечено, что «UTF-8» в данном случае не подходит.

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

Для того, чтобы отслеживать каждый шаг, сразу «подключим» наш файл к программе nanoCAD. Для этого открываем файл »%AppData%\Nanosoft\nanoCAD x64 23.0\Config\nanoCAD.cfg» (с поправкой на версию nanoCAD) и в самый конец добавляем строку, содержащую путь к нашему .cfg файлу, в нашем случай:  
#include «C:\Test\Test.cfg»

d76a33cfeae8126677a0df2962ab66a3.png

ВАЖНО — созданный нами .cfg файл должен «лежать» на диске C. Иначе есть шанс, что он не загрузится в программу.

Далее сохраняем файл «nanoCAD.cfg» без изменения кодировки и закрываем его, он нам больше не понадобится.

Открываем созданный ранее файл («Test.cfg») с помощью любого текстового редактора.

Для начала нам нужно «зарегистрировать» все нужные нам команды. Для этого пишем следующее (переносы строк добавлены исключительно для удобства восприятия)

[\configman]

[\configman\commands]

[\configman\commands\TBSPlus_TextCalculator]

weight=i10 

cmdtype=i1

intername=sTBSPlus_TextCalculator

DispName=sКалькулятор текста

StatusText=sПалитра, содержащая инструменты для арифметических операций с числами в текстовых объектах

BitmapDll=sicons\TBSPlus_TextCalculator.ico

[\configman\commands\TBSPlus_LevelingInstrument]

weight=i10

cmdtype=i1

intername=sTBSPlus_LevelingInstrument

DispName=sНивелир

StatusText=sПалитра для измерения превышения и проложения между точками

BitmapDll=sicons\TBSPlus_LevelingInstrument.ico

08caf69e6dbc56921c3da555b1f49d21.png

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

79f6931e25bb907bf4692c7bb8d1bb78.png

Отметим лишь несколько моментов.

  1. Перед значением параметров указывается префикс, соответствующий типу данных:
    «s» для всех строковых значений (имен, описаний, путей и т.д.);
    «i» для целочисленных значений;
    «f» для булевых значений («f0» — false, «f1» — true);

  2. Внутреннее имя — то имя, которое объявлено в нашем приложении (в случае .NET это значение атрибута «CommandMethod») и используется при вызове команды с командной строки. Это же имя будем использовать в нашем файле в дальнейшем, для создания «кнопки» для нашей команды.

  3. В более сложных случаях для команды можно добавить ключевое слово: Keyword=s^C^C (setq a 1)(load«arh_atr»)(arh_atr)
    Это значение будет «отправлено на консоль» в момент обращения к команде.

  4. Путь к иконке. Здесь задан относительный путь (помним про указанный префикс). В папку icons, «лежащую» рядом с нашим «Test.cfg» файлом, сохраним две иконки с соответствующими именами. Естественно, имя иконки может отличаться от внутреннего имени команды, оно должно лишь совпадать с именем, указанным в «Test.cfg» файле. Из нашего опыта разрешение 32×32 хорошо подходит, как для больших, так и для маленьких кнопок. Используемые иконки расположены в архиве, приложенном к статье.

Сохраняем файл (всегда помня о кодировке) и открываем nanoCAD (необязательный шаг). Здесь и далее важно понимать, что наш файл «считывается» программой во время загрузки, поэтому после внесения любого изменения в .cfg файл для того, чтобы увидеть эти изменения в nanoCAD, мы должны перезагрузить программу.

На текущем этапе наши команды были зарегистрированы (можно увидеть в настройках nanoCAD, скрин выше). Теперь при консольном обращении в списке команд мы видим наши команды, во-первых, с иконками, а во-вторых, с полным (в нашем случае — русскоязычным) названием.

9db6b97007f5c53193e22cd9cce8d3b1.png

Теперь, добавим вкладку с нашими кнопками в классическое меню. Для этого в конец нашего «Test.cfg» добавляем следующее:

[\menu]

[\menu\Test_Menu]

Name=sTBS Plus

[\menu\Test_Menu\Инструменты]

name=sИнструменты

[\menu\Test_Menu\Инструменты\sTBSPlus_TextCalculator]

name=sКалькулятор текста

Intername=sTBSPlus_TextCalculator

[\menu\Test_Menu\Инструменты\sTBSPlus_LevelingInstrument]

name=sНивелир

Intername=sTBSPlus_LevelingInstrument

474f8d26f024fe5744d61cdda208a247.png

Таким образом, мы создали вкладку «Тест», в нее добавили подпункт «Инструменты», а в него уже «вложили» две «кнопки». Вложенность может быть произвольной и многоуровневой. Важно отметить, что внутреннее имя команды должно совпадать с внутренним именем одной из зарегистрированных в программе команд. Очевидно, команда с таким внутренним именем будет выполнена при нажатии на данную «кнопку».

Сохраняем файл и открываем nanoCAD (для самопроверки, шаг опять же необязательный).

Переключить интерфейс с ленточного на классический можно с помощью кнопки в правом верхнем углу (выделена на скрине).

Переключить интерфейс с ленточного на классический можно с помощью кнопки в правом верхнем углу (выделена на скрине).

Как видим, в классическом меню появилась наша вкладка с вложенным подпунктом, в который в свою очередь вложены две «кнопки».

Последнее, что мы сделаем в нашем «Test.cfg» — создадим панель инструментов (toolbar) с нашими кнопками.

В конец файла «Test.cfg» пишем:

[\toolbars]

[\toolbars\Test_Инструменты]

name=sTest_Инструменты

Intername=sTest_Инструменты

[\toolbars\Test_Инструменты\TBSPlus_TextCalculator]

Intername=sTBSPlus_TextCalculator

[\toolbars\Test_Инструменты\TBSPlus_LevelingInstrument]

Intername=sTBSPlus_LevelingInstrument

c89794dec92c850ed5024c9dce2c070e.png

Мы создали панель инструментов Test_Инструменты и добавили на нее две кнопки.

Сохраняем файл, открываем nanoCAD и видим результат (для отображения панели инструментов нужно переключить интерфейс на классический вид и в меню «Панели инструментов» установить видимость созданной нами панели).

fadb7351ecbfe2d157809b6dff2b7861.png

UPD:
Как видим, с созданной нами панелью инструментов есть две сложности: во-первых, она не отображается по умолчанию, во-вторых, она не закреплена и после включения видимости отображается в случайном (с той точки зрения, что мы его не определяли) месте на экране. Эти особенности, а также способ их регулирования осветил участник чата nanoCAD API, рассмотрим это решение.
В конец нашего файла «Test.cfg» добавим следующее:

[\toolbarspos]

[\toolbarspos\Test_Инструменты]

DockPosition=sTop

row=i0

pos=i1

InitialVisible=f1

Теперь наша панель инструментов будет отображаться по умолчанию и будет размещена в указанном нами месте (сверху, в первом ряду; на второй позиции). Возможные значения параметров можно посмотреть в настройках интерфейса nanoCAD (русскоязычные названия приведены на скрине):

b5e95c73e557d72a46618f57e18fc350.png

Переходим к формированию ленточного меню.

Основные элементы, о которых пойдёт речь дальше, подписаны на скрине:

b2c77baf687ee2bececa5a5d2a898a09.png

Создаём текстовый файл с любым названием и расширением ».cui» (например, «RibbonRoot.cui»). Внутреннее содержание файла будет соответствовать xml разметке; кодировка файла — «UTF-8» или «UTF-8 with BOM».

Открываем созданный файл любым текстовым редактором и пишем в него:

            

                        

                                   

                                   

                        

            

            

                        

                                   

                        

            

68287a34fb799ace389c856376e18ff7.png

Разберем состав документа поэлементно.

RibbonRoot — корневой элемент документа.

В него будут вложены два элемента: RibbonPanelSourceCollection и RibbonTabSourceCollection.

Первый из них — это, как можно догадаться из названия, коллекция панелей. В RibbonPanelSourceCollectionбудем вкладывать элементы типа RibbonPanelSource (описание отдельной панели).

Атрибуты RibbonPanelSource:

  1. UID — идентификатор, который мы будем использовать в дальнейшем (для простоты примем такое же значение, как для атрибута Text, хотя, естественно, ничего не мешает нам написать здесь любое значение).

  2. Text — отображаемое название панели.

В элемент RibbonPanelSource у нас вложены элементы типа RibbonCommandButton, описывающие непосредственно кнопки.

Атрибуты RibbonCommandButton:

  1. Text — отображаемое название

  2. ButtonStyle — размер кнопки и наличие текста. Варианты «LargeWithText», «LargeWithoutText», «LargeWithHorizontalText», «SmallWithText», «SmallWithoutText». В нашем примере мы использовали «LargeWithText» — большая с текстом. Значение остальных вариантов можно так же понять из перевода

  3. MenuMacroID — внутреннее имя выполняемой команды.

Второй элемент, входящий в корень документа — RibbonTabSourceCollection. Опять же из названия можно понять, что это коллекция вкладок. В этот элемент будем вкладывать элементы типа RibbonTabSource, каждый из которых соответствует отдельной вкладке.

Атрибуты RibbonTabSource:

  1. Text — отображаемое название вкладки

  2. UID — уникальный идентификатор вкладки. Мы нигде не будем использовать его, но этот атрибут должен быть, поэтому запишем туда какое-либо уникальное значение.

В элемент, соответствующий создаваемой нами вкладке, мы должны вложить необходимые панели, создаваемые выше. Для этого нам потребуется элемент RibbonPanelSourceReference с единственным атрибутом PanelId, ссылающимся на идентификатор (UID) панели.

Итак, сохраняем этот файл.

Далее создаём архив типа «zip» (этот тип проверен на практике, при этом, к примеру »7z», по невыясненной причине не подходит), называем его любым именем с расширением ».cuix» (в нашем примере — «Test.cuix»), в этот архив помещаем созданный ранее файл «RibbonRoot.cui». Таким образом,».cuix» представляет собой обычный архив, содержащий в себе xml файл, описывающий ленточное меню нашей надстройки.

«Подключим» сформированный файл в nanoCAD. Для этого в файл «Test.cfg» (например, в самое начало файла) добавим две строки:

[\ribbon\Test]

CUIX=s%CFG_PATH%\Test.cuix

Здесь »%CFG_PATH%\Test.cuix» — путь к созданному файлу «Test.cuix». При этом »%CFG_PATH%» — это путь к папке с текущим ».cfg» файлом, т.е. «Test.cfg». Такую запись удобно использовать, если файлы «Test.cfg» и «Test.cuix» расположены в одной папке.

Примечание: указанные строки можно было так же добавить и в ранее упоминаемый файл «nanoCAD.cfg» (при этом вместо %CFG_PATH% в общем случае нужно будет указывать абсолютный путь), но в целях минимизации записей в «nanoCAD.cfg» в нашей команде используется именно описанный выше вариант.

Запускаем nanoCAD:

02b43e2dac126e499c12b42fad298ac9.png

Итак, цель достигнута — создана новая вкладка на ленте, в ней создана одна панель, в которую добавлено две кнопки.

Рассмотрим еще пару полезных примеров.

1.  Кнопка с выпадающим списком (RibbonSplitButton)


RibbonSplitButton задается элементом одноименного типа.

Атрибуты RibbonSplitButton:

  1. Text — отображаемое название кнопки

  2. Behavior — поведение. В нашем примере будем использовать значение «SplitFollowStaticText» — выпадающая с запоминанием

  3. ButtonStyle — размер кнопки и наличие текста аналогично атрибуту элемента RibbonCommandButton.

Для того чтобы не регистрировать новых команд и не добавлять новые иконки, будем использовать уже знакомые нам две команды, меняя только их отображаемые имена (атрибут Text).

В элемент RibbonPanelSource вкладываем RibbonSplitButton, в который добавляем кнопки (аналогично предыдущему пункту), которые должны войти в выпадающий список.

            

            

8a649fa341fcafbb3e19054b81dc35a6.png

Сохраняем файл. Для того, чтобы увидеть результат в случае работы с ленточным интерфейсом не обязательно перезагружать nanoCAD, можно использовать команду «ЛЕНТАОБН».

Результат:

88354586b9185f7d118689fd3f787397.png

2.  Маленькая кнопка


Компоновка маленьких кнопок (кнопок со значением атрибута ButtonStyle — «SmallWithText» или «SmallWithoutText») производится немного сложнее.

Если мы попробуем использовать ту же логику и напишем так:

0d22215ff7c773930084fcb2a15b07e3.png

То получим не тот результат, который ожидается:

3c4e9cd7daf76bc342058bb673af863e.png

Внешне кнопки выглядят так, как нужно, но они расположены в один ряд. Чаще всего мы используем маленькие кнопки для экономии места, и хотелось бы, чтобы они скомпоновались в одну колонку.

Для этого используем еще два элемента — RibbonRowPanel и RibbonRow. RibbonRowPanel мы вложим в нужное место RibbonPanelSource, а внутрь RibbonRowPanel вложим от одной до трёх маленьких кнопок, которые мы хотим скомпоновать в колонку. При этом каждый вкладываем элемент RibbonCommandButton «обернём» в RibbonRow.

            

                        

            

            

                        

            

            

                        

            

6c5ba2fb04a33809d375137780a29f19.png

Сохраняем файл, обновляем ленту:

0187aeffbeeb10797dda0b0d9c0a08d9.png

Вынесем часть элементов на отдельную панель:

            

                        

                                   

                                   

                        

                        

                                   

                                               

                                               

                                   

                                   

                                               

                                                           

                                               

                                               

                                                           

                                               

                                               

                                                           

                                               

                                   

                        

            

            

                        

                                   

                                   

                        

            

0d3c2e699cfcacd3cee2638c35ee4479.png

Результат:

b085e6e589f9ebd0a83eaf74dbe0417b.png

Мы рассмотрели основные приёмы формирования ленточного интерфейса, переходим к автоматизации данного процесса.

Часть 2. Автоматизированная сборка файлов меню

По ссылке можно найти репозиторий с нашим решением MenuFilesGen.

Концепция программы заключается в генерации .cfg и .cuix файлов по описанной выше структуре на основании .tsv таблицы строго определенной структуры.

Примечание: формат .tsv был выбран как простой для чтения формат, не имеющий недостатка .csv, накладывающего ограничение на использование запятой (или точки с запятой), которая может использоваться в таблице (к примеру, в описании команды).

Структуру таблицы можно изучить на примере уже упомянутого TBS Plus.

Фрагмент таблицы:

395f5d366a61b38478f8be9afbfd77a6.png

Из неочевидного, считаем нужным прокомментировать:

1. «RibbonSplitButton» — используется для объединения нескольких кнопок в RibbonSplitButton. Для этого нужно прописать в столбец одинаковое название для нужных строк. К примеру, для строк 40–42 со скрины имеем:

d1030407b2fbbe513c80d4dc9dacfaff.png

2. «Не отображать в меню» — если True, команда не будет учтена в меню (используется, к примеру, для тестовых команд на время разработки).

Таким образом, достаточно сформировать таблицу с описанием команд вашего решения, сохранить ее в формате .tsv, запустить MenuFilesGen и указать путь к созданному .tsv файлу. Программа создаст .cfg и .cuix файлы в той же папке. Заметим, что сгенерированные файлы будут иметь название файла с таблицей. Это же название будет и у созданной вкладки. Тут же важно обратить внимание, что одна таблица подразумевает описание одной вкладки.

Дальше остаётся добавить сгенерированные файлы в инсталлятор вашего плагина и «научить» его прописывать путь к вашему .cfg файлу в файл «nanoCAD.cfg». Готово, ваша надстройка теперь обладает интерфейсом, перекомпоновка которого стала очень простой.

Примечание: важно, что .cfg и .cuix файлы у пользователя должны быть расположены в одной папке, в этой же папке должна быть папка «icons», содержащая набор всех необходимых иконок, при этом имена иконок должны совпадать с внутренними именами команд.

Мы намеренно принимаем такие допущения как использование конкретного формата (.tsv), использование имени этого файла, сохранение новых файлов рядом с исходным и тд, потому что понимаем, что каждый разработчик, кто возьмет MenuFilesGen на вооружение, сможет абсолютно без труда настроить его под себя и сократить количество кликов до минимума.

Расскажем, к примеру, как это работает у нас. В проекте MenuFilesGen мы создали несколько конфигураций (по одной на каждый разрабатываемый нами плагин для nanoCAD). Для каждого плагина прописали нужные пути и ссылки:

f17443f294688585a0a32aa75eca721e.png

Теперь для сборки файлов меню выбираем нужную конфигурацию и запускаем проект. Программа сама скачает таблицу в нужном формате и сохранит сгенерированные файлы в нужную папку, откуда их в дальнейшем «заберёт» инсталлятор.

Таким образом, предложенное в этой статье решение является, как нам кажется, хорошей основой для инструмента, который вы можете довести до идеала с учётом специфики разработки в вашей компании.

Материалы статьи можно изучить подробнее по ссылке.

⚫️ Мы, как поставщик ПО Нанософт и CSoft, помогаем нашим клиентам переносить привычные скрипты, плагины и другие средства автоматизации из среды AutoCAD в nanoCAD. Если перенос в исходном виде по каким-то причинам не возможен, то мы можем рассмотреть возможность добавления в инструменты наших плагинов нужного функционала, либо создать необходимый плагин с нуля в том виде в котором он вам необходим.
Подробнее о наших разработках по ссылке.

© Habrahabr.ru