NSTask 字符输出到 NSTextField 无缓冲作为连续流
Posted
技术标签:
【中文标题】NSTask 字符输出到 NSTextField 无缓冲作为连续流【英文标题】:NSTask character output to NSTextField unbuffered as continuous stream 【发布时间】:2011-07-09 13:41:01 【问题描述】:我想要完成的是启动命令行 (CL) 任务(包装的 NSTask)并通过我的 UI 中的 NSTextField 标签实时传输(NSPipe)字符输出作为字符流。文本字段的目的不是以任何方式捕获输出,甚至不允许读取它。它只是为了显示它,一部分是作为 UI 装饰,一部分是作为一种进度指示器。我希望用户在 CL 任务执行其工作时看到(快速)流过的字符流。
我知道如何将 CL 任务包装在 NSTask 中并通过设置 [task setStandardOutput:outputPipe] 获取其输出,然后使用 NSFileHandle 从该输出中读取。而且我想我知道如何使用 NSFileHandle 读取方法之一以“硬”方式执行我想要的操作,并将输出同步切割成块并在文本字段中一个接一个地显示这些块。但我希望可能有一些我没有想到的轻量级方法,将标准输出中的原始 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
【问题讨论】:
【参考方案1】:Asynchronous reading of the file handle、timer、NSMutableData,您可以通过仅保留最后一个字节并删除旧字节以及文本字段中的右对齐来将其限制为固定字节数(比如说 300)。
对于最后一部分,您需要将the default paragraph style、set its alignment 复制到right justification,并将文本字段的attributed string value 设置为段落样式为@987654327 的属性字符串@。
【讨论】:
[皱眉] 你是说使用计时器定期向 NSMutableData 添加新字节,并从 NSMutableData 中删除旧字节,然后在文本字段中重新显示 NSMutableData?我在想如何将所有这些连接起来有点挣扎,但我会试一试并用一些代码报告。感谢您抽出宝贵时间回复。 @pjv:差不多。您将字节添加到 NSMutableData 以响应来自文件句柄的通知;您修剪并更新文本字段以响应计时器触发。以上是关于NSTask 字符输出到 NSTextField 无缓冲作为连续流的主要内容,如果未能解决你的问题,请参考以下文章
达到字符数后,如何以编程方式将光标从一个 NSTextField 移动到另一个?
ControlTextDidChange 不适用于设置 NSTextField 的字符串
NStextfield 显示空占位符(即空字符串),而不是使用核心数据显示“无选择”。苹果漏洞?