Визуализация работы макроса при помощи прогресс-бара

Прогресс-бар на VBA

Данный прогресс-бар позволяет отображать ход выполнения любого макроса.

Для использования этого индикатора перетащите из файла-примера в свой файл модуль класса ProgressIndicator и форму F_Progress

Использовать прогресс бар сравнительно просто - достаточно добавить в макрос несколько строк кода:

Sub ПростейшийПримерИспользованияПрогрессБара()
    Dim pi As New ProgressIndicator    ' создаём новый прогресс-бар
    pi.Show "Подождите, работает макрос"    ' отбражаем индикатор

    ' здесь код вашего макроса

    pi.Hide    ' закрываем индикатор
End Sub

Sub ПримерИспользованияПрогрессБара()
 
    КоличествоЗапусковВнешнегоМакроса = 3000
 
    Dim pi As New ProgressIndicator    ' создаём новый прогресс-бар
    pi.Show "Форматирование ячеек"    ' отбражаем индикатор

    ' первое действие (на шкале индикатора от 0 до 95 процентов) - это окраска ячеек
    pi.StartNewAction 0, 95, "Окраска ячеек", , , КоличествоЗапусковВнешнегоМакроса
 
 
 
    ' цикл с вызовом внешнего макроса "ФорматированиеЯчейки"
    For i = 1 To КоличествоЗапусковВнешнегоМакроса
 
        ' инициируем очередное действие в индикаторе
        pi.SubAction , "Обрабатывается ячейка $index из $count", "$time"
 
        ' собственно, код цикла
        ФорматированиеЯчейки i
    Next
 
    ' всё покрасили - теперь пора чистить ячейки )
    pi.StartNewAction 95, 100, "Очистка ячеек"
    Cells.Clear
 
    pi.Hide    ' закрываем индикатор
End Sub
 
Sub ФорматированиеЯчейки(ByVal n As Long)    ' вызываемый макрос
    Cells(n).Interior.ColorIndex = 15: Cells(n).BorderAround xlContinuous
End Sub

 

Давайте рассмотрим подробнее работу с индикатором.

 

Прогресс-бар выполнен в виде модуля класса, поэтому, для начала работы с ним,
надо прежде всего создать экземпляр этого класса
:

Dim pi As New ProgressIndicator    ' создаём новый прогресс-бар

 

Итак, прогресс-бар создан, и теперь надо его отобразить.

Для этого мы используем метод Show объекта типа ProgressIndicator:

pi.Show "Форматирование ячеек"    ' отбражаем индикатор

При использовании метода Show мы сразу задаём заголовок индикатора (можно здесь указать название вашего макроса)

Индикатор появился на экране - но полоса не отображается, ибо процент выполнения по-умолчанию равен нулю.

 

Для каждого действия мы будем задавать начальный и конечный процент выполнения задачи

К примеру, если первое действие вашего макроса занимает по времени примерно пятую часть от времени выполнения всего макроса,
то мы укажем интервал для индикатора от 0% до 20%:

pi.StartNewAction 0, 20

Как вы заметили, для запуска очередного действия используется метод StartNewAction объекта ProgressIndicator.

При вызове этого метода можно сразу задать текст для каждой из 3 текстовых строк индикатора:

pi.StartNewAction 0, 20, "Текст первой строки", "Текст строки 2", "Текст строки 3"

Если действие состоит из нескольких отдельных "поддействий", то можно также сразу задать и количество этих "поддействий"

(например, основное действие - это форматирование ячеек (от 0% до 20% индикатора), а поддействия - это окраска отдельных ячеек (первая строка - от 0% до 1% индикатора, вторая строка - от 1% до 2%, и т.д.))

Чтобы нам не мучиться с расчётами этих процентов, мы просто задаём количество действий (например, количество форматируемых ячеек, равное 3000),
и индикатор сам разделит диапазон от 0% до 20% на 3000 равных частей, плавно увеличивая длину полосы индикатора по мере форматирования отдельных ячеек.

 pi.StartNewAction 0, 20, "Окраска ячеек", , , 3000

 

Чтобы уведомить индикатор об очередном "поддействии" внутри цикла, мы используем метод SubAction объекта ProgressIndicator

' инициируем очередное действие в индикаторе
        pi.SubAction , "Обрабатывается ячейка $index из $count", "$time"

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

В этом случае (если значения некоторых из 3 строк индикатора не заданы), эти строки не изменяются
(в первой строке индикатора останется текст, заданный ранее при использовании метода StartNewAction)

 

Кроме того, в тексте для строк индикатора можно использовать следующие ключевые слова:

  • $index и $count - для вывода строк типа "Обрабатывается ячейка 515 из 3000",
  • $time - для вывода ожидаемого времени до окончания макроса

(макрос анализирует текущий процент выполнения и затраченное время, и предсказывает, сколько времени осталось до окончания всех действий)

Если же необходимо просто увеличить длину полоски индикатора - можете использовать метод SubAction без параметров:

pi.SubAction

 

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

Вполне допустим следующий код:

    pi.StartNewAction 5, 20, "Действие 1"    ' начинаем не с нуля
    pi.StartNewAction 20, 50, "Действие 2"    ' начинается сразу после предыдущего
    pi.StartNewAction 35, 60, "Действие 3"    ' начинается раньше предыдущего
    pi.StartNewAction 85, 90, "Действие 4"    ' начинается через время после предыдущего
    pi.StartNewAction 10, 100, "Действие 5"    ' начинаем почти всё сначала

 

По окончании макроса желательно закрыть прогресс бар:

pi.Hide    ' закрываем индикатор

 

 

У объекта ProgressIndicator имеется много различных свойств и методов.

 

Вкратце расскажу о некоторых свойствах:

  • свойство Caption позволяет задать новый заголовок индикатора
  • свойство FP позволяет получить доступ к отображаемой форме (и всем её элементам управления)
    (например, код pi.FP.PrintForm выведет индикатор на печать)
  • свойства Line1Line2 и Line3 позволяют в любом месте кода задать текст конкретной строки индикатора
  • свойства ShowPercents и ShowTime включают или выключают отображение процента выполнения и времени в заголовке индикатора
    (по умолчанию оба свойства имеют значение TRUE, т.е. в заголовке отображается и время, и процент выполнения макроса)

 

Из функций объекта мы рассмотрим только одну: AddChildIndicator

Эта функция создаёт дочерний прогресс бар, и отображает его выше или ниже родительского:

' создаём дочерний индикатор, и выводим его ниже основного
    Dim pi2 As ProgressIndicator
    Set pi2 = pi.AddChildIndicator("Раскрашивание ячеек", 1)

При изменении процента выполнения в дочернем индикаторе пропорционально меняется и процент выполнения главного (родительского) прогресс-бара.

 

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

 

На индикаторе присутствует кнопка «Отмена» - её нажатие вызывает останов всех запущенных макросов
(выполняется команда End, останавливающая все макросы, и обнуляющая все переменные)

Поскольку у этой кнопки свойство Cancel установлено в TRUE, нажатие на клавиатуре клавиши ESC равносильно нажатию кнопки «Отмена»
(при нажатии Esc макрос останавливается)

 


добавлено 23.02.2012

Новая версия прогресс-бара - с поддержкой отображения лога на индикаторе, и возможностью отображения лога в виде текстового файла.

новая версия прогресс-бара

 

Высоту текстового поля с логом можно изменять:

вывод лога макроса на индикаторе выполнения

Новая  версия индикатора, и примеры его использования - во втором прикреплённом файле.


PS: Идеальный прогресс-бар должен работать так :)

Вложения:

Комментарии

Настройки просмотра комментариев

Выберите нужный метод показа комментариев и нажмите "Сохранить установки".

