Add menu to libreoffice that persists app restarts via a script - python

I'd like to run one script once that adds a menu that persists through LibreOffice restarts.
I understand how to add a menu to LibreOffice as posted here (but it disappears on app restart).
I see that it's possible to modify the start macro manually as posted here.
Is it possible to modify the start script and add it to the autoexec section via a single script?
I tried to add the menu manually (as in Tools > Macros > Customize) but my (latest 7.4.5.1 x64) version does not have a Customize section of the Macros menu.
I am feeling very foolish as I can't see where you are suggesting to add the start macro
I'm trying this code (which is supposed to add a popup to the startup), but the length of basic_libraries is always zero:
import uno
from com.sun.star.beans import PropertyValue
print('here 1')
# Connect to LibreOffice
localContext = uno.getComponentContext()
print('here 1.1')
resolver = localContext.ServiceManager.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", localContext)
print('here 1.2')
ctx = resolver.resolve("uno:socket,host=localhost,port=8100;urp;StarOffice.ComponentContext")
print('here 1.3')
smgr = ctx.ServiceManager
print('here 2')
# Open a new document
desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx)
doc = desktop.loadComponentFromURL("private:factory/scalc", "_blank", 0, ())
print('here 3')
# Add a start macro
macro_name = "Module1.start"
macro_code = "Sub Main\n msgbox \"Hello World!\"\nEnd Sub"
basic_libraries = doc.BasicLibraries
print(basic_libraries)
print(len(basic_libraries))
for lib in basic_libraries:
print(f'checking {lib}')
if lib.Name == "Standard":
print('adding macro')
lib.createModule(macro_name, macro_code)
break
print('here 4')
# Save the document
prop = PropertyValue()
prop.Name = "FilterName"
prop.Value = "writer_pdf_Export"
print('here 5')
doc.dispose()
Before running the script, I am starting scalc like this:
scalc.exe --accept=socket,host=127.0.0.1,port=8100;urp
My goal is to run the python script that adds the menu instead of the popup (so if you have any idea how to replace it, I would appreciate an help!

The Start Application event can be set to run a macro by going to Tools > Customize and saving under "LibreOffice" in the dropdown list. This took me two tries to successfully apply and save, so it seems a bit tricky.
If you don't want to set up the event manually, according to this post by librebel, the following code will perform the setup, which you could translate to python if desired.
Sub connect_Start_Application_Event()
REM Call this macro once to programmatically connect the LibreOffice "Start Application" event to the Basic macro `on_ApplicationStart()`.
REM
REM #**** Specify here the Name, Module, and Library of your Basic Macro to be called whenever LibreOffice starts:
REM #**** This macro should be located inside your "[My Macros & Dialogs].Standard" library:
REM #**** This macro should be defined with an Event Object as the first parameter:
Const sMacro As String = "on_ApplicationStart" REM The Basic Macro to be called when the Application starts.
Const sModule As String = "Module1" REM The name of the Basic Module that contains the above Macro.
Const sLibrary As String = "Standard" REM The name of the Basic Library that contains the above Module.
Dim aProps(1) As New com.sun.star.beans.PropertyValue
aProps(0).Name = "EventType"
aProps(0).Value = "Script"
aProps(1).Name = "Script"
aProps(1).Value = "vnd.sun.star.script:" & sLibrary & "." & sModule & "." & sMacro & "?language=Basic&location=application"
Dim oGlobalEventBroadcaster As Object
oGlobalEventBroadcaster = GetDefaultContext().getByName( "/singletons/com.sun.star.frame.theGlobalEventBroadcaster" )
oGlobalEventBroadcaster.Events.replaceByName( "OnStartApp", aProps() )
Msgbox "Application Event Connected: <OnStartApp> :---> " & sLibrary & "." & sModule & "." & sMacro
End Sub
However, if what you're after is a persistent menu that calls python-uno code, the normal way is to create an extension. Once installed, the menu will persist until it is uninstalled.
For example, here is part of an Addons.xcu file that adds a menu called "Linguistics", localized depending on the UI language of LO. In this case, the option "Phonology Settings" calls a service (technically not a macro, but it does the same things) written in python-uno that is defined in the extension.
<?xml version="1.0" encoding="UTF-8"?>
<oor:component-data xmlns:oor="http://openoffice.org/2001/registry"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
oor:name="Addons" oor:package="org.openoffice.Office">
<node oor:name="AddonUI">
<node oor:name="OfficeMenuBar">
<node oor:name="name.JimK.LinguisticTools" oor:op="replace">
<prop oor:name="Title" oor:type="xs:string">
<value/>
<value xml:lang="en">Linguistics</value>
<value xml:lang="es">Lingüística</value>
<value xml:lang="fr">Linguistique</value>
</prop>
<prop oor:name="Target" oor:type="xs:string">
<value>_self</value>
</prop>
<prop oor:name="ImageIdentifier" oor:type="xs:string">
<value/>
</prop>
<node oor:name="Submenu">
<node oor:name="m01" oor:op="replace">
<prop oor:name="URL" oor:type="xs:string">
<value>service:name.JimK.LinguisticTools.PhonologySettings?execute</value>
</prop>
<prop oor:name="Title" oor:type="xs:string">
<value/>
<value xml:lang="en">Phonology Settings</value>
<value xml:lang="es">Configuración de fonología</value>
<value xml:lang="fr">Configuration de phonologie</value>
</prop>
<prop oor:name="Target" oor:type="xs:string">
<value>_self</value>
</prop>
<prop oor:name="Context" oor:type="xs:string">
<value>com.sun.star.text.TextDocument</value>
</prop>
</node>
Even if you don't want to make an extension, using Start Application to continually re-create a menu probably isn't the best idea. Instead, write code to customize the menu through configuration, which will then be persistent, for example:
oModuleCfgMgrSupplier = createUnoService(_
"com.sun.star.ui.ModuleUIConfigurationManagerSupplier")
oModuleCfgMgr = oModuleCfgMgrSupplier.getUIConfigurationManager( ModuleName )
oMenuBarSettings = oModuleCfgMgr.getSettings( sMenuBar, true )
The full code is at: https://wiki.openoffice.org/wiki/API/Samples/StarBasic/Office/Favorites-Menu
Here is an example of a property value.
from com.sun.star.beans import PropertyValue
def createProp(name, value):
"""Creates an UNO property."""
prop = PropertyValue()
prop.Name = name
prop.Value = value
return prop
uno_args = (
createProp("Minimized", True),
createProp("FilterName", "Text"),
)

Related

Parse xsi:type in XML with ElementTree in Python

I'm trying to connect to a RESTful API and I'm hacing problems when building the XML request, for that I'm using Elementree library.
I have an example of the XML I have to send in the request. From that example a build a model and then write the different attributes by code. But the output XML is not exactly like the example I was given and I'm unable to connect to the API.
This is the example I have:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetLoc xmlns="http://abc/Getloc">
<request>
<Access>
<string xmlns="http://bcd/Arrays"></string>
</Access>
<Details xsi:type="Request">
<Postcode ></Postcode >
</Details>
<UserConsent>Yes</UserConsent>
</request>
</GetLoc>
</soap:Body>
</soap:Envelope>
This is my code:
tree = ET.parse('model.xml')
root = tree.getroot()
ns = {'loc':'http://abc/Getloc',\
'arr':http://bcd/Arrays',\
'soapenv':'http://schemas.xmlsoap.org/soap/envelope/', \
'xsi':"http://www.w3.org/2001/XMLSchema-instance", \
xsd': "http://www.w3.org/2001/XMLSchema"}
tree.find('.//arr:string', ns).text = 'THC'
tree.find('.//Postcode ', ns).text = '15478'
This is the output XML (SOAP):
<ns0:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://abc/Getloc" xmlns:ns2="http://bcd/Arrays" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ns0:Body>
<ns1:GetLoc >
<ns1:request>
<ns1:Access>
<ns2:string>THC</ns2:string>
</ns1:Access>
<ns1:Details xsi:type="Request">
<ns1:Postcode >15478</ns1:Postcode >
</ns1:Details>
<ns1:UserConsent>Yes</ns1:UserConsent>
</ns1:request>
</ns1:GetLoc >
</ns0:Body>
</ns0:Envelope>
With the example (first above) I have no problem when connecting to the API. However with the second one I get and error:
" status="Service Not Found. The request may have been sent to an invalid URL, or intended for an unsupported operation." xmlns:l7="http://www.layer7tech.com/ws/policy/fault"/>"
Both XML are sent to the same URL with the same headers and auth. I see both XML equivalent so I was expecting same behavior. I don't understand why it isn't working.
EDIT: The output XML needs to be like
<ns0:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://abc/Getloc" xmlns:ns2="http://bcd/Arrays" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ns0:Body>
<ns1:GetLoc >
<ns1:request>
<ns1:Access>
<ns2:string>THC</ns2:string>
</ns1:Access>
<ns1:Details xsi:type="ns1:Request">
<ns1:Postcode >15478</ns1:Postcode >
</ns1:Details>
<ns1:UserConsent>Yes</ns1:UserConsent>
</ns1:request>
</ns1:GetLoc >
</ns0:Body>
</ns0:Envelope>
But I don't know hoy to change the code to get: xsi:type="ns1:Request"
Finally I found the solution myself.
The solution is in here (an incredibly complete article), since I was already using ElementTree. You may find other solutions like using lxml library.
So, for ElementTree I just need to use my own parser instead of the standard ElementTree.parse('file.xml').
The xsi attribute name is handled by the parser, but the parser doesn’t know that the attribute happens to contain a qualified name as well, so it leaves it as is. To be able to handle such a format, you can use a custom parser that knows how to handle certain attributes and elements, or keep track of the prefix mapping for each element.
To do the latter, you can use the iterparse parser, and ask it to report “start-ns” and “end-ns” events. The following snippet adds an ns_map attribute to each element which contains the prefix/URI mapping that applies to that specific element:
def parse_map(file):
events = "start", "start-ns", "end-ns"
root = None
ns_map = []
for event, elem in ET.iterparse(file, events):
if event == "start-ns":
ns_map.append(elem)
elif event == "end-ns":
ns_map.pop()
elif event == "start":
if root is None:
root = elem
elem.ns_map = dict(ns_map)
return ET.ElementTree(root)

how to handle web SQL queries and xml replies in Python

I have a distant database on which I can send SQL select queries through a web service like this:
http://aa.bb.cc.dd:85/SQLWEB?query=select+*+from+machine&output=xml_v2
which returns
<Query>
<SQL></SQL>
<Fields>
<MACHINEID DataType="Integer" DataSize="4"/>
<NAME DataType="WideString" DataSize="62"/>
<MACHINECLASSID DataType="Integer" DataSize="4"/>
<SUBMACHINECLASS DataType="WideString" DataSize="22"/>
<DISABLED DataType="Integer" DataSize="4"/>
</Fields>
<Record>
<MACHINEID>1</MACHINEID>
<NAME>LOADER</NAME>
<MACHINECLASSID>16</MACHINECLASSID>
<SUBMACHINECLASS>A</SUBMACHINECLASS>
<DISABLED>0</DISABLED>
</Record>
<Record>
...
</Record>
...
</Query>
Then I need to insert the records into a local SQL database.
What's the easiest way ? Thanks !
First of all, querys in the url it's a horrible idea for security.
Use xml libs to parse the xml, and then iterate over the result to add to the db.
import xml.etree.ElementTree as ET
tree = ET.parse('xml file')
root = tree.getroot()
# root = ET.fromstring(country_data_as_string) if you use a string
for record in root.findall('Record'):
MACHINEID = record.get('MACHINEID')
NAME = record.get('NAME')
MACHINECLASSID = record.get('MACHINECLASSID')
SUBMACHINECLASS = record.get('SUBMACHINECLASS')
DISABLED = record.get('DISABLED')
#your code to add this result to the db
ElementTree XML API

How to make my PyObjC application AppleScript-able

I want to create an application on OS X with Python, that is AppleScript-able.
First I used this tutorial to create an Application (it works!). Then I used this SO answer to add AppleScript support; I tried to translate the Objective-C stuff into Python. I added a plist-item to the options in setup.py:
OPTIONS = {
#...
'plist': {
'NSAppleScriptEnabled': True,
'OSAScriptingDefinition': 'SimpleXibDemo.sdef',
#...
}
}
I created SimpleXibDemo.sdef
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dictionary SYSTEM "file://localhost/System/Library/DTDs/sdef.dtd">
<dictionary title="SimpleXibDemo">
<suite name="SimpleXibDemo Suite" code="SXiD" description="SimpleXibDemo Scripts">
<command name="increase" code="increxib" description="Increase the value">
<cocoa class="SimpleXibDemoIncreaseCommand"/>
<parameter name="by" type="integer" optional="yes">
<cocoa key="by"/>
</parameter>
<result description="Returns the new value" type="integer"/>
</command>
</suite>
</dictionary>
and I added the class SimpleXibDemoIncreaseCommand:
class SimpleXibDemoIncreaseCommand(NSScriptCommand):
def performDefaultImplementation(self):
args = self.evaluatedArguments()
nr = args.valueForKey("by")
viewController.increment()
return nr + 1
The application itself works, but when I run this AppleScript:
tell application "SimpleXibDemo"
set myResult to increase by 3
end tell
I get this error:
error "SimpleXibDemo got an error: Can’t continue increase." number -1708
I can't find any info on error number -1708. And when I open the application with Script Editor -> Open Dictionary, nothing happens.
What is the problem here? I'm a bit stuck :-/

XBMC Text are not displaying

I'm working on my python script as I would like to change the language using xml when pressing on the enter button of the keyboard.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<strings>
<string id="32000">Hello UK</string>
</strings>
<control type="label">
<description>My hello label</description>
<posx>20</posx>
<posy>20</posy>
<width>180</width>
<height>248</height>
<align>middle</align>
<font>font12</font>
<textcolor>white</textcolor>
<visible>true</visible>
<label>$LOCALIZE[SCRIPT32000]</label>
</control>
Here is the python:
import xbmc
import xbmcgui
import xbmcaddon
#get actioncodes from keyboard.xml
ACTION_ENTER = 7
class MyClass(xbmcgui.WindowXML):
def onAction(self, action):
if action == ACTION_ENTER:
if image1_enabled:
my_hello_string = ADDON.getLocalizedString(32000)
I have got a problem with my python script, because when I press on the enter button, there are no text display on the screen. There are no error on the xbmc logs. I want to add the label to get the strings that I stored in xml to display the strings on the skins. Not sure if I have missing something?
If that is your complete code, it looks like you're not doing anything with the class. You might need to add to the end of your Python code something like:
if __name__ == '__main__':
w = MyClass("myclass.xml")
w.doModal()

python - how to parse xml with different hierarchies and by specifying only 'name'

I'm learning xml parsing and Python and trying to write a function that allows me to only specify 'name' and it can search through my xml and return the text value.
Currently, my function is restricted to search only through 'GeneralSettings':
import xml.etree.ElementTree as ET
tree = ET.parse('template.xml')
def getValues(tree, category):
for prop_node in tree.iterfind("FileTemplate/properties/obj[#name='GeneralSettings']/properties/prop[#name='%s']" % category):
return prop_node.text
print getValues(tree, 'FilePattern')
Is there any good technique where I can just write one function by passing in only search 'name' and it can automatically search for everything under root like in unix, 'find . -name'? For example, if I call get text value for "useelapsedtime", can I use same function to just tell it to search for name=useelapsedtime?
My xml is like this:
<?xml version="1.0" encoding="utf-8"?>
<autostart version="2.0">
<FileState>0</FileState>
<FileTemplate clsid="{6F6FBFC1-3F14-46CA-A269}">
<properties>
<obj name="TypeSettings" clsid="{6F6FBFC1-3F14-46CA-A269}">
<properties>
<prop name="Enumerator" type="8">en0</prop>
<prop name="Name" type="8">en0</prop>
<prop name="Type" type="3">1</prop>
</properties>
</obj>
<obj name="GeneralSettings" clsid="{6F6FBFC1-3F14-46CA-A269}">
<properties>
<prop name="BufferSize" type="21">524288000</prop>
<prop name="FilePattern" type="8">auto_eth0</prop>
</properties>
</obj>
<obj name="NiCSettings" clsid="{6F6FBFC1-3F14-46CA-A269}">
<properties>
<prop name="interface" type="21">eth0</prop>
</properties>
</obj>
<obj name="Trigger" clsid="{E801FFF9-AE26-4DD7-A349">
<trigger enabled="0" notify="1" severity="0" togglecapture="1">
<triggerevents>
<triggereventobj clsid="{EC5E8097-B3D5-4B8D-AA64}">
<triggerevent use="0" useelapsedtime="0" time="0" enabled="0" />
</triggereventobj>
</triggerevents>
</trigger>
</obj>
</properties>
</FileTemplate>
Yes. Use tree.iterfind(".//*[#name='%s']"%name).
Breaking it down:
tree is the starting node. In this case, the root Element of your parse.
. means that the XPATH description starts with tree.
// means to recurse any number of levels
* means to consider any tag
[#name='foo'] means to consider only elements with attributes named foo
%s means to substitue the value of a variable into the string
So:
tree.iterfind(".//*[#name='%s']"%name) means:
Starting at tree,
and going through every level,
find any element, regardless of its tag name,
with an attribute named name,
which attribute has a value equal to the contents of the variable name.

Categories

Resources