[JavaScript] 20 Adobe XDのアイテムデータを取得するプラグイン作成 その6 Pythonスクリプトとの統合 / GPT-4 Turbo

[Mac M2 Pro 12CPU, Ventura 13.6]

ChatGPTがGPT-4 Turboになってトークン数に余裕ができたので、早速仕事をしてもらいました。プラグインのmain.jsにtxtTojson.pyの内容を取り込みました。

自力でもある程度プログラミングできるレベルであれば、ChatGPTによる恩恵は絶大だと思います。

const fs = require("uxp").storage.localFileSystem;

async function myCommand(selection) {
    let items_list = [];
    selection.items.forEach(function (value, index) {
        // ここでPythonスクリプトの処理に相当するデータ抽出を行う
        const name = value.name;
        const bounds = value.globalBounds; // globalBoundsオブジェクトを取得
        const x = bounds.x;
        const y = bounds.y;
        const w = bounds.width; // globalBoundsからwidthを取得
        const h = bounds.height; // globalBoundsからheightを取得
        items_list.push({ name, bounds: [x, y, w, h] });
    });

    // JSONオブジェクトを作成
    const json_obj = {};
    items_list.forEach(item => {
        json_obj[item.name] = item.bounds;
    });

    // JSON文字列に変換
    const json_str = JSON.stringify(json_obj, null, 2);

    // ファイルを保存する
    const file = await fs.getFileForSaving("output.json", { types: ["json"] });
    await file.write(json_str);
}

module.exports = {
    commands: {
        GetItemXY: myCommand
    }
};
AdobeXDプラグインを使って2つの方法でアイテムデータを取得し、比較しています
1.プラグインのmain.jsでアイテムデータをtxtファイル化し、txtTojson.pyでjsonファイルに変換する
2.プラグインのmain.jsでアイテムデータをjsonファイル化する

1の方法では成功しますが、2の方法ではデータの欠損が生じます。

以下にそれぞれのスクリプトを記述しますので、これらを参考に2の方法のmain.jsを修正してください

1の方法
-- main.js
const fs = require("uxp").storage.localFileSystem;

async function myCommand(selection) {
    let items_list = "";
    var num = 1;
    selection.items.forEach(function(value){
        if (num < selection.items.length){
            items_list += value + ";";
        } else {
            items_list += value;
        }
        num += 1;
    });

    // ファイルを保存する
    const file = await fs.getFileForSaving("output.txt", { types: ["txt"] });
    await file.write(items_list);
}

module.exports = {
    commands: {
        GetItemXY: myCommand
    }
};
--

-- txtTojson.py
import json

with open("items.txt", "r") as tf:
    items_list = tf.read().replace("\n","").split(';')
    
# print(items_list)
# print(len(items_list))

# listからデータ抽出し、JSON文字列作成
json_str = ""
num = 0
for item in items_list:
    name = item.split("'")[1]
    print(name + "\n")
    
    xy = (item.split("global X,Y:")[1]).split("parent")[0]
    x = xy.split(",")[0]
    y = xy.split(",")[1]
    print(x + "\n")
    print(y + "\n")
    
    wh = (item.split("{")[1]).split("global")[0]
    w = (wh.split("width:")[1]).split(", height")[0]
    h = wh.split("height:")[1]
    print(w + "\n")
    print(h + "\n")
    
    if num == 0:
        json_str += "{\"" + name + "\"" + ":[" + x + ", " + y + ", " + w + ", " + h + "],\n"
    elif num < len(items_list) -1:
        json_str += "\"" + name + "\"" + ":[" + x + ", " + y + ", " + w + ", " + h + "],\n"
    else:
        json_str += "\"" + name + "\"" + ":[" + x + ", " + y + ", " + w + ", " + h + "]}"

    print(json_str + "\n")
    
    num += 1

# JSONファイル作成
file = open('items.json', mode='w')
file.write(json_str.replace(" ",""))
file.close()
--

