Создание прогресс бара

Автор Тема: Создание прогресс бара  (Прочитано 14207 раз)

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

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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Создание прогресс бара
« Ответ #15 : 05-04-2021, 17:07:52 »
Владимир Шу,
Как часто ты вызвал в коде Application.DoEvents()? Я, когда делал ProgressBar с помощью ObjectARX, использовал код аналогичный Application.DoEvents(). Если его вызов происходил в каждой итерации (а они происходили по несколько десятков в миллисекунду), то иногда я тоже получал Fatal Error. После того как я добавил проверку на время между вызовами аналога Application.DoEvents() и это время установил в диапазоне от 0.1 до 0.5 секунды - во-первых, код стал выполняться значительно быстрее, во-вторых, Fatal Error исчезли вообще. Application.DoEvents() может быть достаточно длительной операцией.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Владимир Шу

  • ADN Club
  • *****
  • Сообщений: 611
  • Карма: 155
    • ПГСу Бложик
Re: Создание прогресс бара
« Ответ #16 : 05-04-2021, 17:31:33 »
Как часто ты вызвал в коде Application.DoEvents()?
После обработки каждого блока.
После того как я добавил проверку на время между вызовами аналога Application.DoEvents() и это время установил в от 0.1 до 0.5 секунды

Переписал метод вот так:
Код - C# [Выбрать]
  1.     private static Dictionary<Db.ObjectId, ExcelRow> ModifyBlockRefToDb(Dictionary<Db.ObjectId, ExcelRow> blockRefForSetParam)
  2.     {
  3.       var pm = new Rtm.ProgressMeter();
  4.       pm.SetLimit(blockRefForSetParam.Count);
  5.       pm.Start("Редактирование блоков");
  6.       System.Windows.Forms.Application.DoEvents();
  7.  
  8.       long start = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;
  9.  
  10.       var blockRefForSetParamX = new Dictionary<Db.ObjectId, ExcelRow>();
  11.       foreach (var ent in blockRefForSetParam)
  12.       {
  13.         Db.ObjectId id = Db.ObjectId.Null;
  14.         using (var blockRef = ent.Key.Open(Db.OpenMode.ForWrite) as Db.BlockReference)
  15.         {
  16.           foreach (var row in ent.Value.Pairs)
  17.           {
  18.             blockRef.SetDynamicProperty(row.Key, row.Value);
  19.           }
  20.           id = blockRef.ObjectId;
  21.         }
  22.         if (!id.IsNull)
  23.           blockRefForSetParamX.Add(id, ent.Value);
  24.  
  25.  
  26.  
  27.         long difference = (DateTime.Now.Ticks / TimeSpan.TicksPerSecond - start);
  28.         if (difference > 1)
  29.         {
  30.           System.Windows.Forms.Application.DoEvents();
  31.           start = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;
  32.         }
  33.         pm.MeterProgress();
  34.       }
  35.  
  36.       pm.Stop();
  37.       System.Windows.Forms.Application.DoEvents();
  38.       return blockRefForSetParamX;
  39.     }
  40.  
не смотря на задержку в 1 сек, Автокад вываливается в фатал. Остальной код программы не менял.
Если закоментить все что относится к прогресс бару и Application.DoEvents(), то программа опять начинает работать нормально.

Может я чего то не правильно делаю....

Оффлайн Привалов Дмитрий

  • ADN Club
  • *****
  • Сообщений: 534
  • Карма: 117
Re: Создание прогресс бара
« Ответ #17 : 05-04-2021, 20:19:39 »
не смотря на задержку в 1 сек, Автокад вываливается в фатал.
1. Проверь, закомментируй все DoEvents(); будет ошибка или нет?

