2011-11-08 12 views
11

Sto provando a visualizzare un foglio su una finestra contenente una singola barra di avanzamento, per mostrare lo stato di avanzamento di alcune funzioni lunghe in modo asincrono utilizzando Grand Central Dispatch. L'ho quasi capito, ma non riesco a mettere a fuoco il foglio, probabilmente perché non ho usato runModalForWindow: o simili.Come visualizzare correttamente un foglio "avanzamento" durante l'uso di Grand Central Dispatch per elaborare qualcosa?

Questo è approssimativamente quello che sto facendo in questo momento, accade come conseguenza di un pulsante nella finestra 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]; 
     }); 
    }); 

Questo funziona, parte la finestra principale è ancora a fuoco, la il foglio non è a fuoco e la barra di avanzamento non si anima (a meno che non utilizzi lo setUsesThreadedAnimation:YES su di esso).

Il problema che penso di avere è che non sono sicuro di come eseguire il foglio in modo modulare senza bloccare il thread principale prima di avviare il calcolo asincrono?

+0

L'esecuzione modale del foglio per la finestra non dovrebbe bloccare il thread principale (altrimenti nulla dopo il '-beginSheet:' bit all'inizio del metodo verrebbe eseguito). Io uso qualcosa di quasi identico a questo (il foglio modale presentato come fai tu, con una barra di avanzamento) nella mia applicazione qui, e si aggiorna bene da un blocco GCD eseguito in background. I controlli sulla finestra in cui il foglio scorre verso il basso sono in grigio, a indicare che la finestra ha perso il focus sul foglio, quindi anche questo sembra normale. Potrebbe esserci qualcos'altro che fa la coda di molte azioni sul thread principale? –

+0

@BradLarson L'unica altra cosa che potevo pensare che potesse causare problemi era un 'dispatch_apply' come parte del calcolo principale, ma sostituirlo con un ciclo standard non faceva alcuna differenza. Altrimenti, non c'è praticamente nient'altro in esecuzione sul thread principale. Posso ancora interagire principalmente con i controlli sulla finestra principale, dietro il foglio, e appaiono ancora a fuoco (cioè, non disattivati). Ad esempio, una finestra di testo sulla finestra principale, mentre il foglio viene visualizzato, appare con un anello di messa a fuoco e posso ancora inserire del testo (anche se non posso selezionare il testo con il mouse). – Robert

risposta

2

Ho avuto esattamente lo stesso problema. Con alcuni tentativi ed errori, ho trovato la soluzione. Assicurati che la finestra del tuo foglio sia (a) una NSWindow non un NSPanel (questo potrebbe non essere importante) e che la finestra ha una barra del titolo (che, dato che si tratta di un foglio che stai usando) non verrà visualizzata.

Ho disattivato la barra del titolo per tale motivo, ma in qualche modo è necessario per ottenere correttamente la messa a fuoco. Ticchettando la casella di controllo Barra del titolo viene visualizzato il mio stato attivo sulla barra di avanzamento.

+0

Sì, la mia finestra non aveva una barra del titolo, aggiungendo che per me il problema era risolto. – Robert

4

Come affermato da Brad, dovrebbe funzionare.

Per eseguire un test rapido, ho creato un foglio a livello di codice (normalmente, probabilmente si utilizzerà un file di pennino, ma è difficile incollarlo in questo testo). Se chiamo il codice qui sotto da un pulsante in una normale finestra di Cocoa, funziona come previsto. Si noti che il campo di testo sul foglio è il primo risponditore e se si digita sulla tastiera mentre è aperto, accetterà l'input.

#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; 
} 

Ci scusiamo per il foglio di brutto, ma a parte che questo codice funziona come previsto, in modo che il problema che state vedendo è probabilmente estraneo a GCD.

Problemi correlati