(2018.1.3追記。Calc(52)追加できるリスナー一覧: その4で作り直しました。)
前の関連記事:Calc(51)追加できるリスナー一覧: その3
リスナーのメソッドが発火したらログファイルを出力するマクロ
このマクロを起動したらその後リスナーのメソッドが発火するたびにこのマクロファイルがあるフォルダにログファイルを出力します。
埋め込みマクロをにしているときは埋め込んだドキュメントのフォルダに出力します。
20171230T113317_88doc_documenteventlistener_documentEventOccured_OnTitleChanged.log
マクロファイルの起動年月日T時分秒_発火順リスナーが追加されているオブジェクト_リスナー名_発火メソッド名_イベント名など.log
ログファイル名はこのようになっています。
発火順は桁揃えが面倒なので10番から始まるようにしています。
最後の要素の「イベント名など」はenum名やイベント名などリスナーの種類によってあったりなかったりします。
ログファイル内に書き出している内容はリスナーによってそれぞれです。
#!/opt/libreoffice5.4/program/python # -*- coding: utf-8 -*- import unohelper # オートメーションには必須(必須なのはuno)。 import os import inspect from datetime import datetime from com.sun.star.awt import XEnhancedMouseClickHandler from com.sun.star.awt import XKeyHandler from com.sun.star.view import XSelectionChangeListener from com.sun.star.view import XPrintJobListener from com.sun.star.view.PrintableState import JOB_STARTED, JOB_COMPLETED, JOB_SPOOLED, JOB_ABORTED, JOB_FAILED, JOB_SPOOLING_FAILED # enum from com.sun.star.util import XCloseListener from com.sun.star.util import XModifyListener from com.sun.star.util import XChangesListener from com.sun.star.frame import XTerminateListener from com.sun.star.frame import XFrameActionListener from com.sun.star.frame import XTitleChangeListener from com.sun.star.frame import XBorderResizeListener from com.sun.star.frame.FrameAction import COMPONENT_ATTACHED, COMPONENT_DETACHING, COMPONENT_REATTACHED, FRAME_ACTIVATED, FRAME_DEACTIVATING, CONTEXT_CHANGED, FRAME_UI_ACTIVATED, FRAME_UI_DEACTIVATING # enum from com.sun.star.document import XDocumentEventListener from com.sun.star.document import XEventListener from com.sun.star.document import XStorageChangeListener from com.sun.star.sheet import XActivationEventListener from com.sun.star.chart import XChartDataChangeEventListener from com.sun.star.chart.ChartDataChangeType import ALL, DATA_RANGE, COLUMN_INSERTED, ROW_INSERTED, COLUMN_DELETED, ROW_DELETED # enum def macro(documentevent=None): # 引数は文書のイベント駆動用。OnStartAppでもDocumentEventが入るがSourceはNoneになる。# import pydevd; pydevd.settrace(stdoutToServer=True, stderrToServer=True) # デバッグサーバーを起動していた場合はここでブレークされる。import pydevdは時間がかかる。 doc = XSCRIPTCONTEXT.getDocument() if documentevent is None else documentevent.Source # ドキュメントのモデルを取得。 path = doc.getURL() if __file__.startswith("vnd.sun.star.tdoc:") else __file__ # このスクリプトのパス。fileurlで返ってくる。埋め込みマクロの時は埋め込んだドキュメントのURLで代用する。 thisscriptpath = unohelper.fileUrlToSystemPath(path) # fileurlをsystempathに変換。 dirpath = os.path.dirname(thisscriptpath) # このスクリプトのあるディレクトリのフルパスを取得。 desktop = XSCRIPTCONTEXT.getDesktop() # デスクトップの取得。 desktop.addTerminateListener(TerminateListener(dirpath, "desktop_terminatelistener")) # TerminateListenerは使い終わったらremoveしないと問題が起こる。 desktop.addFrameActionListener(FrameActionListener(dirpath, "desktop_frameactionlistener")) # FrameActionListener controller = doc.getCurrentController() # コントローラーの取得。 frame = controller.getFrame() # フレームの取得。 frame.addFrameActionListener(FrameActionListener(dirpath, "frame_frameactionlistener")) # FrameActionListener frame.addCloseListener(CloseListener(dirpath, "frame_closelistener")) # CloseListener frame.addTitleChangeListener(TitleChangeListener(dirpath, "frame_titlechangelistener")) # TitleChangeListener controller.addActivationEventListener(ActivationEventListener(dirpath, "controller_activationeventlistener")) # ActivationEventListener controller.addEnhancedMouseClickHandler(EnhancedMouseClickHandler(dirpath, "controller_enhancedmouseclickhandler")) # EnhancedMouseClickHandler controller.addSelectionChangeListener(SelectionChangeListener(dirpath, "controller_selectionchangelistener")) # SelectionChangeListener controller.addBorderResizeListener(BorderResizeListener(dirpath, "controller_borderresizelistener")) # BorderResizeListener controller.addTitleChangeListener(TitleChangeListener(dirpath, "controller_titlechangelistener")) # TitleChangeListener controller.addKeyHandler(KeyHandler(dirpath, "controller_keyhandler")) # KeyHandler doc.addDocumentEventListener(DocumentEventListener(dirpath, "doc_documenteventlistener")) # DocumentEventListener doc.addEventListener(EventListener(dirpath, "doc_eventlistener")) # EventListener doc.addModifyListener(ModifyListener(dirpath, "doc_modifylistener")) # ModifyListener doc.addPrintJobListener(PrintJobListener(dirpath, "doc_printjoblistener")) # PrintJobListener doc.addStorageChangeListener(StorageChangeListener(dirpath, "doc_storagechangelistener")) # StorageChangeListener doc.addTitleChangeListener(TitleChangeListener(dirpath, "doc_titlechangelistener")) # TitleChangeListener doc.addChangesListener(ChangesListener(dirpath, "doc_changelistener")) # ChangesListener sheet = controller.getActiveSheet() # アクティブシートを取得。 sheet.addChartDataChangeEventListener(ChartDataChangeEventListener(dirpath, "sheet_chartdatachangeeventlistener")) # ChartDataChangeEventListener sheet.addModifyListener(ModifyListener(dirpath, "sheet_modifylistener")) # ModifyListener cell = sheet["A1"] # セルの取得。 cell.addChartDataChangeEventListener(ChartDataChangeEventListener(dirpath, "cell_chartdatachangeeventlistener")) # ChartDataChangeEventListener cell.addModifyListener(ModifyListener(dirpath, "cell_modifylistener")) # ModifyListener cells = sheet["A2:C4"] # セル範囲の取得。 cells.addChartDataChangeEventListener(ChartDataChangeEventListener(dirpath, "cells_chartdatachangeeventlistener")) # ChartDataChangeEventListener cells.addModifyListener(ModifyListener(dirpath, "cells_modifylistener")) # ModifyListener class ChartDataChangeEventListener(unohelper.Base, XChartDataChangeEventListener): def __init__(self, dirpath, name): self.args = dirpath, name enums = ALL, DATA_RANGE, COLUMN_INSERTED, ROW_INSERTED, COLUMN_DELETED, ROW_DELETED # enum chartdatachangetypenames = "ALL", "DATA_RANGE", "COLUMN_INSERTED", "ROW_INSERTED", "COLUMN_DELETED", "ROW_DELETED" self.args = dirpath, name, zip(enums, chartdatachangetypenames) def chartDataChanged(self, chartdatachangeevent): dirpath, name, chartdatachangetypes = self.args chartdatachangetype = chartdatachangeevent.Type methodname = "_".join((name, inspect.currentframe().f_code.co_name)) for enum, chartdatachangetypename in chartdatachangetypes: if chartdatachangetype==enum: methodname = "_".join((name, inspect.currentframe().f_code.co_name, chartdatachangetypename)) # ChartDataChangeType名を追加。 createLog(dirpath, methodname, "ChartDataChangeType: {}".format(chartdatachangetypename)) return def disposing(self, eventobject): pass class ChangesListener(unohelper.Base, XChangesListener): def __init__(self, dirpath, name): self.args = dirpath, name def changesOccurred(self, changesevent): dirpath, name = self.args methodname = "_".join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, methodname, "Changes: {}".format(changesevent.Changes)) def disposing(self, eventobject): pass class StorageChangeListener(unohelper.Base, XStorageChangeListener): def __init__(self, dirpath, name): self.args = dirpath, name def notifyStorageChange(self, document, storage): dirpath, name = self.args methodname = "_".join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, methodname, "Storage: {}".format(storage)) def disposing(self, eventobject): pass class PrintJobListener(unohelper.Base, XPrintJobListener): def __init__(self, dirpath, name): self.args = dirpath, name enums = JOB_STARTED, JOB_COMPLETED, JOB_SPOOLED, JOB_ABORTED, JOB_FAILED, JOB_SPOOLING_FAILED # enum printablestatenames = "JOB_STARTED", "JOB_COMPLETED", "JOB_SPOOLED", "JOB_ABORTED", "JOB_FAILED", "JOB_SPOOLING_FAILED" self.args = dirpath, name, zip(enums, printablestatenames) def printJobEvent(self, printjobevent): dirpath, name, printablestates = self.args printablestate = printjobevent.State methodname = "_".join((name, inspect.currentframe().f_code.co_name)) for enum, printablestatename in printablestates: if printablestate==enum: methodname = "_".join((name, inspect.currentframe().f_code.co_name, printablestatename)) # State名も追加。 createLog(dirpath, methodname, "PrintableState: {}, Source: {}".format(printablestatename, printjobevent.Source)) return def disposing(self, eventobject): pass class ModifyListener(unohelper.Base, XModifyListener): def __init__(self, dirpath, name): self.args = dirpath, name def modified(self, eventobject): dirpath, name = self.args methodname = "_".join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, methodname, "Source: {}".format(eventobject.Source)) def disposing(self, eventobject): pass class EventListener(unohelper.Base, XEventListener): def __init__(self, dirpath, name): self.args = dirpath, name def notifyEvent(self, eventobject): dirpath, name = self.args methodname = "_".join((name, inspect.currentframe().f_code.co_name, eventobject.EventName)) # イベント名も追加。 createLog(dirpath, methodname, "EventName: {}, Source: {}".format(eventobject.EventName, eventobject.Source)) def disposing(self, eventobject): pass class DocumentEventListener(unohelper.Base, XDocumentEventListener): def __init__(self, dirpath, name): self.args = dirpath, name def documentEventOccured(self, documentevent): dirpath, name = self.args methodname = "_".join((name, inspect.currentframe().f_code.co_name, documentevent.EventName)) # イベント名も追加。 createLog(dirpath, methodname, "EventName: {}, Source: {}".format(documentevent.EventName, documentevent.Source)) def disposing(self, eventobject): pass class KeyHandler(unohelper.Base, XKeyHandler): def __init__(self, dirpath, name): self.args = dirpath, name def keyPressed(self, keyevent): dirpath, name = self.args methodname = "_".join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, methodname, "KeyCode: {}, KeyChar: {}, KeyFunc: {}, Modifiers: {}".format(keyevent.KeyCode, keyevent.KeyChar, keyevent.KeyFunc, keyevent.Modifiers)) return False def keyReleased(self, keyevent): dirpath, name = self.args methodname = "_".join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, methodname, "KeyCode: {}, KeyChar: {}, KeyFunc: {}, Modifiers: {}".format(keyevent.KeyCode, keyevent.KeyChar, keyevent.KeyFunc, keyevent.Modifiers)) return False def disposing(self, eventobject): pass class BorderResizeListener(unohelper.Base, XBorderResizeListener): def __init__(self, dirpath, name): self.args = dirpath, name def borderWidthsChanged(self, obj, borderwidths): dirpath, name = self.args methodname = "_".join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, methodname, "Top: {}, Left: {}, Right: {}, Bottom: {}, Object: {}".format(borderwidths.Top, borderwidths.Left, borderwidths.Right, borderwidths.Bottom, obj)) def disposing(self, eventobject): pass class SelectionChangeListener(unohelper.Base, XSelectionChangeListener): def __init__(self, dirpath, name): self.args = dirpath, name def selectionChanged(self, eventobject): dirpath, name = self.args methodname = "_".join((name, inspect.currentframe().f_code.co_name)) source = eventobject.Source txt = "Selection: {}".format(source.getSelection()) if source.supportsService("com.sun.star.sheet.SpreadsheetView") else "Source: {}".format(source) # sourceがコントローラーのときは選択しているオブジェクトを書き出す。 createLog(dirpath, methodname, txt) def disposing(self, eventobject): pass class EnhancedMouseClickHandler(unohelper.Base, XEnhancedMouseClickHandler): def __init__(self, dirpath, name): self.args = dirpath, name def mousePressed(self, enhancedmouseevent): dirpath, name = self.args methodname = "_".join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, methodname, "Buttons: {}, ClickCount: {}, PopupTrigger {}, Modifiers: {}, Target: {}".format(enhancedmouseevent.Buttons, enhancedmouseevent.ClickCount, enhancedmouseevent.PopupTrigger, enhancedmouseevent.Modifiers, enhancedmouseevent.Target)) return True def mouseReleased(self, enhancedmouseevent): dirpath, name = self.args methodname = "_".join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, methodname, "Buttons: {}, ClickCount: {}, PopupTrigger {}, Modifiers: {}, Target: {}".format(enhancedmouseevent.Buttons, enhancedmouseevent.ClickCount, enhancedmouseevent.PopupTrigger, enhancedmouseevent.Modifiers, enhancedmouseevent.Target)) return True def disposing(self, eventobject): pass class ActivationEventListener(unohelper.Base, XActivationEventListener): def __init__(self, dirpath, name): self.args = dirpath, name def activeSpreadsheetChanged(self, activationevent): dirpath, name = self.args methodname = "_".join((name, inspect.currentframe().f_code.co_name)) source = activationevent.Source txt = "Selection: {}".format(source.getSelection()) if source.supportsService("com.sun.star.sheet.SpreadsheetView") else "Source: {}".format(source) # sourceがコントローラーのときは選択しているオブジェクトを書き出す。 createLog(dirpath, methodname, txt) def disposing(self, eventobject): pass class TitleChangeListener(unohelper.Base, XTitleChangeListener): def __init__(self, dirpath, name): self.args = dirpath, name def titleChanged(self, titlechangedevent): dirpath, name = self.args methodname = "_".join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, methodname, "Title: {}, Source: {}".format(titlechangedevent.Title, titlechangedevent.Source)) def disposing(self, eventobject): pass class CloseListener(unohelper.Base, XCloseListener): def __init__(self, dirpath, name): self.args = dirpath, name def queryClosing(self, eventobject, getsownership): dirpath, name = self.args methodname = "_".join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, methodname, "getsownership: {}, Source: {}".format(getsownership, eventobject.Source)) def notifyClosing(self, eventobject): dirpath, name = self.args methodname = "_".join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, methodname, "Source: {}".format(eventobject.Source)) def disposing(self, eventobject): pass class FrameActionListener(unohelper.Base, XFrameActionListener): def __init__(self, dirpath, name): enums = COMPONENT_ATTACHED, COMPONENT_DETACHING, COMPONENT_REATTACHED, FRAME_ACTIVATED, FRAME_DEACTIVATING, CONTEXT_CHANGED, FRAME_UI_ACTIVATED, FRAME_UI_DEACTIVATING # enum frameactionnames = "COMPONENT_ATTACHED", "COMPONENT_DETACHING", "COMPONENT_REATTACHED", "FRAME_ACTIVATED", "FRAME_DEACTIVATING", "CONTEXT_CHANGED", "FRAME_UI_ACTIVATED", "FRAME_UI_DEACTIVATING" self.args = dirpath, name, zip(enums, frameactionnames) def frameAction(self, frameactionevent): dirpath, name, frameactions = self.args frameaction = frameactionevent.Action for enum, frameactionname in frameactions: if frameaction==enum: methodname = "_".join((name, inspect.currentframe().f_code.co_name, frameactionname)) # Action名も追加。 createLog(dirpath, methodname, "FrameAction: {}, Source: {}".format(frameactionname, frameactionevent.Source)) return def disposing(self, eventobject): pass class TerminateListener(unohelper.Base, XTerminateListener): # TerminateListener def __init__(self, dirpath, name): # 出力先ディレクトリのパス、リスナーのインスタンス名。 self.args = dirpath, name def queryTermination(self, eventobject): dirpath, name = self.args methodname = "_".join((name, inspect.currentframe().f_code.co_name)) # このメソッド名を取得。メソッド内で実行する必要がある。 createLog(dirpath, methodname, "Source: {}".format(eventobject.Source)) # Sourceを出力。 def notifyTermination(self, eventobject): dirpath, name = self.args methodname = "_".join((name, inspect.currentframe().f_code.co_name)) # このメソッド名を取得。メソッド内で実行する必要がある。 createLog(dirpath, methodname, "Source: {}".format(eventobject.Source)) # Sourceを出力。 desktop = eventobject.Source desktop.removeTerminateListener(self) # TerminateListenerを除去。除去しなmethodname = inspect.currentframe().f_code.co_nameいとLibreOfficeのプロセスが残って起動できなくなる。 def disposing(self, eventobject): pass C = 10 # カウンターの初期値。 TIMESTAMP = datetime.now().isoformat().split(".")[0].replace("-", "").replace(":", "") # コピー先ファイル名に使う年月日T時分秒を結合した文字列を取得。 def createLog(dirpath, methodname, txt): # 年月日T時分秒リスナーのインスタンス名_methodname.logファイルを作成。txtはファイルに書き込むテキスト。dirpathはファイルを書き出すディレクトリ。 global C filename = "".join((TIMESTAMP, "_", str(C), methodname, ".log")) C += 1 with open(os.path.join(dirpath, filename), "w") as f: f.write(txt) g_exportedScripts = macro, #マクロセレクターに限定表示させる関数をタプルで指定。EnhancedMouseClickHandlerを使っているので、コントローラーに追加できるリスナーのうちXMouseClickHandlerは使っていません。
ListenersForCalc.ods
このマクロを埋め込んだCalcドキュメントです。
「文書を開いた時」に埋め込みマクロを起動するようにしています。
マクロを有効化してこのドキュメントを起動してドキュメントの操作をすると、リスナーが発火するたびにドキュメントがあるフォルダにログファイルが出力されます。
リスナーのメソッド内にエラーがあってもエラーメッセージウィンドウはでてこないので、発火していないのかどうかは慎重に判断する必要があります。
0 件のコメント:
コメントを投稿