2011-11-08 1 views
11

J'essaie d'afficher une feuille sur une fenêtre contenant une seule barre de progression, pour montrer la progression de certaines fonctions longues exécutées de manière asynchrone à l'aide de Grand Central Dispatch. Je l'ai presque obtenu, mais je n'arrive pas à faire apparaître la feuille comme étant nette, probablement parce que je n'ai pas utilisé runModalForWindow: ou similaire.Comment afficher correctement une feuille de "progression" en utilisant Grand Central Dispatch pour traiter quelque chose?

C'est à peu près ce que je fais en ce moment, il se produit à la suite d'un bouton pression sur la fenêtre principale:

// Prepare sheet and show it... 

    [NSApp beginSheet:progressSheet modalForWindow:window modalDelegate:nil didEndSelector:NULL contextInfo:NULL]; 

    [progressSheet makeKeyAndOrderFront:self]; 

    [progressBar setIndeterminate:NO]; 
    [progressBar setDoubleValue:0.f]; 
    [progressBar startAnimation:self]; 


    // Start computation using GCD... 

    dispatch_async(dispatch_get_global_queue(0, 0), ^{ 

     for (int i = 0; i < 1000; i ++) { 
      // Do some large computation here 
      // ... 

      // Update the progress bar which is in the sheet: 
      dispatch_async(dispatch_get_main_queue(), ^{ 
       [progressBar setDoubleValue:(double)i]; 
      }); 
     } 


     // Calculation finished, remove sheet on main thread 

     dispatch_async(dispatch_get_main_queue(), ^{ 
      [progressBar setIndeterminate:YES]; 

      [NSApp endSheet:progressSheet]; 
      [progressSheet orderOut:self]; 
     }); 
    }); 

Cela fonctionne, sauf la fenêtre principale est toujours mise au point, la la feuille n'est pas mise au point et la barre de progression ne s'anime pas (sauf si j'utilise setUsesThreadedAnimation:YES dessus).

Le problème que je crois avoir est que je ne suis pas sûr de savoir comment exécuter la feuille modalement sans bloquer le thread principal avant de lancer le calcul asynchrone?

+0

L'exécution modale de la feuille pour la fenêtre ne devrait pas bloquer le thread principal (sinon rien après l'exécution de votre bit '-beginSheet:' au début de la méthode). J'utilise quelque chose de presque identique à celui-ci (feuille modale présentée comme vous le faites, avec une barre de progression dessus) dans mon application ici, et il se met à jour très bien à partir d'un bloc GCD exécuté en arrière-plan. Les contrôles sur la fenêtre à partir desquels la feuille glisse sont grisés, indiquant que la fenêtre a perdu le focus sur la feuille, ce qui semble normal. Pourrait-il y avoir quelque chose d'autre qui met en file d'attente beaucoup d'actions sur le thread principal? –

+0

@BradLarson La seule autre chose que je pouvais penser qui pourrait causer des problèmes était un 'dispatch_apply' dans le cadre du calcul principal, mais le remplacer par une boucle standard ne faisait aucune différence. Sinon, il n'y a pratiquement rien d'autre qui tourne sur le thread principal. Je peux toujours interagir principalement avec les commandes de la fenêtre principale, derrière la feuille, et elles apparaissent toujours en surbrillance (c'est-à-dire qu'elles ne sont pas grisées). Une zone de texte sur la fenêtre principale, par exemple, pendant l'affichage de la feuille, apparaît avec une bague de mise au point et je peux toujours y saisir du texte (bien que je ne puisse pas sélectionner de texte avec la souris). – Robert

Répondre

2

J'avais exactement le même problème. Avec quelques essais et erreurs, j'ai trouvé la solution. Assurez-vous que la fenêtre de votre feuille est (a) un NSWindow pas un NSPanel (cela peut ne pas importer) et que la fenêtre a une barre de titre (qui, comme c'est une feuille que vous utilisez) ne sera pas affichée.

J'ai désactivé la barre de titre pour cette raison, mais d'une manière ou d'une autre, il est nécessaire d'effectuer correctement la mise au point. Cochez la case Barre de titre pour afficher le focus de ma feuille de progression.

+0

Oui, ma fenêtre n'avait pas de barre de titre, ajoutant que ça arrangeait le problème pour moi. – Robert

4

Comme indiqué par Brad, cela devrait fonctionner.

Pour faire un test rapide, j'ai créé une feuille par programmation (normalement, vous utiliseriez probablement un fichier nib, mais ils sont difficiles à coller dans ce texte). Si j'appelle le code ci-dessous à partir d'un bouton dans une fenêtre Cocoa normale, cela fonctionne comme prévu. Notez que le champ de texte sur la feuille est premier répondeur, et si vous tapez sur le clavier pendant qu'il est ouvert, il acceptera l'entrée.

#define maxloop 1000 

- (IBAction)startTask:(id)sender 
{ 
    // Prepare sheet and show it... 

    breakLoop = NO; 

    NSRect sheetRect = NSMakeRect(0, 0, 400, 114); 

    NSWindow *progSheet = [[NSWindow alloc] initWithContentRect:sheetRect 
                 styleMask:NSTitledWindowMask 
                 backing:NSBackingStoreBuffered 
                  defer:YES]; 

    NSView *contentView = [[NSView alloc] initWithFrame:sheetRect]; 

    NSProgressIndicator *progInd = [[NSProgressIndicator alloc] initWithFrame:NSMakeRect(143, 74, 239, 20)]; 

    NSTextField *inputField = [[NSTextField alloc] initWithFrame:NSMakeRect(145, 48, 235, 22)]; 

    NSButton *cancelButton = [[NSButton alloc] initWithFrame:NSMakeRect(304, 12, 82, 32)]; 
    cancelButton.bezelStyle = NSRoundedBezelStyle; 
    cancelButton.title = @"Cancel"; 
    cancelButton.action = @selector(cancelTask:); 
    cancelButton.target = self; 

    [contentView addSubview:progInd]; 
    [contentView addSubview:inputField]; 
    [contentView addSubview:cancelButton]; 

    [progSheet setContentView:contentView]; 


    [NSApp beginSheet:progSheet 
     modalForWindow:self.window 
     modalDelegate:nil 
     didEndSelector:NULL 
      contextInfo:NULL]; 

    [progSheet makeKeyAndOrderFront:self]; 

    [progInd setIndeterminate:NO]; 
    [progInd setDoubleValue:0.f]; 
    [progInd startAnimation:self]; 


    // Start computation using GCD... 

    dispatch_async(dispatch_get_global_queue(0, 0), ^{ 

     for (int i = 0; i < maxloop; i++) { 

      [NSThread sleepForTimeInterval:0.01]; 

      if (breakLoop) 
      { 
       break; 
      } 

      // Update the progress bar which is in the sheet: 
      dispatch_async(dispatch_get_main_queue(), ^{ 
       [progInd setDoubleValue: (double)i/maxloop * 100]; 
      }); 
     } 


     // Calculation finished, remove sheet on main thread 

     dispatch_async(dispatch_get_main_queue(), ^{ 
      [progInd setIndeterminate:YES]; 

      [NSApp endSheet:progSheet]; 
      [progSheet orderOut:self]; 
     }); 
    }); 
} 

- (IBAction)cancelTask:(id)sender 
{ 
    NSLog(@"Cancelling"); 
    breakLoop = YES; 
} 

Toutes mes excuses pour la feuille laide, mais à part que ce code fonctionne comme prévu, de sorte que la question que vous voyez est probablement sans rapport avec GCD.

Questions connexes