2011-11-08 56 views
11

我試圖在包含單個進度欄的窗口上顯示工作表,以顯示使用Grand Central Dispatch異步運行的一些長函數的進度。我幾乎已經知道了,但無法讓工作表看起來很專注,可能是因爲我沒有使用runModalForWindow:或類似工具。如何在使用Grand Central Dispatch處理某些內容時正確顯示「進度」表格?

這大約是我目前做的事情,它發生的主窗口中按下按鈕的結果:

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

這工作,除主窗口仍然是重點,表格沒有對焦,並且進度條不動畫(除非我使用setUsesThreadedAnimation:YES)。

我想我遇到的問題是我不確定如何在啓動異步計算之前在不阻止主線程的情況下以模態方式運行表單。

+0

爲窗口模態地運行工作表不應該阻塞主線程(否則在方法開始處的'-beginSheet:'位後沒有任何內容會被執行)。我在我的應用程序中使用了幾乎與此相同的東西(模態表單,帶有進度條),它在後臺運行的GCD塊中更新得很好。工作表滑下的窗口上的控件呈灰色,表示窗口已失去焦點,因此看起來也很正常。有沒有其他的東西在主線程上排隊呢? –

+0

@BradLarson我能想到的唯一另外一件事情可能會導致問題,就是'dispatch_apply'作爲主要計算的一部分,但是用標準循環替換它並沒有什麼區別。否則,主線程上幾乎沒有其他的東西在運行。我仍然可以主要與主窗口上的控件在表單後面交互,並且它們仍然顯示爲焦點(即不顯示灰色)。例如,在顯示錶單時,主窗口上的文本框會顯示對焦環,我仍然可以在其中輸入文本(儘管我無法使用鼠標選擇文本)。 – Robert

回答

2

我有完全相同的問題。通過一些試驗和錯誤,我找到了解決方案。確保你的工作表窗口是(a)NSWindow不是NSPanel(這可能沒有關係),並且窗口有一個標題欄(它是你正在使用的表單)將不會顯示。

因爲這個原因,我關掉了標題欄,但不知何故它需要正確實現焦點。選中標題欄複選框可以顯示我的進度欄工作表焦點。

+0

是的,我的窗戶沒有標題欄,添加它解決了我的問題。 – Robert

4

正如布拉德所說,它應該工作。

要做一個快速測試,我編程創建了一個工作表(通常,您可能會使用一個nib文件,但它們很難粘貼到這個文本中)。如果我從一個普通的Cocoa窗口中的按鈕調用下面的代碼,它按預期工作。請注意,工作表上的文本字段是第一響應者,並且如果您在打開鍵盤時鍵入它,它將接受輸入。

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

道歉醜表,但除此之外,該代碼正常工作,所以你看到的問題可能是無關的GCD。

相關問題