带有 Applescript 的进度条

Progress Bar with Applescript

有什么办法可以做一个正在加载的进度条 作为 shell 脚本或 shell 命令运行?

我目前使用的是:

property theWindow : missing value
property parent : class "NSObject"
property customView : missing value
property myProgressBar : missing value

on hardwareData()

    set alert to current application's NSAlert's alloc's init()
        tell alert
            its setAccessoryView:customView
            its setMessageText:"Loading Informations"
            its setInformativeText: "Please Wait..."
            its setAlertStyle:1
            its addButtonWithTitle:"Cancel"
            its setShowsSuppressionButton:false
            its beginSheetModalForWindow:theWindow modalDelegate:me didEndSelector:(missing value) contextInfo:(missing value)
        end tell

delay 0.02
set hardwareData to do shell script "system_profiler SPHardwareDataType"

(*This loop loads the progress bar but only after the
shell command was executed and not at run time.*)
set c to 0
        repeat 100 times
            set c to c + 1
            delay 0.06
            tell myProgressBar to setDoubleValue_(c)
            if c > 99 then
                exit repeat
            end if
        end repeat
end hardwareData

我一度认为这是一个假的进度条 它不与 shell 脚本一起执行。

如果您不给系统时间来处理事件,则用户界面将被阻塞 - 例如,通过使用紧密的重复循环。如果需要循环,您可以定期调用处理程序来更新 UI,或者手动处理系统事件。对于第三方可编写脚本的进度指示器后台应用程序,还有来自 MacScripter 常客之一的 SKProgressBar

如果您打算使用 shell 脚本,请注意,如果它需要一些时间才能完成,它也会阻塞用户界面,并且可能不会提供可用于其进度的反馈。任何需要时间才能完成的事情都应该使用异步后台任务来执行,但 AppleScriptObjC 在这方面有点受限。 NSTask 提供了一种通过通知执行后台任务的方法,因此您可能需要检查一下,因为它的使用和围绕通知安排您的应用程序是另一个主题。

您应该开始使用 Objective-C 类别,该类别提供对新的基于块的警报方法的访问,但要继续使用旧的已弃用的 sheet 方法,您需要为任何您要添加的按钮(例如取消)。以下 Xcode 项目(只需创建一个空白的 AppleScriptObjC 项目并复制到 AppDelegate 文件)使用您的计数器来模拟进度:

# AppDelegate.applescript

script AppDelegate
    property parent : class "NSObject"
    property theWindow : missing value
    property alert : missing value  -- this will be the alert
    property myProgressBar : missing value -- this will be the progress indicator
    property alertCancel : false -- this will be a flag to indicate cancel

    to makeButton(title, x, y) -- make a button at the {x, y} position
        tell (current application's NSButton's buttonWithTitle:title target:me action:"buttonAction:")
            its setFrame:{{x, y}, {120, 24}}
            its setRefusesFirstResponder:true -- no highlight
            return it
        end tell
    end makeButton

    on buttonAction:sender -- perform the alert
        if alert is missing value then tell current application's NSAlert's alloc's init()
            set my alert to it
            its setMessageText:"Loading Informations"
            its setInformativeText:"Please Wait..."
            set cancelButton to its addButtonWithTitle:"Cancel"
            cancelButton's setTarget:me
            cancelButton's setAction:"cancelButton:"
            its setAccessoryView:(my makeIndicator())
        end tell
        set my alertCancel to false -- reset
        myProgressBar's setDoubleValue:0
        alert's beginSheetModalForWindow:theWindow modalDelegate:me didEndSelector:(missing value) contextInfo:(missing value)
        doStuff()
    end buttonAction:

    on cancelButton:sender -- mark alert as cancelled
        set my alertCancel to true
        current application's NSApp's endSheet:(alert's |window|)
    end cancelButton:

    to makeIndicator() -- make and return a progress indicator
        alert's layout()
        set theSize to second item of ((alert's |window|'s frame) as list)
        set width to (first item of theSize) - 125 -- match alert width
        tell (current application's NSProgressIndicator's alloc's initWithFrame:{{0, 0}, {width, 22}})
            set myProgressBar to it
            set its indeterminate to false
            set its maxValue to 100
            return it
        end tell
    end makeIndicator

    on doStuff() -- the main progress loop
        set c to 0
        repeat 100 times
            set c to c + 1
            delay 0.06 -- do something
            tell myProgressBar to setDoubleValue:c
            fetchEvents()
            if c > 99 or alertCancel then exit repeat
        end repeat
        current application's NSApp's endSheet:(alert's |window|)
    end doStuff

    on fetchEvents() -- handle user events
        repeat -- forever
            tell current application's NSApp to set theEvent to its nextEventMatchingMask:(current application's NSEventMaskAny) untilDate:(missing value) inMode:(current application's NSDefaultRunLoopMode) dequeue:true
            if theEvent is missing value then return -- none left
            tell current application's NSApp to sendEvent:theEvent -- pass it on
        end repeat
    end fetchEvents

    ##################################################
    #   Delegate methods
    ##################################################

    on applicationWillFinishLaunching:aNotification
        theWindow's contentView's addSubview:makeButton("Show Alert", 180, 30)
    end applicationWillFinishLaunching:

    on applicationShouldTerminate:sender
        return current application's NSTerminateNow
    end applicationShouldTerminate:

end script