2012-01-17 3 views
6

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

Вот как я бегу сценарий:

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

ответ

7

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

echo "working" >&2

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

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

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

Вот заявления:

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 
+0

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

+0

в IBAction, каковы объявления для вещей, строки которых установлены – drewsdunne

+0

, и нужна ли часть с NSMutableArray? Не могу я просто указать на .sh-файл и использовать/usr/bin/sh – drewsdunne

1

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

+0

ок я буду смотреть на это спасибо – drewsdunne

+0

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

+0

Зачем вы так говорите? Вы можете запустить NSTask, получить его результаты, запустить другую задачу и так далее, я написал приложения, которые запускали несколько задач для разных вещей раньше. Единственное различие заключается в том, что я использую класс, который инкапсулирует NSTask, но кроме этого вы должны быть в состоянии сделать тот же https://github.com/Machx/Zangetsu/blob/master/Source/CWTask.m –

0

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

+0

Я никогда не слышал о PS и PID, мог бы вы показать мне пример? – drewsdunne

+0

ps - команда для получения статуса любого процесса. Если вы запустите эту команду с помощью pid (идентификатор процесса, уникальный для процесса) в терминале, он отображает состояние процесса, имеющее это значение как pid.say, например: ps -l 185 –

+0

, но как бы я это вызвал в свое приложение, если его запуск в терминале – drewsdunne

0

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

Оберните ваш сценарий, как это:

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

процессов Вашей стандартную ошибку через трубу и дескриптор и провод его к розетке для обновления статуса текстового или преобразовать его в поплавок для прогресса дисплеев. Т.е.

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

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

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

+0

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

+0

Хорошо, вот контекст. Запустите NSTask в асинхронном режиме. отделить стандартный вывод и стандартную ошибку. Используйте центр уведомлений для ответа на данные по этим каналам. используйте стандартную ошибку для создания текущего состояния текста. В основном, в вашей программе во время ее запуска отправьте обновления статуса стандартной ошибке с помощью команды echo, например: echo «report report»> & 2. Он будет отображаться на стандартной трубке ошибок, и ваша программа может отображать ее на экране. Или вместо текста статуса отправьте числа, подобные 25.0, и преобразуйте их в float, а затем обновите свой определенный индикатор выполнения. – CocoaEv