How to call an existing LibreOffice python macro from a python script

折月煮酒 提交于 2020-01-17 02:24:33

问题


Currently I call an existing existing LibreOffice macro with this:

def OnLOtimestamp(self):
        try:
            pid= Popen(['lowriter '"'"'vnd.sun.star.script:fs2TimeStamp.py$fs2_TimeStamp?language=Python&location=user'"'"],shell=True).pid
        except OSError, e:
            self.notify_show("Timestamp Error",str(e))
        self.ma2.SetLabel("Macro timestamp")
        self.database['Time_stamp'] = self.database['Time_stamp'] + 1

The key bit being the Popen call, where the macro name is fs2TimeStamp.py and the function is fs2_TimeStamp but this feels like a cop out and I would rather perform a direct call via Uno. My research suggests that I may well need to use MasterScriptProvider, XscriptProvider and XscriptInvocation but trying to decipher the Uno API is like swimming through treacle. Has anybody got a code sample of calling an existing macro in Libreoffice, using Uno?

Edit:
So far the answer appears to be No! This is the current state of play.

#!/usr/bin/python3
# -*- coding: utf-8 -*-
##
# a python script to run a libreoffice python macro externally
#
import uno
from com.sun.star.connection import NoConnectException
from com.sun.star.uno  import RuntimeException
from com.sun.star.uno  import Exception
from com.sun.star.lang import IllegalArgumentException
def test2(*args):
    localContext = uno.getComponentContext()
    localsmgr = localContext.ServiceManager
    resolver = localsmgr.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", localContext )
    try:
        ctx = resolver.resolve("uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext")
    except NoConnectException as e:
        print ("LibreOffice is not running or not listening on the port given - ("+e.Message+")")
        return
    except IllegalArgumentException as e:
        print ("Invalid argument given - ( "+ e.Message+ ")")
        return
    except RuntimeException as e:
        print ("An unknown error occurred: " + e.Message)
        return

    servmgr = ctx.ServiceManager
    desktop = servmgr.createInstanceWithContext( "com.sun.star.frame.Desktop",ctx)
    model = desktop.getCurrentComponent()
#    scriptP = model.getScriptProvider()
#    print("scriptP", scriptP)
    scriptx = model.getScriptProvider().getScript('vnd.sun.star.script:fs2TimeStamp.py$fs2_TimeStamp?language=Python&location=user')
    print("scriptx", scriptx)
    try:
        scriptx.invoke("",0,0)
    except IllegalArgumentException as e:
        print ("The command given is invalid ( "+ e.Message+ ")")
        return
    except RuntimeException as e:
        print("An unknown error occurred: " + e.Message)
        return
    except Exception as e:
        print ("Script error ( "+ e.Message+ ")")
        print(e)
        return
    except:
        print("Error")
    return(None)

test2()

This code works happily when invoked as a macro within Libreoffice and scriptx prints out as:

scriptx <pythonscript.PythonScript object at 0x7fa2879c42e8>

however when run from the command line the script does nothing and scriptx prints out as:

scriptx pyuno object (com.sun.star.script.provider.XScript)0x1e749d8{, supportedInterfaces={com.sun.star.lang.XTypeProvider,com.sun.star.script.provider.XScript}}

So either getScriptProvider or getScript are not being provided with something that they require. I am currently at a loss as to what is missing and yet, I feel in my bones that I'm close to a solution.

Can anyone see where I have made a mistake?


回答1:


Finally, I have a working solution. Ding! Dong!

#!/usr/bin/python3
# -*- coding: utf-8 -*-
##
# a python script to run a libreoffice python macro externally
#
import uno
from com.sun.star.connection import NoConnectException
from com.sun.star.uno  import RuntimeException
from com.sun.star.uno  import Exception
from com.sun.star.lang import IllegalArgumentException
def test2(*args):
    localContext = uno.getComponentContext()
    localsmgr = localContext.ServiceManager
    resolver =  localsmgr.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", localContext )
    try:
        ctx = resolver.resolve("uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext")
    except NoConnectException as e:
        print ("LibreOffice is not running or not listening on the port given - ("+e.Message+")")
        return
    msp = ctx.getValueByName("/singletons/com.sun.star.script.provider.theMasterScriptProviderFactory")
    sp = msp.createScriptProvider("")
    scriptx = sp.getScript('vnd.sun.star.script:fs2TimeStamp.py$fs2_TimeStamp?language=Python&location=user')
    try:
        scriptx.invoke((), (), ())
    except IllegalArgumentException as e:
        print ("The command given is invalid ( "+ e.Message+ ")")
        return
    except RuntimeException as e:
        print("An unknown error occurred: " + e.Message)
        return
    except Exception as e:
        print ("Script error ( "+ e.Message+ ")")

return(None)

