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

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

OpenOffice.orgのマクロをPythonで記述して動かす(Calcのセル内容へのアクセスについてとセル内容の操作に関する例)

OpenOffice.orgのマクロをPythonで記述して動かす(Calcのアクティブもしくは任意のシートとセルのオブジェクトの取得)」の続き。
(2014/11/20)Pythonのtimeモジュールについてのリファレンスのリンク先を修正した。

  1. セル内容へのアクセス

セル内容へのアクセス

OpenOffice.orgのセルには

  • 式(「=」で始まるもの)
  • 文字列

といった複数の形があり、式は表計算における「入力」で値や文字列は計算結果の「出力」であるとも言えるが、「OpenOffice.orgのマクロをPythonで記述して動かす(Calcのアクティブもしくは任意のシートとセルのオブジェクトの取得)」の最後で取得方法を扱ったセルオブジェクトからはいずれの形からも読み書きができる。
セルに計算式(単純に演算子でつないだものや関数を呼ぶものなど)が入っている場合、それを式の文字列として取り出すことも計算結果を値として取り出すこともできる。
形式によって用いる(セルオブジェクトの)メンバが以下のように変わる。

  • 式: Formula
  • 値: Value
  • 文字列: String

「Formula」は「式」の意。
これにより、セルに内容を入れるときにはFormula、取り出すときにはValueやStringを用いるといった使い分けができる。もちろん、Formulaへの代入によってOpenOffice.org Calc上の任意の(表計算の)関数が記述可能。
下の例ではセルオブジェクトcellがあるものとする。

(セルに式「=SUM(B6:B9)」を入れる)
cell.Formula = '=SUM(B6:B9)'

(セルの値(計算結果)を取り出してcellvalに代入)
cellval = cell.Value

操作はgetString()のような「get」系メンバ関数による読み込みとsetString()のような「set」系メンバ関数による書き込みによっても行える。
以上の内容を用いると、OpenOffice.org Calcにおけるデータの入力をマクロから(自動で)自由に行うことができるようになる。ただ、式の中で関数を呼び出したりする場合、「同じ行の中の特定の列の内容を足していく」といった作業を行ごとに同様に行うというような場面では引数の記述に工夫が必要となるが、それに関してはここでは扱わない。

OpenOffice.orgのマクロをPythonで記述して動かす(Calcのアクティブもしくは任意のシートとセルのオブジェクトの取得)」の内容と本記事のこれまでの内容を踏まえた上でのマクロ例を以下に貼り付ける。
[任意]ファイル名: [OOoユーザディレクトリ]/user/Scripts/python/calc_cell_test.py

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

import time
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_infodialog(self, title='', message=''):
    """
    情報ダイアログを表示する
    http://api.openoffice.org/docs/common/ref/com/sun/star/awt/XMessageBoxFactory.html
    http://hermione.s41.xrea.com/pukiwiki/pukiwiki.php?OOoPython%2FOverView
    """
    msgbox = self._toolkit.createMessageBox(self._window,
                                            uno.createUnoStruct('com.sun.star.awt.Rectangle'),
                                            # ダイアログの種類を指定する文字列
                                            # infobox,warningbox,errorbox,
                                            # querybox,messboxのいずれか
                                            'infobox',
                                            1,
                                            title,
                                            message)
    msgbox.execute()
    msgbox.dispose()
  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
  # http://hermione.s41.xrea.com/pukiwiki/pukiwiki.php?OOoBasic%2FCalc%2Factivesheet
  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)


def m01_get_a1_cell():
  "現在のシートにおけるセルA1の内容を取得して表示"
  try:
    calc = OOoCalc()
  except NotOOoCalcException:
    return
  # 単一セルオブジェクト
  # getCellByPosition()の引数はそれぞれX方向とY方向の位置
  # http://api.openoffice.org/docs/common/ref/com/sun/star/table/XCellRange.ht ml
  cell = calc.active_sheet.getCellByPosition(0, 0)
  # メンバString(セルの文字列)をダイアログで表示
  # ここのメッセージはunicode文字列にしないと非ASCII(日本語)セルでエラーになる
  calc.run_infodialog(title='セル内容表示のテスト', message=u'位置(0,0)のセル(A1)の内容:\n 式:%s 文字列:%s 値:%s' % (cell.Formula, cell.String, cell.Value))

def m02_set_a2_cell():
  "現在のシートにおけるセルA2に現在時刻を表示"
  try:
    calc = OOoCalc()
  except NotOOoCalcException:
    return
  # ここではgetCellRangeByName()を用いた方法でセルを取得してみる
  # http://api.openoffice.org/docs/common/ref/com/sun/star/table/XCellRange.html
  cell = calc.active_sheet.getCellRangeByName('A2')
  #cell = calc.active_sheet.getCellByPosition(0, 1)

  # セルオブジェクトのプロパティStringかメンバ関数getString()/setString()で
  # 文字列としてセルのデータを読み書きする
  # strftime()のフォーマット文字列は
  # http://docs.python.jp/3/library/time.html
  # を参照
  cell.String = time.strftime('%Y年%m月%d日(%a) %H時%M分%S秒')
  #cell.setString(time.strftime('%Y年%m月%d日(%a) %H時%M分%S秒'))
  # 書き込みではFormulaプロパティを用いるようにしてもよいかも
  #cell.Formula = time.strftime('%Y年%m月%d日(%a) %H時%M分%S秒')

def m03_set_a5_b11_sampledata():
  "サンプルデータを現在のシートのA5からB11の範囲に入力"
  try:
    calc = OOoCalc()
  except NotOOoCalcException:
    return
  sheet = calc.active_sheet
  sheet.getCellRangeByName('A5').String = '科目'
  sheet.getCellRangeByName('B5').String = '点数'
  sheet.getCellRangeByName('A10').String = '合計'
  sheet.getCellRangeByName('A11').String = '平均'
  sample_data = [('国語', 75), ('算数', 85), ('理科', 95), ('社会', 80)]
  # データを入力するセル
  # getCellRangeByName()では「:」を挟んで範囲指定が可能
  cell_range = sheet.getCellRangeByName('A6:B10')
  # ここではサンプルデータ長とセル範囲サイズが一致しているが
  # セル範囲に対してループさせるなら範囲のサイズを取得して処理を行う
  # (cell_range.getRangeAddress()で取得し、各種メンバの値を用いる)
  for i in range(len(sample_data)):
    # 科目名
    # 範囲内の特定のセルをgetCellByPosition()で指定して得ている
    # http://api.openoffice.org/docs/common/ref/com/sun/star/table/XCellRange.html
    cell_range.getCellByPosition(0, i).String = sample_data[i][0]
    # 点数
    # 値として読み書きするにはValueプロパティかgetValue()/setValue()を用いる
    cell_range.getCellByPosition(1, i).Value = sample_data[i][1]
    # 書き込むときにはFormulaプロパティを用いるようにしてもよいかも
    # (値をそのままFormulaに入れても値として処理される)
    #cell_range.getCellByPosition(1, i).Formula = sample_data[i][1]

  # 合計と平均を計算するための式(関数呼び出し)を指定
  # 式(「=」で開始するもの)を式として読み書きするには
  # FormulaプロパティかgetFormula()/setFormula()を用いる
  # 関数の呼び出しも同様
  # 「formula」は式,数式の意
  # 書式はセル上に入力するのと同じ
  sheet.getCellRangeByName('B10').Formula = '=SUM(B6:B9)'
  sheet.getCellRangeByName('B11').Formula = '=AVERAGE(B6:B9)'


# このタプルに名前を書いた関数のみマクロ選択ダイアログから実行できる
# g_exportedScriptsを記述しない場合は全て実行可
g_exportedScripts = (m01_get_a1_cell, m02_set_a2_cell, m03_set_a5_b11_sampledata)

以下は実行内容となる。

  • 1番目のマクロではアクティブシートのセルA1の内容を式/値/文字列のそれぞれの形式についてダイアログで表示
  • 2番目のマクロではアクティブシートのセルA2に現在の日付と時刻を入れる
  • 3番目のマクロではアクティブシートのA5からB11のセル範囲にサンプルデータを入れる


1番目のマクロの出力(A1セルに何も入力しないと出力も空(値は0.0)となる)

2番目(実行したときの日付/時刻となる)と3番目の出力・A1セルの内容は手動で入れたもの

関連URL:

使用したバージョン: