Прогресс-бары или о длительных операциях

Иногда необходимо выполнить длительные вычисления - например, обработать несколько десятков (а то и сотен) файлов, вызвать стороннее приложение и в нем что-то сделать и т.п. Если работать через LISP, то AutoCAD (с точки зрения пользователя) "зависает".

Чтобы этого не было, процесс выполнения надо хоть как-то показывать. Некоторые тонкости этой задачки и хотелось бы рассмотреть.

Чуть ли не самый интересный и стандартный метод - показ некоего прогресс-бара. Для разработчиков на arx / .NET соответствующие классы уже давно существуют, но и лиспописатели могут кое-то сделать.

Некоторый набор уже имеющихся решений:
Тема на аркаде:

Код от VVA:

1
2
3
4
5
6
7
8
9
10
(defun C:TEST ()
    (setq i 0
          n 5000
    ) ;_ end of setq
    (repeat n
        (grtext -1 (strcat "№ " (itoa (setq i (1+ i))) " из " (itoa n)))
    ) ;_ end of repeat
    (getstring "\nPress enter to continue")
      (vl-cmdf "_.redrawall")
) ;_ end of defun

Тема на dwg.ru;
Autodesk discussion

Рано или поздно вопрос переводится либо к рисованию собственного dcl-окна с изменяемым image-окном, либо использование сторонних arx-приложений (например, чертовски многофункциональный прогресс-бар от А.Ривилиса или решение от DosLib), либо использование ExpressTools-функций.

У всех этих решений лично я нашел только одно ограничение (все тестировалось в AutoCAD 2008, в 2009 и дальше я использовал только ExpressTools): если используем стандартный ProgressBar в статусной строке AutoCAD, то количество операций в нем не может превышать 32-с-чем-то-там-тысяч (ну да, там 32-битные числа, что уж тут поделаешь). А у меня несколько раз была ситуация, когда этого оказывалось маловато (и не надо говорить, что я извращенец - я и так это знаю :)).

Чуть ли не основная задача при использовании прогресс-бара - вычисление полного количества циклов, для которых надо выполнять обновление прогресс-бара. И при этом надо предусмотреть ситуацию, когда вроде бы и посчитали, но немного ошиблись - причем не в меньшую сторону (это-то не страшно: ну закрылся прогресс чуть раньше - делов-то), а в большую (при неудачном решении прогресс может "замереть").

В результате некоторых экспериментов родился такой код:

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
(defun _lispru-progress-start (msg range)
                             ;|
*    Инициализирует прогресс-бар
*    Параметры вызова:
  msg   показываемое сообщение
  range общая длина прогресс-бара
|;

  (cond
    ((and msg acet-ui-progress)
     (acet-ui-progress msg (min 32000 range))
     )
    ((and (not msg) acet-ui-progress)
     (acet-ui-progress (min 32000 range))
     )
    ((and msg progressbar)
     (progressbar msg (min 32000 range))
     )
    ((and (not msg) progressbar)
     (progressbar (min 32000 range))
     )
    (t
     (_lispru-progress-cmd msg range)
     )
    ) ;_ end of cond
  ) ;_ end of defun

(defun _lispru-progress-cmd (msg pos / lst)
                           ;|
*    Выводит в ком.строку сообщение с "прогрессом"
*    Параметры вызова:
  msg   строковое сообщение
  pos   счетчик выполняемых действий
|;

  (if msg
    (princ (strcat "\r" msg " : " (nth (rem pos 4) '("-" "\" "|" "/"))))
    (princ "
\n" msg " закончено")
    ) ;_ end of if
  ) ;_ end of defun

(defun _lispru-progess-continue (msg pos)
                               ;|
*    Заполняет прогресс-бар
*    Параметры вызова:
  msg    выводимое сообщение
  pos    текущая позиция
|;
  (cond
    (acet-ui-progress
     (acet-ui-progress (rem pos 32000))
     )
    (progressbar
     (progressbar (rem pos 32000))
     )
    (t
     (_lispru-progress-cmd msg pos)
     )
    ) ;_ end of cond
  ) ;_ end of defun

(defun _lispru-progress-end ()
                           ;|
*    Завершение прогресс-бара
|;
  (cond
    (acet-ui-progress
     (acet-ui-progress)
     )
    (progressbar
     (progressbar)
     )
    (t (princ))
    ) ;_ end of cond
  ) ;_ end of defun

Я не стал оформлять это несколькими функциями, закинув все в один lsp-файл (исходник здесь:
_lispru-progress.lsp.

Критикуйте :)

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



Комментарии

Есть 1 комментарий к “Прогресс-бары или о длительных операциях”
  1. Кулик Алексей aka kpblc пишет:

    Дополню. Уже не помню, по каким причинам, но код пришлось модифицировать:

    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
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    (defun _kpblc-progress-start (msg range) ;|
    *    Инициализирует прогресс-бар
    *    Параметры вызова:
      msg    показываемое сообщение
      range  общая длина прогресс-бара
    |;

      (setq *kpblc-progress-range*
             (list (cons "progress" (min 32000 range)))
            range (cdr (assoc "progress" *kpblc-progress-range*))
            ) ;_ end of setq
      (cond ((and msg progressbar) (progressbar msg range))
            ((and (not msg) progressbar) (progressbar range))
            ((and msg acet-ui-progress) (acet-ui-progress msg range))
            ((and (not msg) acet-ui-progress) (acet-ui-progress range))
            (t (_kpblc-progress-cmd msg range))
            ) ;_ end of cond
      ) ;_ end of defun

    (defun _kpblc-progress-continue (msg pos) ;|
    *    Заполняет прогресс-бар
    *    Параметры вызова:
      msg    выводимое сообщение
      pos    текущая позиция
    |;

      (if (and (cdr (assoc "progress" *kpblc-progress-range*))
               (> 0 (cdr (assoc "progress" *kpblc-progress-range*)))
               (> pos (cdr (assoc "progress" *kpblc-progress-range*)))
               ) ;_ end of and
        (while (> pos (cdr (assoc "progress" *kpblc-progress-range*)))
          (setq pos (- pos (cdr (assoc "progress" *kpblc-progress-range*))))
          ) ;_ end of while
        ) ;_ end of if
      (cond (progressbar (progressbar (rem pos 32000)))
            (acet-ui-progress (acet-ui-progress (rem pos 32000)))
            (t (_kpblc-progress-cmd msg pos))
            ) ;_ end of cond
      ) ;_ end of defun

    (defun _kpblc-progress-cmd (msg pos / lst) ;|
    *    Выводит в ком.строку сообщение с "прогрессом"
    *    Параметры вызова:
      msg    строковое сообщение
      pos    счетчик выполняемых действий
    |;

      (if msg
        (princ (strcat "\r" msg " : " (nth (rem pos 4) '("-" "\" "|" "/"))))
        (princ (strcat "
    \n" msg " закончено"))
        ) ;_ end of if
      ) ;_ end of defun

    (defun _kpblc-progress-end ();|
    *    Завершение прогресс-бара
    |;
      (setq *kpblc-progress-range* nil)
      (cond (progressbar (progressbar))
            (acet-ui-progress (acet-ui-progress))
            (t (princ))
            ) ;_ end of cond
      ) ;_ end of defun

    И примерчик использования:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    (defun test-progress (/ pos len msg)
      (setq len 33600
            pos 0
            msg "Проверка прогресс-бара"
            ) ;_ end of setq
      (_kpblc-progress-start msg len)
      (while (< pos len) (_kpblc-progress-continue msg (setq pos (1+ pos))))
      (_kpblc-progress-end)
      ) ;_ end of defun

    Все коды можно забрать здесь

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


Я не робот.