Filedini Pythonスクリプティング入門

このドキュメントは、アプリケーションのスクリプティング機能の使い方を学ぶためのチュートリアルです。基本的な操作から始めて、徐々に高度なダイアログ作成までを学びます。


English

はじめに: スクリプトの基本構造と開発環境のヒント

すべてのスクリプトは、host というグローバルオブジェクトを通じてアプリケーションと対話します。スクリプト内で host オブジェクトの機能を利用する関数を定義する場合、必ず引数として (host: HostAPI) を受け取るようにしてください

def my_script_function(host: HostAPI):
    host.log("スクリプトが実行されました!")

型ヒントと入力補完の活用

スクリプトファイルの先頭に以下の「Type Checking Block」を追加することで、VS Codeなどの統合開発環境 (IDE) で host オブジェクトのメソッドやプロパティに対する強力な入力補完(IntelliSense)が利用できるようになります。これは開発効率を大きく向上させます。

from __future__ import annotations
from typing import TYPE_CHECKING

# =============================================================================
# Type Checking Block - IDEの入力補完を有効にするためのブロック
# =============================================================================
# このブロックはコードエディタ(VS Codeなど)がインテリジェントなコード補完を
# 提供するためにのみ使用されます。実際のスクリプト実行時には動作しません。
if TYPE_CHECKING:
    try:
        # 'host_stubs'から'HostAPI'の型定義をインポートします。
        # これは実行時には存在しない仮想的なモジュールです。
        from host_stubs import HostAPI
    except ImportError:
        pass

# これにより、以降の関数定義で 'host: HostAPI' と書くことで
# hostオブジェクトのメソッドの補完が効くようになります。

第1章: 基本的な操作

スクリプティングの第一歩として、アプリケーションの状態を取得したり、簡単なメッセージを表示したりする方法を学びましょう。この章の内容は主に command_sample.py に基づいています。

1.1 ログへのメッセージ出力

スクリプトの動作を確認するために、アプリケーションのログにメッセージを出力できます。これはデバッグに非常に便利です。

host.log() メソッドを使います。

def log_example(host: HostAPI):
    # アプリケーションのログにメッセージを出力します
    host.log("Hello, World!")

1.2 アプリケーションの状態を取得する

host.state モジュールを使って、現在開いているフォルダのパスや、選択されているアイテムの情報を取得できます。

  • host.state.get_current_folder_path(): 現在のフォルダパスを取得します。
  • host.state.get_cursor_path(): カーソル下のアイテムのパスを取得します。
  • host.state.get_selected_paths(): 選択中のすべてのアイテムのパスをリストで取得します。

例: 選択されたアイテムの合計サイズを計算する

import os

# ヘルパー関数 (ファイル内に定義されていると仮定)
def get_directory_size(folder_path: str) -> int:
    total_size = 0
    if not os.path.exists(folder_path) or not os.path.isdir(folder_path):
        return 0
    for dirpath, dirnames, filenames in os.walk(folder_path):
        for f in filenames:
            fp = os.path.join(dirpath, f)
            if not os.path.islink(fp):
                try:
                    total_size += os.path.getsize(fp)
                except OSError:
                    pass
    return total_size

def get_readable_size(total_bytes: int) -> str:
    # ... 適切なサイズ表示フォーマットのロジック ...
    if total_bytes < 1024:
        return f"{total_bytes} Bytes"
    elif total_bytes < (1024**2):
        return f"{total_bytes / 1024:.2f} KB"
    elif total_bytes < (1024**3):
        return f"{total_bytes / (1024**2):.2f} MB"
    else:
        return f"{total_bytes / (1024**3):.2f} GB"

def calculate_selected_size(host: HostAPI):
    # 1. 選択されているすべてのアイテムのパスを取得します
    selected_paths = host.state.get_selected_paths()

    if not selected_paths:
        host.ui.ok_dialog("Selected Size", "No items are selected.")
        return

    total_bytes = 0

    # 2. 各アイテムのサイズを合計します
    for path in selected_paths:
        if os.path.isdir(path):
            total_bytes += get_directory_size(path)
        elif os.path.isfile(path):
            total_bytes += os.path.getsize(path)

    # 3. 結果をダイアログで表示します
    host.ui.ok_dialog(
        "Selected Items Size",
        f"Items: {len(selected_paths)}\nTotal Size: {get_readable_size(total_bytes)}"
    )

1.3 シンプルなダイアログの表示

スクリプトの結果をユーザーに知らせるために、簡単なダイアログボックスを表示できます。host.ui モジュールには、いくつかの定義済みダイアログが用意されています。

  • host.ui.ok_dialog(title, message): [OK]ボタンだけのダイアログ。
  • host.ui.ok_cancel_dialog(title, message): [OK]と[Cancel]ボタンがあるダイアログ。
  • host.ui.input_text_dialog(title, message, initial_text): テキスト入力フィールドを持つダイアログ。InputTextDialogResponse オブジェクトを返します。
  • host.ui.yes_no_dialog(title, message): [Yes]と[No]ボタンがあるダイアログ。ユーザーの選択に応じて DialogResult.YES または DialogResult.NO を返します。

