クリップボード内容保存ツール save_clipboard.py 技術ドキュメント

プログラムの動作

このプログラム save_clipboard.py は、Windows環境のクリップボードに現在格納されている内容を自動的に検出し、ファイルとして保存することを目的としています。主要な機能は、クリップボードにテキストデータがあればそれをテキストファイル(.txt)として、なければ画像データ(DIB形式)があればそれを画像ファイル(.png)として保存することです。

プログラムの主な機能は以下の通りです。

  • クリップボード内容の自動判別: まずテキスト形式 (CF_UNICODETEXT) を優先し、次にDIB形式 (CF_DIB) の画像データを検出します。

  • ファイル保存:

    • 検出されたテキストはUTF-8エンコーディングのテキストファイルとして保存されます。

    • 検出された画像はPNG形式の画像ファイルとして保存されます。

  • ファイル名指定: コマンドライン引数で出力ファイルのベース名を指定できます。指定がない場合はデフォルト名が使用されます。

  • reStructuredText (rst) スニペット生成: ファイル保存後、そのファイルをreStructuredText (Sphinxなどのドキュメント生成ツールで使われるマークアップ言語) ドキュメントで参照・表示するためのコード例を標準出力に表示します。

  • クリップボード形式の表示: テキストも画像も見つからなかった場合、現在クリップボードで利用可能な他の形式IDを一覧表示し、デバッグや情報収集に役立てます。

このプログラムは、特に技術ドキュメントを作成する際に、クリップボードのスクリーンショットやコードスニペットを素早くファイル化し、さらにドキュメントに組み込むための手間を削減するという課題を解決します。

原理

save_clipboard.py は、Pythonの ctypes モジュールを使用してWindowsのネイティブAPIを直接呼び出すことで、クリップボードの操作とデータ取得を実現しています。

  • Windows API と ctypes:

    • ctypes は、PythonからC言語互換のDLL(ここでは user32.dll, ole32.dll, kernel32.dll)を呼び出すための強力なライブラリです。プログラムはこれらのDLL内の関数を宣言し、構造体やポインタの型を定義することで、Windowsのクリップボードおよびメモリ管理機能を直接操作します。

    • OLE API (ole32.dll): 画像データの取得には、OLE (Object Linking and Embedding) APIが使用されます。

      • OleInitialize(None): OLEライブラリを初期化し、プロセス内でOLEサービスを利用できるようにします。これはOLE APIを使用する前に一度だけ呼び出す必要があります。

      • OleGetClipboard(): クリップボードに格納されているOLEデータオブジェクト (IDataObject) へのポインタを取得します。

      • IDataObject::GetData(): 取得した IDataObject のメソッドを呼び出し、特定の形式 (FORMATETC 構造体で指定) のデータを STGMEDIUM 構造体に格納して取得します。画像データの場合、CF_DIB (Device Independent Bitmap) 形式が要求されます。

      • ReleaseStgMedium(): STGMEDIUM 構造体内に保持されているリソースを解放します。

    • User32 API (user32.dll): テキストデータの取得やクリップボードの一般的な操作に使用されます。

      • OpenClipboard() / CloseClipboard(): クリップボードを開閉し、他のアプリケーションからのアクセスを排他的に制御します。

      • GetClipboardData(): CF_UNICODETEXT (WindowsのUnicodeテキスト形式) など、指定された形式のクリップボードデータへのハンドル (HGLOBAL) を取得します。

      • IsClipboardFormatAvailable(): 指定された形式のデータがクリップボードに存在するかを確認します。

      • EnumClipboardFormats(): 利用可能なクリップボード形式を順次列挙し、デバッグ情報として利用します。

    • Kernel32 API (kernel32.dll): クリップボードから取得したグローバルメモリハンドル (HGLOBAL) を管理するために使用されます。

      • GlobalLock(): HGLOBAL が指すメモリブロックをロックし、実際のデータへのポインタを取得します。これにより、アプリケーションはそのメモリに直接アクセスできます。

      • GlobalUnlock(): ロックされたメモリブロックを解除します。

      • GlobalSize(): HGLOBAL が指すメモリブロックのサイズを取得します。

  • データ形式の検出と処理:

    • テキスト: get_clipboard_text() 関数は、CF_UNICODETEXT 形式のデータが存在するかを確認し、存在すれば GetClipboardData でハンドルを取得します。GlobalLock でメモリをロックし、ctypes.c_wchar_p(ptr).value を使ってUTF-16形式のデータをPythonの文字列として読み取ります。その後、UTF-8エンコーディングとCRLF (\r\n) 改行コードでファイルに保存されます。

    • 画像: get_clipboard_image_fixed() 関数は、IDataObject::GetData を使って CF_DIB 形式のデータを取得します。DIBデータは生のビットマップ情報とピクセルデータを含みますが、完全なBMPファイルヘッダーは含まれていません。そのため、プログラムは標準のBMPファイルヘッダーをDIBデータの前に付加し、io.BytesIO を介してPillowライブラリの Image.open() 関数に渡します。Pillowはこの結合されたデータストリームを完全なBMPとして解釈し、最終的にPNG形式で保存されます。

  • reStructuredText (rst) スニペット生成: 保存されたファイルの拡張子と種類に基づいて、Sphinxなどのドキュメント生成ツールで利用できるrstディレクティブの例を生成します。

    • 画像には .. image::.. figure:: ディレクティブの使用例を。

    • テキストファイルには .. literalinclude:: ディレクティブ(外部ファイルのコード埋め込み)や、sphinx-design 拡張の .. dropdown:: ディレクティブ(折り畳み可能なコンテンツ)の使用例を提供します。

必要な非標準ライブラリとインストール方法

このプログラムは、標準ライブラリに加えて以下の非標準Pythonライブラリを使用します。

  • Pillow (PILの後継プロジェクト)

    • 用途: クリップボードから取得したDIB形式の画像データを解析し、PNG形式などの一般的な画像ファイルとして保存するために使用されます。

    • インストール方法:

      pip install Pillow
      

必要な入力ファイル

このプログラムは、ファイルからの入力を必要としません。 代わりに、実行時にWindowsクリップボードに格納されている内容を直接読み込み、入力として扱います。

生成される出力ファイル

プログラムの実行結果として、以下のいずれかのファイルが生成されます。

  1. テキストファイル

    • ファイル名:

      • コマンドライン引数でベース名が指定された場合: [指定されたファイル名].txt

      • 指定がない場合: clipboard_output.txt

    • 内容: クリップボードから取得されたUnicodeテキストデータが、UTF-8エンコーディング、かつWindows標準のCRLF (\r\n) 改行コードで保存されます。

  2. 画像ファイル

    • ファイル名:

      • コマンドライン引数でベース名が指定された場合: [指定されたファイル名].png

      • 指定がない場合: clipboard_output.png

    • 内容: クリップボードから取得されたDIB (Device Independent Bitmap) 形式の画像データが、PillowライブラリによってPNG形式に変換されて保存されます。

コマンドラインでの使用例 (Usage)

save_clipboard.py プログラムは、以下の形式でコマンドラインから実行できます。

python save_clipboard.py [ファイル名_ベース]
  • [ファイル名_ベース] (オプション): 生成されるテキストファイルまたは画像ファイルのベースとなる名前を指定します。

    • この引数を省略した場合、デフォルトで clipboard_output が使用されます。

    • 指定されたファイル名には、プログラムによって自動的に適切な拡張子 (.txt または .png) が付加されます。例えば my_image と指定すると my_image.png または my_image.txt が生成されます。

コマンドラインでの具体的な使用例