test2()

Note: For clarity the existing python script is called fs2TimeStamp.py, it contains 1 (one) function defined as def fs2_TimeStamp(*args):
See the line:

scriptx = sp.getScript('vnd.sun.star.script:fs2TimeStamp.py$fs2_TimeStamp?language=Python&location=user')   

and it is stored in $HOME/.config/libreoffice/4/user/Scripts/python

For this solution to work, libreoffice must be running in listening mode, so start libreoffice with a command like:

soffice "--accept=socket,host=127.0.0.1,port=2002,tcpNoDelay=1;urp;" --writer --norestore

OR

nohup soffice "--accept=socket,host=127.0.0.1,port=2002,tcpNoDelay=1;urp;" --writer --norestore &

Alternatively you could use the more direct method (for writer in this example):

lowriter "--accept=socket,host=127.0.0.1,port=2002,tcpNoDelay=1;urp"

OR

nohup lowriter "--accept=socket,host=127.0.0.1,port=2002,tcpNoDelay=1;urp" &

Also note that you must run the script with python3




回答2:


This is a simpler and more generic version of the first answer, which was very much slanted to my specific problem, which called a macro, which then talked to another program via TCP before inserting text sent via the TCP connection.
This version will run standalone and should be instantly replicable.
As before you must start LibreOffice writer in listening mode (see previous answer for options):

lowriter "--accept=socket,host=127.0.0.1,port=2002,tcpNoDelay=1;urp"

This is the external python routine, which must be run with python3:

#!/usr/bin/python3
# -*- coding: utf-8 -*-
##
# a python script to run a libreoffice python macro externally
# NOTE: for this to run start libreoffice in the following manner
# soffice "--accept=socket,host=127.0.0.1,port=2002,tcpNoDelay=1;urp;" --writer --norestore
# OR
# nohup soffice "--accept=socket,host=127.0.0.1,port=2002,tcpNoDelay=1;urp;" --writer --norestore &
#
import uno
from com.sun.star.connection import NoConnectException
from com.sun.star.uno  import RuntimeException
from com.sun.star.uno  import Exception
from com.sun.star.lang import IllegalArgumentException
def uno_directmacro(*args):
    localContext = uno.getComponentContext()
    localsmgr = localContext.ServiceManager
    resolver = localsmgr.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", localContext )
    try:
        ctx = resolver.resolve("uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext")
    except NoConnectException as e:
        print ("LibreOffice is not running or not listening on the port given - ("+e.Message+")")
        return
    msp = ctx.getValueByName("/singletons/com.sun.star.script.provider.theMasterScriptProviderFactory")
    sp = msp.createScriptProvider("")
    scriptx = sp.getScript('vnd.sun.star.script:directmacro.py$directmacro?language=Python&location=user')
    try:
        scriptx.invoke((), (), ())
    except IllegalArgumentException as e:
        print ("The command given is invalid ( "+ e.Message+ ")")
        return
    except RuntimeException as e:
        print("An unknown error occurred: " + e.Message)
        return
    except Exception as e:
        print ("Script error ( "+ e.Message+ ")")
        print(e)
        return
    return(None)

uno_directmacro()

This is the LibreOffice python macro directmacro.py which should be located in:

$HOME/.config/libreoffice/4/user/Scripts/python

(assuming use of LibreOffice version 4 here)

The directmacro.py macro:

#!/usr/bin/python
class FontSlant():
    from com.sun.star.awt.FontSlant import (NONE, ITALIC,)

def directmacro(*args):
#get the doc from the scripting context which is made available to all scripts
    desktop = XSCRIPTCONTEXT.getDesktop()
    model = desktop.getCurrentComponent()
    text = model.Text
    tRange = text.End
    cursor = desktop.getCurrentComponent().getCurrentController().getViewCursor()
    doc = XSCRIPTCONTEXT.getDocument()
    parentwindow = doc.CurrentController.Frame.ContainerWindow

# your cannot insert simple text and text into a table with the same method
# so we have to know if we are in a table or not.
# oTable and oCurCell will be null if we are not in a table
    oTable = cursor.TextTable
    oCurCell = cursor.Cell
    insert_text = "This is text inserted into a LibreOffice Document\ndirectly from a macro called externally"
    Text_Italic = FontSlant.ITALIC
    Text_None = FontSlant.NONE
    cursor.CharPosture=Text_Italic
    if oCurCell == None: # Are we inserting into a table or not?
        text.insertString(cursor, insert_text, 0)
    else:
        cell = oTable.getCellByName(oCurCell.CellName)
        cell.insertString(cursor, insert_text, False)
    cursor.CharPosture=Text_None
    return None 


来源:https://stackoverflow.com/questions/29417113/how-to-call-an-existing-libreoffice-python-macro-from-a-python-script

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!