gpt4 book ai didi

macos - 如何在后台运行 NSTask 并在运行时在模态 NSWindow 中显示结果

转载 作者:行者123 更新时间:2023-12-03 17:02:47 24 4
gpt4 key购买 nike

我想使用 NSTask 执行命令,并能够在模式窗口中查看进度。例如,如果我执行“ls -R/”,我希望看到 block 出现在 NSTextView 中。

我想出了以下内容,除了更新部分之外,一切正常。任务被执行(使用旋转的 beachbal),完成后我看到结果出现在 TextView 中。

@interface ICA_RunWindowController ()

@property (strong) IBOutlet NSTextView* textResult;
@property (strong) IBOutlet NSButton* buttonAbort;
@property (strong) IBOutlet NSButton* buttonOK;

- (IBAction) doOK:(id) sender;
- (IBAction) doAbort:(id) sender;

@end

@implementation ICA_RunWindowController {
NSTask * executionTask;
id taskObserver;
NSFileHandle * errorFile;
id errorObserver;
NSFileHandle * outputFile;
id outputObserver;
}

@synthesize textResult,buttonAbort,buttonOK;

- (IBAction)doOK:(id)sender {
[[self window] close];
[NSApp stopModal];
}

- (IBAction)doAbort:(id)sender {
[executionTask terminate];
}

- (void) taskCompleted {
NSLog(@"Task completed");
[[NSNotificationCenter defaultCenter] removeObserver:taskObserver];
[[NSNotificationCenter defaultCenter] removeObserver:errorObserver];
[[NSNotificationCenter defaultCenter] removeObserver:outputObserver];
[self outputAvailable];
[self errorAvailable];
executionTask = nil;
[buttonAbort setEnabled:NO];
[buttonOK setEnabled:YES];
}


- (void) appendText:(NSString *) text inColor:(NSColor *) textColor {
NSDictionary * makeUp = [NSDictionary dictionaryWithObject:textColor forKey:NSForegroundColorAttributeName];
NSAttributedString * extraText = [[NSAttributedString alloc] initWithString:text attributes:makeUp];
[textResult setEditable:YES];
[textResult setSelectedRange:NSMakeRange([[textResult textStorage] length], 0)];
[textResult insertText:extraText];
[textResult setEditable:NO];
[textResult display];
}

- (void) outputAvailable {
NSData * someData = [outputFile readDataToEndOfFile];
if ([someData length] > 0) {
NSLog(@"output Available");
NSString * someText = [[NSString alloc] initWithData:someData encoding:NSUTF8StringEncoding];
[self appendText:someText inColor:[NSColor blackColor]];
}
}

- (void) errorAvailable {
NSData * someData = [errorFile readDataToEndOfFile];
if ([someData length] > 0) {
NSLog(@"Error Available");
NSString * someText = [[NSString alloc] initWithData:someData encoding:NSUTF8StringEncoding];
[self appendText:someText inColor:[NSColor redColor]];
}
}

- (void) runCommand:(NSString *) command {
// make sure all views are initialized
[self showWindow:[self window]];
// some convience vars
NSArray * runLoopModes = @[NSDefaultRunLoopMode, NSRunLoopCommonModes];
NSNotificationCenter * defCenter = [NSNotificationCenter defaultCenter];
// create an task
executionTask = [[NSTask alloc] init];
// fill the parameters for the task
[executionTask setLaunchPath:@"/bin/sh"];
[executionTask setArguments:@[@"-c",command]];
// create an observer for Termination
taskObserver = [defCenter addObserverForName:NSTaskDidTerminateNotification
object:executionTask
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note)
{
[self taskCompleted];
}
];

// Create a pipe and a filehandle for reading errors
NSPipe * error = [[NSPipe alloc] init];
[executionTask setStandardError:error];
errorFile = [error fileHandleForReading];
errorObserver = [defCenter addObserverForName:NSFileHandleDataAvailableNotification
object:errorFile
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note)
{
[self errorAvailable];
[errorFile waitForDataInBackgroundAndNotifyForModes:runLoopModes];
}
];
[errorFile waitForDataInBackgroundAndNotifyForModes:runLoopModes];

// Create a pipe and a filehandle for reading output
NSPipe * output = [[NSPipe alloc] init];
[executionTask setStandardOutput:output];
outputFile = [output fileHandleForReading];
outputObserver = [defCenter addObserverForName:NSFileHandleDataAvailableNotification
object:outputFile
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note)
{
[self outputAvailable];
[outputFile waitForDataInBackgroundAndNotifyForModes:runLoopModes];
}
];
[outputFile waitForDataInBackgroundAndNotifyForModes:runLoopModes];

// start task
[executionTask launch];

// show our window as modal
[NSApp runModalForWindow:[self window]];
}

我的问题:任务运行时是否可以更新输出?如果是的话,我怎样才能实现这一目标?

最佳答案

模态窗口在 NSModalPanelRunLoopMode 中运行运行循环,因此您需要将其添加到 runLoopModes 中。

你不应该得到旋转的沙滩球。原因是您在 -outputAvailable-errorAvailable 方法中调用 -readDataToEndOfFile 。鉴于您使用的是 -waitForDataInBackgroundAndNotifyForModes:,您可以使用 -availableData 方法来获取可用数据而不阻塞。

或者,您可以使用 -readInBackgroundAndNotifyForModes:,监视 NSFileHandleReadCompletionNotification 通知,并在处理程序中使用 [[ 从通知对象获取数据注意 userInfo] objectForKey:NSFileHandleNotificationDataItem]。换句话说,让 NSFileHandle 为您完成读取数据的工作。

无论哪种方式,一旦您获得文件结束指示符(空的 NSData),您就不应该重新发出 ...InBackgroundAndNotifyForModes:称呼。如果这样做,您将忙于旋转,因为它会一遍又一遍地向您提供相同的文件结束指示符。

不需要手动-显示您的 TextView 。一旦修复了导致色轮光标旋转的阻塞调用,这也将允许正常的窗口更新自动发生。

关于macos - 如何在后台运行 NSTask 并在运行时在模态 NSWindow 中显示结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24961725/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com