2. Потом, попробуй слегка модифицированный код.

   
Код - C# [Выбрать]
  1.  private static Dictionary<Db.ObjectId, ExcelRow> ModifyBlockRefToDb(Dictionary<Db.ObjectId, ExcelRow> blockRefForSetParam)
  2.     {
  3.       var pm = new Rtm.ProgressMeter();
  4.       pm.Start("Редактирование блоков");
  5.       pm.SetLimit(blockRefForSetParam.Count);//Сперва старт, затем установка лимита. Для плавности отображения прогрессбара. На ошибку не должен влиять.
  6.  
  7.       long start = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;
  8.  
  9.       var blockRefForSetParamX = new Dictionary<Db.ObjectId, ExcelRow>();
  10.       foreach (var ent in blockRefForSetParam)
  11.       {
  12.         Db.ObjectId id = Db.ObjectId.Null;
  13.         using (var blockRef = ent.Key.Open(Db.OpenMode.ForWrite) as Db.BlockReference)
  14.         {
  15.           foreach (var row in ent.Value.Pairs)
  16.           {
  17.             blockRef.SetDynamicProperty(row.Key, row.Value);
  18.           }
  19.           id = blockRef.ObjectId;
  20.         }
  21.         if (!id.IsNull)
  22.           blockRefForSetParamX.Add(id, ent.Value);
  23.  
  24.         long difference = (DateTime.Now.Ticks / TimeSpan.TicksPerSecond - start);
  25.         if (difference > 1)
  26.         {
  27.           start = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;
  28.           pm.MeterProgress();//Тоже не стоит вызывать часто. Отобразит не корректно, не дойдет до конца, проверь пока так.
  29.           System.Windows.Forms.Application.DoEvents();
  30.         }
  31.       }
  32.  
  33.       pm.Stop();
  34.       return blockRefForSetParamX;
  35.     }

3. Если упадет в 1 и 2 случае, попробуй закомментить pm.MeterProgress(); а DoEvents(); оставь.

Оффлайн Владимир Шу

  • ADN Club
  • *****
  • Сообщений: 611
  • Карма: 155
    • ПГСу Бложик
Re: Создание прогресс бара
« Ответ #18 : 05-04-2021, 20:36:34 »
1 вариант - упал с фаталом
2 вариант - сначала просто по тихому выключился акад как будто из диспетчера задач процесс убили, второй запуск с фаталом
3 вариант - System.AccessViolationException: "Попытка чтения или записи в защищенную память. Это часто свидетельствует о том, что другая память повреждена" и на второй раз просто фатал
4 вариант - удаляем прогресс бар и DoEvents(); и все работает.

Я бы понял если бы ошибки были бы одинаковые или почти одинаковые, значит я где то накосячил с кодом, но тут полный раздрай.
На всякий случай Win7x64 и ADT 2017.

Оффлайн Привалов Дмитрий

  • ADN Club
  • *****
  • Сообщений: 534
  • Карма: 117
Re: Создание прогресс бара
« Ответ #19 : 05-04-2021, 21:02:22 »
Я бы понял если бы ошибки были бы одинаковые или почти одинаковые, значит я где то накосячил с кодом, но тут полный раздрай.
Похоже, что проблема где-то в другом месте.
В функции не видно блокировки документа. Посмотри что с ней.

Попробуй закомментить. Возможно проблема в коде
Код - C# [Выбрать]
  1. Db.ObjectId id = Db.ObjectId.Null;
  2.         using (var blockRef = ent.Key.Open(Db.OpenMode.ForWrite) as Db.BlockReference)
  3.         {
  4.           foreach (var row in ent.Value.Pairs)
  5.           {
  6.             blockRef.SetDynamicProperty(row.Key, row.Value);
  7.           }
  8.           id = blockRef.ObjectId;
  9.         }
  10.         if (!id.IsNull)
  11.           blockRefForSetParamX.Add(id, ent.Value)

Оффлайн Дмитрий Загорулькин

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Re: Создание прогресс бара
« Ответ #20 : 05-04-2021, 22:21:47 »
Что-то сложно как-то время вычисляется. Я так и не разобрался - есть там секунда или нет.
Код - C# [Выбрать]
  1. ...
  2. var start = DateTime.Now;
  3. ...
  4. var difference = DateTime.Now - start;
  5. if (difference.TotalSeconds > 1)
  6. ...
  7.  
Вроде как, правильнее делать DoEvents после того, как прогрессметер "пнули", а не до этого, как в коде из #16.
А, Привалов Дмитрий уже поправил это в коде из #17.

