MyTetra Share
Делитесь знаниями!
Правильная индикация прогресса цикла на клиенте
Время создания: 08.09.2016 15:45
Автор: Сергей Старых aka tormozit
Текстовые метки: 1c.ui
Раздел: Программирование - 1с - Интерфейс
Запись: xintrea/mytetra_anatolean/raw/master/base/14733387140p6wbsa5w0/text.html на bitbucket.org

Правильная индикация прогресса цикла на клиенте

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

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

 

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

// Получает структуру для индикации прогресса цикла.

//

// Параметры:

// КоличествоПроходов – Число - максимальное значение счетчика;

// ПредставлениеПроцесса – Строка, "Выполнено" – отображаемое название процесса;

// ВнутреннийСчетчик - Булево, *Истина - использовать внутренний счетчик с начальным значением 1,

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

// КоличествоОбновлений - Число, *100 - всего количество обновлений индикатора;

// ЛиВыводитьВремя - Булево, *Истина - выводить приблизительное время до окончания процесса;

// РазрешитьПрерывание - Булево, *Истина - разрешает пользователю прерывать процесс.

// МинимальныйПериодОбновления - Число, *1 - с, обновлять не чаще чем этот период, 0 - по количеству обновлений,

// эта реализация не поддерживает дробные значения;

//

// Возвращаемое значение:

// Структура - которую потом нужно будет передавать в метод ЛксОбработатьИндикатор.

//

Функция ЛксПолучитьИндикаторПроцесса(Знач КоличествоПроходов = 0, ПредставлениеПроцесса = "Выполнение", ВнутреннийСчетчик = Истина, Знач КоличествоОбновлений = 100, ЛиВыводитьВремя = Истина, РазрешитьПрерывание = Истина, МинимальныйПериодОбновления = 1) Экспорт

Индикатор = Новый Структура;

Если КоличествоПроходов = 0 Тогда

Состояние(ПредставлениеПроцесса + "...");

КоличествоПроходов = 1;

КонецЕсли;

Индикатор.Вставить("КоличествоПроходов", КоличествоПроходов);

Индикатор.Вставить("ПредставлениеПроцесса", ПредставлениеПроцесса);

Индикатор.Вставить("ЛиВыводитьВремя", ЛиВыводитьВремя);

Индикатор.Вставить("РазрешитьПрерывание", РазрешитьПрерывание);

Индикатор.Вставить("ДатаНачалаПроцесса", ТекущаяДата());

Индикатор.Вставить("МинимальныйПериодОбновления", МинимальныйПериодОбновления);

Индикатор.Вставить("ДатаСледующегоОбновления", Дата('00010101'));

Индикатор.Вставить("ВнутреннийСчетчик", ВнутреннийСчетчик);

Если КоличествоОбновлений > 0 Тогда

Шаг = КоличествоПроходов / КоличествоОбновлений;

Иначе

Шаг = 0;

КонецЕсли;

Индикатор.Вставить("Шаг", Шаг);

Индикатор.Вставить("СледующийСчетчик", 0);

Индикатор.Вставить("Счетчик", 0);

Возврат Индикатор;

КонецФункции // ЛксПолучитьИндикаторПроцесса()

// Проверяет и обновляет индикатор. Нужно вызывать на каждом проходе индицируемого цикла.

//

// Параметры:

// Индикатор – Структура – индикатора, полученная методом ЛксПолучитьИндикаторПроцесса;

// Счетчик – Число – внешний счетчик цикла, используется при ВнутреннийСчетчик = Ложь.

//

Процедура ЛксОбработатьИндикатор(Индикатор, Счетчик = 0) Экспорт

Если Индикатор.ВнутреннийСчетчик Тогда

Счетчик = Индикатор.Счетчик + 1;

Индикатор.Счетчик = Счетчик;

КонецЕсли;

Если Индикатор.РазрешитьПрерывание Тогда

ОбработкаПрерыванияПользователя();

КонецЕсли;

ОбновитьИндикатор = Истина;

ТекущаяДата = ТекущаяДата();

Если Индикатор.МинимальныйПериодОбновления > 0 Тогда

Если ТекущаяДата >= Индикатор.ДатаСледующегоОбновления Тогда

Индикатор.ДатаСледующегоОбновления = ТекущаяДата + Индикатор.МинимальныйПериодОбновления;

Иначе

ОбновитьИндикатор = Ложь;

КонецЕсли;

КонецЕсли;

Если ОбновитьИндикатор Тогда

Если Индикатор.Шаг > 0 Тогда

Если Счетчик >= Индикатор.СледующийСчетчик Тогда

Индикатор.СледующийСчетчик = Цел(Счетчик + Индикатор.Шаг);

Иначе

ОбновитьИндикатор = Ложь;

КонецЕсли;

КонецЕсли;

КонецЕсли;

Если ОбновитьИндикатор Тогда

Индикатор.СледующийСчетчик = Цел(Счетчик + Индикатор.Шаг);

Если Индикатор.ЛиВыводитьВремя Тогда

ТекущаяДата = ТекущаяДата();

ПрошлоВремени = ТекущаяДата - Индикатор.ДатаНачалаПроцесса;

Осталось = ПрошлоВремени * (Индикатор.КоличествоПроходов / Счетчик - 1);

Часов = Цел(Осталось / 3600); Осталось = Осталось - (Часов * 3600);

Минут = Цел(Осталось / 60);

Секунд = Цел(Цел(Осталось - (Минут * 60)));

ОсталосьВремени = Формат(Часов, "ЧЦ=2; ЧН=00; ЧВН=") + ":" + Формат(Минут, "ЧЦ=2; ЧН=00; ЧВН=") + ":" + Формат(Секунд, "ЧЦ=2; ЧН=00; ЧВН=");

ТекстОсталось = "Осталось: ~" + ОсталосьВремени;

Иначе

ТекстОсталось = "";

КонецЕсли;

ТекстСостояния = Индикатор.ПредставлениеПроцесса + " " + Формат(Счетчик / Индикатор.КоличествоПроходов * 100, "ЧЦ=3; ЧДЦ=0") + "% " + ТекстОсталось;

Если ТипЗнч(Индикатор) = Тип("СтрокаТаблицыЗначений") Тогда
ТаблицаИндикаторов
= Индикатор.Владелец();

ИндексИндикатора = ТаблицаИндикаторов.Индекс(Индикатор);

Если ИндексИндикатора > 0 Тогда
ТекстСостояния
= ТаблицаИндикаторов[ИндексИндикатора - 1].ТекстСостояния + " >> " + ТекстСостояния;

КонецЕсли;

Индикатор.ТекстСостояния = ТекстСостояния;

КонецЕсли;

Состояние(ТекстСостояния);

КонецЕсли;

Если Счетчик = Индикатор.КоличествоПроходов Тогда

Состояние("");

КонецЕсли;

КонецПроцедуры // ЛксОбработатьИндикатор()

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

Вот пример их использования.

КоличествоДанных = 100000;

Индикатор = ЛксПолучитьИндикаторПроцесса(КоличествоДанных, "Проверка данных");

Для Счетчик = 1 По КоличествоДанных Цикл

ЛксОбработатьИндикатор(Индикатор, Счетчик);

КонецЦикла;

КоличествоДанных = 100000;

Индикатор = ЛксПолучитьИндикаторПроцесса(КоличествоДанных, "Проверка данных", Истина);

Для Счетчик = 1 По КоличествоДанных Цикл

ЛксОбработатьИндикатор(Индикатор);

КонецЦикла;

Так же в этом разделе:
 
MyTetra Share v.0.59
Яндекс индекс цитирования