2011-11-08 8 views
11

Estoy tratando de mostrar una hoja en una ventana que contiene una única barra de progreso, para mostrar el progreso de una función larga que se ejecuta de forma asíncrona utilizando Grand Central Dispatch. Casi lo tengo, pero no puedo hacer que la hoja parezca estar enfocada, probablemente porque no he usado runModalForWindow: o similar.¿Cómo mostrar correctamente una hoja de "progreso" modalmente mientras se usa Grand Central Dispatch para procesar algo?

Esto es aproximadamente lo que estoy haciendo en este momento, que sucede como resultado de una pulsación de botón en la ventana principal:

// 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]; 
     }); 
    }); 

Esto funciona, excepto la ventana principal se encuentra todavía en el enfoque, la la hoja está fuera de foco, y la barra de progreso no se anima (a menos que use setUsesThreadedAnimation:YES en ella).

El problema que creo que tengo es que no estoy seguro de cómo ejecutar la hoja modalmente sin bloquear el hilo principal antes de iniciar el cálculo asincrónico.

+0

La ejecución modal de la hoja para la ventana no debe bloquear el hilo principal (de lo contrario, nada después de que se ejecute el bit '-beginSheet:' al principio del método). Utilizo algo casi idéntico a esto (hoja modal presentada como lo hace, con una barra de progreso) en mi aplicación aquí, y se actualiza perfectamente desde un bloque GCD ejecutado en segundo plano. Los controles en la ventana por la que se desliza la hoja están en gris, lo que indica que la ventana ha perdido el foco en la hoja, por lo que parece normal también. ¿Podría haber algo más haciendo cola en muchas acciones en el hilo principal? –

+0

@BradLarson La única otra cosa en la que podía pensar que podría estar causando problemas era un 'dispatch_apply' como parte del cómputo principal, pero reemplazarlo con un ciclo estándar no hizo ninguna diferencia. De lo contrario, no hay prácticamente nada más ejecutándose en el hilo principal. Todavía puedo interactuar principalmente con los controles en la ventana principal, detrás de la hoja, y aún aparecen en foco (es decir, no atenuados). Un cuadro de texto en la ventana principal, por ejemplo, mientras se muestra la hoja, aparece con un anillo de enfoque y aún puedo ingresar texto en él (aunque no puedo seleccionar texto con el mouse). – Robert

Respuesta

2

Tuve exactamente el mismo problema. Con un poco de prueba y error, encontré la solución. Asegúrese de que la ventana de su hoja sea (a) una NSWindow, no un NSPanel (esto puede no importar) y que la ventana tiene una barra de título (que, como es una hoja que está utilizando) no se mostrará.

Desactivé la barra de título por ese motivo, pero de alguna manera es necesario para lograr el enfoque correctamente. Al marcar la casilla de verificación de la barra de título, mi foco de hoja de barras de progreso se enfoca.

+0

Sí, mi ventana no tenía una barra de título, y me solucionó el problema. – Robert

4

Como dice Brad, debería funcionar.

Para hacer una prueba rápida, creé una hoja programáticamente (normalmente, probablemente usaría un archivo de punta, pero son difíciles de pegar en este texto). Si llamo al código siguiente desde un botón en una ventana normal de Cocoa, funciona como se esperaba. Tenga en cuenta que el campo de texto en la hoja es la primera respuesta, y si escribe en el teclado mientras está abierto, aceptará la entrada.

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

Disculpas por la hoja fea, pero aparte de que este código funciona como se esperaba, por lo que el tema que está viendo es probable que no guardan relación con GCD.

Cuestiones relacionadas