كيفية استخدام مؤشر NSProgressIndicator المحدد للتحقق من تقدم NTask؟- كاكاو

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

سؤال

ما لدي هو أن NTask يقوم بتشغيل برنامج نصي طويل مسبق الصنع وأريد أن يتحقق 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];
}

أحتاج إلى وضع شريط تقدم للتحقق من تقدم هذه المهمة أثناء حدوثها وتحديثها وفقًا لذلك.

هل كانت مفيدة؟

المحلول

فيما يلي مثال على NTask غير المتزامن الذي يقوم بتشغيل برنامج نصي يونكس.يوجد داخل البرنامج النصي 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

نصائح أخرى

يجب أن يكون لديك طريقة ما لمعاودة الاتصال أو مقاطعة تقدم المهمة بشكل أو بآخر لمعرفة مدى التقدم الذي أحرزته.إذا كنت تتحدث عن برنامج نصي شل ، فيمكنك تقسيم نص برمجي واحد إلى نصوص متعددة وعند الانتهاء من قسم من البرنامج النصي ، قم بتحديث مؤشر التقدم.قامت تطبيقات أخرى بأشياء من هذا القبيل ، iirc Sparkle قامت ببعض المنطق المخصص في كود إلغاء الضغط الخاص بها لفك الضغط في الأجزاء حتى تتمكن من تحديث مؤشر التقدم.إذا كنت ترغب في تحقيق نفس التأثير ، فسيتعين عليك القيام بشيء مماثل.

احصل على PID (معرف العملية) للأمر الذي قمت بتشغيله ، باستخدامه مع أمر PS (حالة العملية) ، يمكنك الحصول على حالة العملية الخاصة بك ، استخدم هذه القيمة في التعليمات البرمجية لإظهارها في شريط التقدم

a simple way to have your script (nstask) communicate with your obj c controller is by using standard error as a communication channel. Not saying this is perfect but works quite well. You have to setup your task as an asynchronous nstask and use notifications to read from standard input and standard error separately. I can post 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

Process Your standard error through a pipe and filehandle and wire it to an outlet for textual status updates or convert it to a float for progress displays. I.e

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

If you need to capture real std error then trap it and merge it to std out like my example. This is not a perfect approach but I use it frequently and it works well.

On my iPad and don't have code. Let me know if you need a better example.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top