How to use a specific NSProgressIndicator to check NSTask progress? - Cocoa

I have an NSTask running with a long finished shell script, and I want the NSProgressIndicator to check how much has been done. I tried a lot of things, but just can't get it to work. I know how to use it if the progress bar is not defined, but I want it to load as the task progresses.

This is how I run the script:

- (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]; } 

I need to put a progress bar, which checks the progress of this task during its occurrence and is updated accordingly.

+6
source share
4 answers

Here is an example of async NSTask that runs a unix script. The Unix script has echo commands that return the current state to a standard error as follows:

echo "working" >&2

This is processed by the notification center and sent to the display.

To update a specific progress bar, simply send status updates β€œ25.0” to β€œ26.0” and convert them to float and send to the progress bar.

note: I got this work after a lot of experimentation and with many tips from this site and other links. therefore, I hope this will be useful to you.

Here are the announcements:

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

Here are the main software modules:

  - (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 
+7
source

You must have some way to call back or interrupt the progress of the task in another, to let you know how much you have done. If you are talking about a shell script, you can break 1 script into several scripts and, at the end of the script section, update the progress indicator. Other applications did things like this: iirc Sparkle performed some user logic in its decompression code to unpack the pieces so that it could update the progress indicator. If you want to achieve the same effect, you will have to do something similar.

+1
source

To get the PID (process identifier) ​​for the command that you ran using it in combination with the PS (Process state) command, you can get the status of your process using this value in your code to show it in the execution line

0
source

an easy way to link your script (nstask) to your obj c controller using a standard error as a communication channel. Not to say that this is great, but it works pretty well. You must configure your task as an asynchronous nstask and use notifications to read from standard input and standard error separately. I can publish it later if you need it.

Wrap your script like this:

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

Handle the standard error through the handset and file descriptor and plug it into a power outlet to update the text status, or convert it to a floating file to display progress. Ie

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

If you need to fix the real std error, then catch it and merge it into std, as in my example. This is not an ideal approach, but I often use it and it works well.

There is no code on my iPad. Let me know if you need a better example.

0
source

All Articles