Comment utiliser un nsprogressindicator déterminé pour vérifier les progrès de NSTAK? - Cacao

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

Question

Ce que j'ai, c'est NSTASK exécutant un long script de shell préfabriqué et je veux que le nsprogressindicator vérifie le montant. J'ai essayé beaucoup de choses, mais je n'arrive pas à le faire fonctionner. Je sais comment l'utiliser si la barre de progression est indéterminée, mais je veux qu'elle se charge au fil de la tâche.

Voici comment j'utilise le 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];
}

J'ai besoin de mettre une barre de progression dans ce qui vérifie les progrès de cette tâche pendant que cela se produit et met à jour en conséquence.

Était-ce utile?

La solution

Voici un exemple d'un nstask asynchronisé exécutant un script UNIX. Dans le script Unix, il y a echo Commandes qui renvoient l'état actuel à l'erreur standard comme ceci:

echo "working" >&2

Ceci est traité par le centre de notification et envoyé à l'affichage.

Pour mettre à jour une barre de progression déterminée, envoyez simplement des mises à jour d'état comme "25.0" "26.0" et convertissez-vous pour flotter et envoyer à la barre de progression.

Remarque: j'ai fait fonctionner cela après beaucoup d'expérimentation et en utilisant de nombreux conseils de ce site et d'autres références. J'espère donc que cela vous sera utile.

Voici les déclarations:

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

Voici les principaux modules de programme:

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

Autres conseils

Vous devez avoir un moyen de rappeler ou d'interrompre la progression d'une tâche dans Oder pour dire les progrès que vous avez réalisés. Si vous parlez d'un script shell, vous pouvez casser 1 script en plusieurs scripts et à la fin d'une section du script mettent à jour l'indicateur de progression. D'autres applications ont fait des choses comme celle-ci, IIRC Sparkle a fait une logique personnalisée dans son code de décompression pour se compresser en morceaux afin qu'il puisse mettre à jour un indicateur de progression. Si vous voulez réaliser le même effet, vous devrez faire quelque chose de similaire.

Obtenez le PID (ID de processus) pour la commande que vous avez exécutée, en l'utilisant en conjonction avec la commande ps (État de processus) Vous pouvez obtenir l'état de votre processus utiliser cette valeur dans votre code pour l'afficher dans la barre de progression

Un moyen simple de faire en sorte que votre script (nstask) communique avec votre contrôleur obj c consiste à utiliser l'erreur standard comme canal de communication. Ne pas dire que c'est parfait mais fonctionne assez bien. Vous devez configurer votre tâche en tant que NSTASK asynchrone et utiliser des notifications pour lire séparément les entrées standard et les erreurs standard. Je peux le poster plus tard si vous en avez besoin.

Enveloppez votre script comme ceci:

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

Traitez votre erreur standard via un tuyau et un fichier et câblez-le en une prise pour les mises à jour d'état textuel ou convertissez-le en flotteur pour les affichages de progrès. C'est à dire

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

Si vous avez besoin de capturer une véritable erreur std, piégez-la et fusionnez-la à STD comme mon exemple. Ce n'est pas une approche parfaite mais je l'utilise fréquemment et cela fonctionne bien.

Sur mon iPad et je n'ai pas de code. Faites-moi savoir si vous avez besoin d'un meilleur exemple.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top