Добрый день. Пытаюсь использовать ваш индикатор у себя, но он выдает ошибку в классе class_initialize в строке Set FP.indicator = Me
Ошибка Compile error Method or data member not found. Как поправить?

Огромное спасибо разработчику!!!
Несколько лет применял ваш ProgressIndicator (выдрал из какого-то примера), просто, эффектно. И вот наконец обнаружил автора.

Большое спасибо Автору! Великолепный макрос! Думал что не нужный, пока не пришлось обрабатывать однотипные файлы тысячами! А так сразу понятно, на сколько можно идти пить чай!

Еще раз добрый день!
Заменил строку LogString = LogString & vbNewLine & currtime$ & txt на LogString = LogString & vbLf & currtime$ & txt в вашей процедуре Log, все стало нормально.
Спасибо
С уважением Александр

Доброго дня!
Попробовал Ваш код с поддержкой отображения лога на индикаторе
Очередная строка лога лепится к предыдущей, а не пишется на индикаторе с новой строки
Что посоветуете посмотреть?
Спасибо
С уважением Александр

Надо внутрь кода вычислений добавить строку DoEvents
чтобы комп не подвисал на выполнении вычислений.
Тогда прогрессбар будет отрисовываться постоянно, и ничего пропадать не будет
PS: все ваши 6 строк кода, к этой проблеме никакого отношения не имеют.

Добрый день!
Использую прогресс бар для индикации во время объемных вычислений (парсинг большого объема JSON, общее время около 8 - 10 минут). Когда тестировал на меньших объемах (1-2 минуты) все работало отлично, но когда загрузил реальные данные, то прогресс бар стал периодически пропадать на несколько секунд, а потом снова появляться уже с обновленными данными. Причем количество таких появлений и пропаданий прогресс бара плавающее. Подскажите, может кто-то сталкивался с такой же проблемой?

Все параметры обновления перед запуском сбрасываю:
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
ActiveSheet.DisplayPageBreaks = False
Application.DisplayStatusBar = False
Application.DisplayAlerts = False

Спасибо!

Подскажите, если при использовании дочернего индикатора обращаться к нему напрямую (например, если брать Ваш пример, так: pi.AddChildIndicator("ИМЯ_ИНДИКАТОРА").SubAction "Действие $index из $count"), без конструкции With, то на стадии обновления дочернего индикатора (добавления действия) возникает ошибка деления на ноль: почему так происходит?

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

Добрый день,
я не понял как и где вставлять сам код между моим макросом. Форму и модуль я скопировал.
У меня следующий процесс.
1. Отключаю экранчик.
2. Открываю файл 1 и проверяю его на наличие в указанной папке.
3. Если файл1 есть то в самом файле я снимаю объединения ячеек.
4. Открываю файл2 повторяю пункт 2-3.
5. В файле1 делаю автофильтр и удаляю все не нужное и копирую данные в свой "материнский" файл через который запустил макрос. Закрываю файл 1 без сохранения(поскольку не знаю как макросом вернуть удаленное).
6. опять открываю файл1 п.2-3
7. Тяну ВПР с файла 2, фильтрую не нужные данные в файле1 и удаляю их, копирую в другую таблицу "материнского" файла.
8. закрываю файл 1,2 не сохраняя, и включаю экран.

Прогрес бар тут нужен, поскольку долго обрабатывается, и файлы большие, но это намного быстрей чем все делать ручками. Хотелось бы увидеть общий прогресс бар просто с полосой и время.

Игорь, в настройках парсера: дополнительно-разное, есть галочка "отображать второй прогресс-бар" уже не актуальна) , т.к. первый после апа проги вмещает 6 строк. Было бы очень здорово если бы на следующий ап галочку изменили на "отображать прогресс-бар"))) Спасибо!

Здравствуйте, Юрий
В парсере отключение прогресс-бара не предусмотрено
От версии Excel зависит (у меня в Excel 2010 прогресс-бар отображается только в Excel, если в этот момент работать в других программах, то прогресс-бар не мешает. Но на некоторых компах проблема проявляется, и индикатор отображается поверх всех приложений)