Оффлайн Дмитрий Загорулькин

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Re: Создание прогресс бара
« Ответ #21 : 05-04-2021, 22:34:01 »
На всякий случай Win7x64 и ADT 2017.
Идея, конечно, из области догадок. Но может ADT в данном случае как-то вмешивается в работу прогрессбара? Возможно, что штатный прогрессбар в этот момент сам ADT задействует и возникает какой-то конфликт. У меня было такое, что я обрабатывал чертежи в фоне и пытался использовать этот штатный прогрессбар. В итоге в какой-то момент его "перехватывал" сам автокад и управление прогрессбаром из моего приложения прекращалось. Фатала не было но и не работало как надо. Мне пришлось переписать с использованием самописного погрессбара на основе немодального WPF окошка.

Оффлайн Привалов Дмитрий

  • ADN Club
  • *****
  • Сообщений: 534
  • Карма: 117
Re: Создание прогресс бара
« Ответ #22 : 06-04-2021, 07:40:53 »
значит я где то накосячил с кодом

...Подумал утром, что смущает в коде.
1. работаешь без транзакции, могут быть различные глюки.
Код - C# [Выбрать]
  1. using (var blockRef = ent.Key.Open(Db.OpenMode.ForWrite) as Db.BlockReference)
  2. {
  3.    if (blockRef != null) //if (!id.IsNull)... 2. какая-то нереализованная проверка?
  4.    {
  5.       foreach (var row in ent.Value.Pairs)
  6.       {
  7.          blockRef.SetDynamicProperty(row.Key, row.Value);//3. Выставляешь динамическое свойство. А существует ли оно и можно ли менять?
  8.       }
  9.       blockRefForSetParamX.Add(ent.Key, ent.Value);
  10.    }
  11. }

Оффлайн Владимир Шу

  • ADN Club
  • *****
  • Сообщений: 611
  • Карма: 155
    • ПГСу Бложик
Re: Создание прогресс бара
« Ответ #23 : 06-04-2021, 08:16:05 »
И с транзакцие (в разных ее вариациях) и без нее работал, все едино.
Да и не сошелся свет клином именно на этом методе, я его только для примера привел, то же самое поведение будет если я вставлю прогресс бар внутрь любого из методов использующихся в DrawBlocks или в самом DrawBlocks

Вроде как, правильнее делать DoEvents после того, как прогрессметер "пнули", а не до этого, как в коде из #16.
А, Привалов Дмитрий уже поправил это в коде из #17.
Вот результат



Я ранее расписывал общую структуру программы
Тут создаем и блокируем документ.
Сейчас тут AppContextOpenDocument(), ранее было создание базы и ReadDwg(), но теперешний вариант стабильнее и нужно что бы документ после завершения работы оставался открытым
Код - C# [Выбрать]
  1.     internal static void CreateDWG(string outPutDWG, List<ExcelRow> excelRows)
  2.     {
  3.       App.DocumentCollection acDocMgr = App.Application.DocumentManager;
  4.       //Создается чертеж по шаблону
  5.       acDocMgr.AppContextOpenDocument(outPutDWG);
  6.       App.Document docA = App.Application.DocumentManager.MdiActiveDocument;
  7.  
  8.       using (App.DocumentLock docLock = docA.LockDocument())
  9.       {
  10.         using (WorkingDatabaseSwitcher switcher = new WorkingDatabaseSwitcher(docA.Database))
  11.         {
  12.           //Проверяем регистрацию приложения для группировки
  13.           XDataExtension.SetXDataApp(docA.Database);
  14.  
  15.           //Рисуем блоки
  16.           DrawBlocks(docA, excelRows);
  17.         }
  18.       }
  19.  
  20.       docA.Database.SaveAs(outPutDWG, true, Db.DwgVersion.Current, null);
  21.     }

Тут собственно создаются, изменяются и растаскиваются по местам блоки
Код - C# [Выбрать]
  1.     private static void DrawBlocks(App.Document doc, List<ExcelRow> excelRows)
  2.     {
  3.       Db.Database newDb = doc.Database;
  4.       List<Db.ObjectId> eraseIds = new List<Db.ObjectId>();
  5.       //Просто очищаем пространство модели
  6.       var msId = Db.SymbolUtilityServices.GetBlockModelSpaceId(newDb);
  7.       using (var ms = msId.Open(Db.OpenMode.ForRead) as Db.BlockTableRecord)
  8.         foreach (Db.ObjectId id in ms)
  9.           if (id.ObjectClass.IsDerivedFrom(Rtm.RXObject.GetClass(typeof(Db.Entity))))
  10.             eraseIds.Add(id);
  11.  
  12.       //Размещаем блоки
  13.       var blockIds_mod = new Dictionary<Db.ObjectId, ExcelRow>();
  14.       blockIds_mod = PostBlockRefToDb(newDb, excelRows);
  15.  
  16.       if (blockIds_mod.Count == 0) return;
  17.  
  18.  
  19.       //Модифицируем блоки
  20.       var blockIds = new Dictionary<Db.ObjectId, ExcelRow>();
  21.       blockIds = ModifyBlockRefToDb(blockIds_mod);
  22.  
  23.       if (blockIds.Count == 0) return;
  24.  
  25.       //Маркеруем блоки
  26.       MarkerGroupb(blockIds);
  27.  
  28.       //Давай сделаем словарик, с номером группы и наибольшей высотой блока в этой группе
  29.       Dictionary<int, double> maxBlockRowHeigth = new Dictionary<int, double>();
  30.       maxBlockRowHeigth = GetMaxBlockRowHeigth(blockIds.Keys.ToList());
  31.  
  32.       if (maxBlockRowHeigth.Count == 0) return;
  33.  
  34.       //Двигаем блоки, сейчас они все стоят в точке 0,0,0
  35.       TransformBlocks(blockIds.Keys.ToList(), maxBlockRowHeigth);
  36.  
  37.       //Удалить объекты из модели, которые были там до начала работы скрипта
  38.       foreach (Db.ObjectId id in eraseIds)
  39.         using (var ent = id.Open(Db.OpenMode.ForWrite) as Db.DBObject)
  40.           ent.Erase();
  41.  
  42.       eraseIds.Clear();
  43.     }

Вот это вот: "Удалить объекты из модели, которые были там до начала работы скрипта" , это потому, что я думал что глюки при создании дин. блоков и потому переписывал на копирование блоков которые есть в модели в шаблоне, но нет, не помогает и обратно исправил на создание...

Метод ModifyBlockRefToDb показывал ранее, но вот поправленный вариант с результатом на картинке выше
Код - C# [Выбрать]
  1.      private static Dictionary<Db.ObjectId, ExcelRow> ModifyBlockRefToDb(Dictionary<Db.ObjectId, ExcelRow> blockRefForSetParam)
  2.     {
  3.       var pm = new Rtm.ProgressMeter();
  4.       pm.Start("Редактирование блоков");
  5.       pm.SetLimit(blockRefForSetParam.Count);
  6.       //System.Windows.Forms.Application.DoEvents();
  7.  
  8.  
  9.      var start = DateTime.Now;
  10.  
  11.       var blockRefForSetParamX = new Dictionary<Db.ObjectId, ExcelRow>();
  12.       foreach (var ent in blockRefForSetParam)
  13.       {
  14.         Db.ObjectId id = Db.ObjectId.Null;
  15.         using (var blockRef = ent.Key.Open(Db.OpenMode.ForWrite) as Db.BlockReference)
  16.         {
  17.           foreach (var row in ent.Value.Pairs)
  18.           {
  19.             blockRef.SetDynamicProperty(row.Key, row.Value);
  20.           }
  21.           id = blockRef.ObjectId;
  22.         }
  23.  
  24.         if (!id.IsNull)
  25.           blockRefForSetParamX.Add(id, ent.Value);
  26.  
  27.  
  28.         pm.MeterProgress();
  29.  
  30.         var difference = DateTime.Now - start;
  31.         if (difference.TotalSeconds > 1)
  32.         {
  33.           System.Windows.Forms.Application.DoEvents();
  34.           start = DateTime.Now;
  35.         }
  36.  
  37.  
  38.       }
  39.  
  40.       pm.Stop();
  41.       //System.Windows.Forms.Application.DoEvents();
  42.       return blockRefForSetParamX;
  43.     }
  44.  
  45.  

 но это все не имеет значения, я даже убирал блокировку в методе CreateDWG и переносил ее DrawBlocks и оборачивал блокированием документа только вызовы методов PostBlockRefToDb, ModifyBlockRefToDb, MarkerGroupb, GetMaxBlockRowHeigth, TransformBlocks, что бы сделать прогресс бар хотя бы по этапам. "Всё фигня, Миша. Начинаем сначала". Я на эту фигню более 30 часов убил и теперь просто принимаю как данность, лучше не использовать прогресс бар и DoEvents при работе с динамическими блоками


