Calling python script from excel/vba - python

I have a python code that reads 3 arguments (scalars) and a text files and then returns me a vector of double. I want to write a macro in vba to call this python code and write the results in one of the same excel sheet. I wanted to know what was the easiest way to do it, here are some stuffs that I found:
call the shell() function in vba but it doesn't seem so easy to get the return value.
register the python code as a COM object and call it from vba--> i don't know how to do that so if you have some examples it would be more than welcome
create a custom tool in a custom toolbox, in vba create a geoprocessing object and then addtoolbox and then we can use the custom tool directly via the geoprocessing object but this is something as well that I don't know how to do..
Any tips?

Follow these steps carefully
Go to Activestate and get ActivePython 2.5.7 MSI installer.
I had DLL hell problems with 2.6.x
Install in your Windows machine
once install is complete open Command Prompt and go to
C:\Python25\lib\site-packages\win32comext\axscript\client
execute \> python pyscript.py
you should see message Registered: Python
Go to ms office excel and open worksheet
Go to Tools > Macros > Visual Basic Editor
Add a reference to the Microsoft Script control
Add a new User Form. In the UserForm add a CommandButton
Switch to the code editor and Insert the following code
Dim WithEvents PyScript As
MSScriptControl.ScriptControl
Private Sub CommandButton1_Click()
If PyScript Is Nothing Then
Set PyScript = New MSScriptControl.ScriptControl
PyScript.Language = "python"
PyScript.AddObject "Sheet", Workbooks(1).Sheets(1)
PyScript.AllowUI = True
End If
PyScript.ExecuteStatement "Sheet.cells(1,1).value='Hello'"
End Sub
Execute. Enjoy and expand as necessary

Do you have to call the Python code as a macro? You could use COM hooks within the Python script to direct Excel and avoid having to use another language:
import win32com.client
# Start Excel
xlApp = win32com.client.Dispatch( "Excel.Application" )
workbook = xlApp.Workbooks.Open( <some-file> )
sheet = workbook.Sheets( <some-sheet> )
sheet.Activate( )
# Get values
spam = sheet.Cells( 1, 1 ).Value
# Process values
...
# Write values
sheet.Cells( ..., ... ).Value = <result>
# Goodbye Excel
workbook.Save( )
workbook.Close( )
xlApp.Quit( )

Here is a good link for Excel from/to Python usage:
continuum.io/using-excel
mentions xlwings, DataNitro, ExPy, PyXLL, XLLoop, openpyxl, xlrd, xlsxwriter, xlwt
Also I found that ExcelPython is under active development.
https://github.com/ericremoreynolds/excelpython
2.
What you can do with VBA + Python is following:
Compile your py scripts that take inputs and generate outputs as text files or from console. Then VBA will prepare input for py, call the pre-compiled py script and read back its output.
3.
Consider Google Docs, OpenOffice or LibreOffice which support Python scripts.
This is assuming that available options with COM or MS script interfaces do not satisfy your needs.
This is not free approach, but worth mentioning (featured in Forbes and New York Times):
https://datanitro.com
This is not free for commercial use:
PyXLL - Excel addin that enables functions written in Python to be called in Excel.

Updated 2018
xlwings is a BSD-licensed Python library that makes it easy to call Python from Excel and vice versa.
Scripting: Automate/interact with Excel from Python using a syntax that is close to VBA.
Macros: Replace your messy VBA macros with clean and powerful Python code.
UDFs: Write User Defined Functions (UDFs) in Python (Windows only).
Installation
Quickstart

There's a tutorial on CodeProject on how to do this.
See http://www.codeproject.com/Articles/639887/Calling-Python-code-from-Excel-with-ExcelPython.

Another open source python-excel in process com tool.
This allows executing python scripts from excel in a tightly integrated manner.
https://pypi.python.org/pypi/Python-For-Excel/beta,%201.1

Related

Can LibreOffice/OpenOffice programmatically add passwords to existing .docx/.xlsx/.pptx files?

TL;DR version - I need to programmatically add a password to .docx/.xlsx/.pptx files using LibreOffice and it doesn't work, and no errors are reported back either, my request to add a password is simply ignored, and a password-less version of the same file is saved.
In-depth:
I'm trying to script the ability to password-protect existing .docx/.xlsx/.pptx files using LibreOffice.
I'm using 64-bit LibreOffice 6.2.5.2 which is the latest version at the time of writing, on Windows 8.1 64-bit Professional.
Whilst I can do this manually via the UI - specifically, I open the "plain" document, do "Save As" and then tick "Save with Password", and enter the password in there, I cannot get this to work via any kind of automation. I'm been trying via Python/Uno, but to no gain. Although the code below correctly opens and saves the document, my attempt to add a password is completely ignored. Curiously, the file size shrinks from 12kb to 9kb when I do this.
Here is my code:
import socket
import uno
import sys
localContext = uno.getComponentContext()
resolver = localContext.ServiceManager.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", localContext)
ctx = resolver.resolve( "uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext" )
smgr = ctx.ServiceManager
desktop = smgr.createInstanceWithContext( "com.sun.star.frame.Desktop",ctx)
from com.sun.star.beans import PropertyValue
properties=[]
oDocB = desktop.loadComponentFromURL ("file:///C:/Docs/PlainDoc.docx","_blank",0, tuple(properties) )
sp=[]
sp1=PropertyValue()
sp1.Name='FilterName'
sp1.Value='MS Word 2007 XML'
sp.append(sp1)
sp2=PropertyValue()
sp2.Name='Password'
sp2.Value='secret'
sp.append(sp2)
oDocB.storeToURL("file:///C:/Docs/PasswordDoc.docx",sp)
oDocB.dispose()
I've had great results using Python/Uno to open password-protected files, but I cannot get it to protect a previously unprotected document. I've tried enabling the macro recorder and recording my actions - it recorded the following LibreOffice BASIC code:
sub SaveDoc
rem ----------------------------------------------------------------------
rem define variables
dim document as object
dim dispatcher as object
rem ----------------------------------------------------------------------
rem get access to the document
document = ThisComponent.CurrentController.Frame
dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")
rem ----------------------------------------------------------------------
dim args1(2) as new com.sun.star.beans.PropertyValue
args1(0).Name = "URL"
args1(0).Value = "file:///C:/Docs/PasswordDoc.docx"
args1(1).Name = "FilterName"
args1(1).Value = "MS Word 2007 XML"
args1(2).Name = "EncryptionData"
args1(2).Value = Array(Array("OOXPassword","secret"))
dispatcher.executeDispatch(document, ".uno:SaveAs", "", 0, args1())
end sub
Even when I try to run that, it...saves an unprotected document, with no password encryption. I've even tried converting the macro above into the equivalent Python code, but to no avail either. I don't get any errors, it simply doesn't protect the document.
Finally, out of desperation, I've even tried other approaches that don't include LibreOffice, for example, using the Apache POI library as per the following existing StackOverflow question:
Python or LibreOffice Save xlsx file encrypted with password
...but I just get an error saying "Error: Could not find or load main class org.python.util.jython". I've tried upgrading my JDK, tweaking the paths used in the example, i.e. had an "intelligent" go, but still no joy. I suspect the error above is trivial to fix, but I'm not a Java developer and lack the experience in this area.
Does anyone have any solution? Do you have some LibreOffice code that can do this (password-protect .docx/.xlsx/.pptx files)? Or OpenOffice for that matter, I'm not precious about which package I use. Or something else entirely!
NOTE: I appreciate this is trivial using full-fat Microsoft Office, but thanks to Microsoft's licensing restrictions, is a complete no-go for this project - I have to use an alternative.
The following example is from page 40 (file page 56) of Useful Macro Information
For OpenOffice.org by Andrew Pitonyak (http://www.pitonyak.org/AndrewMacro.odt). The document is directed to OpenOffice.org Basic but is generally applicable to LibreOffice as well. The example differs from the macro recorder version primarily in its use of the documented API rather than dispatch calls.
5.8.3. Save a document with a password
To save a document with a password, you must set the “Password”
attribute.
Listing 5.19: Save a document using a password.
Sub SaveDocumentWithPassword
Dim args(0) As New com.sun.star.beans.PropertyValue
Dim sURL$
args(0).Name ="Password"
args(0).Value = "test"
sURL=ConvertToURL("/andrew0/home/andy/test.odt")
ThisComponent.storeToURL(sURL, args())
End Sub
The argument name is case sensitive, so “password” will not work.

Python libreoffice set margin values, optimal height and print document with uno

I trying to interact with libreoffice with Python, which integrated in Libreoffice installation. And I didn't found anywhere how can I set margins in PageStyle, to set optimal height of row and prind few copies of document. Or, maybe, I can write macro in Libreoffice and run it from python. Code below is not working.
pageStyle = document.getStyleFamilies().getByName("PageStyles")
page = pageStyle.getByName("Default")
page.LeftMargin = 500
P.S. Sorry for my english.
In most versions of LibreOffice, the name of the default style is "Default Style". In Apache OpenOffice, it is named "Default" instead.
Here is the complete code. For example, name the file change_settings.py.
import uno
def set_page_style_margins():
document = XSCRIPTCONTEXT.getDocument()
pageStyle = document.getStyleFamilies().getByName("PageStyles")
page = pageStyle.getByName("Default Style")
page.LeftMargin = 500
g_exportedScripts = set_page_style_margins,
On my Windows 10 system, this script is located under the directory C:\Users\<your username>\AppData\Roaming\LibreOffice\4\user\Scripts\python. You will need to create the last two directories, and case must match.
Now, in LibreOffice Writer, go to Tools -> Macros -> Run Macro. Expand to My Macros -> change_settings and select the macro name set_page_style_margins.
For a full introduction to Python with LibreOffice:
https://wiki.openoffice.org/wiki/Python/Transfer_from_Basic_to_Python
http://christopher5106.github.io/office/2015/12/06/openoffice-libreoffice-automate-your-office-tasks-with-python-macros.html

xlwings doesn't write data in file.xlsm when running through excel

I am trying to use xlwings for simple consolidation from several xls files.
For this, I have a all_files.xlsm file that contains a button with macros assigned to it. The macros looks like:
Sub check_data()
RunPython ("import python_code; python_code.consolidation()")
End Sub
In same folder I have a file python_code.py with function "consolidation" inside.
I also use Workbook.set_mock_caller() in order to have an opportunity to run a code through python interface. It looks like:
def consolidation(file_path):
*** smth to get the data I need ***
...
*** after I got data ***
Range('A1').table.clear_contents() #string1
Range('A1').value = data #string2
def main():
consolidation(file_path)
if __name__ == '__main__':
xl.Workbook.set_mock_caller(path_to_file)
main()
The problem is that when I am running the script through the button in excel file last two strings (string1 and string2) - Range('A1').table.clear_contents() and Range('A1').value = data doesn't work. Although the rest of the code works fine (however, it contains xlwing also). Moreover, if I run the script through the python interface using set mock caller, it works just fine, including string1 and string2 (marked in the code).
Any help and advices are really appreciated!
I found decision for my problem. For using xlwings in python you should import xlwings.bas in your excel file (see manual for xlwings). It turns out, that I imported it for this macros before I've updated xlwings. So I deleted this file and imported a new one. Everything works fine now.

Use Python to Write VBA Script?

This might be a bit of a stretch, but is there a possibility that a python script can be used to create VBA in MS Excel (or any other MS Office product that uses VBA) using pythonwin or any other module.
Where this idea came from was pythons openpyxl modules inability to do column autowidth. The script I have creates a workbook in memory and eventually saves it to disc. There are quite a few sheets and within each sheet, there are quite a few columns. I got to thinking....what if I just use python to import a VBA script (saved somewhere in notepad or something) into the VBA editor in excel and then run that script from python using pythonwin.
Something like:
Workbooks.worksheets.Columns("A:Z").EntireColumn.Autofit
Before you comment, yes I have seen lots of pythonic examples of how to work around auto adjusting columns in openpyxl, but I see some interesting opportunities that can be had utilizing the functionality you get from VBA that may not be available in python.
Anyways, I dug around the internet a bit and I didn't see anything that indicates i can, so i thought I'd ask.
Cheers,
Mike
Yes, it is possible. You can start looking at how you can generate a VBA macro from VB on that Microsoft KB.
The Python code below is illustrating how you can do the same ; it is a basic port of the first half of the KB sample code:
import win32com.client as win32
import comtypes, comtypes.client
xl = win32.gencache.EnsureDispatch('Excel.Application')
xl.Visible = True
ss = xl.Workbooks.Add()
sh = ss.ActiveSheet
xlmodule = ss.VBProject.VBComponents.Add(1) # vbext_ct_StdModule
sCode = '''sub VBAMacro()
msgbox "VBA Macro called"
end sub'''
xlmodule.CodeModule.AddFromString(sCode)
You can look at the visible automated Excel macros, and you will see the VBAMacro defined above.
The top answer will only add the macro, if you actually want to execute it there is one more step.
import win32com.client as win32
xl = win32.gencache.EnsureDispatch('Excel.Application')
xl.Visible = True
ss = xl.Workbooks.Add()
xlmodule = ss.VBProject.VBComponents.Add(1)
xlmodule.Name = 'testing123'
code = '''sub TestMacro()
msgbox "Testing 1 2 3"
end sub'''
xlmodule.CodeModule.AddFromString(code)
ss.Application.Run('testing123.TestMacro')
Adding a module name will help deconflict from any existing scripts.

Using Python to read VBA from an Excel spreadsheet

I would like to write a VBA diff program in (preferably) Python. Is there a Python library that will allow me to read the VBA contained in an Excel spreadsheet?
Here's some quick and dirty boilerplate to get you started. It uses the Excel COM object (a Windows only solution):
from win32com.client import Dispatch
wbpath = 'C:\\example.xlsm'
xl = Dispatch("Excel.Application")
xl.Visible = 1
wb = xl.Workbooks.Open(wbpath)
vbcode = wb.VBProject.VBComponents(1).CodeModule
print vbcode.Lines(1, vbcode.CountOfLines)
This prints the silly macro I recorded for this example:
Sub silly_macro()
'
' silly_macro Macro
'
'
Range("B2").Select
End Sub
Note that Lines and VBComponents use 1-based indexing. VBComponents also supports indexing by module name. Also note that Excel requires backslashes in paths.
To dive deeper see Pearson's Programming The VBA Editor. (The above example was cobbled together from what I skimmed from there.)
I have created an application that does this called VbaDiff. If you provide it two Excel files it will compare the VBA code in each. You can also run it from the command line, or use the version that comes with an API if you want to integrate it with your own programs.
You can find out more at http://www.technicana.com/vbadiff-information.html
Chris

Categories

Resources