例: [OK]ダイアログの表示

def show_simple_ok_dialog(host: HostAPI):
    host.ui.ok_dialog("Folder Size", "The total size is 123 MB.")

テキスト入力ダイアログの使用

host.ui.input_text_dialog は、ユーザーからのテキスト入力を受け取るためのダイアログです。この関数は InputTextDialogResponse オブジェクトを返します。このオブジェクトには、ダイアログの結果 (.result) とユーザーが入力したテキスト (.text) が含まれています。

例: ユーザーに名前を尋ねる

def ask_for_name(host: HostAPI):
    # テキスト入力ダイアログを表示し、応答を受け取ります
    response = host.ui.input_text_dialog("User Info", "Please enter your name:", "名無しさん")

    # 結果をチェックして処理を分岐します
    if response.result == host.ui.DialogResult.OK:
        # OKが押された場合、入力されたテキストを取得
        name = response.text
        host.log(f"User entered name: {name}")
        host.ui.ok_dialog("Hello", f"こんにちは、{name}さん!")
    else:
        # Cancelが押された場合
        host.log("User cancelled name entry.")
        host.ui.ok_dialog("Cancelled", "名前の入力はキャンセルされました。")

戻り値を使った処理の分岐

ok_cancel_dialogyes_no_dialog など、複数のボタンを持つダイアログは、どのボタンが押されたかに応じて戻り値を返します。この戻り値(host.ui.DialogResult)をチェックすることで、ユーザーの選択に応じてスクリプトの動作を変えることができます。

例: 処理を続けるかユーザーに確認する

def confirm_action(host: HostAPI):
    # OK/Cancelダイアログを表示し、ユーザーの選択を受け取ります
    result = host.ui.ok_cancel_dialog("Confirm", "Do you want to proceed?")

    # 戻り値をチェックして処理を分岐します
    if result == host.ui.DialogResult.OK:
        # OKが押された場合の処理
        host.log("User chose to proceed.")
        host.ui.ok_dialog("Proceeding", "The action will be performed.")
    else:
        # Cancelが押されたか、ダイアログが閉じられた場合の処理
        host.log("User cancelled the action.")
        host.ui.ok_dialog("Cancelled", "The action was cancelled.")

1.4 Pythonライブラリの利用

スクリプトでは、Pythonの標準ライブラリ(os, sys, datetimeなど)を自由にimportして利用できます。これにより、ファイル操作や日付計算など、複雑な処理を簡単に行うことができます。

外部ライブラリのインストールと利用

さらに、host.install() メソッドを使えば、PyPIで公開されている外部のライブラリ(例: numpy, requests, pandas)をスクリプト実行中にインストールして利用できます。

host.install() は、ライブラリがまだインストールされていない場合にのみインストールを実行します。すでに利用可能な場合は何も行いません。

例: numpyライブラリをインストールして利用する

def use_numpy_example(host: HostAPI):
    # 1. 'numpy'パッケージのインストールをリクエストします。
    #    これにより、import前にライブラリが利用可能であることが保証されます。
    if host.install("numpy"):
        # 2. ライブラリをインポートします。
        import numpy as np

        # 3. ライブラリの機能を利用します。
        arr = np.array([1, 2, 3])
        
        # 4. 結果をダイアログに表示します。
        host.ui.ok_dialog(
            "NumPy Example",
            f"NumPy version {np.__version__} is available.\nCreated array: {arr}"
        )
    else:
        host.ui.ok_dialog(
            "NumPy Example",
            "Failed to install NumPy."
        )

第2章: カスタムダイアログの作成

host.ui の機能を使えば、テキスト入力やボタンを自由に配置したカスタムダイアログを作成できます。この章は custom_dialog_sample.py を参考にしています。

2.1 ダイアログの基本構造

  1. host.ui.dialog(title) でダイアログビルダーを開始します。
  2. ビルダーにコントロール(テキストボックス、ボタンなど)を追加します。
  3. dlg.show_modal() でダイアログをモーダル表示し、ユーザーが閉じるのを待ちます。
  4. show_modal() の戻り値で、どのボタンが押されたかを判断します。

2.2 コントロールの追加とイベント処理

  • テキスト入力: dlg.text(label, initial_text)
  • ボタン: dlg.button(text)

ボタンがクリックされたときの動作は、.clicked イベントに lambda 式や関数を += で追加して定義します。

ダイアログを閉じるには、dlg.close(result) を呼び出します。result には host.ui.DialogResult.OKhost.ui.DialogResult.CANCEL などを指定します。

例: 簡単な入力ダイアログ

def show_modal_dialog_example(host: HostAPI):
    # 1. ダイアログビルダーを作成
    dlg = host.ui.dialog("Modal Dialog Sample")

    # 2. "Enter your name"というラベルのテキスト入力コントロールを追加
    name_input = dlg.text("Enter your name", "no-name")

    # 3. ボタンを追加し、クリックイベントを定義
    #    "Close"ボタンが押されたら、OKの結果でダイアログを閉じる
    close_button = dlg.button("Close")
    close_button.clicked += lambda s, e: dlg.close(host.ui.DialogResult.OK)

    # 4. ダイアログを表示し、ユーザーの操作を待つ
    result = dlg.show_modal()

    # 5. ユーザーが "Close" ボタンを押した場合の処理
    if result == host.ui.DialogResult.OK:
        # テキスト入力の値を取得してログに出力
        host.log(f"Name entered: {name_input.text}")

2.3 コントロールのグルーピング

dlg.group(direction) を使うと、コントロールを水平または垂直に並べることができます。

  • host.ui.LayoutDirection.HORIZONTAL: 水平に並べる
  • host.ui.LayoutDirection.VERTICAL: 垂直に並べる(デフォルト)

例: ボタンを水平に並べる

def show_horizontal_buttons(host: HostAPI):
    dlg = host.ui.dialog("Confirmation")
    # 水平グループを作成
    buttons_group = dlg.group(host.ui.LayoutDirection.HORIZONTAL)

    # このグループに追加されたコントロールは横に並ぶ
    buttons_group.button("Yes").clicked += lambda s, e: dlg.close(host.ui.DialogResult.YES)
    buttons_group.button("No").clicked += lambda s, e: dlg.close(host.ui.DialogResult.NO)

    dlg.show_modal()

第3章: 発展的なUIの構築

advanced_dialog_sample.py を参考に、より複雑なレイアウトや多様なコントロールを持つダイアログの作成方法を学びます。

3.1 多彩なコントロール

カスタムダイアログでは、テキスト入力やボタン以外にも多くのコントロールが使えます。

  • チェックボックス: dlg.bool(label, initial_value)
  • 整数スライダー: dlg.int(label, initial_value, min, max)
  • 小数スライダー: dlg.float(label, initial_value, min, max)
  • 選択(ドロップダウン/ラジオボタン): dlg.choice(label, items_list, initial_index, style)
  • ラベル: dlg.label(text)
  • 区切り線: dlg.separator()

各コントロールの value プロパティにアクセスすることで、現在の値を取得できます。

3.2 高度なレイアウト

複雑なUIを構築するために、グループを入れ子にしたり、タブやグループボックスを使ったりできます。

  • グループボックス: dlg.group_box(title, direction)
    • タイトル付きの枠でコントロールを囲みます。
  • タブ: dlg.tab()
    • tab.page(title) でタブページを追加し、そのページにコントロールを配置します。

例: 設定画面のようなダイアログ

def show_settings_dialog(host: HostAPI):
    dlg = host.ui.dialog("Settings")

    # 1. タブコントロールを作成
    tab = dlg.tab()

    # 2. 「一般」タブページを作成し、コントロールを追加
    general_page = tab.page("General")
    general_page.bool("Auto-save", True)
    general_page.int("Font size:", 12, 8, 24)

    # 3. 「外観」タブページを作成し、コントロールを追加
    appearance_page = tab.page("Appearance")
    themes = ["Light", "Dark", "Auto"]
    theme_choice = appearance_page.choice("Theme", themes, 0)
    scale_slider = appearance_page.float("UI Scale:", 1.0, 0.5, 2.0)

    # 4. OK/Cancelボタンを追加
    buttons = dlg.group(host.ui.LayoutDirection.HORIZONTAL)
    buttons.button("Apply").clicked += lambda s, e: dlg.close(host.ui.DialogResult.OK)
    buttons.button("Cancel").clicked += lambda s, e: dlg.close(host.ui.DialogResult.CANCEL)

    # ダイアログを表示
    result = dlg.show_modal()

    # OKなら設定値をログに出力
    if result == host.ui.DialogResult.OK:
        host.log("Settings applied!")
        host.log(f"  Theme: {theme_choice.selected_item}")
        host.log(f"  UI Scale: {scale_slider.value}")

3.3 イベントの活用

各コントロールには、値が変更されたときに発生するイベントがあります(例: text_changed, value_changed, selected_changed)。これらを使うと、ユーザーの操作にリアルタイムで反応する動的なダイアログを作成できます。

例: チェックボックスとスライダーの連動

def show_linked_controls_dialog(host: HostAPI):
    dlg = host.ui.dialog("Linked Controls")
    left = dlg.group(host.ui.LayoutDirection.VERTICAL)

    # "Enabled" チェックボックス
    enabled_check = left.bool("Enabled", True)

    # 整数スライダー
    int_slider = left.int("Int", 10, -100, 100)

    # チェックボックスの値が変わったら、スライダーの有効/無効を切り替える
    def on_enabled_changed(s, e):
        int_slider.enabled = enabled_check.value

    enabled_check.value_changed += on_enabled_changed

    dlg.group(host.ui.LayoutDirection.HORIZONTAL).button("Close").clicked += \
        lambda s, e: dlg.close(host.ui.DialogResult.OK)
    dlg.show_modal()

このチュートリアルで、スクリプティングの基本から応用までを学びました。これらのサンプルを改造したり、APIリファレンスを参照したりして、あなただけの便利なスクリプトを作成してみてください。


APIはまだ発展段階にあり、現状は十分足りている状態ではありません。リクエストなどあればぜひお伝えください。