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

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

OpenOffice.orgのマクロを Pythonで記述して動かす(Calcでスケジュールカレンダーを生成)

Pythonのカレンダーモジュールを用いる」で扱ったカレンダーのモジュールを用いたOpenOffice.org Calc向けのマクロを作成した。これを実行すると既存のシートの後ろに「2011年1月」から「2011年12月」までの12のシートが自動的に作成され、その中に「日付」と「予定」が日付順に縦方向に並び(日付については月ごとに全ての日付/曜日が自動的に入力される)、土日のみ色も付く。ただし、日本の祝日は強調されない。

予定の欄は全て空なので自由に書き込める(予定の内容は例として書き込んだもの)

最後の大晦日まで、年と月の値が入った12のシートが生成される
下のスクリプトは文字列オブジェクトにformat()を用いている関係でPython 2.6以上を対象としている。マクロは1つだけなので、それを実行する。
[任意]ファイル名: [OOoユーザディレクトリ]/user/Scripts/python/calc_gen_schedule_calendar.py ライセンス: GPL-3 (or lator)

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

# OpenOffice.org Calcでスケジュールカレンダーを生成
# version 20101220
# (C) 2010 kakurasan
# Licensed under GPLv3+

import unohelper
import calendar
import uno

class Config:
  """
  設定項目
  """
  year = 2011

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

class InvalidConfigException (Exception):
  """
  設定項目の値が不正なときに発生する例外
  """
  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')
  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 ()
    # 設定値のチェック
    try:
      Config.year = int (Config.year)
      if Config.year < 1 or Config.year > 9999:
        self.run_errordialog (title='エラー', message='年({0})が無効な範囲の値です'.format (Config.year))
        raise InvalidConfigException ()
    except ValueError:
      self.run_errordialog (title='エラー', message='年({0})が無効です'.format (Config.year))
      raise InvalidConfigException ()
  # Calcの全シート
  # プロパティSheetsかメンバ関数getSheets()
  # http://api.openoffice.org/docs/common/ref/com/sun/star/sheet/XSpreadsheetDocument.html
  @property
  def sheets (self): return self._document.Sheets


def m01_gen_schedule_calendar ():
  "OOo Calcで新規に12のシートから成るスケジュール記入型カレンダーを生成"
  try:
    calc = OOoCalc ()
  except:
    return
  sheets = calc.sheets
  sheets_count_before = sheets.Count
  for month in range (12):
    # 新規シートを作成
    sheetname = '{0}年{1}月'.format (Config.year, month + 1)
    # シートの存在をチェックする
    # 指定された名前のシートが既に存在していればTrue
    # http://api.openoffice.org/docs/common/ref/com/sun/star/container/XNameAccess.html
    if sheets.hasByName (sheetname):
      calc.run_errordialog (title='エラー', message='既にシート "{0}" が存在します'.format (sheetname))
      return
    else:
      # 1番目の引数は名前
      # 2番目の引数は追加位置(0は一番左に追加する)
      # http://api.openoffice.org/docs/common/ref/com/sun/star/sheet/XSpreadsheets.html
      try:
        sheets.insertNewByName (sheetname, sheets_count_before + month)
      except unohelper.RuntimeException:
        # 上限シート数を超えると例外unohelper.RuntimeExceptionが発生
        calc.run_errordialog (title='エラー', message='シート数が上限に達したため\n新しいシートを追加できませんでした')
        return
      # ここから追加したシートに日付を追加していく
      sheet = sheets.getByIndex (sheets_count_before + month)
      # ヘッダ
      sheet.getCellByPosition (0, 0).Formula = '日付'
      sheet.getCellByPosition (1, 0).Formula = '予定'
      sheet.getCellRangeByPosition (0, 0, 1, 0).CellBackColor = 0xeaebec
      sheet.getCellRangeByPosition (0, 0, 1, 0).HoriJustify = Bridge.com_sun_star_table_CellHoriJustify_CENTER
      # 日付
      wday = ['月', '火', '水', '木', '金', '土', '日']
      day = 0
      c = calendar.Calendar ()  # 既定では月曜日開始(月=0 日=7)
      for (i, d) in enumerate (c.itermonthdates (Config.year, month + 1)):
        if d.month == month + 1:
          cell = sheet.getCellByPosition (0, 1 + day)  # 1足すのはヘッダ行の分
          day += 1
          cell.Formula = '{0}/{1} ({2})'.format (d.month, d.day, wday[i % 7])
          if i % 7 == 5:    # 土曜
            cell.CellBackColor = 0xeeeeff
            cell.CharColor = 0x000066
          elif i % 7 == 6:  # 日曜
            cell.CellBackColor = 0xffeeee
            cell.CharColor = 0x660000


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

(2010/12/20)OOoCalcオブジェクト生成時の例外の処理を微調整
年は初期値が2011年となっているが

class Config:
  year = 2011

の部分の値を変更することで他の年でも動作する。

関連:日付の自動生成を行う別の方法

適当なセルに「2011/1/1」のような書式の文字列(カレンダー上に存在しない日付は除く)を入力すると「1月1日」のような表示(セルの書式設定で変更可)になり、選択されているセルの右下の四角の部分を始点として下もしくは右にドラッグ・アンド・ドロップすると、数値と同様に日付の連番が作れる(書式は元のセルと同じものが作られる)。また、向きを逆にして左か上にすることでさかのぼった連番も作れる。

使用したバージョン: