how to run libreoffice python script using scriptforge - python

I am trying to organize my python project that currently only adds a menu to a libreoffice calc menu bar:
# this file is called AddMenu.py which is located in 'C:\Program Files\LibreOffice\share\Scripts\python'
def create_menu(args=None):
o_doc = CreateScriptService("Document")
o_menu = o_doc.CreateMenu("Test Menu")
o_menu.AddItem("About", command="About")
o_menu.AddItem("Testing",
script="vnd.sun.star.script:AddMenu.py$item_b_listener?language=Python&location=user")
def item_b_listener(args):
bas = CreateScriptService("Basic")
s_args = args.split(",")
msg = f"Menu name: {s_args[0]}\n"
msg += f"Menu item: {s_args[1]}\n"
msg += f"Item ID: {s_args[2]}\n"
msg += f"Item status: {s_args[3]}"
bas.MsgBox(msg)
The menu and buttons are added as expected, and About works fine. However, when I click on the "Test Menu" I get an error:
Library : ScriptForge
Service : Session
Method : ExecutePythonScript
Arguments: [Scope], Script, arg0[, arg1] ...
A serious error has been detected in your code on argument : « Script ».
The requested Python script could not be located in the given libraries and modules.
« Scope » = user
« Script » = AddMenu.py$item_b_listener
THE EXECUTION IS CANCELLED.
Do you want to receive more information about the 'ExecutePythonScript' method ?
Any suggestions?
Follow up question: how do I run a python script when calc starts? Since the menu doesn't persist on restarting calc, I need to run the macro to reinstall it

&location=user
That indicates the LibreOffice user profile directory, for example C:\Users\(your username)\AppData\Roaming\LibreOffice\4\user\Scripts\python.
So either change that part to &location=application or move the script to the user profile directory.
https://help.libreoffice.org/latest/sq/text/sbasic/python/python_locations.html

It looks like the script is unable to locate the item_b_listener function. The script that is trying to call the function item_b_listener is located in a different location than the definition of the function. To resolve this issue, you need to make sure that the script is able to locate the function.
One way to do this is to create a separate Python module (file) containing the item_b_listener function, and then import that module into AddMenu.py.
Regarding your follow-up question, you can run a python script when LibreOffice Calc starts by adding it to the Calc startup macro. To do this, follow these steps:
Go to the Tools menu and select Macros > Organize Macros > Python.
In the Python macro dialog, select your script and click on the Edit button.
In the Python macro editor, add the following code to the global section of the script:
def Main():
# your script code here
Save and close the macro editor.
In the Python macro dialog, click on the Run button to test your script.
To make sure your script runs every time Calc starts, go to Tools > Options > LibreOffice > Advanced and check the box for "Run macro from an autoexec section."
Save the options and restart Calc.
Now, your script should run every time Calc starts.

Related

name 'Application' not defined

I'm trying to open an excel workbook and run a macro using python. However, the macro exists in my Personal Macro Library and I need to run it from there. When I try to do that I got the following error:
"Cannot run the macro Macro may not be available in this workbook..."
To remedy this problem, I used Application.run() hoping that this would allow me to run a macro not present in the workbook I am trying to run it on. However, when I do this I get another error:
"name 'Application' is not defined"
Here is my code below. It's real simple.
.
.
.
# Open workbook
wb2 = xw.Book('C:/Users/AChakrav/Documents/LTspiceXVII/Circuits/INV/INVAMP_MEAS_Script.xlsm')
time.sleep(5)
Application.Run "'PERSONAL.XLSB'!CompiledTableGenerator"
#ExcelMacro_2 = wb2.macro('PERSONAL.XLSB!CompiledTableGenerator')
#ExcelMacro_2()
time.sleep(5)
You may take a look at the problem from a different angle - e.g., try to refer the Personal.xlsb file in Python and give it a try from there. E.g., to locate it, run this in the immediate window in VBA:
?Application.StartupPath
and hardcode it later in the Python code.
Then write something simple as this one in Personal.xlsb:
In Python, install pipiwin32 and run the code like this:
import win32com.client
xl=win32com.client.Dispatch('Excel.Application')
xl.Workbooks.Open('C:\\Users\\gropc\\AppData\\Roaming\\Microsoft\\Excel\\XLSTART\\Personal.xlsb')
xl.Application.Run('Personal.xlsb!Modul1.SomeApplication')
The message box with the date and time will show up. The VBA code should be well written, to make sure it refers correctly the workbooks you need, but this is another issue.

