Please note, this is a self answered question for reference. I haven’t found it documented for python, in spite of determined searching.
Rather than create a listener for a specific element in a dialog, I want to “listen” to keyboard input for a text document. The object being to perform an action if certain keys or combinations are seen.
Having created the following code using an Uno com.sun.star.awt XKeyListener
, I had expected to see at least some indictation that it was functioning.
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)
The code will activate and run but produces no output for keyboard input, after “listener added”.
Where am I going wrong?
Advertisement
Answer
Counter-intuitively, it appears that adding a KeyListener to the CurrentController doesn’t
do what one would think. It would appear to prime the document to receive input, which you’d
think, it would be doing anyway.
The function that will produce a response to keyboard input, is an XKeyHandler
.
It isn’t added as an EventListener though, it’s added as a KeyHandler.
This function has it’s own quirk, in that to cancel it, you need to remove the handler with
exactly the same instance of oEventListener
as was used to start it. Not an issue with oobasic,
where the instance can be stored as “Global” and I assume, because it’s integral, it is maintained or stored in some way.
The problem for python is it can’t be stored in this way, unless someone reading this, knows of a way. It will not “pickle” and I ended up in a pickle, as to how to store it. I may well have missed something obvious.
The solution I ended up using, was to make the function self-cancelling, based on keyboard input.
Caveat: This has been tested on Linux only
The following can tested by starting LibreOffice from the command line lowriter
and then run
as any other macro, terminate with 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 activenTo 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,