OpenOffice.orgのマクロをPythonで記述して動かす(Calcのセル内容の操作に関する例その2)
「OpenOffice.orgのマクロをPythonで記述して動かす(Calcのセル操作に関する追加メモ・ページ1/3)」「OpenOffice.orgのマクロをPythonで記述して動かす(Calcのセル内容へのアクセスに関する追加メモ・ページ2/3)」「OpenOffice.orgのマクロをPythonで記述して動かす(Calcのセル内容へのアクセスに関する追加メモ・ページ3/3)」の内容を踏まえて、OpenOffice.org Calcのセル操作に関するマクロの2つ目の例を下に貼り付ける。
これまでにも似たような名簿のようなサンプルを貼り付けたことがあるが、例の中で使用した名前は架空のものであり、実在の個人名や団体名などと一致するものがあったとしても関係はない。
(2014/11/20)Pythonのrandomモジュールについてのリファレンスのリンク先を修正した。
[任意]ファイル名: [OOoユーザディレクトリ]/user/Scripts/python/calc_cell_test_2.py
# -*- mode: python; coding: utf-8 -*- import random import uno #import unohelper # このコードでは未使用 class InvalidColorException(Exception): """ RGB()に不正な色が指定された場合に発生する例外 """ def __init__(self, red, green, blue): (self.__red, self.__green, self.__blue) = (red, green, blue) def __str__(self): return 'Invalid color(R=%d,G=%d,B=%d)' % (self.__red, self.__green, self.__blue) class NotOOoCalcException(Exception): """ OOo Calcの中でマクロが呼ばれなかったときに発生する例外 """ pass class Bridge(object): """ PythonとOOoの仲立ちをする各種オブジェクトと それを用いた幾つかの操作を提供 """ # enum定数(どれが選択されたかだけを示し、値に意味がないもの)は # uno.Enumオブジェクトとして得る # com.sun.star.table.CellHoriJustify.CENTERの場合は # uno.Enum('com.sun.star.table.CellHoriJustify', 'CENTER')とする com_sun_star_table_CellHoriJustify_CENTER = uno.Enum('com.sun.star.table.CellHoriJustify', 'CENTER') # constant定数(名前から値に展開されるもの)は # uno.getConstantByName()で得る com_sun_star_awt_FontWeight_NORMAL = uno.getConstantByName('com.sun.star.awt.FontWeight.NORMAL') com_sun_star_awt_FontWeight_BOLD = uno.getConstantByName('com.sun.star.awt.FontWeight.BOLD') 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 class Data: """ 各種データ """ header = ('出席番号', '性別', 'Family', 'Given', '国語', '算数', '理科', '社会', '合計', '平均') students = \ [ # 出席番号, 性別(M:True F:False), Family, Given (1, True, 'Tanaka', 'Ichiro'), (2, False, 'Yamana', 'Hanako'), (3, True, 'Urashima', 'Saburo'), (4, True, 'Kurusu', 'Santa'), (5, True, 'Handa', 'Fuuta'), (6, False, 'Umeno', 'Tsubomi'), (7, True, 'Yoshi', 'Yaruzo'), (8, False, 'Kawai', 'Nuko'), (9, True, 'Hoshi', 'Kintaro'), (10, False, 'Shirayuki', 'Himeko'), (11, False, 'Ashigaka', 'Yui'), (12, False, 'Ageyanagi', 'Masako'), (13, True, 'Torino', 'Kenta'), (14, True, 'Kubota', 'Mochio'), (15, False, 'Kuroi', 'Sora'), (16, True, 'Hirai', 'Shin'), (17, False, 'Akai', 'Midori'), (18, False, 'Nakano', 'Anko'), (19, True, 'Imai', 'Takeo'), (20, True, 'Kouno', 'Torio'), (21, True, 'Yoshino', 'Yasu'), (22, True, 'Komatsu', 'Taro'), (23, True, 'Kondo', 'Musashi'), (24, True, 'Ono', 'Ken'), (25, True, 'Mochida', 'Usuichi'), (26, False, 'Mochida', 'Kineko'), (27, False, 'Honma', 'Kayo'), (28, True, 'Matsuno', 'Sarunosuke'), (29, False, 'Nishi', 'Minami'), (30, False, 'Usui', 'Hikaru'), (31, True, 'Sato', 'Toshio'), (32, True, 'Doi', 'Tsubasa'), (33, False, 'Ishimaru', 'Denko'), (34, False, 'Usami', 'Mimi'), (35, True, 'Hattori', 'Shinobu'), (36, False, 'Kago', 'Yuri'), (37, True, 'Takeda', 'Ingen'), (38, True, 'Kai', 'Dankichi'), (39, True, 'Okusa', 'Ben'), (40, True, 'Hara', 'Tatsuo'), (41, False, 'Mizuno', 'Shizuku'), (42, False, 'Baba', 'Nana'), ] def RGB(red, green, blue): """ com.sun.star.util.Color(内部的にはlong)型の色を RGB各成分を0(0x0)-255(0xff)の範囲で受け付けて指定する """ if red > 0xff or red < 0x00 or \ green > 0xff or green < 0x00 or \ blue > 0xff or blue < 0x00: raise InvalidColorException(red, green, blue) return red * 0x010000 + green * 0x000100 + blue * 0x000001 # 以下マクロ本体の関数 def m01_set_sampledata(): "サンプルデータを入力" try: calc = OOoCalc() except NotOOoCalcException: return else: sheet = calc.active_sheet # ヘッダを入力 for (i, item) in enumerate(Data.header): cell = sheet.getCellByPosition(i, 0) cell.Formula = item # 単なる文字列なのでStringでもよい # セルの左右方向の中央寄せ # http://api.openoffice.org/docs/common/ref/com/sun/star/table/CellProperties.html # http://api.openoffice.org/docs/common/ref/com/sun/star/table/CellHoriJustify.html cell.HoriJustify = Bridge.com_sun_star_table_CellHoriJustify_CENTER # 隣のヘッダ項目と色を変える if i % 2: cell.CellBackColor = 0xffffee else: cell.CellBackColor = 0xffeeff for (i, item) in enumerate(Data.students): y = i + 1 (num, gender, family, given) = item # 点数はランダムで生成することにする (lang, math, sci, soc) = (random.randint(0, 100), random.randint(0, 100), random.randint(0, 100), random.randint(0, 100)) # それぞれのデータを入力 sheet.getCellByPosition(0, y).Formula = num if gender: sheet.getCellByPosition(1, y).Formula = '♂' else: sheet.getCellByPosition(1, y).Formula = '♀' sheet.getCellByPosition(2, y).Formula = family sheet.getCellByPosition(3, y).Formula = given sheet.getCellByPosition(4, y).Formula = lang sheet.getCellByPosition(5, y).Formula = math sheet.getCellByPosition(6, y).Formula = sci sheet.getCellByPosition(7, y).Formula = soc # ガイドのシマシマを付ける # getCellRangeByPosition()はセル範囲を得る・引数は # 1:新しい選択範囲の左上セルの(外範囲から見た)X(列/右)方向index # 2:新しい選択範囲の左上セルの(外範囲から見た)Y(行/下)方向index # 3:新しい選択範囲の右下セルの(外範囲から見た)X(列/右)方向index # 4:新しい選択範囲の右下セルの(外範囲から見た)Y(行/下)方向index # となる(indexは0から始まる整数) if y % 2: # 背景色なし sheet.getCellRangeByPosition(0, y, 9, y).CellBackColor = -1 else: # 背景色を「#eaebec」にする sheet.getCellRangeByPosition(0, y, 9, y).CellBackColor = 0xeaebec def m02_calculate(): "データの計算を行う式を設定する\nこの後でデータが変更されるとすぐに反映される" try: calc = OOoCalc() except NotOOoCalcException: return sheet = calc.active_sheet sheetname = sheet.Name for (i, item) in enumerate(Data.students): y = i + 1 y2 = y + 1 # セル名の数字部分を項目の番号から決めている sheet.getCellByPosition(8, y).Formula = '=SUM(E%d:H%d)' % (y2, y2) sheet.getCellByPosition(9, y).Formula = '=AVERAGE(E%d:H%d)' % (y2, y2) def m03_calculate_2(): "データの計算を行う式を設定する\nこの後でデータが変更されるとすぐに反映される" try: calc = OOoCalc() except NotOOoCalcException: return sheet = calc.active_sheet sheetname = sheet.Name for (i, item) in enumerate(Data.students): y = i + 1 y2 = y + 1 # セル名でなく座標を用いて表計算の関数を呼ぶには # INDIRECT()関数とADDRESS()関数を組み合わせて引数に渡す # http://wiki.services.openoffice.org/wiki/Documentation/OOo3_User_Guides/Calc_Guide/Address,_Indirect,_Offset,_Index # ADDRESS()の3番目の引数は「4」にする # 詳しくはヘルプの「Calc - 表計算ドキュメント - 表計算ドキュメントの関数」 # ADDRESS()の引数は1からなので指定するときにはズラす必要がある(y2とした) sheet.getCellByPosition(8, y).Formula = '=SUM(INDIRECT(ADDRESS(%d;5;4;"%s")):INDIRECT(ADDRESS(%d;8;4;"%s")))' % (y2, sheetname, y2, sheetname) sheet.getCellByPosition(9, y).Formula = '=AVERAGE(INDIRECT(ADDRESS(%d;5;4;"%s")):INDIRECT(ADDRESS(%d;8;4;"%s")))' % (y2, sheetname, y2, sheetname) def m04_check_failed(): "赤点(30点未満とする)をチェック\n" try: calc = OOoCalc() except NotOOoCalcException: return sheet = calc.active_sheet for (i, item) in enumerate(Data.students): y = i + 1 # 出席番号-Givenまでの背景色を初期化 # 強調しない状態ではガイドのシマシマを付ける if i % 2: sheet.getCellRangeByPosition(0, y, 3, y).CellBackColor = 0xeaebec else: sheet.getCellRangeByPosition(0, y, 3, y).CellBackColor = -1 # 科目ごとに赤点をチェック for j in range(4): # 科目の強調表示を初期化 sheet.getCellByPosition(4 + j, y).CharColor = -1 sheet.getCellByPosition(4 + j, y).CharWeight = Bridge.com_sun_star_awt_FontWeight_NORMAL # NORMALの値は100(%) # 「4 + j」としているのは国語のマスの右に算数,理科,社会が並ぶため # 国語のマスのX座標を基準にしている if sheet.getCellByPosition(4 + j, y).Value < 30: # 赤点の科目を赤くして強調表示 # http://api.openoffice.org/docs/common/ref/com/sun/star/style/CharacterProperties.html # ユーザ定義関数RGB()を用いた例 # 下に続く2つのコメント行は同じ色指定を別の形で書いたもの sheet.getCellByPosition(4 + j, y).CharColor = RGB(204, 0, 0) # sheet.getCellByPosition(4 + j, y).CharColor = RGB(0xcc, 0x00, 0x00) # sheet.getCellByPosition(4 + j, y).CharColor = 0xcc0000 # 更に太字にする sheet.getCellByPosition(4 + j, y).CharWeight = Bridge.com_sun_star_awt_FontWeight_BOLD # BOLDの値は150(%) # 赤点者の出席番号-Givenまでの背景色を変更する場合は # 下の行のコメントを外す # sheet.getCellRangeByPosition(0, y, 3, y).CellBackColor = 0xffcccc def m05_invalid_rgb(): "ユーザ定義関数RGB()に不正な値を指定して例外が発生することを確認するテスト\n" try: calc = OOoCalc() except NotOOoCalcException: return # 256という不正な値を入れているため例外が発生して実行が止まる calc.active_sheet.getCellByPosition(0, 0).CellBackColor = RGB(64, 128, 256) # このタプルに名前を書いた関数のみマクロ選択ダイアログから実行できる # g_exportedScriptsを記述しない場合は全て実行可 g_exportedScripts = (m01_set_sampledata, m02_calculate, m03_calculate_2, m04_check_failed, m05_invalid_rgb)
以下は実行内容となる。処理が重いマクロもあるので、CPUの処理能力によってはしばらく固まることがあるかもしれない。
- 1番目のマクロではサンプルデータを現在のシートに入力する
- 2番目のマクロではSUM()関数とAVERAGE()関数を行ごとに入れていき、合計と平均の列を埋める
- 3番目のマクロでは2番目と同様の処理をINDIRECT()関数とADDRESS()関数を用いて行う(シート名依存になるので注意)
- 4番目のマクロでは各生徒の科目ごとの点数をチェックし、30点未満のものがある場合にそれを赤い太字で強調する
- 5番目のマクロでは「OpenOffice.orgのマクロをPythonで記述して動かす(Calcのセル内容へのアクセスに関する追加メモ・ページ2/3)」で扱ったユーザ定義関数RGB()にわざと不正な値を代入して例外が起こることを確認できる
参考URL:
使用したバージョン:
- OpenOffice.org(Go-oo) 3.1.1
- Python 2.6.4