如何使用 python 宏监控 Libreoffice 文档中的键盘输入?

How to monitor keyboard input in a Libreoffice document using a python macro?

请注意,这是一个自答题,仅供参考。 尽管进行了确定的搜索,但我还没有找到 python 的文档。

我不想为对话框中的特定元素创建侦听器,而是想“侦听”文本文档的键盘输入。如果看到某些键或组合,该对象将执行操作。
使用 Uno com.sun.star.awt XKeyListener 创建了以下代码后,我预计至少会看到它正在运行的一些指示。

import unohelper
from com.sun.star.awt import XKeyListener

def fs_listen(*args):
    doc = XSCRIPTCONTEXT.getDocument()
    desktop = XSCRIPTCONTEXT.getDesktop()
    model = desktop.getCurrentComponent()
    contr = model.getCurrentController()
    url_current = doc.getLocation()
    oEventListener = KeyListen(doc)
    contr.addEventListener(oEventListener)

class KeyListen(unohelper.Base, XKeyListener):
    def __init__(self, parent):
        self.parent = parent
        print("listener added")

    def keyPressed( self,  event ):
        """ is invoked when a key has been pressed."""
        print("event",event)

    def keyReleased( self, event ):
        """ is invoked when a key has been released."""
        print("release",event)
    

代码将激活并且 运行 但在“添加侦听器”之后不会为键盘输入产生任何输出。
我哪里错了?

与直觉相反,向 CurrentController 添加 KeyListener 似乎并没有 做一个人会想的。它似乎准备文档接收输入,你会 想想,反正都会做。
将对键盘输入产生响应的函数是 XKeyHandler.
虽然它没有作为 EventListener 添加,但它作为 KeyHandler 添加。

这个函数有它自己的怪癖,因为要取消它,你需要删除处理程序 oEventListener 的实例与用于启动它的实例完全相同。 oobasic 不是问题, 其中实例可以存储为“全局”,我假设,因为它是不可或缺的,所以它以某种方式维护或存储。

python 的问题是它不能以这种方式存储,除非有人读到这个,知道 的一种方式。它不会“泡菜”,关于如何储存它,我最后泡菜了。我可能错过了一些明显的东西。

我最终使用的解决方案是根据键盘使功能自动取消 输入。

警告:仅在 Linux

上进行了测试

可以通过从命令行启动 LibreOffice lowriter 然后 运行 来测试以下内容 与任何其他宏一样,以 Shift+Alt+Ctrl+k

终止
#!/usr/bin/python
import unohelper
from com.sun.star.awt import XKeyHandler
from com.sun.star.awt import Key
from com.sun.star.awt.MessageBoxButtons import BUTTONS_OK
from com.sun.star.awt.MessageBoxType import INFOBOX

fs_fkeys={} # dictionary of keys to identify each key
for key in dir(Key):
    fs_fkeys[getattr(Key, key)] = key

#Idiosyncrasies
# Shift_L, Ctrl_L, Alt_L are not reported as separate keys but are reported as modifiers
# 1,2 and 4 respectively. Shift_R and Ctrl_R are identical to their Left twins
# Alt_R (AltGr) is not reported and not a modifier.
# Super_R (Right Windows) is not reported but is a modifier, even though it doesn't modify any keys.
# It reports as modifier 8
# Super_L (Left Windows) is not reported and not a modifier.
# Caps_Lock and Num_Lock report as unidentified keys but not as modifiers.

# track key input with option of consuming the input (return True)
def fs_Tracker(*args):
    doc = XSCRIPTCONTEXT.getDocument()
    desktop = XSCRIPTCONTEXT.getDesktop()
    global contr, oEventHandler

    contr = desktop.getCurrentComponent().getCurrentController()
    oEventHandler = KeyHandler(doc)
    contr.addKeyHandler(oEventHandler)
    mess = "Key tracker active\nTo deactivate close document or Shift+Alt+Ctrl K"
    heading = "Key Tracker"
    MessageBox(None, mess, heading, INFOBOX, BUTTONS_OK)

class KeyHandler( unohelper.Base, XKeyHandler ):
    def __init__(self, parent):
        self.parent = parent
        return None

    def Terminate ( self, event ):
        mess = "Key tracker deactivated!"
        heading = "Key Tracker"
        MessageBox(None, mess, heading, INFOBOX, BUTTONS_OK)

        contr.removeKeyHandler(oEventHandler)

    def keyPressed( self,  event ):
        k = event.KeyCode
        c = event.KeyChar.value 
        mods = event.Modifiers
        # mods are additive
        # 0 - None
        # 1 - Shift
        # 2 - Ctrl
        # 4 - Alt
        # 8 - Super_R   
        if c == "K" and mods == 7: #Shift+Ctrl+Alt+k
            self.Terminate(None)
            return True # Returning True consumes the key
                        # Thus assigning this macro to the same keyboard shortcut means that
                        # the macro is toggled On/Off by Shift+Alt+Ctrl+k
        if k in fs_fkeys:
            name = fs_fkeys[k]
        else:
            name = "Undefined"
        print(name, k, c, mods)
        return False

    def keyReleased( self, event ):
        return False

def MessageBox(ParentWindow, MsgText, MsgTitle, MsgType, MsgButtons):
    ctx = XSCRIPTCONTEXT.getComponentContext()
    sm = ctx.ServiceManager
    si = sm.createInstanceWithContext("com.sun.star.awt.Toolkit", ctx)
    mBox = si.createMessageBox(ParentWindow, MsgType, MsgButtons, MsgTitle, MsgText)
    mBox.execute()

#List components that are accessible
g_exportedScripts = fs_Tracker,