LibreOffice Calc を外部から Python で操作する

Microsoft Office 365 が値上げをされたので、無料の LibreOffice に移行しましょう…ってのが流行ったがどうかわかりませんが、まあ、いままで Excel で作っていた資料を LibreOffice Calc でうまく作れるかどうかが問題ですよね。これは Google Workspace の Calc でも同じ問題を抱えるわけですが。

全体の操作感は別として(個人的には Excel が慣れているので、他の表計算ソフトを使うのはちょっと避けたいです)、いままで作っていた Excel VBA のマクロだとか、あらたな Calc でうまく移植できるのか? が問題になってきます。実は Excel VBA の開発環境もほぼ化石のままだし、LibreOffice Calc の Basic マクロの開発環境は化石みたいなものです。現在の vscode + copilot のような AI を使ったコード環境に比べると、ちょっとこのまま Excel VBA で開発のするのは避けたい…というのがプログラマの心情なのですが、俺にはこれしかないんだ、これが一番なのだという 97式自動小銃を持ち出す松本零士キャラならば、まあ、確かにそうですよね、とも言えなくもありません。

LibreOffice Calc は Excel VBA の互換環境なのか?

一見作れそうな感じなのですが、全然だめでした。全く互換はありません。

おなじみの Excel VBA のコードですが、これを LibreOffice Calc の Basic にコピペしても動きません。

Sub test()
    Dim sh As Worksheet
    Set sh = ActiveSheet
    sh.Cells(1, 1).Value = "増田智明"
End Sub

エラーになるので ChatGPT でコンバートすると以下のようになります。

Sub test
    Dim oDoc As Object
    Dim oSheet As Object
    Dim oCell As Object

    ' 現在のドキュメントを取得
    oDoc = ThisComponent

    ' アクティブシートを取得
    oSheet = oDoc.CurrentController.ActiveSheet

    ' A1セルを取得 (行・列は0始まり)
    oCell = oSheet.getCellByPosition(0, 0)

    ' 値を設定
    oCell.String = "増田智明"
End Sub

いや…これだと無理ですね。複雑な Excel VBA の移植は到底無理だし、簡単な関数も結構手間なような感じです。

あらたに Calc 用の Basic を覚えるのは馬鹿馬鹿しいので、新規一転して Python を使ってみましょう。

LibreOffice に Python エディタはついていない!!!

理由はよくわからないのですが、LibreOffice には Basic マクロのエディタは付いているのですが、Python マクロのエディタは付属していません。

「ツール」→「マクロ」→「マクロの編集」を開くと Basic マクロのエディタが開きます。

このエディタは文法は Basic だけど、Excel VBA とも違うし、さらに言うと Excel VBA エディタと同じぐらい化石なのでなんとも使い勝手が悪いです。「As」の後の補完候補もでないので、Excel VBA よりも使い勝手は悪いです…どうやってマクロプログラミングをするのでしょうか?

で、Basic のほうはおまけみたいなものらしく、結局のところ Basic じゃなくて Python で組む方が良さそうとのこと。

Python マクロを使う

実は LibreOffice Calc のPython マクロを使うのには2種類のやり方があります。

  • 内部マクロにして XSCRIPTCONTEXT モジュールを使って組む
  • 外部マクロにして UNO API 経由で組む

Excel VBA のように Excel ファイルに組み込まれた形なのは前者の XSCRIPTCONTEXT モジュールのほうになるのですが、どうも使い勝手が悪いので、ここでは UNO API 経由のほうを紹介します。

UNO API 経由のほうは、LibreOffice Calc がサーバーになって socket 経由で Python から Calc の内部を操作することができます。これは、Excel オブジェクトを C# で操作するような感じで使えるので、意外と使い勝手がよいです。特に外部から自動実行や AI 経由で何かさせたいときはこっちのほうが良さそうです。

UNO API 経由

Calc を起動しておく。

PowerShell で、以下のコマンドを実行しておく。ポート 2002 番で LibreOffice のサーバーを実行させます。

& "C:\Program Files\LibreOffice\program\soffice" --accept="socket,host=localhost,port=2002;urp;" --norestore --nologo

vscode で以下のコードを記述(これは Copilot に作って貰ってます)
コード名は calc001.py です。

# UNO接続の準備
local_ctx = uno.getComponentContext()
resolver = local_ctx.ServiceManager.createInstanceWithContext(
    "com.sun.star.bridge.UnoUrlResolver", local_ctx)

# LibreOfficeに接続
ctx = resolver.resolve(
    "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext")

smgr = ctx.ServiceManager
desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx)

# 現在開いているCalcドキュメントを取得
doc = desktop.getCurrentComponent()
sheet = doc.Sheets[0]  # 1枚目のシート

# A1セルに文字列を入力
cell = sheet.getCellByPosition(0, 0)  # (列=0, 行=0) → A1
cell.setString("tomoaki masuda")

PowerShell で、以下を実行

& "C:\Program Files\LibreOffice\program\python" .\calc001.py

A1 セルに文字列「tomoaki masuda」が入る。

この方法が良いのは、

  • Calc は開いたままでよい
  • Python コードは vscode で書ける
  • コードが即反映される(Python.exe を動かすので当たり前)

ただし、コードの前提条件として、

  • import uno が必要
  • LibreOffice に接続するまでが少々手間
  • おそらくデバッグ実行はできないのでは?
  • Python は、LibreOffice 内蔵の “C:\Program Files\LibreOffice\program\python” を動かす必要あり。
  • シート付属ではないので、環境依存になりがち

という形で結構制限が多いです。Excel VBA のときのように、Excel VBA エディタを開きながらぽちぽちとコーディングをしたり、シートにダイアログやボタンを貼り付けたりすることはできませんが(これは別途後で調べます)

  • テキストを読み込んで、Calc にシートを作成して表形式で整える
  • 既存のシートを読み込んで、Python で計算した後に、シートに計算結果を入れる
  • シートのデータをもとに、グラフを作成して貼り付ける

のような定型作業には便利そうです。

import uno のコード補完を有効にする

python が特殊なものを参照しているので、import uno のコード補完が有効になりません。これはかなり不便なので、コード補完が有効になるように settings.json を追加します。

{
    "python.analysis.autoImportCompletions": true,
    "python.analysis.extraPaths": [
        "C:/Program Files/LibreOffice/program",
        "C:/Program Files/LibreOffice/program/python-core-3.11.13/lib"
    ]
}

Lib を参照している Python のバージョン部分「python-core-3.11.13」を開発環境で揃えないといけませんが、これで import uno のコード補完が有効になります。
ただし、もともと Any 型になっているものが多いのか、あまり役に立たないのですが、メソッド名とかクラス名からライブラリのコードにジャンプできるのはいいかもしれません。

Markdown の表を読み込んでシートに書き出す

もう少し複雑な例として Markdown 形式で書かれた表を読み取ってシートに貼り付ける Python マクロを書いてみましょう。これも最初のコメント部分だけ書いておいて、コードは Copilot に書いて貰っています。

"""
markdown のテーブルを読み取って、LibreOffice Calc のシートに書き出すスクリプト

使用方法:
1. LibreOffice Calc を開いた状態で実行
2. Markdownテーブルを含むファイルを指定

実行コマンド:
& "C:\Program Files\LibreOffice\program\python" .\calc002.py
"""

import uno
import re
from pathlib import Path


def parse_markdown_table(markdown_text: str) -> list[list[str]]:
    """
    Markdownテーブルをパースして2次元リストに変換する
    
    Args:
        markdown_text: Markdownテーブルを含むテキスト
    
    Returns:
        2次元リスト(各行のセルデータ)
    """
    lines = markdown_text.strip().split('\n')
    table_data = []
    
    for line in lines:
        # テーブル行かどうかを確認(|で始まる行)
        if not line.strip().startswith('|'):
            continue
        
        # 区切り行(|---|---|など)をスキップ
        if re.match(r'^\|[\s\-:]+\|$', line.strip().replace('|', '|').replace('-', '-')):
            if '---' in line or ':-' in line or '-:' in line:
                continue
        
        # セルを分割
        cells = line.split('|')
        # 最初と最後の空要素を除去
        cells = [cell.strip() for cell in cells[1:-1]]
        
        if cells:  # 空でない行のみ追加
            table_data.append(cells)
    
    return table_data


def connect_to_libreoffice():
    """
    LibreOffice に接続してデスクトップオブジェクトを取得する
    
    Returns:
        (desktop, model, sheet) のタプル
    """
    local_ctx = uno.getComponentContext()
    resolver = local_ctx.ServiceManager.createInstanceWithContext(
        "com.sun.star.bridge.UnoUrlResolver", local_ctx)
    # LibreOfficeに接続
    ctx = resolver.resolve(
        "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext")
    smgr = ctx.ServiceManager
    desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx)
    
    # 現在のドキュメントを取得
    model = desktop.getCurrentComponent()
    
    if model is None:
        raise Exception("LibreOffice Calc ドキュメントが開かれていません")
    
    # アクティブなシートを取得
    sheet = model.getCurrentController().getActiveSheet()
    
    return desktop, model, sheet


def write_to_calc(sheet, table_data: list[list[str]], start_row: int = 0, start_col: int = 0):
    """
    LibreOffice Calc のシートにデータを書き込む
    
    Args:
        sheet: Calcのシートオブジェクト
        table_data: 書き込むデータ(2次元リスト)
        start_row: 開始行(0始まり)
        start_col: 開始列(0始まり)
    """
    for row_idx, row_data in enumerate(table_data):
        for col_idx, cell_value in enumerate(row_data):
            cell = sheet.getCellByPosition(start_col + col_idx, start_row + row_idx)
            cell.setString(cell_value)


def main():
    """メイン処理"""
    # サンプルのMarkdownテーブル
    sample_markdown = """
| 名前 | 年齢 | 職業 |
|------|------|------|
| 田中 | 30 | エンジニア |
| 佐藤 | 25 | デザイナー |
| 鈴木 | 35 | マネージャー |
"""
    
    try:
        # Markdownテーブルをパース
        table_data = parse_markdown_table(sample_markdown)
        print(f"パースしたデータ: {table_data}")
        
        # LibreOffice に接続
        desktop, model, sheet = connect_to_libreoffice()
        print("LibreOffice Calc に接続しました")
        
        # データを書き込み
        write_to_calc(sheet, table_data)
        print("データの書き込みが完了しました")
        
    except Exception as e:
        print(f"エラーが発生しました: {e}")


if __name__ == "__main__":
    main()

コードを見ると、Markdown 形式の文字列から読み込んでいるので、なんちゃってツールではありますが、これで動作は確認できます。実は、そのままでは動かなかったので、connect_to_libreoffice 関数や、以下のアクティブシートのところだけ手作業で書き変えています。

    # アクティブなシートを取得
sheet = model.getCurrentController().getActiveSheet()

アクティブワークブックやアクティブシートは、現在開いているワークブックやシートに書き込むので結構頻繁に使います。

コマンドラインから実行

すると Calc のアクティブシートにデータ書き込まれます。

2列目に「—」が入ったままなのはご愛敬なのですが、CSV 形式からの貼り付けとか、別フォーマットからの貼り付けとかには使えそうです。表形式の場合は、ここから罫線を付けたり、タイトル行に色を付けたりするので、そのあたりも Python マクロにできれば一応便利になるかなと。

で、Excel & Excel VBA から LibreOffice & Python に移行できそうか?

結論から言うと、Excel VBA から Python への移行は敷居が高いです。Excel VBA には ActiveWorkbook とか Me とか、Visual Basic 風な便利なグローバルオブジェクトが多いんですよね。import uno がどこまでグローバルオブジェクトが作られているか分からないのですが、この部分がないと結構コーディングが手間です。

逆に言えば、AI エージェント使ったコーディングができれば Python コードでも特に問題がなさそうです。おそらく、Excel VBA ベースのグローバル変数を追加して、いろいろな前提条件んどを端折れるようにすれば業務的に使えるようになるとは思います。

何か内製できそうなツールを作ってみるといいんじゃないでしょうか?

  • 複数のアンケートシートからひとつの集計結果にまとめる
  • データベースの情報を収集して、Calc に週間レポートや月間レポートしてグラフ付きで作成する
  • シートに関数をつけて計算しておいて、更に集計してまとめるようなツール

このあたりが、社内でツールとして作れればよいかな、と。M365 の Python マクロってどのくらい使われているんでしょうね?

追記 2025/12/16

LibreOffice Calc で Python から帳票作成(シートのスタイル以外は) https://www.moonmile.net/blog/archives/11840
LibreOffice Calc で Python から帳票作成(テンプレートファイルの利用) https://www.moonmile.net/blog/archives/11849

追記 2026/01/05

LibreOffice Calc で使う Excel Like UNO を仮リリース https://www.moonmile.net/blog/archives/11916

カテゴリー: 開発, LibreOffice パーマリンク