試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

OpenOffice.orgのマクロをPythonで記述して動かす(Calcのセル内容へのアクセスに関する追加メモ・ページ3/3)

OpenOffice.orgのマクロをPythonで記述して動かす(Calcのセル内容へのアクセスに関する追加メモ・ページ2/3)」の続き。

同様な計算を繰り返す際の式について

OpenOffice.orgのマクロをPythonで記述して動かす(Calcのセル内容へのアクセスについてとセル内容の操作に関する例)」の中で少し触れたが、例えば、CSV(コンマ区切り)表記で

92,79,83,95
79,92,97,90
94,94,96,84

というデータがあったとして、それぞれの行の末尾に行ごとの合計を入れるとすると

92,79,83,95,=SUM(A1:D1)
79,92,97,90,=SUM(A2:D2)
94,94,96,84,=SUM(A3:D3)

となるが、マクロでこのような式を入れていく場合、各行ごとに関数(上の例ではSUM())の引数のセル名の数字部分に変数の値が入るようにしてループで処理する。

# 注意:別途、後述のクラス定義部分が動作に必要
def test():
  "test"
  try:
    calc = OOoCalc()
  except NotOOoCalcException:
    return
  sheet = calc.active_sheet
  data = [[92, 79, 83, 95],
          [79, 92, 97, 90],
          [94, 94, 96, 84]]
  for (i, line) in enumerate(data):
    y = i + 1
    for (j, item) in enumerate(line):
      # 各データを入れる
      sheet.getCellByPosition(j, i).Formula = item
    # SUM()を行ごとに入れる
    sheet.getCellByPosition(4, i).Formula = '=SUM(A%d:D%d)' % (y, y)

これを実行したときのE列の内容は

  • E1(値は349): =SUM(A1:D1)
  • E2(値は358): =SUM(A2:D2)
  • E3(値は368): =SUM(A3:D3)

となる。
場合によっては、セル名を用いずにセルの座標のみを用いてセル名を得て処理したいことがあるが、そのときには表計算関数INDIRECT()ADDRESS()を組み合わせる。それぞれの関数の引数に関する詳細はヘルプが詳しいのでここでは扱わない。

# 注意:別途、後述のクラス定義部分が動作に必要
def test2():
  "test2"
  try:
    calc = OOoCalc()
  except NotOOoCalcException:
    return
  sheet = calc.active_sheet
  sheetname = sheet.Name
  data = [[92, 79, 83, 95],
          [79, 92, 97, 90],
          [94, 94, 96, 84]]
  for (i, line) in enumerate(data):
    y = i + 1
    for (j, item) in enumerate(line):
      sheet.getCellByPosition(j, i).Formula = item
    sheet.getCellByPosition(4, i).Formula = '=SUM(INDIRECT(ADDRESS(%d;1;4;"%s")):INDIRECT(ADDRESS(%d;4;4;"%s")))' % (y, sheetname, y, sheetname)

これをシート「Sheet1」上で実行したときのE列の内容は

  • E1(値は349): =SUM(INDIRECT(ADDRESS(1,1,4,1,"Sheet1")):INDIRECT(ADDRESS(1,4,4,1,"Sheet1")))
  • E2(値は358): =SUM(INDIRECT(ADDRESS(2,1,4,1,"Sheet1")):INDIRECT(ADDRESS(2,4,4,1,"Sheet1")))
  • E3(値は368): =SUM(INDIRECT(ADDRESS(3,1,4,1,"Sheet1")):INDIRECT(ADDRESS(3,4,4,1,"Sheet1")))

となる。
この方法には

  • セルの式を見たときにどこを指しているのかが分かりにくい
  • シート名を引数にとる部分があるため、シート名を後で変更するとエラー(502)が出る

といった欠点もある。
上記2つの例をマクロとして実行するにはマクロ本体よりも手前(上)に以下の内容をコピペする。

# -*- mode: python; coding: utf-8 -*-

import uno
#import unohelper  # このコードでは未使用

class NotOOoCalcException(Exception):
  """
  OOo Calcの中でマクロが呼ばれなかったときに発生する例外
  """
  pass

class Bridge(object):
  """
  PythonとOOoの仲立ちをする各種オブジェクトと
  それを用いた幾つかの操作を提供
  """
  def __init__(self):
    """
    各種オブジェクトの取得
    """
    # http://api.openoffice.org/docs/common/ref/com/sun/star/script/provider/XScriptContext.html
    # http://api.openoffice.org/docs/common/ref/com/sun/star/uno/XComponentContext.html
    self._context = XSCRIPTCONTEXT.getComponentContext()
#    self._manager = self._context.ServiceManager  # このコードでは未使用
    self._desktop = XSCRIPTCONTEXT.getDesktop()
    self._document = XSCRIPTCONTEXT.getDocument()
    # http://api.openoffice.org/docs/common/ref/com/sun/star/frame/XDesktop.html
    self._frame = self._desktop.CurrentFrame
    # http://api.openoffice.org/docs/common/ref/com/sun/star/frame/XFrame.html
    self._window = self._frame.ContainerWindow
    self._toolkit = self._window.Toolkit
  def run_errordialog(self, title='', message=''):
    """
    エラーダイアログを表示する
    http://api.openoffice.org/docs/common/ref/com/sun/star/awt/XMessageBoxFactory.html
    """
    msgbox = self._toolkit.createMessageBox(self._window,
                                            uno.createUnoStruct('com.sun.star.awt.Rectangle'),
                                            'errorbox',
                                            1,
                                            title,
                                            message)
    msgbox.execute()
    msgbox.dispose()

class OOoCalc(Bridge):
  """
  OOo Calcの制御
  BridgeをベースにCalcのサポート状態とシート一覧を追加したもの
  """
  def __init__(self):
    Bridge.__init__(self)  # 必須
    # Calcの中から実行されたならTrue,それ以外ならFalse
    # http://api.openoffice.org/docs/common/ref/com/sun/star/lang/XServiceInfo.html
    if not self._document.supportsService('com.sun.star.sheet.SpreadsheetDocument'):
      self.run_errordialog(title='エラー', message='このマクロはOpenOffice.org Calcの中で実行してください')
      raise NotOOoCalcException()
    # http://api.openoffice.org/docs/common/ref/com/sun/star/frame/XModel.html
    self.__current_controller = self._document.CurrentController
  def get_active_sheet(self):
    return self.__current_controller.ActiveSheet
  def set_active_sheet(self, value):
    self.__current_controller.ActiveSheet = value
  active_sheet = property(get_active_sheet, set_active_sheet)
  @property
  def supported(self): return self.__supported

# この後ろにマクロtest,test2をコピペ

使用したバージョン: