1

フォルダーの各サブフォルダーで bash スクリプトを実行し、実行中にプログレスバーを表示する単純な Xcode cocoa-applescript プログラムを作成しようとしています。

私はいくつかの有益なテキストを表示するためにリンクさNSProgressIndicatorれるべきセットアップを持っています:barStatusNSTextFieldlblStatus

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 の変更は行われません。そのため、ループが完了すると、プログレスバーが消えて再び表示されます。

これは、ちらつきのある UI のYouTubeビデオです。

私は何を間違っていますか?xcodeでプログレスバーをスムーズに描画するにはどうすればよいですか?

Xcode アプリを作成するのはこれが初めてであり、Applescript に関する私の知識はやや大ざっぱであることに注意してください。

編集:

同じ関数を処理する関数を呼び出しても、メニュー項目から呼び出された場合 (またはそのメニュー項目にバインドされたキー コンボを使用した場合)、ちらつきのある UI がないことがわかりました。

4

1 に答える 1

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:」を呼び出す前に、これらの有効なプロパティを false に設定し、作業が完了したら再度有効にします。これを行うには、それらを有効にする別のハンドラーを用意し、バックグラウンド スレッド コードの最後からそれを呼び出します。

于 2012-12-21T12:49:30.973 に答える