Как сделать индикатор времени окончания запущенного процесса ?


#1

Есть простой процесс. Для массива strings (порядка 20 000 и больше) проверяется содержатся ли эти strings в другой длинной string и если содержатся то заполняется другой массив найденными strings. Затем эти найденные strings из массива суммируются в одну string и далее выводим ее в NSTextView.

Вопрос. Как сделать индикатор времени остающегося до окончания процесса?
Пока есть представление что нужно как то получить live time для одного цикла и затем зная количество элементов в массиве подсчитать время на весь процесс умножая количество элементов массива на время для одного цикла, ну и как то графически представить. Основной вопрос как получить live time для одного цикла ?

Может вообще надо как то иначе такой индикатор делать, подскажите пожалуйста!


#2

Похоже что мне надо использовать NSProgress. Может кто уже использовал этот класс ? Есть сайт с описанием того как его использовать, но на Objective-C, пока не понимаю как мне это все привязать к моему случаю. Было бы наверное проще понять если бы это было бы на Swift. Может кто перевести в Swift код с этого сайта ? - https://oleb.net/blog/2014/03/nsprogress/


#3

в свифте он называется NSProgressIndicator есть в документации. xcode>help>documentations and API reference> в поиске вписываешь то, что тебе нужно. Профит, если не знал :wink:


#4

Знал. NSProgressIndicator получает текущее значение. Вот это текущее значение и нужно как то получить для моего случая. В этом вопрос.


#5

Пытаюсь разобраться с использованием NSProgress. Пока не работает написаное. Нужно перевести в Swift следующий Objectiv-C код ниже (по ссылке выше). Кто-нибудь может перевести ?

// ImageFilter is a custom class that performs the work
    ImageFilter *imageFilter = [[ImageFilter alloc] initWithImage:self.image];
    [imageFilter filterImageWithCompletionHandler:
        ^(UIImage *filteredImage, NSError *error)
    {
        [progress removeObserver:self
            forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
            context:ProgressObserverContext];

        // Image filtering finished
        ...
    }];

#6

Есть такой сайт, называется https://objectivec2swift.com/

Он платный, но конвертит до 30-40 строк бесплатно


#7

Никак ни получается сделать индикатор. Сейчас застрял на том что индикатор не обновляется в процессе. Текстовое сообщение тоже не обновляется, появляется только к концу процесса о его 100 % завершении. Индикатор показывает к концу процесса толко малую часть как завершенную.

Подскажите что не так !!!

Есть класс где происходите основной процесс:

class TestProgress: NSObject, ProgressReporting
{
let progress: Progress

override init()
{
    progress = Progress()
    super.init()
}

func findRepeatsWithProgressReporting(stringToSearch: String, minimalLength: Int, maximalLength: Int) -> [String]
{
    var arrayOfRepeats = [String]()

    progress.totalUnitCount = Int64((minimalLength...maximalLength).count)

    for i in minimalLength...maximalLength
    {
        let arrayOfStrings = stringToSearch.chopString(stringOut: stringToSearch, length: i)
        let arrayOfUniqueStrings = Array(Set(arrayOfStrings))

        for each in arrayOfUniqueStrings
        {
            let arrayOfNSRanges = stringToSearch.searchForNSRangesOfStringInString(stringOut: stringToSearch, stringIn: each)

            var positions = String()

            if arrayOfNSRanges.count > 1
            {
                for each1 in arrayOfNSRanges
                {
                    let repeatStart = String(each1.location + 1)
                    let repeatEnd = String(each1.location + each1.length)
                    positions += "(" + repeatStart + "-" +  repeatEnd + ")"
                }
                let stringToShow = each + " " + positions
                arrayOfRepeats.append(stringToShow)
            }
        }
        progress.completedUnitCount += 1
    }
    return  arrayOfRepeats
}

}

В контроллере этот прогресс этого процесса добавляется как child и есть KVO для обновления индикатора и текстового сообщения о процессе.

private var progressObservationContext = 0

