確定的な NSProgressIndicator を使用して NSTask の進行状況を確認するにはどうすればよいですか?- ココア

StackOverflow https://stackoverflow.com/questions/8889006

質問

私が持っているのは、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];
}

タスクの進行状況を確認し、それに応じて更新するプログレスバーを配置する必要があります。

役に立ちましたか?

解決

以下は、UNIX スクリプトを実行する非同期 NSTask の例です。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

他のヒント

タスクの進行状況をコールバックまたは中断して、進行状況を確認する方法が必要です。シェルスクリプトについて話している場合は、1つのスクリプトを複数のスクリプトに分割し、スクリプトのセクションが完了すると、進行状況インジケーターを更新できます。他のアプリはこのようなことを行っています。iircSparkleは、解凍コードでカスタムロジックを実行して、進行状況インジケーターを更新できるようにチャンクで解凍しました。同じ効果を達成したい場合は、同様のことをする必要があります。

実行したコマンドのPID(プロセスID)を取得し、PS(プロセス状態)コマンドと組み合わせて使用すると、プロセスの状態を取得できます。コードでその値を使用して、進行状況バーに表示します

スクリプト(nstask)をobj cコントローラーと通信させる簡単な方法は、通信チャネルとして標準エラーを使用することです。これが完璧だとは言えませんが、非常にうまく機能します。タスクを非同期nstaskとして設定し、通知を使用して標準入力と標準エラーから別々に読み取る必要があります。必要に応じて後で投稿できます。

スクリプトを次のようにラップします: ジェネラコディセタグプレ

パイプとファイルハンドルを介して標準エラーを処理し、テキストステータスの更新のためにコンセントに配線するか、進行状況を表示するためにフロートに変換します。つまり ジェネラコディセタグプレ

実際の標準エラーをキャプチャする必要がある場合は、それをトラップして、私の例のように標準アウトにマージします。これは完璧なアプローチではありませんが、私は頻繁に使用し、うまく機能します。

iPadで、コードがありません。より良い例が必要な場合はお知らせください。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top