Python: Passing values from GUI to other python script

I have a python script (we'll say "script.py") that I want to grab values from using a separate GUI python script ("GUI.py"). When I run my GUI.py script, I want to have these text fields in the GUI sent over to script.py after clicking a button in the GUI. I am thinking that my best solution might be to have the GUI.py create another script ("lib.py") that holds all these values and it just writes to it. It then runs the "script.py" script. So all in all the GUI.py will have a function that when called will look something like this:
def on_button(self):
username = self.usernameInput.get()
file = open(“lib.py”,”w”)
file.write(“username = ” + username)
os.system("script.py")
I think this will work, but I am just wondering, does this seem like a practical solution? Let me know what you guys think.
No, I don't think this is the practical solution.
Do you consider instead making the python script you want to run into a module or package that you can call directly inside your GUI? I think that is the cleanest approach. For using your scripts as modules, see the docs or for 2.7.
Basically a module is a python file, script.py, and as long as it is in the python path (say, your current directory), you can import it:
from script import action
So you could try:
def on_button(self):
username = self.usernameInput.get()
result = action(username) # and any other args you want to pass
print(result)
That is, if the script in question uses a if __name__ == "__main__": statement (or can otherwise be run from the command line), try putting the operations in some def action(args): function and importing it into your GUI.

Running Libreoffice BASIC macro from python

I've a macro in LibreOffice BASIC and I want to run it from my python program. I've found some threads in which they use this code:
import os
import win32com.client
if os.path.exists("excelsheet.xlsm"):
xl=win32com.client.Dispatch("Excel.Application")
xl.Workbooks.Open(Filename="C:\Full Location\To\excelsheet.xlsm", ReadOnly=1)
xl.Application.Run("excelsheet.xlsm!modulename.macroname")
## xl.Application.Save() # if you want to save then uncomment this line and change delete the ", ReadOnly=1" part from the open function.
xl.Application.Quit() # Comment this out if your excel script closes
del xl
But this is for windows Excell program and I want for the LibreOffice program. Is it possible to do this?
Thanks :)
My preferred way is to put the python script in the Scripts/python subfolder of your LibreOffice user directory. Then add this function at the bottom:
def call_basic_macro():
document = XSCRIPTCONTEXT.getDocument()
frame = document.getCurrentController().getFrame()
ctx = XSCRIPTCONTEXT.getComponentContext()
dispatcher = ctx.ServiceManager.createInstanceWithContext(
'com.sun.star.frame.DispatchHelper', ctx)
url = document.getURL()
macro_call = ('macro:///Standard.Module1.Macro1("%s")' % url)
dispatcher.executeDispatch(frame, macro_call, "", 0, ())
g_exported_scripts=call_basic_macro,
Now run the python script from Writer by going to Tools -> Macros -> Run Macro. Expand My Macros and select the name of the script.
Another way which seems closer to your Excel example is to start a listening instance of LibreOffice with a system call:
start soffice -accept=socket,host=0,port=2002;urp;
I typically do that part in a shell script (batch file on Windows) rather than python. Then in python, get the document context from the instance:
import uno
localContext = uno.getComponentContext()
After that, the code would look similar to what is above. Note that with this approach on Windows, the python.exe included with LibreOffice must be used in order to load the uno module.
A third way is to simply do a system call:
soffice "macro:///Standard.Module1.Macro1()"
For more on this third approach, see https://forum.openoffice.org/en/forum/viewtopic.php?f=20&t=8232.

Open specific file type with Python script?

How can I make a Python script to be a specific file type's (e.g., *.foo) default application? As in, when I double click the file in the Finder / Explorer I want the file to open in the Python script.
Is this possible to do in Win and/or OS X? The application is a PySide app if that matters.
Mac OS X
On Mac OS X you can use Automator to create an application that calls your python app and passes the input file path as a string argument. In the application workflow wizard, add action "Run Shell Script", select Pass input: as as arguments, and in the text box add:
python /path/to/my/app/myapp.py "$#"
The "$#" passes along whatever arguments were in the input (aka the selected file) as strings. As long as your script is set up to deal with the input (sys.argv) as a list of strings (the first one being the python app path), then it will work.
When you save that Automator workflow, it is treated by OS X like any other app, and you can set that app as the default for files of type "*.foo". To associate "*.foo" with that app, right click a .foo file, Get Info, Open with: Other..., choose the app you created in Automator, then click the Change All... button.
Windows
A similar but hopefully less-involved approach might work in Windows. You could probably create a batch file (.bat) with the following:
python C:\path\to\my\app\myapp.py %*
The %* expands to all arguments.
As long as you can associate a file extension with that batch file, then you could do that, and that's your solution. However, I haven't tried this Windows solution, so take it with a grain of salt. The Mac solution, on the other hand, I have tested.
By example, here's a universal solution I wrote for:
1) opening a Windows desktop link (*.URL) that's been copied to a Linux box.
Or
2) opening a Linux .Desktop link that's been copied to a Windows box.
Here's the Python script that handles both cases:
# UseDesktopLink.py
import sys
import webbrowser
script, filename = sys.argv
file_object = open(filename,'r')
for line in file_object:
if line[0:4]=="URL=":
url=line[4:]
webbrowser.open_new(url)
file_object.close()
On Windows, use Scott H's method (via a bat file) to handle the association.
On Linux, right-click a Windows URL file. Choose Properties, and Open With. Click Add to add a new application. Then at the bottom of the "Add Application" window, click "Use a custom command". Then browse to the UseDesktopLink.py file and click Open. But before you click Add, in the textbox below "Use a custom command", put "python " before the filename (without the quotes). Then click Add and Close.
Hope that helps.
Find any file of type foo
right-click -> Get Info or Click on the file icon,then click Get info or click on the file and hit Command+I
In the Open With pane that shows up, select the path to the python binary
Once selected, You can click the change All button
It'll ask for confirmation, just say continue
I found this old question while looking for an answer myself, and I thought I would share my solution. I used a simple c program to direct the arguments to a python script, allowing the python script to stay a script instead of needing to compile it to make things work. Here is my c program:
int main(int argc, char *argv[]){
char cmd[0xFF];
// For me, argv[1] is the location of the file that is being opened. I'm not sure if this is different on other OSes
snprintf(cmd,sizeof cmd,"python YOUR_PYTHON_SCRIPT_HERE.py -a %s", argv[1]);
system(cmd);
return 0;
}
I then compiled the c program and set that as the default application for the file extension.
Then, in the python script YOUR_PYTHON_SCRIPT_HERE.py, I receive the argument like this:
import sys
assert len(sys.argv) > 2 # Breaks if you call the script without the arguments
theFile = " ".join(sys.argv[2:]) # What the c program gives us
print(theFile) # Print it out to prove that it works
theFile will contain the location of the file that is being opened
Get the contents of the file by using:
with open(theFile,"r") as f:
fileContents = f.read()
On Windows:
Right click the file (I used a .docx file for this example)
Select Open with...
From the applications list, select python
Optional: Select the Always use the selected program to open this kind of file.
Note: this will run the contents of the .docx file in context of the python shell. It will immediately close once it is finished evaluating the contents of the file. If you'd like to edit the file in a word processor, perhaps you should download notepad++, and select that application as the default.

Adding context menu option through Python

I'm trying to make a small Python script that is executed by clicking an option in a file's context menu. It would execute something like "path_to_script %L", where %L is (I think) the location of the file the user has right-clicked. I know I have to add something to the registry for that option to appear, but _winreg is getting confusing. What do I have to do to add a registry entry (through Python) so my script can be called like that?
I dont know how you can remove from registry (probably manually or _winreg) but you can follow a way like that per your custom python script to registration to windows.
registerOne.reg
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\*\shell\One]
[HKEY_CLASSES_ROOT\*\shell\One\command]
#="python.exe one.py \"%1\""
one.py
def registerScriptToContextMenu ():
# http://support.microsoft.com/kb/310516
cmdLine = 'regedit.exe registerOne.reg'
import os
os.system(cmdLine)
def one_main (*args):
pass

Categories

Resources