class myVewContrloler: NSViewController
{
...

var testProgress = TestProgress ()
var repeatsProgress = Progress() 

@IBOutlet weak var repeatsSearchProgressBar: NSProgressIndicator!
@IBOutlet weak var repeatsPercentText: NSTextField!

@IBOutlet weak var minimalLength: NSTextField!
@IBOutlet weak var maximalLength: NSTextField!
@IBOutlet var foundRepeats: NSTextView!

@IBAction func actionFindRepeats(_ sender: AnyObject)
{
    repeatsProgress = Progress(totalUnitCount: 10)

    let options : NSKeyValueObservingOptions = [.new, .old, .initial, .prior]
    repeatsProgress.addObserver(self, forKeyPath: "fractionCompleted", options: options, context: &progressObservationContext)
    repeatsProgress.addObserver(self, forKeyPath: "localizedDescription", options: options, context: &progressObservationContext)

    var arrayOfRepeats = [String]()

    repeatsProgress.becomeCurrent(withPendingUnitCount: 10)
    arrayOfRepeats = testProgress.findRepeatsWithProgressReporting(stringToSearch: stringToSearch, minimalLength: minimalLength.integerValue, maximalLength: maximalLength.integerValue)

    ...

    repeatsProgress.removeObserver(self, forKeyPath: "fractionCompleted")
    repeatsProgress.removeObserver(self, forKeyPath: "localizedDescription")

    repeatsProgress.resignCurrent()
    }
}

//KVO

 override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
{
    guard context == &progressObservationContext else {
    super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
    return
}

if keyPath == "fractionCompleted"
{
    OperationQueue.main.addOperation{
            let progress = object as! Progress
            self.repeatsSearchProgressBar.doubleValue = progress.fractionCompleted
            self.repeatsPercentText.stringValue = progress.localizedDescription
    }
}
}

#8

Исходя из примеров что я видел надо использовать в коде выше

 var testProgress: TestProgress ? 

Но я использовал

 var testProgress = TestProgress() 

поскольку если использовать

 var testProgress: TestProgress ? 

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

 arrayOfRepeats = (testProgress?.findRepeatsWithProgressReporting(stringToSearch: stringToSearch, minimalLength: minimalLength.integerValue, maximalLength: maximalLength.integerValue))!

Вопрос почему эта ошибка возникает. Может быть где то в этом причина того что NSprogress не работает как должен ?


#9

Предположу, что в этом варианте

var repeatsProgress: Progress?

вы не инициализируете repeatsProgress.


#10

Извиняюсь, перепутал, имел ввиду testProgress. Исправил.
С этим NSprogress внимание и терпение уже на исходе.

Сделал тестовый проект, и метод из класса описанного выше где проверяется прогресс засунул в AppDelegate, а также все что было в отдельном контроллере в основном проекте, но все осталось также. Не работает как надо, результат выдает такой же как в основном проекте. В чем причина ума не приложу !


#11

Продолжаем выяснение причин проблемы с NSProgress . Для этого в наблюдатель

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?)
{
   ....
}

добавил

 print("Observed Something")

чтобы посмотреть что происходить с работой наблюдателя. Увидел следующее. При запуске процесса "Observed Something" печатается сразу два раза и затем при окончании процесса еще сразу шесть раз. Если бы наблюдатель обновлялся каждый раз когда происходит "fractionCompleted" в течении процесса то "Observed Something" должны были бы появляться один за другим а не сразу. Или? Кто-нибудь может быть понимает почему одномоментно могут появлятся “Observed Something”` ?


#12

Проблему решил.

  1. Главное, надо было разнести потоки, основные вычисления и NSProgress должны быть не в main потоке, а в global.
  2. NSProgressIndicator рабоатет не понятно как в моем El Capitan, в тестовом проекте работает как надо, а в основном проекте прорисовывает только часть, код идентичен. При этом localizedDescription работает как положено в обоих проектах. Нашел в интернете обсуждение (https://forums.developer.apple.com/thread/22591 ) того что в OS X El Capitan NSProgressIndicator не работает как надо хотя в других версиях OSX все работало. Остановился на localizedDescription , все равно это более информативно чем гадать по картинке сколько осталось до конца процесса.

#13

Предлагаю обновить систему до последней OS Sierra :slight_smile: