我想要完成的是通过我的UI中的NSTextField标签启动命令行(CL)任务(包装NSTask)和管道(NSPipe)字符输出,实时作为字符流。文本字段的目的不是以任何方式捕获输出,甚至不允许读取输出。它只是显示它,部分是作为UI装饰,部分是作为一种进度指示器。我希望用户在CL任务完成其工作时看到一个流经(快速)的字符流。
我知道如何在NSTask中包装CL任务并通过设置[task setStandardOutput:outputPipe]获取其输出,然后使用NSFileHandle从该输出中读取。而且我想我知道如何使用NSFileHandle读取方法中的一种“硬”方式执行操作,同步将输出切换为块并在文本字段中逐个显示这些块。但是我希望可能有一些轻量级的方法,我没有想到将stdout中的原始ascii字符实时爆炸到文本字段中。
有人有想法吗?
编辑:这是一些基于@Peter Hosey答案的工作代码。它正在做我想要的,但我不知道我是否彻底了解彼得的概念,或者我是否在这里做任何不可思议的事情,所以请随时发表评论。再次感谢彼得!
关于此代码的说明:
1)将init中的scheduledTimerWithTimeInterval从.001更改为.005是文本滚动效果的一个有趣的可视范围。
2)我使用的标签只是在界面构建器中的UI上创建的简单文本标签。为了我的目的,我不需要用右对齐的属性字符串来做彼得答案的第二部分。我只是在界面构建器中设置了文本标签的对齐方式。
@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
答案 0 :(得分:3)
Asynchronous reading of the file handle,一个timer,一个NSMutableData,通过仅保留最后一个字节并删除旧字节,并在文本字段中右对齐,将其限制为固定的字节数(假设为300)
对于最后一部分,您需要制作the default paragraph style,set its alignment到right justification的可变副本,并将文本字段的attributed string value设置为属性字符串段落样式为one of its attributes。