我正在尝试编写一个简单的Xcode cocoa-applescript程序,该程序在文件夹的每个子文件夹上执行bash脚本,并在执行时显示进度条。
我设置的NSProgressIndicator
设置为与barStatus
相关联,而NSTextField
与lblStatus
相关联,以显示一些信息性文字:
property barStatus : missing value
property lblStatus : missing value
这是主要动作发生的循环:
tell application "Finder" to set subfolders to every folder of folder macPath
barStatus's setIndeterminate_(false)
barStatus's setDoubleValue_(0)
barStatus's setMaxValue_(count of subfolders)
repeat with eachFolder in subfolders
set posixPath to (POSIX path of (eachFolder as text)) as text
-- set text of progress indicator text
lblStatus's setStringValue_("Resizing '" & ( do shell script "basename \"" & (posixPath) & "\" | sed \"s/^.*_//\"" ) & "'...")
delay 0.5
-- do the shell script
do shell script "bash ~/.uploader/image.sh \"" & posixPath & "\""
-- step the indicator
barStatus's incrementBy_(1)
end repeat
lblStatus's setStringValue_("Done!")
所有似乎都正常工作,但UI有点毛躁。进度条不是只是平滑地增加,而是在每一步消失,并在短时间内显示,然后再次消失。 lblStatus
中的文字确实可以顺利更改。
当我从循环中删除delay
时,事情完全丢失:没有进行UI更改(即使脚本正常运行),直到循环结束。因此,当循环完成时,进度条会消失并重新出现。
这是flickery UI的youtube video。
我做错了什么?如何让xcode顺利绘制进度条?
请注意,这是我第一次编写Xcode应用程序,而且我对Applescript的了解有点粗略。
我发现调用处理相同函数的函数在从菜单项调用时(或者使用绑定到该菜单项的键组合)没有flickery UI。
答案 0 :(得分:1)
我不确定为什么进度条隐藏起来。你是否将它的可见属性绑定到可能使它隐藏的东西?
当我从循环中删除延迟时,事情完全丢失:没有UI 做出改变
一般情况下,虽然“没有进行UI更改”,但是因为应用程序在主线程上太忙而无法实时更新接口项。您的所有代码都在主线程上运行。你需要在后台线程上进行一些操作。因此,我有2个建议供您尝试。
首先尝试使用进度条的方法“setUsesThreadedAnimation:”。将它添加到重复循环上方...
barStatus's setUsesThreadedAnimation_(true)
其次,如果这没有帮助,那么尝试将重复循环工作移动到后台线程(使用NSThread的detachNewThreadSelector:)...但是在主线程上进行接口更新。我不知道ApplescriptObjC语言所以下面的代码可能是完全错误的。你必须正确地写它,但它会告诉你这个想法......
[NSThread detachNewThreadSelector:@selector(processFolderListOnBacgroundThread_) toTarget:self withObject:subfolders];
-- this will happen on the main thread
on updateProgressBarByOne_()
barStatus's' incrementBy_(1)
end updateProgressBarByOne_
-- this will happen on the main thread
on updateStatusTextWithText_(statusText)
lblStatus's' setStringValue_(statusText)
end updateInterfaceItemsOnMainThreadWithObject_
-- this will happen on a background thread
on processFolderListOnBacgroundThread_(subFolders)
repeat with eachFolder in subfolders
set posixPath to (POSIX path of (eachFolder as text)) as text
-- set text of progress indicator text
my updateStatusTextWithText_("Resizing '" & ( do shell script "basename \"" & (posixPath) & "\" | sed \"s/^.*_//\"" ) & "'...")
-- do the shell script
do shell script "bash ~/.uploader/image.sh \"" & posixPath & "\""
-- step the indicator
my updateProgressBarByOne_()
end repeat
my updateStatusTextWithText_("Done!")
end processFolderListOnBacgroundThread_
如果使用后台线程方法,则可能必须在后台线程工作时使接口中的按钮处于非活动状态(因此用户无法按下它们并启动新任务)。因此,在调用“detachNewThreadSelector:”之前,只需将其enabled属性设置为false,并在工作完成后再次启用它们。你可以通过另一个启用它们的处理程序来实现这一点,并从后台线程代码的末尾调用它。