I was wondering whether it would be possible to run an excel macro via Python (using a Mac, I specify the machine because I know that most of the codes used win32., which is not for Mac users). Did someone try to figure it out?
Have a look at xlwings. It is a well thought out python package that allows you to control an excel application from python (and vice versa). It supports both Windows and Mac. On Mac it uses psutil and appscript behind the scenes to communicate with the excel application.
The xlwings documentation gives the following example for executing an excel VBA macro from python code:
Examples
This VBA function:
Function MySum(x, y)
MySum = x + y
End Function
can be accessed like this:
>>> import xlwings as xw
>>> wb = xw.books.active
>>> my_sum = wb.macro('MySum')
>>> my_sum(1, 2)
3
Related
I'm trying to call two Python UDFs in excel via xlwings, i saw the following error message:
Make sure that this workbook contains the xlwings module and you are trusting access to the VBA project object module(Options).
Macro setting is already enabled, the workbook also referenced to xlwings. The xlwings version in Anaconda is 0.20.0, i think this is the latest version, but i can only see "import Python UDFs" button under xlwings Add-in settings, nothing else, which is different from the Add-in settings i saw in some videos. I'm using Jupyter Notebook, my python code is saved in "M:\SolverFunction.ipynb". I also saved the code in .py extension with the same name in the same directory, not sure if i can just use .ipynb extension directly. Below is my function settings in VBA, can someone please check if everything is correct:
PYTHON_WIN = "C:\Program Files\Anaconda3\pythonw.exe"
PYTHON_MAC = ""
PYTHON_FROZEN = ThisWorkbook.Path & "\build\exe.win32-2.7"
PYTHONPATH = "M:\"
UDF_MODULES = "SolverFunction"
UDF_DEBUG_SERVER = False
LOG_FILE = ""
SHOW_LOG = True
OPTIMIZED_CONNECTION = False
when i run the macro Run_Python_Function, it shows SyntaxError:
Syntax Error in Macro
I need to call two python UDFs,don't really know how to fix this error, there is no error in the python code itself.
Another question: in the python code i use 'xw.Book' to call the existing sheet
wb = xw.Book(r'M:\SolverFunction.xlsm')
If the sheet is saved in another directory with another name, say "C:\Desktop\DVA Totem Submission\xyz.xlsm", except modifying the code
wb = xw.Book(r'C:\Desktop\DVA Totem Submission\xyz.xlsm')
do i need to change the function settings in VBA as well?
Thanks,
I have written a python code, which takes 3 inputs, and return one output val.
I try to write an excel function, which passes the three inputs to the python function and returns the output.
I have looked into XLwings, but there is so many issues (and the documentation is insanely poor/poorly written) thus it seems useless.
So: is there any other way to call a python function (which takes inputs) from excel?
[SOLVED (ish):]
I managed, after roughly 8 hours of trying, 4 youtube videos and the xlwings homepage, to make it work.
Video for installing: https://training.zoomeranalytics.com/courses/xlwings/lectures/4231276
Video for making a function which takes input and returns output: https://www.youtube.com/watch?v=qn8xGrDuRCg&t=16s
You could try xlOil (disclaimer: I wrote it). The docs are here, but to write a simple three input function, you would install xlOil using:
pip install xloil
xloil install
Then write:
import xloil
#xloil.func
def myfunc(x, y, z):
return x + y * z
Put this code either:
In a py file in the same directory as your spreadsheet, named SpreadsheetName.py
In a py file on your python module path, then edit %APPDATA%\xlOil\xlOil.ini to load it as described in the docs.
Start Excel, open your spreadsheet and the function will be available as myfunc.
You could also try xlSlim (disclaimer I wrote it). The docs are here.
Installation is as simple as downloading and running a Windows installer from here The process is described in the docs and there is a YouTube video https://youtu.be/Zl5QM8rGBC8 It is straightforward, just download and run the installer. The installer is digitally signed and virus scanned.
Then to create the same function as Steve's you would write your function in a Python module:
def myfunc(x,y,z):
return x + y + z
(Note how no changes were made for the function work with xlSlim - this is a defining feature of xlSlim, no decorators or additional Python packages are required.)
Then in Excel use the RegisterPyModule() function to register the module (assuming you saved the module as mymod.py)
=RegisterPyModule("C:\Users\russe\Documents\mymod.py")
The function is now available for use within Excel as myfunc. Any type hints and doc strings are also processed.
I'm trying to execute some Matlab scripts (not a function definition) from Python 3 using oct2py module.
Those scripts (a large amount) contains a very extended definition to read a specific ASCIII files (contained in the same directory).
I do not know how to get the data read by Python with the Matlab (octave) scripts.
Here what I am doing:
from oct2py import octave
import numpy as np
import os
import pprint
hom_dir='/path_to/files&scripts_dir/'
os.chdir(hom_dir)
octave.addpath(/path_to/files&scripts_dir/')
out=octave. matlab_file # (matlab_file.m)
output:
Out[237]: <function oct2py.core.Oct2Py._make_octave_command.<locals>.octave_command>”
pprint.pprint(out)
<function Oct2Py._make_octave_command.<locals>.octave_command at 0x7f2069d669d8>”
No error is returned, but I do not know how to get the data (that were read in a Octave session). The examples that I have found for execute .m files using oct2py where about files that define functions, however that is not my case.
Assuming your script places the results on the (virtual) octave workspace, you can simply attempt to access the workspace.
Example:
%% In file myscript.m
a = 1
b = 2
Python code:
>>> octave.run('myscript.m')
>>> vars = octave.who(); vars
[u'A__', u'a', u'b']
>>> octave.a()
1.0
>>> octave.b()
2.0
Some notes / caveats:
I ran into problems when I tried running a script, as it complained I was trying to run it as a function; you can bypass this using the run command.
Your octave current directory may not be the same as your python current directory (this depends on how the octave engine is started). For me python started in my home directory but octave started in my desktop directory. I had to manually check and go to the correct directory, i.e.:
octave.pwd()
octave.cd('/path/to/my/homedir')
Those weird looking variables A__ (B__, etc) in the workspace reflect the most recent arguments you passed into functions via the oct2py engine (but for some reason they can't be called like normal variables). E.g.
>>> octave.plus(1,2)
3.0
>>> print octave.who()
[u'A__', u'B__', u'a', u'b']
>>> octave.eval('A__')
A__ = 1
>>> octave.eval('B__')
B__ = 2
As you may have noticed from above, the usual ans variable is not kept in the workspace. Do not rely on any script actions that reference ans. In the context of oct2py it seems that ans will always evaluate to None
So I'm currently working on a simple python code to run a simple R script. The R script is only about 6 lines but uses the package "pracma". Using the subprocesses module in python, It runs the script but with the typical "Error in library(pracma) : there is no package called 'pracma' ". I am just looking for a simple solution to be able to run R scripts that have non-base packages installed. I know you can somehow do this using rpy2, but I cannot get that to install using pip, and I'm also using Anaconda3.
All in all, I'd just like a simple package that runs R scripts that have packages installed in them. Any help would be greatly appreciated.
This is the relevant piece of my Python code:
sp.run('Rscript Hausdorff.R', shell=True)
bFile = open("HausdorffData/hausdorff.txt", "r")
result = bFile.read()
bFile.close()
hausdorff_dist = float(result)
return hausdorff_dist
This is my R script:
library(pracma)
setwd('HausdorffData')
PointsA <- as.matrix(read.table("HFileA.txt", header = FALSE))
PointsB <- as.matrix(read.table("HFileB.txt", header = FALSE))
H = hausdorff_dist(PointsA, PointsB)
write(H, file = "hausdorff.txt",ncolumns = 1, append = FALSE)
This produces the following error:
Error in library(pracma) : there is no package called 'pracma'
Execution halted
Thanks to #Dilettant for this:
Even when R can seemingly run the script individually without this, when running the R script from something like python, we need to make sure the script knows what path our library is in. This is achieved by adding this line to the start of the code:
.libPaths(dir\to\package)
If the code has any other lines involving changing working directory, those are unaffected by changing the library path.
I've read the API docs for xlwings, and played around with Workbook and Sheet objects in the interpreter, but I can't figure out how to call a macro from Python.
How do I use xlwings to call an Excel macro from Python?
This is not implemented yet, but there's an open issue for it, see here. In the meantime, you can work around it like so (this is for Windows, but the Mac version works accordingly, see again in the issue):
from xlwings import Workbook
wb = Workbook(...)
wb.application.xl_app.Run("your_macro")
update: for more recent versions, you have to do:
from xlwings import Workbook, Application
wb = Workbook(...)
Application(wb).xl_app.Run("your_macro")
update 2: This functionality is now natively supported from >=v0.7.1. Let's assume, there is a VBA function YourMacro that sums up two numbers:
>>> import xlwings as xw
>>> wb = xw.Book(r'C:\path\to\mybook.xlsm')
>>> your_macro = wb.macro('YourMacro')
>>> your_macro(1, 2)
3.0
I got issues when I updated xlwings to 0.9+ version.
To run vba macro with xlwings, I used the code written below for running macros inside the personal workbook (PERSONAL.XLSB).
The updated code no2 of Felix didn't work for me, for macro inside the personal workbook.
import xlwings as xw
wb = xw.Book(excel_file_path)
app = wb.app
# into brackets, the path of the macro
macro_vba = app.macro("'PERSONAL.XLSB'!my_macro")
macro_vba()
Hope it will help.
I know it is a late answer, but all the ways posted above didn't work for me.
But I have found another way, by using the awesome api-interface provided by xlwings.
Here is my code I used to run a macro:
xlApp = xw.App(visible=False)
wb= xw.books.open('.\\Path\\To\\File.xlsm')
a = xlApp.api.Application.Run("macroTest")
My macro opened a MsgBox and returned the value 1 just for test and it worked very well.
Although one should avoid using MsgBox, since it was opened in background.
Btw. the api-interface is available in many (when not all) objects and it is really powerfull if you are used to VBA programming.
Another simple way if you already open a excel.
xw.apps.active.macro('yourMacro')(1, 2)
Extending #Felix Zumstein's answer and all other answers here, please ensure you have disabled Macro settings as you may face COM issues later.
You can change that under,
File > Options > Trust Center > Trust Center Settings > Macro Settings > Enable all macros (not recommended...)