带有 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
有什么办法可以做一个正在加载的进度条 作为 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