-- items.json
{"input":{"x":20,"y":23,"w":14,"h":18},
"長方形11":{"x":6,"y":50,"w":30,"h":30},
"inputLine":{"x":40,"y":30,"w":370,"h":130},
"showBtn":{"x":40,"y":10,"w":370,"h":20},
"showBtn":{"x":20,"y":165,"w":160,"h":30},
"長方形7":{"x":20,"y":200,"w":460,"h":320},
"Codeカウント":{"x":196,"y":165,"w":40,"h":30},
"Codeカウント":{"x":252,"y":165,"w":60,"h":30},
"showBtn":{"x":327,"y":165,"w":28,"h":30},
"Codeカウント":{"x":340,"y":525,"w":40,"h":20},
"長方形9":{"x":20,"y":550,"w":460,"h":82},
"showBtn":{"x":370,"y":165,"w":28,"h":30},
"showBtn":{"x":387,"y":525,"w":26,"h":20}}
--

2の方法
-- main.js
const fs = require("uxp").storage.localFileSystem;

async function myCommand(selection) {
    let items_list = [];
    selection.items.forEach(function (value) {
        // ここでPythonスクリプトの処理に相当するデータ抽出を行う
        const name = value.name;
        const x = value.globalBounds.x;
        const y = value.globalBounds.y;
        const w = value.width;
        const h = value.height;
        items_list.push({ name, bounds: [x, y, w, h] });
    });

    // JSONオブジェクトを作成
    const json_obj = {};
    items_list.forEach(item => {
        json_obj[item.name] = item.bounds;
    });

    // JSON文字列に変換
    const json_str = JSON.stringify(json_obj, null, 2);

    // ファイルを保存する
    const file = await fs.getFileForSaving("output.json", { types: ["json"] });
    await file.write(json_str);
}

module.exports = {
    commands: {
        GetItemXY: myCommand
    }
};
--

-- output.json(データが欠損している)
{
  "長方形 7": [
    20,
    200,
    460,
    320
  ],
  "showBtn": [
    327,
    165,
    null,
    null
  ],
  "長方形 9": [
    20,
    550,
    460,
    82
  ],
  "Codeカウント": [
    252,
    165,
    null,
    null
  ]
}
--
2の方法でデータの欠損が生じている原因は、同じ名前のアイテムが複数存在する場合、JSONオブジェクトにおいて後から追加されたアイテムで以前のアイテムのデータが上書きされてしまうためです。JSONオブジェクトのキーは一意でなければならないため、同じ名前のアイテムがあると問題が発生します。

この問題を解決するためには、アイテムの名前が重複しないように一意のキーを生成するか、アイテムのリストを配列としてJSONに保存する必要があります。以下に修正したスクリプトを示します。

```javascript
const fs = require("uxp").storage.localFileSystem;

async function myCommand(selection) {
    let items_list = [];
    selection.items.forEach(function (value, index) {
        // ここでPythonスクリプトの処理に相当するデータ抽出を行う
        const name = value.name;
        const x = value.globalBounds.x;
        const y = value.globalBounds.y;
        const w = value.width;
        const h = value.height;
        // 同じ名前のアイテムがある場合に備えて、インデックスを名前に追加
        const uniqueName = `${name}_${index}`;
        items_list.push({ name: uniqueName, bounds: [x, y, w, h] });
    });

    // JSONオブジェクトを作成
    const json_obj = {};
    items_list.forEach(item => {
        json_obj[item.name] = item.bounds;
    });

    // JSON文字列に変換
    const json_str = JSON.stringify(json_obj, null, 2);

    // ファイルを保存する
    const file = await fs.getFileForSaving("output.json", { types: ["json"] });
    await file.write(json_str);
}

module.exports = {
    commands: {
        GetItemXY: myCommand
    }
};
```

この修正により、各アイテムに一意の名前が付けられ、JSONオブジェクトに保存される際にデータの上書きが発生しなくなります。ただし、この方法では元のアイテム名とインデックスが結合された形で保存されるため、元の名前だけを知りたい場合は後処理が必要になるかもしれません。それを避けるためには、アイテムを配列として保存する方法も考えられます。