2011-07-09 23 views
2

我想要實現的是通過我的UI中的NSTextField標籤啓動命令行(CL)任務(包裝NSTask)和管道(NSPipe)字符輸出,時間就像一串人物。文本字段的目的不是以任何方式捕獲輸出,甚至不允許讀取它。這只是爲了展示它,部分作爲用戶界面的裝飾,部分作爲一種進度指標。我希望用戶在CL任務完成其工作時看到流(快)的字符流。NSTask字符輸出到NSTextField緩衝爲連續流

我知道如何將CL任務包裝到NSTask中,並通過設置[task setStandardOutput:outputPipe]來獲得輸出,然後使用NSFileHandle從該輸出中讀取。我想我知道如何使用NSFileHandle讀取方法之一來實現我想要的「硬」方式,同時將輸出切分爲塊並在文本字段中逐個顯示這些塊。但我希望可能有一些輕量級的方式,我沒有想過要將標準輸出中的原始ASCII字符實時地傳送到文本字段中。

任何人有想法?

編輯:這是一些基於@Peter Hosey的答案的工作代碼。它正在做我想做的事情,但我不知道我是否徹底改變了彼得的概念,或者如果我在這裏做了任何蠢事,請隨時發表評論。再次感謝彼得!在此代碼

筆記:

1)改變scheduledTimerWithTimeInterval在從0.001到0.005的INIT是一個有趣的視覺範圍的文本滾動效果。

2)我使用的標籤只是一個簡單的文本標籤,在我的界面構建器的用戶界面上創建。爲了我的目的,我不需要用正確的屬性字符串來做彼得答案的第二部分。我只是在界面生成器中設置文本標籤的理由。

@interface MyWrapper : NSObject 

@property (assign) NSMutableData *_outputData; 
@property (assign) NSFileHandle *_fileHandle; 
@property (assign) IBOutlet NSTextField *label; 
@property (assign) NSTimer *_timer; 

-(void) readData:(NSNotification *)notification; 
-(void) displayOutput; 
-(void) doIt; 

@end 

@implementation MyWrapper 

@synthesize _outputData, _fileHandle, label, _timer; 

- (id)init { 
    self = [super init]; 
    if (self) { 

     [[NSNotificationCenter defaultCenter] addObserver:self 
               selector:@selector(readData:) 
                name:NSFileHandleReadCompletionNotification 
               object:nil]; 
     _outputData = [[NSMutableData alloc] initWithCapacity:300]; 
     _timer = [NSTimer scheduledTimerWithTimeInterval:.001 
               target:self 
              selector:@selector(displayOutput) 
              userInfo:nil 
               repeats:YES]; 
    } 

    return self; 
} 
- (void)dealloc { 
    [[NSNotificationCenter defaultCenter] removeObserver:self]; 
    [_timer invalidate]; 
    [super dealloc]; 
} 

-(void) readData:(NSNotification *)notification { 

    if([notification object] != _fileHandle) 
    return; 

    [_outputData appendData:[[notification userInfo] 
      objectForKey:NSFileHandleNotificationDataItem]]; 

    [_fileHandle readInBackgroundAndNotify]; 
} 

-(void) displayOutput { 

    if ([_outputData length] == 0) { 
    return; 
    } 

    NSString *labelText = [label stringValue]; 
    NSData *nextByte; 
    NSString *nextChar; 

    // pull first character off of the outputData 
    nextByte = [_outputData subdataWithRange:NSMakeRange(0, 1)]; 
    nextChar = [[NSString alloc]initWithData:nextByte 
            encoding:NSASCIIStringEncoding]; 

    // get rid of first byte of data 
    [_outputData replaceBytesInRange:NSMakeRange(0, 1) withBytes:NULL length:0]; 

    if (! [nextChar isEqualToString:@"\n"]) { 
    if ([labelText length] > 29) { 
     labelText = [labelText substringFromIndex:1]; 
    } 

    labelText = [labelText stringByAppendingString:nextChar]; 
    [label setStringValue:labelText]; 
    } 
} 

-(void)doIt { 

    NSTask *theTask = [[NSTask alloc] init]; 
    NSPipe *outPipe =[NSPipe pipe]; 
    //write output to outputData in background 
    _fileHandle = [outPipe fileHandleForReading]; 
    [_fileHandle readInBackgroundAndNotify]; 

    [theTask setLaunchPath:@"path/to/executable"]; 
    [theTask setStandardOutput:outPipe]; 
    [theTask setStandardError:[NSPipe pipe]]; 
    [theTask launch]; 
    [theTask waitUntilExit]; 
} 

@end 

回答

3

Asynchronous reading of the file handle,一個timer,一個NSMutableData您限制到一個固定的字節數(假設300)僅保留最後的字節,刪除舊的字節,並在文本字段右對齊。

對於最後一部分,你需要做的the default paragraph style一個可變副本,set its alignmentright justification,並設置文本字段的attributed string value到具有段落樣式作爲one of its attributes的屬性串。

+0

[wrinkled brow]你是說使用定時器週期性地添加新字節,並從NSMutableData中刪除舊字節,然後在文本字段中重新顯示NSMutableData?我在想如何連接所有這些方面掙扎着,但我會試一試並用一些代碼報告。感謝您花時間回覆。 – pjv

+0

@pjv:差不多。您將字節添加到NSMutableData以響應來自文件句柄的通知;您可以根據定時器觸發修剪和更新文本字段。 –