Python : 监视文档事件

目录
索引 🔎︎

# 监视文档事件

在将宏分配给事件旁边，可以监视 LibreOffice 文档所引发的事件。应用程序编程接口 (API) 广播器负责调用事件脚本。与需要定义所有支持的方法 (即使未使用) 的监听器不同, 文档监视器对已挂钩的事件脚本只需要两个方法。

## 监听文档事件

本文采用 Basic 与 Python 语言，通过面向对象编程来演示监视功能。将 OnLoad 脚本分配给 文档打开 事件, 就足以启动和终止文档事件监视。工具 – 自定义...菜单的事件选项卡用于分配各脚本。

侦听事件有助于在条件之前和之后安放脚本, 如加载与卸载库, 或在后台跟踪脚本处理进度。Access2Base 跟踪 模块的使用就是第二种情况的体现。

### 使用 Python

事件监视从对象实例化开始, 并在 Python 释放对象时最终停止。引发的事件通过Access2Base 控制台报告。

OnLoad 与 OnUnload 事件可用于分别设置和取消设置 Python 程序路径。它们被称为文档打开 和 文档关闭 。

             # -*- coding: utf-8 -*-
             from __future__ import unicode_literals

             import os.path, uno, unohelper
             from com.sun.star.document import DocumentEvent, \
                 XDocumentEventListener as AdapterPattern
             from com.sun.star.lang import EventObject

             class UiDocument(unohelper.Base, AdapterPattern):
                 """ 监视文档事件 """
                 '''
                 改编自「用于监视 OnSave 事件的 Python 脚本」，见于
                 https://forum.openoffice.org/en/forum/viewtopic.php?t=68887
                 '''
                 def __init__(self):
                     """ 文档事件监视器 """
                     ''' 使用 Access2Base.Trace 控制台报告，*或*
                     在 Calc 文档的第一个工作表的第一列报告 '''
                     ctx = uno.getComponentContext()
                     smgr = ctx.getServiceManager()
                     desktop = smgr.createInstanceWithContext(
                     'com.sun.star.frame.Desktop' , ctx)
                     self.doc = desktop.CurrentComponent
                     #self.row = 0  # 仅对 Calc 文档取消注释
                     Console.setLevel("DEBUG")
                     self.listen()  # 开始监视文档事件

                 @property
                 def Filename(self) -> str:
                     sys_filename = uno.fileUrlToSystemPath(self.doc.URL)
                     return os.path.basename(sys_filename)

                 def setCell(self, calcDoc, txt: str):
                     """ 在 Calc 电子表格的第一列输出文档事件 """
                     sheet = calcDoc.getSheets().getByIndex(0)
                     sheet.getCellByPosition(0,self.row).setString(txt)
                     self.row = self.row + 1

                 def listen(self, *args):  # 尽可能早执行 OnLoad/OnNew
                     """ 开始文档事件监视 """
                     self.doc.addDocumentEventListener(self)
                     Console.log("INFO", "正在记录文档事件", True)

                 def sleep(self, *args):  # 最后执行 OnUnload (可选)
                     """ 停止文档事件监视 """
                     self.doc.removeDocumentEventListener(self)
                     Console.log("INFO", "文档事件已记录", True)

                 def documentEventOccured(self, event: DocumentEvent):
                     """ 截取所有文档事件 """
                     #self.setCell(event.Source, event.EventName) # 仅适用于 Calc 文档
                     Console.log("DEBUG",
                         event.EventName+" in "+self.Filename,
                         False)

                 def disposing(self, event: EventObject):
                     """ 释放所有活动 """
                     self.sleep()
                     Console.show()

             def OnLoad(*args):  #「文档打开」事件
                 listener = UiDocument()  # Initiates listening

             def OnUnload(*args):  # '文档已关闭' 事件
                 pass  # (可选) 销毁时执行

             g_exportedScripts = (OnLoad,)

             from com.sun.star.script.provider import XScript
             class Console():
                 """
                 用于报告/记录程序执行的后台/前台控制台。
                 """
                 @staticmethod
                 def trace(*args,**kwargs):
                     """ 向控制台打印空闲项目列表 """
                     scr = Console._a2bScript(script='DebugPrint', module='Compatible')
                     scr.invoke((args),(),())
                 @staticmethod
                 def log(level: str, text: str, msgBox=False):
                     """ 日志消息追加到控制台，可选用户提示 """
                     scr = Console._a2bScript(script='TraceLog')
                     scr.invoke((level,text,msgBox),(),())
                 @staticmethod
                 def setLevel(logLevel: str):
                     """ 设置日志消息下限 """
                     scr = Console._a2bScript(script='TraceLevel')
                     scr.invoke((logLevel,),(),())
                 @staticmethod
                 def show():
                     """ 显示控制台内容/对话框 """
                     scr = Console._a2bScript(script='TraceConsole')
                     scr.invoke((),(),())
                 @staticmethod
                 def _a2bScript(script: str, library='Access2Base',
                     module='Trace') -> XScript:
                     ''' Grab application-based Basic script '''
                     sm = uno.getComponentContext().ServiceManager
                     mspf = sm.createInstanceWithContext(
                         "com.sun.star.script.provider.MasterScriptProviderFactory",
                         uno.getComponentContext())
                     scriptPro = mspf.createScriptProvider("")
                     scriptName = "vnd.sun.star.script:"+library+"."+module+"."+script+"?language=Basic&location=application"
                     xScript = scriptPro.getScript(scriptName)
                     return xScript

注意拼写错误的 documentEventOccured 方法, 该错误继承自 LibreOffice 应用程序编程接口 (API)。

启动应用程序和关闭应用程序事件可分别用于为用户脚本或 LibreOffice 脚本设置和取消设置 Python 路径。类似地, 可以使用文档打开 和 文档关闭 事件来加载和释放基于文档的 Python 库或模块。有关详细信息, 请参阅导入 Python 模块。

### 使用 LibreOffice Basic

将Onload脚本分配给文档打开事件可通过工具 – 自定义...菜单事件选项卡进行。事件监视从 ConsoleLogger 对象实例化的那一刻起就开始了，并在 Basic 引擎释放它时终止。 OnLoad事件可加载必要的 Basic 库, 同时使用Access2Base.Trace 报告捕获的事件。

              REM controller.Events 模块
              Option Explicit
              Private _obj As Object ' controller.ConsoleLogger 实例

              Sub OnLoad(evt As com.sun.star.document.DocumentEvent) ' >> 文档打开 <<
                  _obj = New ConsoleLogger : _obj.Start(evt)
              End Sub ' controller.OnLoad
              ' ----
              REM controller.ConsoleLogger 类模块
              Option Explicit
              Option Compatible
              Option ClassModule

              '「适配器」设计模式对象，将在 « 文档打开 » 事件中实例化
              Private Const UI_PROMPT = True
              Private Const UI_NOPROMPT = False ' Set it to True to visualise documents events

              ' 构造函数/析构函数
              Private Sub Class_Initialize()
              End Sub ' controller.ConsoleLogger.Initialize
              Private Sub Class_Terminate()
              End Sub ' controller.ConsoleLogger.Terminate

              ' 成员
              Private _evtAdapter As Object ' com.sun.star.document.XDocumentEventBroadcaster

              ' PROPERTIES
              Private Property Get FileName As String
                  ''' 受系统影响的文件名 '''
                  Const _LIBRARY = "Tools" : With GlobalScope.BasicLibraries
                      If Not .IsLibraryLoaded(_LIBRARY) Then .LoadLibrary(_LIBRARY)
                  End With
                  Filename = Tools.Strings.FilenameOutofPath(ThisComponent.URL)
              End Property ' controller.ConsoleLogger.Filename

              ' 方法
              Private Sub _documentEventOccured(evt As com.sun.star.document.DocumentEvent)
                  ''' 监视文档事件 '''
                  Access2Base.Trace.TraceLog("DEBUG", _
                      evt.EventName &" in "& Filename(evt.Source.URL), _
                      UI_NOPROMPT)
                  Select Case evt.EventName
                      Case "OnUnload" : _Stop(evt)
                  End Select
              End Sub ' controller.ConsoleLogger._documentEventOccured

              Private Sub _disposing(evt As com.sun.star.lang.EventObject)
              End Sub ' controller.ConsoleLogger.disposing

              Public Sub Start(Optional evt As com.sun.star.document.DocumentEvent)
                  ''' 初始化文档事件记录 '''
                  Const _LIBRARY = "Access2Base" : With GlobalScope.BasicLibraries
                      If Not .IsLibraryLoaded(_LIBRARY) Then .LoadLibrary(_LIBRARY)
                  End With : Access2Base.Trace.TraceLevel("DEBUG")
                  Access2Base.Trace.TraceLog("INFO", _
                      IIf(IsMissing(evt),"",evt.EventName & "-") & "正在记录文档事件", _
                      UI_PROMPT)

                  _evtAdapter = CreateUnoListener( "_", "com.sun.star.document.XDocumentEventListener" )
                  ThisComponent.addDocumentEventListener( _evtAdapter )
              End Sub ' controller.ConsoleLogger.Start

              Private Sub _Stop(Optional evt As com.sun.star.document.DocumentEvent)
                  ''' 终止文档事件记录 '''
                  ThisComponent.removeDocumentEventListener( _evtAdapter )
                  Access2Base.Trace.TraceLog("INFO", _
                      IIf(IsMissing(evt),"",evt.EventName & "-") & "文档事件已记录", _
                      UI_PROMPT)
                  Access2Base.Trace.TraceConsole() ' Captured events dialog
              End Sub ' controller.ConsoleLogger._Stop

              ' EVENTS
              ' 用于已处理事件的代码放在这里

注意拼写错误的 _documentEventOccured 方法, 该错误继承自 LibreOffice 应用程序编程接口 (API)。

## 探索文档事件

广播器 API 对象提供了它所负责的事件列表:

### 使用 Python

             # -*- coding: utf-8 -*-
             from __future__ import unicode_literals

             import uno, apso_utils as ui

             def displayAvailableEvents():
                 """ 显示文档事件 """
                 '''
                 改编自 DisplayAvailableEvents()，作者为 A. Pitonyak
                 https://forum.openoffice.org/en/forum/viewtopic.php?&t=43689
                 '''
                 ctx = XSCRIPTCONTEXT.getComponentContext()
                 smgr = ctx.ServiceManager
                 geb = smgr.createInstanceWithContext(
                     "com.sun.star.frame.GlobalEventBroadcaster", ctx)
                 events = geb.Events.getElementNames()
                 ui.msgbox('; '.join(events))

             g_exportedScripts = (displayAvailableEvents,)

「替代 Python 脚本管理器 (APSO)」扩展程序用于在屏幕上渲染事件信息。

### 使用 LibreOffice Basic

             Sub DisplayAvailableEvents
                 ''' 显示文档事件 '''
                 Dim geb As Object ' com.sun.star.frame.GlobalEventBroadcaster
                 Dim events() As String
                 geb = CreateUnoService("com.sun.star.frame.GlobalEventBroadcaster")
                 events = geb.Events.ElementNames()
                 MsgBox Join(events, "; ")
             End Sub

相关主题

创建事件监听器

导入 Python 模块

Python 编程示例

用 Python 脚本编程