例1: クリップボードにテキストがある場合

  1. 任意のテキスト(例: This is a sample text for the clipboard.)をクリップボードにコピーします。

  2. コマンドプロンプトまたはターミナルを開き、save_clipboard.py が保存されているディレクトリに移動します。

  3. 以下のコマンドを実行します。

    python save_clipboard.py my_document_notes
    

    実行結果の説明: プログラムはクリップボードにテキストデータ (CF_UNICODETEXT) を検出し、それを my_document_notes.txt というファイル名で保存します。標準出力には、保存されたファイルに関する情報と、このテキストファイルをreStructuredText (rst) ドキュメントに埋め込むためのスニペットが以下のように表示されます。

    --- クリップボードの内容を確認中... ---
    [情報] クリップボード内にテキストが見つかりました。
    成功: テキストを 'my_document_notes.txt' として保存しました (UTF-8)。
    文字数: 38
    
    ============================================================
    --- reStructuredText (rst) 用表示コード (my_document_notes.txt) ---
    ============================================================
    
    1. 折り畳み表示 (sphinx-design dropdown) ※要拡張機能:
    .. dropdown:: my_document_notes.txt の内容を表示(クリックで展開)
       :color: info
       :icon: file-code
    
       .. literalinclude:: ./my_document_notes.txt
          :language: text
          :linenos:
    
    2. <pre>タグ相当での埋め込み (parsed-literal):
    .. parsed-literal::
    
       This is a sample text for the clipboard.
       ...
    
    3. 通常の外部ファイル取り込み (literalinclude):
    .. literalinclude:: ./my_document_notes.txt
       :language: text
    
    ============================================================
    

例2: クリップボードに画像がある場合

  1. 任意の画像(例: スクリーンショット)をクリップボードにコピーします。

  2. コマンドプロンプトまたはターミナルを開き、save_clipboard.py が保存されているディレクトリに移動します。

  3. 以下のコマンドを実行します。

    python save_clipboard.py project_diagram_v1
    

    実行結果の説明: プログラムはクリップボードに画像データ (CF_DIB) を検出し、それを project_diagram_v1.png というファイル名で保存します。標準出力には、保存されたファイルに関する情報と、この画像ファイルをreStructuredText (rst) ドキュメントに埋め込むためのスニペットが以下のように表示されます。

    --- クリップボードの内容を確認中... ---
    [情報] クリップボード内に画像(DIB)が見つかりました。
    成功: 画像を 'project_diagram_v1.png' として保存しました。
    画像サイズ: 1920x1080, モード: RGB  # 画像サイズとモードは環境によって異なります
    
    ============================================================
    --- reStructuredText (rst) 用表示コード (project_diagram_v1.png) ---
    ============================================================
    
    1. シンプルなファイルリンク:
    `project_diagram_v1.png <./project_diagram_v1.png>`_
    
    2. 画像表示 (imageディレクティブ):
    .. image:: ./project_diagram_v1.png
       :alt: project_diagram_v1.png
    
    3. 図表表示 (figureディレクティブ - キャプション付き):
    .. figure:: ./project_diagram_v1.png
       :alt: project_diagram_v1.png
       :align: center
    
       project_diagram_v1.png の図面
    ============================================================
    

例3: クリップボードにテキストも画像もない場合

  1. クリップボードをクリアするか、本プログラムがサポートしない形式のデータ(例: ファイルオブジェクトなど)をコピーします。

  2. 以下のコマンドを実行します。

    python save_clipboard.py
    

    実行結果の説明: プログラムはクリップボードにテキストも画像(CF_DIB形式)も見つけられず、その旨を通知します。デバッグ情報として、現在クリップボードで利用可能な他の形式IDがリストアップされる場合があります。

    --- クリップボードの内容を確認中... ---
    [情報] クリップボードにテキストまたは画像(CF_DIB)は見つかりませんでした。
    利用可能な形式ID: [2, 16, 7]  # 表示されるIDは環境やクリップボードの内容によって異なります