前の関連記事:Calc(54)追加できるリスナー一覧: その6
listenersforcalc2.pyでリスナーのメソッドの発火をファイル名に記録する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 |
#!/opt/libreoffice5.4/program/python # -*- coding: utf-8 -*- import unohelper # オートメーションには必須(必須なのはuno)。 import os import inspect import platform from datetime import datetime from com.sun.star.awt import XEnhancedMouseClickHandler from com.sun.star.awt import XKeyHandler from com.sun.star.awt import XTopWindowListener from com.sun.star.awt import Key # 定数 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 # ドキュメントのモデルを取得。 desktop = XSCRIPTCONTEXT.getDesktop() # デスクトップの取得。 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) # このスクリプトのあるディレクトリのフルパスを取得。 listeners = {} listeners[ "desktop_terminatelistener" ] = TerminateListener(dirpath, "desktop_terminatelistener" ) listeners[ "desktop_frameactionlistener" ] = FrameActionListener(dirpath, "desktop_frameactionlistener" ) desktop.addTerminateListener(listeners[ "desktop_terminatelistener" ]) # TerminateListener desktop.addFrameActionListener(listeners[ "desktop_frameactionlistener" ]) # FrameActionListener controller = doc.getCurrentController() # コントローラーの取得。 frame = controller.getFrame() # フレームの取得。 listeners[ "frame_frameactionlistener" ] = FrameActionListener(dirpath, "frame_frameactionlistener" ) listeners[ "frame_closelistener" ] = CloseListener(dirpath, "frame_closelistener" ) listeners[ "frame_titlechangelistener" ] = TitleChangeListener(dirpath, "frame_titlechangelistener" , frame) frame.addFrameActionListener(listeners[ "frame_frameactionlistener" ]) # FrameActionListener frame.addCloseListener(listeners[ "frame_closelistener" ]) # CloseListener frame.addTitleChangeListener(listeners[ "frame_titlechangelistener" ]) # TitleChangeListener containerwindow = frame.getContainerWindow() # フレームのコンテナウィンドウの取得。 listeners[ "containerwindow_topwindowlistener" ] = TopWindowListener(dirpath, "containerwindow_topwindowlistener" ) containerwindow.addTopWindowListener(listeners[ "containerwindow_topwindowlistener" ]) controller.addActivationEventListener(ActivationEventListener(dirpath, "controller_activationeventlistener" , controller)) # ActivationEventListener controller.addEnhancedMouseClickHandler(EnhancedMouseClickHandler(dirpath, "controller_enhancedmouseclickhandler" , controller)) # EnhancedMouseClickHandler controller.addSelectionChangeListener(SelectionChangeListener(dirpath, "controller_selectionchangelistener" , controller)) # SelectionChangeListener controller.addBorderResizeListener(BorderResizeListener(dirpath, "controller_borderresizelistener" , controller)) # BorderResizeListener controller.addTitleChangeListener(TitleChangeListener(dirpath, "controller_titlechangelistener" , controller)) # TitleChangeListener controller.addKeyHandler(KeyHandler(dirpath, "controller_keyhandler" , controller)) # KeyHandler doc.addDocumentEventListener(DocumentEventListener(dirpath, "doc_documenteventlistener" , doc, desktop, frame, containerwindow, listeners)) # DocumentEventListener doc.addEventListener(EventListener(dirpath, "doc_eventlistener" , doc)) # EventListener doc.addModifyListener(ModifyListener(dirpath, "doc_modifylistener" , doc)) # ModifyListener doc.addPrintJobListener(PrintJobListener(dirpath, "doc_printjoblistener" , doc)) # PrintJobListener doc.addStorageChangeListener(StorageChangeListener(dirpath, "doc_storagechangelistener" , doc)) # StorageChangeListener doc.addTitleChangeListener(TitleChangeListener(dirpath, "doc_titlechangelistener" , doc)) # TitleChangeListener doc.addChangesListener(ChangesListener(dirpath, "doc_changelistener" , doc)) # ChangesListener sheet = controller.getActiveSheet() # アクティブシートを取得。 sheet.addChartDataChangeEventListener(ChartDataChangeEventListener(dirpath, "sheet_chartdatachangeeventlistener" , sheet)) # ChartDataChangeEventListener sheet.addModifyListener(ModifyListener(dirpath, "sheet_modifylistener" , sheet)) # ModifyListener cell = sheet[ "A1" ] # セルの取得。 cell.addChartDataChangeEventListener(ChartDataChangeEventListener(dirpath, "cell_chartdatachangeeventlistener" , cell)) # ChartDataChangeEventListener cell.addModifyListener(ModifyListener(dirpath, "cell_modifylistener" , cell)) # ModifyListener cells = sheet[ "A2:C4" ] # セル範囲の取得。 cells.addChartDataChangeEventListener(ChartDataChangeEventListener(dirpath, "cells_chartdatachangeeventlistener" , cells)) # ChartDataChangeEventListener cells.addModifyListener(ModifyListener(dirpath, "cells_modifylistener" , cells)) # ModifyListener class TopWindowListener(unohelper.Base, XTopWindowListener): def __init__( self , dirpath, name): self .args = dirpath, name def windowOpened( self , eventobject): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) def windowClosing( self , eventobject): # 呼ばれない。いつ呼ばれる? dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) def windowClosed( self , eventobject): # フレームが閉じた後デスクトップが閉じる前に呼ばれる。 dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) def windowMinimized( self , eventobject): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) def windowNormalized( self , eventobject): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) def windowActivated( self , eventobject): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) def windowDeactivated( self , eventobject): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) def disposing( self , eventobject): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) class ChartDataChangeEventListener(unohelper.Base, XChartDataChangeEventListener): def __init__( self , dirpath, name, subj): self .subj = subj 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 filename = "_" .join((name, inspect.currentframe().f_code.co_name)) for enum, chartdatachangetypename in chartdatachangetypes: if chartdatachangetype = = enum: filename = "_" .join((name, inspect.currentframe().f_code.co_name, chartdatachangetypename)) # ChartDataChangeType名を追加。 createLog(dirpath, filename, "ChartDataChangeType: {}" . format (chartdatachangetypename)) return def disposing( self , eventobject): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) self .subj.removeChartDataChangeEventListener( self ) class ChangesListener(unohelper.Base, XChangesListener): def __init__( self , dirpath, name, subj): self .subj = subj self .args = dirpath, name def changesOccurred( self , changesevent): dirpath, name = self .args base = changesevent.Base if base.supportsService( "com.sun.star.sheet.SpreadsheetDocument" ): # ドキュメントの時 basetxt = "Base URL: {}" . format (__file__) # ドキュメントのURLを取得。 else : basetxt = "Base: {}" . format (base) txts = [basetxt] # ログファイルに出力する行のリスト。 changes = changesevent.Changes for change in changes: txts.append( "Accessor: {}" . format (change.Accessor)) for element in change.Element: if hasattr (element, "Name" ) and hasattr (element, "Value" ): propertyname, propertyvalue = element.Name, element.Value if "Color" in propertyname: # 色の時は16進数で出力する。 propertyvalue = hex (propertyvalue) txts.append( "{}: {}" . format (propertyname, propertyvalue)) replacedelement = getStringAddressFromCellRange(change.ReplacedElement) # 変更対象オブジェクトから文字列アドレスを取得する。 replacedelement = replacedelement or change.ReplaceElement # 文字列アドレスを取得できないオブジェクトの時はオブジェクトをそのまま文字列にする。 txts.append( "ReplacedElement: {}" . format (replacedelement)) filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "\n" .join(txts)) def disposing( self , eventobject): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) self .subj.removeChangesListener( self ) class StorageChangeListener(unohelper.Base, XStorageChangeListener): def __init__( self , dirpath, name, subj): self .subj = subj self .args = dirpath, name def notifyStorageChange( self , document, storage): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "Storage: {}" . format (storage)) def disposing( self , eventobject): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) self .subj.removeStorageChangeListener( self ) class PrintJobListener(unohelper.Base, XPrintJobListener): def __init__( self , dirpath, name, subj): self .subj = subj 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 filename = "_" .join((name, inspect.currentframe().f_code.co_name)) for enum, printablestatename in printablestates: if printablestate = = enum: filename = "_" .join((name, inspect.currentframe().f_code.co_name, printablestatename)) # State名も追加。 createLog(dirpath, filename, "PrintableState: {}, Source: {}" . format (printablestatename, printjobevent.Source)) return def disposing( self , eventobject): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) self .subj.removePrintJobListener( self ) class ModifyListener(unohelper.Base, XModifyListener): def __init__( self , dirpath, name, subj): self .subj = subj self .args = dirpath, name def modified( self , eventobject): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) def disposing( self , eventobject): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) self .subj.removeModifyListener( self ) class EventListener(unohelper.Base, XEventListener): def __init__( self , dirpath, name, subj): self .subj = subj self .args = dirpath, name def notifyEvent( self , eventobject): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name, eventobject.EventName)) # イベント名も追加。 createLog(dirpath, filename, "EventName: {}, Source: {}" . format (eventobject.EventName, eventobject.Source)) def disposing( self , eventobject): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) self .subj.removeEventListener( self ) class DocumentEventListener(unohelper.Base, XDocumentEventListener): def __init__( self , dirpath, name, subj, desktop, frame, containerwindow, listeners): self .subj = subj self .args = dirpath, name, desktop, frame, containerwindow, listeners def documentEventOccured( self , documentevent): dirpath, name, desktop, frame, containerwindow, listeners = self .args eventname = documentevent.EventName filename = "_" .join((name, inspect.currentframe().f_code.co_name, eventname)) # イベント名も追加。 if eventname = = "OnUnload" : # ドキュメントを閉じてもdisposeされないデスクトップ、フレーム、コンテナウィンドウにつけたリスナーを除去する。 desktop.removeTerminateListener(listeners[ "desktop_terminatelistener" ]) # TerminateListener desktop.removeFrameActionListener(listeners[ "desktop_frameactionlistener" ]) # FrameActionListener frame.removeFrameActionListener(listeners[ "frame_frameactionlistener" ]) # FrameActionListener frame.removeCloseListener(listeners[ "frame_closelistener" ]) # CloseListener frame.removeTitleChangeListener(listeners[ "frame_titlechangelistener" ]) # TitleChangeListener containerwindow.removeTopWindowListener(listeners[ "containerwindow_topwindowlistener" ]) # TopWindowListener filename = "_" .join((name, inspect.currentframe().f_code.co_name, eventname, "RemoveListeners" )) # イベント名も追加。 createLog(dirpath, filename, "EventName: {}, Source: {}" . format (eventname, documentevent.Source)) def disposing( self , eventobject): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) self .subj.removeDocumentEventListener( self ) class KeyHandler(unohelper.Base, XKeyHandler): def __init__( self , dirpath, name, subj): self .subj = subj self .keycodes = { Key.DOWN: "DOWN" , Key.UP: "UP" , Key.LEFT: "LEFT" , Key.RIGHT: "RIGHT" , Key.HOME: "HOME" , Key.END: "END" , Key.RETURN: "RETURN" , Key.ESCAPE: "ESCAPE" , Key.TAB: "TAB" , Key.BACKSPACE: "BACKSPACE" , Key.SPACE: "SPACE" , Key.DELETE: "DELETE" } # キーは定数。特殊文字を文字列に置換する。 self .args = dirpath, name def keyPressed( self , keyevent): keychar = self ._keycharToText(keyevent) self ._createLogFile(keyevent, keychar, inspect.currentframe().f_code.co_name) return False def keyReleased( self , keyevent): keychar = " " if platform.system()==" Windows" else self ._keycharToText(keyevent) # Windowsの時日本語入力ではKeyCharを使うとすべて文字化けするので使わない。 self ._createLogFile(keyevent, keychar, inspect.currentframe().f_code.co_name) return False def disposing( self , eventobject): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) self .subj.removeKeyHandler( self ) def _keycharToText( self , keyevent): keycode = keyevent.KeyCode keychar = "" # KeyCharが特殊文字の場合はその後のテキストが表示されないときがあるので書き込まない。 if keycode in self .keycodes: # self.keycodesにキーがある特殊文字は文字列に置換する。 keychar = self .keycodes[keycode] elif 255 <keycode< 267 or 511 <keycode< 538 : # 数値かアルファベットの時 keychar = keyevent.KeyChar.value return keychar def _createLogFile( self , keyevent, keychar, methodname): dirpath, name = self .args if keychar: filename = "_" .join((name, methodname, keychar)) txt = "KeyCode: {}, KeyChar: {}, KeyFunc: {}, Modifiers: {}" . format (keyevent.KeyCode, keychar, keyevent.KeyFunc, keyevent.Modifiers) else : filename = "_" .join((name, methodname)) txt = "KeyCode: {}, KeyFunc: {}, Modifiers: {}" . format (keyevent.KeyCode, keyevent.KeyFunc, keyevent.Modifiers) createLog(dirpath, filename, txt) class BorderResizeListener(unohelper.Base, XBorderResizeListener): def __init__( self , dirpath, name, subj): self .subj = subj self .args = dirpath, name def borderWidthsChanged( self , obj, borderwidths): dirpath, name = self .args if obj.supportsService( "com.sun.star.sheet.SpreadsheetView" ): # objがコントローラーの時。 cellrangeaddressconversion = obj.getModel().createInstance( "com.sun.star.table.CellRangeAddressConversion" ) # ドキュメントからCellRangeAddressConversionを取得。 cellrangeaddressconversion.Address = obj.getVisibleRange() # 表示されているセル範囲のCellRangeAddressを取得。 txt = "Visible Range: {}" . format (cellrangeaddressconversion.PersistentRepresentation) # 表示されているセル範囲の文字列アドレスの取得。 else : txt = "Top: {}, Left: {}, Right: {}, Bottom: {}, Object: {}" . format (borderwidths.Top, borderwidths.Left, borderwidths.Right, borderwidths.Bottom, obj) filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, txt) def disposing( self , eventobject): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) self .subj.removeBorderResizeListener( self ) class SelectionChangeListener(unohelper.Base, XSelectionChangeListener): def __init__( self , dirpath, name, subj): self .subj = subj self .args = dirpath, name def selectionChanged( self , eventobject): dirpath, name = self .args txt = "" source = eventobject.Source if source.supportsService( "com.sun.star.sheet.SpreadsheetView" ): # sourceがコントローラーのとき selection = source.getSelection() # 選択しているオブジェクトを取得。 stringaddress = getStringAddressFromCellRange(selection) if stringaddress: filename = "_" .join((name, inspect.currentframe().f_code.co_name, stringaddress.replace( ":" , ""))) txt = "Selection: {}" . format (stringaddress) if not txt: txt = "Source: {}" . format (source) createLog(dirpath, filename, txt) def disposing( self , eventobject): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) self .subj.removeSelectionChangeListener( self ) class EnhancedMouseClickHandler(unohelper.Base, XEnhancedMouseClickHandler): def __init__( self , dirpath, name, subj): self .subj = subj self .args = dirpath, name def mousePressed( self , enhancedmouseevent): self ._createLog(enhancedmouseevent, inspect.currentframe().f_code.co_name) return True def mouseReleased( self , enhancedmouseevent): self ._createLog(enhancedmouseevent, inspect.currentframe().f_code.co_name) return True def disposing( self , eventobject): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) self .subj.removeEnhancedMouseClickHandler( self ) def _createLog( self , enhancedmouseevent, methodname): dirpath, name = self .args target = enhancedmouseevent.Target target = getStringAddressFromCellRange(target) or target # sourceがセル範囲の時は選択範囲の文字列アドレスを返す。 clickcount = enhancedmouseevent.ClickCount filename = "_" .join((name, methodname, "ClickCount" , str (clickcount))) createLog(dirpath, filename, "Buttons: {}, ClickCount: {}, PopupTrigger {}, Modifiers: {}, Target: {}" . format (enhancedmouseevent.Buttons, clickcount, enhancedmouseevent.PopupTrigger, enhancedmouseevent.Modifiers, target)) class ActivationEventListener(unohelper.Base, XActivationEventListener): def __init__( self , dirpath, name, subj): self .subj = subj self .args = dirpath, name def activeSpreadsheetChanged( self , activationevent): dirpath, name = self .args activesheet = activationevent.ActiveSheet activesheetname = activesheet.getName() txt = "" source = activationevent.Source if source.supportsService( "com.sun.star.sheet.SpreadsheetView" ): # sourceがコントローラーのとき selection = source.getSelection() # 選択しているオブジェクトを取得。 stringaddress = getStringAddressFromCellRange(selection) if stringaddress: txt = "Selection: {}" . format (stringaddress) if not txt: txt = "Source: {}" . format (source) txt = "ActiveSheet: {}, {}" . format (activesheetname, txt) # アクティブシート名を取得。 filename = "_" .join((name, inspect.currentframe().f_code.co_name, activesheetname)) createLog(dirpath, filename, txt) def disposing( self , eventobject): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) self .subj.removeActivationEventListener( self ) class TitleChangeListener(unohelper.Base, XTitleChangeListener): def __init__( self , dirpath, name, subj): self .subj = subj self .args = dirpath, name def titleChanged( self , titlechangedevent): dirpath, name = self .args title = titlechangedevent.Title filename = "_" .join((name, inspect.currentframe().f_code.co_name, title)) createLog(dirpath, filename, "Title: {}, Source: {}" . format (title, titlechangedevent.Source)) def disposing( self , eventobject): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) self .subj.removeTitleChangeListener( self ) class CloseListener(unohelper.Base, XCloseListener): def __init__( self , dirpath, name): self .args = dirpath, name def queryClosing( self , eventobject, getsownership): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "getsownership: {}, Source: {}" . format (getsownership, eventobject.Source)) def notifyClosing( self , eventobject): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) def disposing( self , eventobject): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) 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: filename = "_" .join((name, inspect.currentframe().f_code.co_name, frameactionname)) # Action名も追加。 createLog(dirpath, filename, "FrameAction: {}, Source: {}" . format (frameactionname, frameactionevent.Source)) return def disposing( self , eventobject): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) class TerminateListener(unohelper.Base, XTerminateListener): # TerminateListener def __init__( self , dirpath, name): # 出力先ディレクトリのパス、リスナーのインスタンス名。 self .args = dirpath, name def queryTermination( self , eventobject): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) # このメソッド名を取得。メソッド内で実行する必要がある。 createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) # Sourceを出力。 def notifyTermination( self , eventobject): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) # このメソッド名を取得。メソッド内で実行する必要がある。 createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) # Sourceを出力。 def disposing( self , eventobject): dirpath, name = self .args filename = "_" .join((name, inspect.currentframe().f_code.co_name)) createLog(dirpath, filename, "Source: {}" . format (eventobject.Source)) def getStringAddressFromCellRange(source): # sourceがセル範囲の時は選択範囲の文字列アドレスを返す。文字列アドレスが取得できないオブジェクトの時はオブジェクトの文字列を返す。 stringaddress = "" propertysetinfo = source.getPropertySetInfo() # PropertySetInfo if propertysetinfo.hasPropertyByName( "AbsoluteName" ): # AbsoluteNameプロパティがある時。 absolutename = source.getPropertyValue( "AbsoluteName" ) # セル範囲コレクションは$Sheet1.$A$4:$A$6,$Sheet1.$B$4という形式で返る。 names = absolutename.replace( "$" , " ").split(" ,") # $を削除してセル範囲の文字列アドレスのリストにする。 stringaddress = ", " .join(names) # コンマでつなげる。 return stringaddress C = 100 # カウンターの初期値。 TIMESTAMP = datetime.now().isoformat().split( "." )[ 0 ].replace( "-" , " ").replace(" : ", " ") # コピー先ファイル名に使う年月日T時分秒を結合した文字列を取得。 def createLog(dirpath, filename, txt): # 年月日T時分秒リスナーのインスタンス名_メソッド名(_オプション).logファイルを作成。txtはファイルに書き込むテキスト。dirpathはファイルを書き出すディレクトリ。 global C filename = " ".join((TIMESTAMP, " _ ", str(C), filename, " .log")) C + = 1 with open (os.path.join(dirpath, filename), "w" ) as f: f.write(txt) g_exportedScripts = macro, #マクロセレクターに限定表示させる関数をタプルで指定。 |
ListenersForCalc2.ods
このマクロを埋め込んだCalcドキュメントです。
ドキュメントを開いた時にこのマクロが起動し、ドキュメントと同じフォルダにログファイルを出力します。
ListenersForCalc2-DisabledLine228.ods
228行目のif文をコメントアウトしてデスクトップ、フレーム、コンテナウィンドウのリスナーを削除しないバージョンです。
リスナーを削除するタイミング
リスナーは終了時に削除した方がいいらしいので、disposing()メソッドの最後で除去しています。
リスナーのオブジェクトがすべてのサブジェクトから削除されるとすぐに消えてしまうせいか、XModifyListenerのdisposing()メソッドで除去した後にコードを実行するとLibreOfficeがクラッシュしましたので、リスナーを削除するのは一番最後に実行しています。
ドキュメントを閉じたときにフレームも閉じられると思っていましたが、フレームはウィンドウが増えなければ同じものが使い回しされるようで、フレームにXCloseListenerを付けてもそのメソッドはドキュメントのウィンドウを閉じるだけでは呼び出されませんでした。
コンテナウィンドウに追加したXTopWindowListenerのwindowClosed()ならドキュメントを閉じたときに呼び出されると思いましたが、呼び出されるタイミングはフレームと同じでした。
XTopWindowListenerのwindowClosing()メソッドはいつ呼ばれるのかわかりませんでした(OOoBasic/Window/Listeners - ...?の時と変わっているようです)。
ドキュメントを一つ開いたところから、ファイル→閉じる、を実行するとスタートセンターのウィンドウに切り替わります。
(2018.1.5追記。スタートセンターに切り替わるのは、開いているドキュメントが一つで、ファイル→閉じる、でドキュメントを閉じたときだけでした。ドキュメントのウィンドウが複数開いているときや、ドキュメントが一つしか開いていなくても、ウィンドウの閉じるボタンで閉じたときはスタートセンターに切り替わりませんでした。windowClosing()メソッドの発火も異なりました。Calc(58)追加できるリスナー一覧: その9参照。)
デスクトップ、フレーム、コンテナウィンドウのリスナーを削除しないまま、「閉じる」、ところから呼び出されたリスナーの発火のログはこのようになっていました。
ドキュメントを閉じても6行目でCOMPONENT_DETACHINGしたフレームが、下から3行目でCOMPONENT_REATTACHEDされて、フレームタイトルがドキュメント名からLibreOfficeに変更になって、スタートセンターのウィンドウに使い回しされていることがわかります。
この状態で再度上記のマクロを実行するとフレームとデスクトップ、コンテナウィンドウには同じリスナーが再度追加されて、今度は2回ずつリスナーのメソッドが発火してしまいました。
COMPONENT_REATTACHEDした後フレームとコンテナウィンドウのメソッドが2回ずつ発火していることがわかります。
ということでドキュメントを閉じるタイミングでフレームとコンテナウィンドウのリスナーを削除しないといけないことがわかりました。
デスクトップに追加するリスナーも同様にしないといけません。
どのリスナーのメソッドでこれらのリスナーの削除をするかを考えないといけません。
デスクトップ、フレーム、コンテナウィンドウのリスナーを削除しないまま、ファイル→閉じる、ではなく、ファイル→LibreOfficeの終了、としたときはフレームのnotifyClosing()メソッドとそれに続いてコンテナウィンドウのwindowClosed()メソッドが(なぜ2回なのかは不明)呼びだされていることがわかりました。
これらのメソッドはドキュメントを閉じるだけでは呼び出されないので、フレームなどのリスナーを削除するのには使えません。
ドキュメントを閉じるときに発火するのは下から7行目のシートのXModifyListenerのdisposing()メソッドまでです。
コンテナウィンドウのwindowDeactivated()メソッドやドキュメントのOnUnfocusイベントはドキュメントを閉じるときに限らずに発火するので、ドキュメントのOnUnloadイベントが発生したときにフレームとコンテナウィンドウ、デスクトップのリスナーを削除することにしました(上記マクロの228行目)。
これで再度ドキュメントを開いてもフレームなどに複数無駄なリスナーが追加されることを避けることができました。
上記のマクロのWindows10での実行結果です。
linuxBean14.04でも同じ結果でした。
12行目のRemoveListenersとあるファイル名のタイミングでフレームとコンテナウィンドウ、デスクトップのリスナーを削除しています。
(2018.1.14追記。Sourceアトリビュートの内容をファイルに出力するように追加した
ListenersForCalcSourceAttr.odsを作成しました。disposing()の引数のEventObject StructのSourceアトリビュートにはそのリスナーを追加したオブジェクトが入ってくるようです。)
参考にしたサイト
OOoBasic/Window/Listeners - ...?
ウィンドウが閉じられるときに呼ばれるリスナーの解説。今回と結果が異なりました。
0 件のコメント:
コメントを投稿