確定的な NSProgressIndicator を使用して NSTask の進行状況を確認するにはどうすればよいですか?- ココア
-
29-10-2019 - |
質問
私が持っているのは、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で、コードがありません。より良い例が必要な場合はお知らせください。