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

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

OpenOffice.orgのマクロを Pythonで記述して動かす(Calcで整形されたカレンダーを生成)

OpenOffice.orgのマクロを Pythonで記述して動かす(Calcでスケジュールカレンダーを生成)」とは異なり、今度は曜日が横に並んだ形のカレンダーを生成するOpenOffice.org Calc向けのマクロを作成した。
実行すると既存のシートの後ろに「2011年1月」から「2011年12月」までの12のシートが自動的に作成され、その中にカレンダーが書き込まれて前後の月の日付も薄い色で表示される。
今回も土日の日付に色が付いているが、日本の祝日は強調されない。また、セルは日付で埋まっており、予定などを書き込む場合は日付のセルの内容を上書きすることになる。

日曜日開始版

月曜日開始版
下のスクリプトは文字列オブジェクトにformat()を用いている関係でPython 2.6以上を対象としている。
[任意]ファイル名: [OOoユーザディレクトリ]/user/Scripts/python/calc_gen_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')
  # 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_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_calendar_from_sunday ():
  "OOo Calcで新規に12のシートから成るカレンダーを生成\n日曜日開始版"
  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)
      # ヘッダ(曜日)
      for (i, name) in enumerate (['日', '月', '火', '水', '木', '金', '土']):
        cell = sheet.getCellByPosition (i, 0)
        cell.Formula = name
        cell.HoriJustify = Bridge.com_sun_star_table_CellHoriJustify_CENTER
        cell.CharWeight = Bridge.com_sun_star_awt_FontWeight_BOLD
        if i == 0:    # 日曜
          cell.CellBackColor = 0xffeeee
          cell.CharColor = 0x660000
        elif i == 6:  # 土曜
          cell.CellBackColor = 0xeeeeff
          cell.CharColor = 0x000066
        else:         # 土日以外
          if i % 2:   # シマシマにする
            cell.CellBackColor = 0xf3f4f4
          else:
            cell.CellBackColor = 0xe6e7e7
      # 日付の追加
      line = 1  # 行「2」から下
      # 「6」を指定すると日曜開始のカレンダーとなる
      c = calendar.Calendar (6)
      # itermonthdates()が返すジェネレータを用いてループすると
      # その月のカレンダー上の開始曜日に合わせた前の月の日から
      # 終了曜日に合わせた後の月の日までが順番に返される
      # monthは0から11
      for (i, d) in enumerate (c.itermonthdates (Config.year, month + 1)):
        cell = sheet.getCellByPosition (i % 7, line)
        if d.month == month + 1:
          # この月
          cell.Formula = d.day
          if i % 7 == 0:    # 日曜
            cell.CharColor = 0x660000
          elif i % 7 == 6:  # 土曜
            cell.CharColor = 0x000066
        else:
          # 前後の月
          # cell.Formulaとすると割り算の式になるのでcell.Stringにする
          cell.String = '{0}/{1}'.format (d.month, d.day)
          if i % 7 == 0:
            cell.CharColor = 0xccbbbb
          elif i % 7 == 6:
            cell.CharColor = 0xbbbbcc
          else:
            cell.CharColor = 0xc0c1c1
        # いずれのセルも中央寄せにする
        cell.HoriJustify = Bridge.com_sun_star_table_CellHoriJustify_CENTER
        # 曜日が一周したら次の行へ
        if i % 7 == 6:
          line += 1

def m02_gen_calendar_from_monday ():
  "OOo Calcで新規に12のシートから成るカレンダーを生成\n月曜日開始版"
  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)
    if sheets.hasByName (sheetname):
      calc.run_errordialog (title='エラー', message='既にシート "{0}" が存在します'.format (sheetname))
      return
    else:
      try:
        sheets.insertNewByName (sheetname, sheets_count_before + month)
      except unohelper.RuntimeException:
        calc.run_errordialog (title='エラー', message='シート数が上限に達したため\n新しいシートを追加できませんでした')
        return
      sheet = sheets.getByIndex (sheets_count_before + month)
      for (i, name) in enumerate (['月', '火', '水', '木', '金', '土', '日']):
        cell = sheet.getCellByPosition (i, 0)
        cell.Formula = name
        cell.HoriJustify = Bridge.com_sun_star_table_CellHoriJustify_CENTER
        cell.CharWeight = Bridge.com_sun_star_awt_FontWeight_BOLD
        if i == 6:    # 日曜
          cell.CellBackColor = 0xffeeee
          cell.CharColor = 0x660000
        elif i == 5:  # 土曜
          cell.CellBackColor = 0xeeeeff
          cell.CharColor = 0x000066
        else:         # 土日以外
          if i % 2:
            cell.CellBackColor = 0xf3f4f4
          else:
            cell.CellBackColor = 0xe6e7e7
      line = 1
      c = calendar.Calendar ()  # 既定値は0(月曜日開始)
      for (i, d) in enumerate (c.itermonthdates (Config.year, month + 1)):
        cell = sheet.getCellByPosition (i % 7, line)
        if d.month == month + 1:
          cell.Formula = d.day
          if i % 7 == 6:    # 日曜
            cell.CharColor = 0x660000
          elif i % 7 == 5:  # 土曜
            cell.CharColor = 0x000066
        else:
          cell.String = '{0}/{1}'.format (d.month, d.day)
          if i % 7 == 6:
            cell.CharColor = 0xccbbbb
          elif i % 7 == 5:
            cell.CharColor = 0xbbbbcc
          else:
            cell.CharColor = 0xc0c1c1
        cell.HoriJustify = Bridge.com_sun_star_table_CellHoriJustify_CENTER
        if i % 7 == 6:
          line += 1


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

使用したバージョン: