きみはねこみたいなにゃんにゃんなまほう

ねこもスクリプトをかくなり

ExtendScriptでプログレスバーを表示する

lightbulbcat.hatenablog.com

前回の続きです。

f:id:lightbulbcat:20211226224228p:plain

の各アートボードのテキストを取得する処理中に、どのアートボードを現在処理中であるかを示す

f:id:lightbulbcat:20211227003349p:plain

のようなプログレスバーを表示します。 今回は ProgressWindow というクラスを定義して利用しました。 この辺はお好みですね。

var CONFIG = {
    outFile: '~/Desktop/descs.txt',
}

function map(items, fn) {
    var res = []
    for (var i = 0; i < items.length; i++) res[i] = fn(items[i], i)
    return res
}

function filter(items, fn) {
    var res = []
    for (var i = 0; i < items.length; i++) {
        if (fn(items[i], i)) res.push(items[i])
    }
    return res
}

function log(input) {
    var now = new Date();
    var output = now.toTimeString() + ": " + input;
    $.writeln(output)
}

function ProgressWindow(opt) {
    opt = opt || {}
    var w = this.w = new Window('palette', opt.title || 'Progress')
    var t = this.t = w.add('statictext')
    t.preferredSize = [450, -1]

    var p = this.p = w.add('progressbar', undefined, 0, 100)
    p.value = 0
    p.preferredSize = [450, -1]

    w.show()
}

ProgressWindow.prototype.set = function (progressRatio, message) {
    this.p.value = progressRatio * 100
    this.t.text = message
    this.w.update()
}

ProgressWindow.prototype.close = function () {
    this.w.close()    
}

try {
    log('Start script')

    var allTexts = []
    var pw = new ProgressWindow({ title: 'copy-textbox' })
    var doc = app.activeDocument

    for (var i = 0; i < doc.artboards.length; i++) {
        doc.selection = null
        doc.artboards.setActiveArtboardIndex(i)
        doc.selectObjectsOnActiveArtboard()

        var textFrames = filter(doc.selection, function(item) {
            return item instanceof TextFrame
        })

        var texts = map(textFrames, function(tf) {
            log('Artboard' + i + ': ' + tf.contents)
            return tf.contents
        })

        pw.set(i / doc.artboards.length, 'Artboard ' + i + ': ' + texts.join(',').slice(0, 100))
        allTexts = allTexts.concat(texts)
    }
    log('Detect ' + allTexts.length + ' textFrames total')

    allTexts = map(allTexts, function(text) {
        return text.replace(/\r\n|\r|\n/g, '\\n')
    })

    var f = File(CONFIG.outFile)
    f.encoding = 'UTF-8'
    f.lineFeed = 'Unix'
    f.open('w')
    f.write(allTexts.join('\n'))
    f.close()
    pw.close()
} catch (e) {
    alert('Error: ' + e)
    $.writeln(e)
}

無事動きました。

ループをキャンセルするボタンを設置できない

GUIを表示させられ、欲が出たので処理のキャンセル機能もつけようとキャンセルボタンの設置を試みたのですが、できませんでした。 正確にはボタンの設置はできるけれど、そのボタンにループを停止させる機能を持たせるのが無理そう、ということがわかりました。

色々探して、これかなと思った方法は全てCEPの機能を使うもので、例えば

Solved: Need an ExtendScript timer - Adobe Support Community - 6117453

で説明されている方法も同様にCEPを使うものです。他にも探したのですがExtendScriptのみで行う方法は見つかりませんでした。

JavaScript に存在するような setTimeout のように非同期実行処理のキューにタスクを積むような処理ができれば、 ループを、コールバックを積む処理の連続で置き換えられるのでユーザの処理を待てるかと思いますが、 調べたところそのような処理はExtendScriptにはなさそうなので、諦めてCEPのスクリプトで行うしかなさそうです。