Здравствуйте, вопрос в следующем. Как отключить прогресс-бар в парсере? Очень не удобно когда парсер запускается каждую минуту и всплывает поверх окон. Спасибо!

Спасибо!

Александр, надо задать количество действий - selection.cells.count
и при обработке каждой ячейки вызывать медом .SubAction прогрессбара
Тогда время и проценты будут считаться

PS: а можно просто макрос немного переписать, чтобы вся обработка выполнялась за десятую долю секунду. - тогда и прогрессбар не понадобится
Поищите в инете советы, как в макросе уйти от использования Selection, обрабатывая массив ячеек целиком

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

Нажатие на красный крестик не останавливает макрос, а просто скрывает форму прогресс-бара.

Чтобы крестиком форма не закрывалась, добавьте код в ФОРМУ F_Progress

Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    If CloseMode = 0 Then Cancel = True ' отмена закрытия крестиком
End Sub

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

Вышел из положения так. Сначала запускается маленький легкий макрос, он показывает юзеру чистый лист с предупреждающей надписью и стоящим на 20% прогресс-баром, пока что фальшивым, статичным.
Ведь главное, чтобы юзер спокойно ждал и не дергался!
Далее из этого макроса запускается основной большой макрос и прогресс-бар начинает ехать обычным порядком.
Прогресс-бар использовал свой, простейший, см. ранее, повтором буквы g шрифта Webdings.

У меня сложный макрос долго работал.
Но пока налаживал этот прогресс-бар, выяснилась интересная штука.
Собственно, и необходимость в нем отпала.
Выяснилось, что основное замедление (до 15-20 секунд) происходит еще ДО НАЧАЛА выполнения операций!
Получается, что на этапе выделения памяти под переменные! Больше быть нечему.
У меня там реально много: по 20 переменных типа Long и Single, 8 String, 4 динамических массива Range, 8 массивов String тоже динамических и т.д. Сократить бы можно, но - за счет понятности кода...
За эту гипотезу говорит то, что замедление на компе с 8G памяти практически не чувствуется, а с 2G достигает 15 сек. Та же Win-7 и тот же Office-2010.

Это правда может такое быть?

Я пытался запускать прогресс-бар еще до определения переменных. Однако ничего не меняется.
Да и кстати - по ходу придумал наипростейший прогресс-бар, он работает всегда!
На любой винде есть шрифт Webdings, в нем буква "g" с номером 103 - это просто закрашенный квадратик. Создаем прогресс-бар повтором этих квадратиков функцией String(), на отдельном листе с заранее отформатированной ячейкой. Размер, цвет надо будет подобрать.
Допустим, в ячейку помещается 23 квадратика.
Прогресс-бар в одну строчку:

Sheets("ProgressBar").Range("D7").Value = String(Round(23 * i / n), 103)

где i и n - ваши параметры. Например, выполнено i действий из n.

Алексей, форма должна быть немодальной, чтобы прогресс-бар отображался сверху
В свойствах формы поставьте ShowModal = FALSE

Прогресс-бар работает быстро, - за секунду может сменить десятки состояний.
Если у вас что-то медленно работает, - проблема в вашем коде (или неправильном использовании прогресс-бара)
Смотрите пример файл из статьи, - там всё быстро работает.

Добрый день
Почему-то тогда я пытаюсь показать прогресс-бар перед своей формой(окно) она отображается за ней, и я прогресс-бар не вижу, а вижу только свою форму. Также остается проблема, когда я свою форму скрываю на время. При этом чтобы прогресс-бар работал быстрее надо мышкой на него левой кнопкой нажать (а так ждать около 1 мин против 5 сек).

Чтобы все работало - надо скопировать в ваш файл модуль класса и форму из прикреплённого файла

Здравствуйте выдает ошибку pi As New ProgressIndicator

