Как использовать определенный NSProgressIndicator для проверки прогресса NSTask? - какао - PullRequest
6 голосов
/ 17 января 2012

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

Вот как я запускаю скрипт:

- (IBAction)pressButton:(id)sender {
    NSTask *task = [[NSTask alloc] init];
    [task setLaunchPath:@"/bin/sh"];
    [task setArguments:[NSArray arrayWithObjects:[[NSBundle mainBundle] pathForResource:@"script" ofType:@"sh"], nil]];
    [task launch];
}

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

Ответы [ 4 ]

7 голосов
/ 05 февраля 2012

Вот пример асинхронного NSTask, выполняющего сценарий Unix.В сценарии Unix есть echo команды, которые возвращают текущий статус к стандартной ошибке, например:

echo "working" >&2

Это обрабатывается центром уведомлений и отправляется на дисплей.

Чтобы обновить определенный индикатор выполнения, просто отправьте обновления состояния, такие как «25,0», «26,0», и преобразуйте их в число с плавающей точкой и отправьте в индикатор выполнения.

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

Вот объявления:

NSTask *unixTask;
NSPipe *unixStandardOutputPipe;
NSPipe *unixStandardErrorPipe;
NSPipe *unixStandardInputPipe;
NSFileHandle *fhOutput;
NSFileHandle *fhError;
NSData *standardOutputData;
NSData *standardErrorData;

Вот основные программные модули:

    - (IBAction)buttonLaunchProgram:(id)sender {

    [_unixTaskStdOutput setString:@"" ];
    [_unixProgressUpdate setStringValue:@""];
    [_unixProgressBar startAnimation:sender];

    [self runCommand];
}
- (void)runCommand {

    //setup system pipes and filehandles to process output data
    unixStandardOutputPipe = [[NSPipe alloc] init];
    unixStandardErrorPipe =  [[NSPipe alloc] init];

    fhOutput = [unixStandardOutputPipe fileHandleForReading];
    fhError =  [unixStandardErrorPipe fileHandleForReading];

    //setup notification alerts
    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];

    [nc addObserver:self selector:@selector(notifiedForStdOutput:) name:NSFileHandleReadCompletionNotification object:fhOutput];
    [nc addObserver:self selector:@selector(notifiedForStdError:)  name:NSFileHandleReadCompletionNotification object:fhError];
    [nc addObserver:self selector:@selector(notifiedForComplete:)  name:NSTaskDidTerminateNotification object:unixTask];

    NSMutableArray *commandLine = [NSMutableArray new];
    [commandLine addObject:@"-c"];
    [commandLine addObject:@"/usr/bin/kpu -ca"]; //put your script here

    unixTask = [[NSTask alloc] init];
    [unixTask setLaunchPath:@"/bin/bash"];
    [unixTask setArguments:commandLine];
    [unixTask setStandardOutput:unixStandardOutputPipe];
    [unixTask setStandardError:unixStandardErrorPipe];
    [unixTask setStandardInput:[NSPipe pipe]];
    [unixTask launch];

    //note we are calling the file handle not the pipe
    [fhOutput readInBackgroundAndNotify];
    [fhError readInBackgroundAndNotify];
}
-(void) notifiedForStdOutput: (NSNotification *)notified
{

    NSData * data = [[notified userInfo] valueForKey:NSFileHandleNotificationDataItem];
    NSLog(@"standard data ready %ld bytes",data.length);

    if ([data length]){

        NSString * outputString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];  
        NSTextStorage *ts = [_unixTaskStdOutput textStorage];
        [ts replaceCharactersInRange:NSMakeRange([ts length], 0)
                          withString:outputString];
    }

    if (unixTask != nil) {

        [fhOutput readInBackgroundAndNotify];
    }

}
-(void) notifiedForStdError: (NSNotification *)notified
{

    NSData * data = [[notified userInfo] valueForKey:NSFileHandleNotificationDataItem];
    NSLog(@"standard error ready %ld bytes",data.length);

    if ([data length]) {

        NSString * outputString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];  
        [_unixProgressUpdate setStringValue:outputString];
    }

    if (unixTask != nil) {

        [fhError readInBackgroundAndNotify];
    }

}
-(void) notifiedForComplete:(NSNotification *)anotification {

    NSLog(@"task completed or was stopped with exit code %d",[unixTask terminationStatus]);
    unixTask = nil;

    [_unixProgressBar stopAnimation:self];
    [_unixProgressBar viewDidHide];

    if ([unixTask terminationStatus] == 0) {
        [_unixProgressUpdate setStringValue:@"Success"]; 
    }
    else {
        [_unixProgressUpdate setStringValue:@"Terminated with non-zero exit code"];
    }
}
@end
1 голос
/ 17 января 2012

У вас должен быть какой-то способ перезвонить или прервать выполнение задачи в oder, чтобы сообщить, какой прогресс вы сделали.Если вы говорите о сценарии оболочки, вы можете разбить 1 сценарий на несколько сценариев и по завершении раздела сценария обновить индикатор выполнения.Другие приложения делали что-то вроде этого, iirc Sparkle в коде декомпрессии использовал некоторую пользовательскую логику для распаковки кусками, чтобы он мог обновлять индикатор прогресса.Если вы хотите добиться того же эффекта, вам придется сделать что-то похожее.

0 голосов
/ 04 февраля 2012

простой способ установить связь между вашим сценарием (nstask) и вашим контроллером obj c, используя стандартную ошибку в качестве канала связи. Не сказать, что это идеально, но работает довольно хорошо. Вы должны настроить свою задачу как асинхронный nstask и использовать уведомления для чтения из стандартного ввода и стандартной ошибки отдельно. Я могу опубликовать это позже, если вам это нужно.

Оберните ваш скрипт так:

echo "now starting" >&2
for i in $(ls /tmp)
do 
echo $i 2>&1
done
echo "now ending" >&2

Обработайте вашу стандартную ошибку через канал и дескриптор файла и подключите ее к выходу для обновления текстового статуса или преобразуйте его в число с плавающей точкой для отображения прогресса. I.e

echo "25.0" >&2. Can be captured and converted.

Если вам нужно перехватить реальную ошибку std, перехватите ее и объедините в std out, как в моем примере. Это не идеальный подход, но я часто его использую, и он хорошо работает.

На моем iPad нет кода. Дайте мне знать, если вам нужен лучший пример.

0 голосов
/ 17 января 2012

Получите PID (ID процесса) для команды, которую вы выполнили, используя ее в сочетании с командой PS (Process state), вы можете получить состояние вашего процесса, используя это значение в своем коде, чтобы показать его в индикаторе выполнения

...