if (blockRef != null) //if (!id.IsNull)... 2. какая-то нереализованная проверка?
В моем коде такого нет.
3. Выставляешь динамическое свойство. А существует ли оно и можно ли менять?
Да, существует, да можно менять, да соответствует нужному типу, да укладывается в перечень разрешенных значений.
Код - C# [Выбрать]
  1.     public static void SetDynamicProperty(this Db.BlockReference acBlockRef, string PropName, object PropValue)
  2.     {
  3.       if (acBlockRef.IsDynamicBlock)
  4.       {
  5.         Db.DynamicBlockReferencePropertyCollection acBlockDynProp =
  6.           acBlockRef.DynamicBlockReferencePropertyCollection;
  7.  
  8.         if (acBlockDynProp != null)
  9.         {
  10.           foreach (Db.DynamicBlockReferenceProperty obj in acBlockDynProp)
  11.           {
  12.  
  13.             if (!obj.ReadOnly && obj.PropertyName.ToUpper() == PropName.ToUpper())
  14.             {
  15.               //Если есть что менять...
  16.               if (obj.Value.ToString() != PropValue.ToString())
  17.               {
  18.                 object val = null;
  19.                 if (obj.PropertyTypeCode == (short)DwgDataType.kDwgReal)
  20.                 {
  21.                   double v = 0;
  22.                   if (double.TryParse(PropValue.ToString(), out v))
  23.                   {
  24.                     val = v;
  25.                   }
  26.                 }
  27.                 else if (obj.PropertyTypeCode == (short)DwgDataType.kDwgInt32){...}
  28.                 else if (obj.PropertyTypeCode == (short)DwgDataType.kDwgInt16){...}
  29.                 else if (obj.PropertyTypeCode == (short)DwgDataType.kDwgInt8){...}
  30.                 else if (obj.PropertyTypeCode == (short)DwgDataType.kDwgText)
  31.                 {
  32.                   val = (object)PropValue.ToString();
  33.                 }
  34.  
  35.                 //А остальное нам не нужно.
  36.                 if (val != null)
  37.                 {
  38.                   var fff = new List<object>(obj.GetAllowedValues());
  39.                   if (fff.Count == 0 || fff.Any(q => q.ToString() == PropValue.ToString()))
  40.                   {
  41.                     obj.Value = val;
  42.                     break;
  43.                   }
  44.                 }
  45.               }
  46.             }
  47.           }
  48.         }
  49.       }
  50.     }

Что-то сложно как-то время вычисляется. Я так и не разобрался - есть там секунда или нет.
Есть, на отладке специально перепроверял. Я сначала пытался через тики 0,1.. 0,5 сек использовать и только потом до 1 секунды дошел. Не помогло.
« Последнее редактирование: 06-04-2021, 09:30:27 от Владимир Шу »

Оффлайн Привалов Дмитрий

  • ADN Club
  • *****
  • Сообщений: 534
  • Карма: 117
Re: Создание прогресс бара
« Ответ #24 : 06-04-2021, 10:42:55 »
Сейчас тут AppContextOpenDocument(), ранее было создание базы и ReadDwg(), но теперешний вариант стабильнее и нужно что бы документ после завершения работы оставался открытым

А почему просто не открыть документ и не заблокировать?
Cad.DocumentManager.Open(...);

Меня смущает:
1. что ты что-то открываешь AppContextOpenDocument(outPutDWG);
2. Блокируешь документ
3. Переключаешь рабочую базу данных WorkingDatabaseSwitcher switcher = new WorkingDatabaseSwitcher(docA.Database). Причем указываешь саму базу документа.

Насколько корректно работает переключение БД  после блокировки документа? Нужно ли вызывать WorkingDatabaseSwitcher ?

Оффлайн Дмитрий Загорулькин

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Re: Создание прогресс бара
« Ответ #25 : 06-04-2021, 10:50:44 »
Я на эту фигню более 30 часов убил и теперь просто принимаю как данность, лучше не использовать прогресс бар и DoEvents при работе с динамическими блоками
Думаю, тут проблема не в работе с динамическими блоками, а в том, что идёт работа с неактивным чертежом (если я правильно понял код).

Оффлайн Владимир Шу

  • ADN Club
  • *****
  • Сообщений: 611
  • Карма: 155
    • ПГСу Бложик
Re: Создание прогресс бара
« Ответ #26 : 06-04-2021, 10:55:33 »
А почему просто не открыть документ и не заблокировать?
Cad.DocumentManager.Open(...);
DocumentManager это объект класса DocumentCollection у него нет метода Open() и  AppContextOpenDocument(путь_к_открываемому_файлу) это именно открытие файла в редакторе, по крайне мере я другого способа не знаю.
Я пробовал различные комбинации, сначала блокировать потом переключать базу или сначала базу переключить потом блокировать. Результат один.

Думаю, тут проблема не в работе с динамическими блоками, а в том, что идёт работа с неактивным чертежом (если я правильно понял код).
Я так же подумал и переписал с ReadDwg() и работу в фоне, на AppContextOpenDocument() т.е. сознательно делаю и чертеж и базу активными. Результат не меняется.

Я думаю что это все таки связанно с дин. блоками, т.к. при изменении дин.свойств, автокад пересоздает объекты, по крайней мере Id объектов меняются (потому в коде пересозданние коллекций) и значит автокад обрабатывает какие то там зависимости.... и если в этот момент разрешить автокаду самовольничать, то все идет лесом.

Оффлайн Дмитрий Загорулькин

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Re: Создание прогресс бара
« Ответ #27 : 06-04-2021, 13:07:48 »
Я так же подумал и переписал с ReadDwg() и работу в фоне, на AppContextOpenDocument() т.е. сознательно делаю и чертеж и базу активными. Результат не меняется.
Я не увидел, а где чертёж делается активным? И если он активный, то зачем рабочую базу переключать?

Оффлайн Владимир Шу

  • ADN Club
  • *****
  • Сообщений: 611
  • Карма: 155
    • ПГСу Бложик
Re: Создание прогресс бара
« Ответ #28 : 06-04-2021, 13:19:32 »
Я не увидел, а где чертёж делается активным?
AppContextOpenDocument() открывает и делает активным, вот тут есть пример кода и видяшка https://adn-cis.org/forum/index.php?topic=8979.0 . Загвоздка только в том, что AppContextOpenDocument не возвращает ничего и я был бы рад скормить MdiActiveDocument или CurrentDocument что то...  но на нет и суда нет  (это я про строчку 41 в коде Александра в указанной теме... и еще вот тут https://github.com/kevinzhwl/ObjectARXCore/blob/master/2015/inc/acdocman.h в строке 382 сказано, что открывает и переключает на нужный вид). Если просто закоментить  отрисовку блока, то программа откроет документ в редакторе и можно сразу начинать с ним работать, т.е. он активный

И если он активный, то зачем рабочую базу переключать?
Потому что идей почему оно падает не было и на всякий случай и базу переключаю, может и масло масленое.

Оффлайн Дмитрий Загорулькин

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Re: Создание прогресс бара
« Ответ #29 : 06-04-2021, 19:10:47 »
А вот интересно, в чём смысл AppContextOpenDocument(), если можно так?
Вопрос без подвоха, я правда не понимаю...