У меня такое же было из-за того, что я скопировал в свой проект классмодуль ProgressIndicator, но забыл скопировать форму F_Progress - нужно копировать и то и другое, как скопировал ошибку перестало вываливать.
П.С. Автору индекатора/сайта уклон до земли, респект, уважуха .....

Здравствуйте, Денис
Я не пробовал, - но, вроде, ничего такого в коде нет, что помешало бы использовать в других приложениях, поддерживающих VBA.
Если что и надо будет подправить в коде - то совсем чуть-чуть.
Попробуйте, - всё должно получиться

Игорь, добрый день.
Будет ли компонент работать в MS Access 2007 и выше?

Все получилось!
Огромное спасибо за помощь!

Здравствуйте, Николай

Чтобы открыть текст в Блокноте, вам понадобится макрос ShowText:
http://excelvba.ru/code/ShowText

Пример кода:

Public Log_TEXT As String
 
Sub ЭтоВашОсновнойМакрос()
    Dim pi As New ProgressIndicator
    pi.Show "Проверка"
    pi.AddButton "Показать лог", "ShowLogText"
 
    ' тут ваш макрос, с использованием прогресс-бара
    pi.Log "Первая строка лога"
    pi.Log "Вторая строка"
 
    ' в конце макроса, помещаем текст лога в глобальную текстовую переменную Log_TEXT
    Log_TEXT = pi.FP.TextBox_Log.Text
End Sub
 
Sub ShowLogText()
    On Error Resume Next
    ' макрос сохраняет текст из глобальной переменной Log_TEXT в текстовый файл
    ' (файл создаётся в папке для временных файлов)
    ' После создания текстового файла он открывается в программе по-умолчанию (например, в Блокноте)

    ' формируем имя для временного файла
    filename$ = Environ("TEMP") & "\text" & Left(Rnd() * 1E+15, 10) & ".txt"
    ' сохраняем текст в файл
    With CreateObject("scripting.filesystemobject").CreateTextFile(filename, True)
        .Write Log_TEXT: .Close
    End With
    ' открываем созданный файл
    CreateObject("wscript.shell").Run """" & filename$ & """"
End Sub

Вставьте этот код в новый модуль, проверьте, - все должно заработать.

Здравствуйте!
Добавил на прогресс-бар кнопку "Лог", которая появляется в конце работы.
Подскажите, пожалуйста, как привязать к ее нажатию запуск метода класса ShowLog? Т.е. я хочу чтобы при нажатии этой кнопки лог "открывался" в блокноте...
Заранее спасибо!

Прогрессбар создает копию формы,
потому обращайтесь в коде в Label так:
pi.FP.Label3.Visible = True

Здравствуйте! Спасибо Вам за макрос.

Возник один вопрос:
Я добавил на форму свой элемент (Label), который в зависимости от условий должен быть либо виден, либо нет.
Изменение свойств посредством F_Progress.Label3.Visible = True либо F_Progress.Label3.Caption = FileName полностью игнорируется (ничего не происходит)
Подскажите - куда копать? у самого уже фантазия иссякла...
Что мешает изменению свойств объектов?

Сергей, спасибо.
Денежку получил

Спасибо, все замечательно работает !
Обязательно переведу немного денежки.

Спасибо ОГРОМНОЕ разработчику данного макроса!

Примерно так:

Sub ОсновнойМакрос()
    Dim pi As New ProgressIndicator, i As Long
    pi.Show "Заголовок индикатора"
    pi.StartNewAction , , , , , 5
 
    For i = 1 To 5
        Подпрограмма i, pi
    Next
    pi.Hide
End Sub
 
Sub Подпрограмма(ByVal i As Long, ByRef pi As ProgressIndicator)
    pi.SubAction "Выполняется действие $index из $count"
    MsgBox i
End Sub

Как передать объект в подпрограмму?
для того чтобы вызвать pi.subaction?

Ну так вы скопируйте в свой файл не только пример макроса из статьи,
но и форму с модулем класса.
И все заработает.
(посмотрите прикреплённый файл с примером - там же все работает)

У меня на строке Dim pi As New ProgressIndicator выводит ошибку User-defined type not defined.

Здравствуйте, Игорь. Все пытаюсь "прикрутить" любой из ваших прогресс-баров к своему макросу, но что-то не выходит, пишет 0%, а на самом деле процесс идет. Немогли бы вы, пожалуйста, помочь "прикрутить" любой из баров? Ниже ссылка на файл с макросом. Спасибо!
_http://files.mail.ru/TBZFOK

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

Risagitov, вот и попробуйте «локализовать», а мы потом посмотрим, что получилось

Например, после выполнения первого pi.run, насколько должна продвинуться (увеличиться) полоска прогресс-бара?
Индикатор ведь не знает, сколько ещё строк с pi.run есть в макросе...
А если надо вывести какой-то текст? Опять усложнять код?

Я делал прогресс-бар под свои потребности - я его использую в десятках макросов,
и ни разу не было случая, чтобы мне не хватало его функционала.
А написать пару лишних строк кода, в угоду универсальности решения, - для меня не проблема.

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

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

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

в таких случаях нужно вот что..

sub m()
pi.ini 'инициализация бара
''''''''''''код 
''''''''''''код
pi.run
''''''''''''код 
''''''''''''код
pi.run
''''''''''''код 
''''''''''''код
pi.run
''''''''''''код 
''''''''''''код
pi.run
 
pi.done ' завершение его работы
end sub

Нет, нет, ответ исчерпывающий, все понятно, это я привел пример листа по аналогии книги(чайник ведь), спосибо за ответ суть прогрессБара я понял.
Я еще много чего не понимаю, но когда столкнулся изучать все это уж больно все заинтересовало это програмирование и хочется все изучить, большая проблема не в понимании кода а в английском.ВОТ
С Уважением Дмитрий!

Здравствуйте, Дмитрий.
При помощи макросов это реализовать не получится.
Макрос может зафиксировать момент открытия файла, но не больше (макрос не сможет отследить запуск команды открытия файла, не может получить информацию о том, сколько ещё по времени будет открываться файл, и т.п.)

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

Подскожите пожалйсто, нигде я не встречал, макроса который бы выполнял такую функцию, т.е., как можно сделать, что бы прогрессБар показывал визуальную работу загрузку приложения которое открывается, а не с кнопки, к примеру открываю я книгу или лист, а мне показывает прогрессБар процесс загруски открытия!
С Уважением Дмитрий!!!!

1. С завершением индикации - понятно.

2. Запустите Ваш макрос при "Option Base 1".
Забавные показания pi выдаст! :)
Они объяснимы, но не желательны.

Как это в pi устранить?

Перед строкой

pi.Hide

добавьте следующий код:
pi.StartNewAction 100, 100, "Все макросы выполнены"
    Application.Wait Now + 1 / 86400    ' пауза 1 секунду

Вопрос:
В приведенном Вами примере на полоске индикатора цветом не отображается факт завершения "Макроса10".

Где? и Как? "притормозить" pi, чтобы он отобразил цветом(!) завершения процесса "Макроса10" ? :)

C определение переменных разобрался. Должно быть:

Dim ИменаМакросов As Variant
Dim macro As Variant

Отправить комментарий

Содержание этого поля является приватным и не предназначено к показу.
CAPTCHA
Подтвердите, пожалуйста, что вы - человек:
                          ____    _   ___ 
_ _ __ _ _ __ |___ \ / | |_ _|
| | | | / _` | | '_ \ __) | | | | |
| |_| | | (_| | | |_) | / __/ | | | |
\__,_| \__,_| | .__/ |_____| |_| |___|
|_|
Введите код, изображенный в стиле ASCII-арт.

Не получается применить макрос? Не удаётся изменить код под свои нужды?

Оформите заказ у нас на сайте, не забыв прикрепить примеры файлов, и описать, что и как должно работать.