I am trying to set up an comfortable environment to develop some python scripts for LibreOffice Calc.
Before I tried to edit .py files directly from "/home/user/.config/libreoffice/4/user/Scripts/python/". This would not let me debug the script, because if I start it in debug mode, most of the components, like XSCRIPTCONTEXT
would not be found.
I decided to connect to LO directly via uno
from my venv
elsewhere.
I start my virtual environment with python -m venv --system-site-packages venv
and activate it
Now which python
gives ../somefolder/venv/bin/python
and pip list
shows pyuno (version 0.1.1)
and unoserver (version 1.3)
.
EDIT: I installed them with pip
from virtual environment. I just realized that pyuno
in pip
-repo is a different thing entirely (https://pypi.org/project/pyuno/) and I uninstalled it and its dependencies. So only unoserver
is installed inside venv
and uno
-path is: /usr/lib/libreoffice/program/uno.py
Then I start unoserver
with unoserver &
. This created a soffice
process listening on localhost:2002
, which I verified with netstat -anpe | grep "2002"
.
Then I open a calc document. As mentioned here: https://ask.libreoffice.org/t/why-does-desktop-getcurrentcomponent-return-none-in-pyuno/59902 you should do it before running the script, lets say MySpreadsheet.ods
and try tu run the following script (adapted from the link):
import uno
# import time
context = uno.getComponentContext()
resolver = context.ServiceManager.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", context)
ctx = resolver.resolve( "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext" )
smgr = ctx.ServiceManager
desktop = smgr.createInstanceWithContext( "com.sun.star.frame.Desktop",ctx)
doc = desktop.getCurrentComponent()
print(doc.Title)
Upon variable inspection I can see that, all the variables are created. I do not know what to look for in all of them to verify the process, but when desktop = ...
line is executed, that desktop object has all of the field None
:
ComponentWindow: None
ContainerWindow: None
CurrentComponent: None
etc
which leads to the fact that desktop.getCurrentComponent()
evaluates to None
as well.
EDIT: When executing code I printed the created object sequence and it returns this:
context:
pyuno object (com.sun.star.uno.XComponentContext)0x558e2a7a1b20{, supportedInterfaces={com.sun.star.uno.XComponentContext,com.sun.star.container.XNameContainer,com.sun.star.lang.XTypeProvider,com.sun.star.uno.XWeak,com.sun.star.lang.XComponent}}
resolver
pyuno object (com.sun.star.uno.XInterface)0x558e2a941c60{implementationName=com.sun.star.comp.bridge.UnoUrlResolver, supportedServices={com.sun.star.bridge.UnoUrlResolver}, supportedInterfaces={com.sun.star.lang.XServiceInfo,com.sun.star.bridge.XUnoUrlResolver,com.sun.star.lang.XTypeProvider,com.sun.star.uno.XWeak}}
ctx
pyuno object (com.sun.star.uno.XInterface)0x7f1d88002b58{, supportedInterfaces={com.sun.star.uno.XComponentContext,com.sun.star.container.XNameContainer,com.sun.star.lang.XTypeProvider,com.sun.star.uno.XWeak,com.sun.star.lang.XComponent}}
smgr
pyuno object (com.sun.star.lang.XMultiComponentFactory)0x7f1d88002e78{implementationName=com.sun.star.comp.cppuhelper.bootstrap.ServiceManager, supportedServices={com.sun.star.lang.MultiServiceFactory,com.sun.star.lang.ServiceManager}, supportedInterfaces={com.sun.star.lang.XServiceInfo,com.sun.star.lang.XMultiServiceFactory,com.sun.star.lang.XMultiComponentFactory,com.sun.star.container.XSet,com.sun.star.container.XContentEnumerationAccess,com.sun.star.beans.XPropertySet,com.sun.star.beans.XPropertySetInfo,com.sun.star.lang.XEventListener,com.sun.star.lang.XInitialization,com.sun.star.lang.XTypeProvider,com.sun.star.uno.XWeak,com.sun.star.lang.XComponent}}
This answer to another post: https://stackoverflow.com/a/19158549/15452958 suggest to set PYTHONPATH
to /usr/lib/libreoffice/program
. Indeed pyuno.so
is available in that folder and during debugging I can verify that uno is loaded from /usr/lib/libreoffice/program/uno.py
.
Accepted answer here: Why does desktop.getCurrentComponent() return None in PyUNO? shows that I should wait until desktop
becomes online. I tried and it did not work. I let it run like this for 5 minutes or so and doc
was still None
.
EDIT2: The output of netstat -anpe | grep "2002"
now reads additional two lines for established connection.
tcp 0 0 127.0.0.1:2002 0.0.0.0:* LISTEN 1000 13310724 213378/soffice.bin
tcp 0 0 127.0.0.1:51848 127.0.0.1:2002 ESTABLISHED 1000 818657597 292648/python
tcp 0 0 127.0.0.1:2002 127.0.0.1:51848 ESTABLISHED 1000 815824701 213378/soffice.bin
In the documentation of older unoconv this resource is mentioned: [Tutorial] Import uno module to a different Python install http://user.services.openoffice.org/en/forum/viewtopic.php?f=45&t=36370&p=166783
As far as I understand this is irrelevant for this case as libreoffice uses system python anyway in linux.
I tried to run wget -O find_uno.py https://gist.githubusercontent.com/regebro/036da022dc7d5241a0ee97efdf1458eb/raw/find_uno.py python3 find_uno.py
as shown in unoserver
readme
and it only gave me system python path.
What am I missing? Any hints or suggestions are appreciated.
Also if possible could someone explain the sequence of:
context, resolver,ctx, smgr, desktop
and what do these object means in layman terms?
(Answer adapted from the comments which led to resolution of the problem):
Be sure to work through the tutorial at http://christopher5106.github.io/office/2015/12/06/openoffice-libreoffice-automate-your-office-tasks-with-python-macros.html.
From the comments, it sounds like you're stuck somewhere in the tutorial. Did you successfully enter the following in a terminal, and can you see the resulting window:
soffice --calc --accept="socket,host=localhost,port=2002;urp;StarOffice.ServiceManager"
You also asked for more information about pylint fakes. After further reading, I do not think this will be relevant for you. Anyway, for example if pylint complains about UNO objects when using a separate python distribution on Windows for static code checking, a workaround is to create a pylint-fakes
directory as described at https://stackoverflow.com/a/9616857/5100564. For example, if pylint does not find XActionListener, you could create a file named pylint-fakes/com/sun/star/awt/__init__.py
with the following mock code:
class XActionListener():
pass