Как указать переменное кол-во данных в команде acedCommand для AutoCAD2015-2016

Автор Тема: Как указать переменное кол-во данных в команде acedCommand для AutoCAD2015-2016  (Прочитано 9798 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Тема содержит сообщение с Решением. Нажмите здесь чтобы посмотреть его.

Оффлайн UraАвтор темы

  • ADN OPEN
  • Сообщений: 45
  • Карма: 0
Добрый день

В старых версиях AutoCAD использовалась команда acedCommand
Если требовалось вводить переменное кол-во данных, можно было команду разделять на несколько частей

Сейчас команду acedCommand заменили командой acedCommandS (когда имеется фиксированный набор данных)
и acedCommandС (если пользователь в ручную вводит неопределенное кол-во данных)

Вопрос как выполнить команду, в которой имеется переменное кол-во параметров, при этом данные содержатся в отдельном массиве (а не вводятся пользователем)?

Вот пример старого кода, который объединяет в полилинию указанные объекты.


Код - C++ [Выбрать]
  1.                 for (i=0; i < KolObj; i++) {
  2.                         if (AllObj[i].numTypeLine != 1) continue;       //проверка типа
  3.                         if (!newline) { //если это первый объект - выполняем заголовок команды
  4.                                 acedCommand(RTSTR, _T("_.pedit"), RTSTR, _T("_M"), 0);
  5.                                 newline=true;
  6.                         }
  7.                         acedCommand(RTENAME, AllObj[i].Name , 0);       //вводим неопределенный масив данных
  8.                 }
  9.  
  10.                 if (newline) { //если в наборе имеется хотябы один объект, завершаем команду
  11.                         acedCommand(RTSTR, _T(""), RTSTR, _T("_Y"), RTSTR, _T("_J"), RTSTR, _T("0.0"), RTSTR, _T(""), 0);
  12.                 }

Фактически команда делится на 3 части
Начало, ввод данных и завершение команды (если было введено одно или несколько значений)
      
Как мне выполнить команду в AutoCAD2015-2016?
« Последнее редактирование: 24-08-2016, 15:45:50 от Александр Ривилис »

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
1. Прочитай у меня в подписи как следует форматировать код на форуме.
2. Прочитай эту статью: http://adndevblog.typepad.com/autocad/2014/04/migration-after-fiber-is-removed-in-autocad-2015.html
Если будет что-то непонятно - продолжим. Хотя конечно лучше не пользоваться командными методами, а переписать всё средствами ObjectARX. Например так: http://adn-cis.org/kak-obedinit-otrezki-i-dugi-v-poliliniyu.html
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн UraАвтор темы

  • ADN OPEN
  • Сообщений: 45
  • Карма: 0
Спасибо за ссылки и код


Сделал тестовый пример для ввода, используя функцию acedCommandC
Функция работает, но только при ручном вводе.

Код - C++ [Выбрать]
  1. void testcmd()
  2. {
  3.         //функция вычерчивания линии
  4.         acedCommandC(&myCallbackFn, NULL,
  5.                                 RTSTR, _T("_Line"),
  6.                                 RTSTR, _T("0,0"),
  7.                                 RTSTR, _T("111,111"),
  8.                                 RTNONE);
  9. }
  10.  
  11. //функция обратного вызова - продолжение ввода данных пользователем
  12. int myCallbackFn(void * pData){
  13.       int nReturn = RTNONE;
  14.       if (isLineActive())  nReturn = acedCommandC(&myCallbackFn, NULL,
  15.                                         RTSTR, PAUSE,   //ввод данных пользователем
  16.                                         RTNONE);
  17.          else { //Если функция успешно завершена
  18.                 acutPrintf(_T("\nВвод данных завершен - выход из процедуры ввода\n"));
  19.          }
  20.    return nReturn;
  21. }
  22.  
  23. //проверка состояния команды - активна или нет
  24. Adesk::Boolean isLineActive()
  25. {
  26.         struct resbuf rb;
  27.         acedGetVar(_T("CMDNAMES"), &rb);
  28.         if (_tcsstr(rb.resval.rstring, _T("LINE")))
  29.                 return Adesk::kTrue;
  30.         return Adesk::kFalse;
  31. }

На сколько я понял, для программного ввода данных, в функцию нужно передавать второй параметр MyData,
acedCommandC(&myCallbackFn, MyData, ...
где MyData - указатель на структуру, содержащую массив данных и текущий индекс массива данных
При этом функция контроля состояния isLineActive() ненужна. Мы сами контролируем ввод внутри функции myCallbackFn()

В функции myCallbackFn() нужно организовать ввод одного элемента массива и увеличить индекс (счетчик)
Функция myCallbackFn() будет вызывать сама себя, пока не введет весь массив данных.

По завершении массива, нужно ввести завершающую часть команды (выход из команды)
nReturn = acedCommandC(&myCallbackFn, NULL, RTSTR, _T(""), RTNONE);

При следующей итерации нужно просто выйти из функции (не вызывая acedCommandC)
вернув значение RTNONE
int nReturn = RTNONE;
...
return nReturn;


Разработчики существенно усложнили создание кода.  :(
Теперь нужно будет создавать множество дополнительных функций обратного вызова
или думать как их можно унифицировать...

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Функция работает, но только при ручном вводе.
Не понял эту фразу. Ты смог сделать то, что хотел или нет? Подумай над использованием acedCmdS или acedCmdC. Для первой из указанных функций ты можешь динамически создать связный список resbuf на основе своих данных, если не требуется во время работы команды взаимодействовать с пользователем. Тут функция обратного вызова не потребуется. Для второй функции аналогично, но можно и взаимодействовать с пользователем в функции обратного вызова по аналогии с acedCommandC.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн UraАвтор темы

  • ADN OPEN
  • Сообщений: 45
  • Карма: 0
Я сделал упрощенный тестовый пример, как описал, проверил работу программы в отладчике - работает
Полный вариант собирался сделать немного позже, нужно адаптировать приложение для AutoCAD2015-2016, а там таких мест много.
Тест делал упрощенный, поэтому код и не выкладывал, а только описал логику.

Я рассматривал возможность использования команд acedCmdS и acedCmdC
Было бы удобно создать сразу список и потом его выполнить
Но я не нашел какой функцией можно добавлять записи в список resbuf

Для создания списка resbuf используется команда acutBuildList()
Но в команде мы указываем фиксированное кол-во данных.
Какой командой к существующему списку resbuf добавить новые данные я не нашел.
В С# такая команда вроде бы имеется.

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

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Для создания списка resbuf используется команда acutBuildList()
Не только. Есть еще функция acutNewRb для создания единичного resbuf, который можно добавить в хвост к существующему списку resbuf.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Отмечено как Решение Ura 27-08-2016, 00:32:14

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Подготовил пример. Думаю что с ним должно быть всё понятно:

Код - C++ [Выбрать]
  1. //-----------------------------------------------------------------------------
  2. //----- acrxEntryPoint.cpp
  3. //-----------------------------------------------------------------------------
  4. #include "StdAfx.h"
  5. #include "resource.h"
  6. #include <acedCmdNF.h>
  7.  
  8. //-----------------------------------------------------------------------------
  9. #define szRDS _RXST("")
  10.  
  11. //-----------------------------------------------------------------------------
  12. //----- ObjectARX EntryPoint
  13. class CArxProject8App : public AcRxArxApp {
  14. public:
  15.         CArxProject8App () : AcRxArxApp () {}
  16.         virtual AcRx::AppRetCode On_kInitAppMsg (void *pkt) {
  17.                 AcRx::AppRetCode retCode = AcRxArxApp::On_kInitAppMsg (pkt) ;
  18.                 return (retCode) ;
  19.         }
  20.  
  21.         virtual AcRx::AppRetCode On_kUnloadAppMsg (void *pkt) {
  22.                 AcRx::AppRetCode retCode = AcRxArxApp::On_kUnloadAppMsg (pkt) ;
  23.                 return (retCode) ;
  24.         }
  25.  
  26.         virtual void RegisterServerComponents () {      }
  27.        
  28.         static void RivilisMyLine () {
  29.                 AcGePoint3dArray pts;
  30.                 ads_point pt;
  31.                 // Запрашиваем поочередно точки для команды
  32.                 while (acedGetPoint((pts.length() == 0) ? NULL : asDblArray(pts.last()),
  33.                         L"\nУкажите очередную точку: ", pt) == RTNORM) {
  34.                         pts.append(asPnt3d(pt));
  35.                 }
  36.                 // Готовим список для команды
  37.                 resbuf *cmdbuf = acutBuildList(RTSTR, L"_LINE", RTNONE);
  38.                 for (int i = 0; i < pts.length(); i++)
  39.                 {
  40.                         AddToResbufList(cmdbuf,
  41.                                 acutBuildList(
  42.                                         RTSTR, L"_none", // Убираем привязки
  43.                                         RT3DPOINT, asDblArray(pts[i]), // Задаём очередную точку
  44.                                         RTNONE)
  45.                                 );
  46.                 }
  47.                 cmdbuf = AddToResbufList(cmdbuf,
  48.                         acutBuildList(RTSTR, L"", RTNONE)); // Завершение команды
  49.                 acedCmdS(cmdbuf);
  50.                 acutRelRb(cmdbuf);
  51.         }
  52.         // Функция добавляет к списку новый хвост
  53.         static resbuf *AddToResbufList(
  54.                 resbuf *head, // Голова списка
  55.                 resbuf *tail  // Новый хвост списка
  56.         )
  57.         {
  58.                 if (head == NULL) return tail;
  59.                 resbuf *rb = head;
  60.                 while (rb->rbnext) rb = rb->rbnext;
  61.                 rb->rbnext = tail;
  62.                 return head;
  63.         }
  64.  
  65. } ;
  66.  
  67. //-----------------------------------------------------------------------------
  68. IMPLEMENT_ARX_ENTRYPOINT(CArxProject8App)
  69. ACED_ARXCOMMAND_ENTRY_AUTO(CArxProject8App, Rivilis, MyLine, MyLine, ACRX_CMD_MODAL, NULL)

Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн UraАвтор темы

  • ADN OPEN
  • Сообщений: 45
  • Карма: 0
Спасибо за пример
Это наиболее удобный вариант ввода команды
Использование acutNewRb - это слишком низкоуровневый способ формирования списка, много возни
Использование acutBuildList существенно удобней

Созданную вами функцию AddToResbufList можно сразу размещать в библиотеку, ее будет удобно использовать.
Странно, что этого не сделали разработчики.

Удивительно то, что уже вышло несколько версий AutoCAD 2015-2017, а почти нет ни кокой информации как пользоваться
всеми перечисленными выше функциями - ни в справочной информации ни в интернете, кругом обрывочная информация и никаких примеров...
Я раньше искал информацию по этому вопросу, информации было недостаточно...
Примеры начали появляться только недавно, и то, если хорошо поискать...

Спасибо

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Созданную вами функцию AddToResbufList можно сразу размещать в библиотеку, ее будет удобно использовать.
Странно, что этого не сделали разработчики.
Сделали. Как минимум 17 лет назад. В составе ObjectARX SDK есть пример ARXDBG, в составе которого масса полезных примеров. И в частности класс ArxDbgRbList, который функционально намного мощнее, чем та функция, которую я сделал просто для того, чтобы сократить код.
Примеров по использованию acedCmdC/acedCmdS и acedCommandC/acedCommandS действительно не так много, но подразумевается, то программист знакомый с ObjectARX и функциями acedCommand и acedCmd сам легко эти примеры сделает, основываясь на статьях о появлении этих функций и замене тех, которые были в предыдущих версиях.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение