やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

LibreOfficeCalcのファイルを新規作成するPythonマクロ

 GUIを表示せねば不可。ダサイ……。でもこれ以外の方法が見つけられず。

成果物

コード

run.sh

soffice --calc --norestore "--accept=pipe,name=librepipe;urp;" & {
    sleep 3
    ./new_doc.py
    jobs
    kill %%
}

 要点は以下。

  • --headlessを削除した
    • これがあるとXSCRIPTCONTEXT.getDesktop().getCurrentComponent()からシートを取得できない。ファイルも新規保存できない。
  • --norestoreを追加した
    • これがないと不要なダイアログが起動してしまいマクロが停止してしまう
  • sleep 3
    • LibreOffice Calc の GUI が起動するまでの時間だけ待機させる必要がある。さもなくば以下エラーになる
    • __main__.NoConnectException: Connector : couldn't connect to pipe librepipe(10)

 --headlessがないためGUIが立ち上がってしまう。つまり、ファイルを新規保存するためには、必ずGUIを起動させる必要がある。マクロ実行完了後はkillしているが、ウザいわ―……。

 --invisibleすればいいかと思うでしょ? 残念、以下エラーになりました。

Traceback (most recent call last):
  File "./new_doc.py", line 29, in <module>
    sheet = doc.getSheets().getByIndex(0)
AttributeError: 'NoneType' object has no attribute 'getSheets'

 ヘルプを翻訳したら「非表示モード」って書いてあるのに。ドキュメントとダイアログはAPIを介して制御およびオープンされます。って書いてあるのに、NoneTypeなんですけど。なんか間違ってるの?

   --invisible非表示モードで開始します。スタートアップロゴも
                       最初のプログラムウィンドウが表示されます。応用
                       制御することができ、ドキュメントとダイアログは
                       APIを介して制御およびオープンされます。パラメータを使用して、
                       プロセスは、taskmanagerを使用してのみ終了できます
                       (Windows)またはkillコマンド(UNIXライクなシステム)。それ
                       --quickstartと組み合わせて使用​​することはできません。

 どうすればGUIを起動せず新規ファイル作成できるのか……。とりあえずこれで妥協する。

new_doc.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import uno
import unohelper
from pythonscript import ScriptContext
import os.path
import datetime

def connect_script_context(host='localhost', port='2002', namedpipe=None):
    UNO_RESOLVER = "com.sun.star.bridge.UnoUrlResolver"
    UNO_DESKTOP = "com.sun.star.frame.Desktop"
    localCtx = uno.getComponentContext()
    localSmgr = localCtx.ServiceManager
    resolver = localSmgr.createInstanceWithContext(UNO_RESOLVER, localCtx)
    if namedpipe is None:
        uno_string = 'uno:socket,host=%s,port=%s;urp;StarOffice.ComponentContext' % (host, port)
    else:
        uno_string = 'uno:pipe,name=%s;urp;StarOffice.ComponentContext' % namedpipe
    ctx = resolver.resolve(uno_string)
    smgr = ctx.ServiceManager
    XSCRIPTCONTEXT = ScriptContext(ctx,
                                   smgr.createInstanceWithContext(UNO_DESKTOP, ctx),
                                   None)
    return XSCRIPTCONTEXT

XSCRIPTCONTEXT = connect_script_context(namedpipe='librepipe')
desktop = XSCRIPTCONTEXT.getDesktop()
doc = desktop.getCurrentComponent()
sheet = doc.getSheets().getByIndex(0)
sheet.getCellByPosition(0,0).Value = 1234

now = datetime.datetime.now()
timestamp = now.strftime('%Y%m%d%H%M%S')
file = os.path.join(os.path.dirname(os.path.abspath(__file__)), timestamp + '.ods')
url  = unohelper.systemPathToFileUrl(file)
doc.storeToURL(url, ())

 現時刻をファイル名にする。yyyymmddHHMMSS.ods。出力先はnew_doc.pyファイルが存在するディレクトリである。

 ググるとよく見つかる以下コードではシートを取得できなかった。APIわけわからん。コマンド引数によってAPIの動作が変化するの?

doc = XSCRIPTCONTEXT.getDocument()
sheet = doc.getSheets().getByIndex(0)
Traceback (most recent call last):
  File "./new_doc.py", line 31, in <module>
    sheet = doc.getSheets().getByIndex(0)
AttributeError: getSheets

所感

 GUIを表示せずバッチ処理ods新規ファイルを作成したかったのに……。sofficeコマンド単独でできてもいいくらい基本的なことだと思うのだが……。

対象環境

$ uname -a
Linux raspberrypi 4.19.97-v7l+ #1294 SMP Thu Jan 30 13:21:14 GMT 2020 armv7l GNU/Linux