2

私が達成したいのは、コマンド ライン (CL) タスク (ラップされた NSTask) を開始し、文字のストリームとしてリアルタイムで、UI の NSTextField ラベルを介して文字出力をパイプ (NSPipe) することです。テキストフィールドの目的は、出力をキャプチャしたり、読み取れるようにしたりすることではありません。一部はUIの装飾として、一部は一種の進行状況インジケーターとして表示するだけです。CL タスクが作業を行っているときに、文字列が (高速で) 流れているのをユーザーに見てもらいたいのです。

CL タスクを NSTask でラップし、[task setStandardOutput:outputPipe] を設定してその出力を取得し、その出力から NSFileHandle を読み取る方法を知っています。そして、NSFileHandle読み取りメソッドの1つを使用し、出力を同期的にチャンクに切り刻み、それらのチャンクをテキストフィールドに1つずつ表示するという「難しい」方法を実行する方法を知っていると思います。しかし、生のASCII文字をstdoutからテキストフィールドにリアルタイムで爆破する、私が考えていなかった軽量の方法があるかもしれないと思っています。

誰にもアイデアがありますか?

編集: @Peter Hosey の回答に基づいた実用的なコードを次に示します。それは私がやりたいことをやっていますが、ピーターのコンセプトを完全に理解したのか、ここで何かおかしなことをしているのかわからないので、お気軽にコメントしてください. 再びピーターに感謝します!

このコードに関する注意事項:

1) init の scheduleTimerWithTimeInterval を .001 から .005 に変更すると、テキスト スクロール効果の視覚的な範囲が興味深いものになります。

2) 私が使用しているラベルは、インターフェイス ビルダーの UI で作成された単純なテキスト ラベルでした。私の目的のために、Peter の回答の 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
4

1 に答える 1

3

ファイル ハンドルタイマー、最後のバイトのみを保持して古いバイトを削除することで固定バイト数 (たとえば 300) に制限する NSMutableData の非同期読み取り、およびテキスト フィールドの右寄せ。

最後の部分では、デフォルトの段落スタイルの変更可能なコピーを作成し、その配置右揃えに設定し、テキスト フィールドの属性付き文字列値を、属性の1 つとして段落スタイルを持つ属性付き文字列に設定する必要があります。

于 2011-07-09T20:23:34.540 に答える