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.
Related
When I run the code below, I get different results when I run in .py compared to when I run in .exe using pyinstaller
import win32com.client
import os
ConfigMacroName = "test.xls"
xl=win32com.client.Dispatch("Excel.Application")
Configmacrowb = xl.Workbooks.Open(os.getcwd()+ "\\Completed\\" + ConfigMacroName)
SlotPlansheet = Configmacrowb.Sheets("SlotPlan")
Header = SlotPlansheet.Rows(1)
SOcol = Header.Find('SO', LookAt=1).Column #I used LookAt=1 which is equivalent to LookAt:=xlWhole in excel VBA
SOlinecol = Header.Find('SO Line').Column
print("SO is " + str(SOcol) + "\nSo line is " + str(SOlinecol))
SlotPlansheet = None
Configmacrowb.Close(False)
Configmacrowb = None
xl.Quit()
xl = None
The excel input
The output in .py
The output in .exe
The output in .py file is the correct output I need. If I run it in .exe there will be duplicate variable since they both will refer to column B. For temporary solution I can just loop through the header to check each cell.
But I'm using find() function a lot so I don't know if my other programs are also affected by this inconsistency
Try changing the object creation line to:
xl=win32com.client.gencache.EnsureDispatch(‘Excel.Application’)
In my experience, the win32com.client.Dispatch() function can sometimes cause issues in that it does not guarantee the same result every time it runs. The caller doesn't know if they have an early- or late-bound object. If you have no cached makepy files then you will get a late-bound IDispatch automation interface, but if win32com finds an early-bound interface then it will use it (even if it wasn't your programme that created it). Hence code that ran fine previously may stop working.
Unless you have a good reason to be indifferent, I think it is better to be explicit and choose win32com.client.gencache.EnsureDispatch() or win32com.client.dynamic.Dispatch() for early- or late-binding respectively. I generally choose the EnsureDispatch() route, as it is quicker, enforces case-sensitivity, and gives access to any constants in the type library (eg win32com.client.constants.xlWhole) rather than rely on 'magic' integers.
Also, in the past, I have experienced odd behaviour around indexing (eg this SO question), and this was cured by deleted any gencache wrappers (see below).
Add this line to your debug code:
print('Cache directory:',win32com.client.gencache.GetGeneratePath())
This will tell you where the gencache early-binding python files are being generated, and where win32com.client.Dispatch() will look for any cached wrapper files to attempt early-binding. If you want to clear the cached of generated files just delete the contents of this directory. It will be interesting to see if the OP's two routes have the same directory.
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 am a Python beginner and wrote some Python code that I want to run from my C# code.
In all the answers I have seen already, the way was to make a .exe file from the .py one and run it by system call.
However, I want it so that I do not need to make the .exe file and can write the commands with arguments as I could in the command line.
C:\Users\ntuser> python C:\Users\ntuser\Documents\run_python.py 3
Is there a way to do it?
I found a way to pass just one command—but I need to make two: 1. Go to "C:\Users\ntuser" 2. Run the Python code.
Thanks!
OK, so I found a solution.
Thanks to UnholySheep for the help.
What you need to do is:
Make the python as system variable
Go by code to your home directory:
Directory.SetCurrentDirectory(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile));
Add this code for calling the python code:
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;//if you want to hide the window
startInfo.FileName = "cmd.exe";
startInfo.Arguments = "/C python pytonPath\\python_code.py";
process.StartInfo = startInfo;
process.Start();
process.WaitForExit();//if you want to wait
I am trying write a simple user defined function in Python that I pass a value to from Excel via Xlwings. I ran across some examples with an Add-in that you need to import user defined functions, but that seems overly complex.
Why isn't my example working?
VBA:
Function Hello(name As String) As String
RunPython ("import Test; Test.sayhi(name)")
End Function
Python (Test.py):
from xlwings import Workbook, Range
def sayhi(name):
wb = Workbook.caller()
return 'Hello {}'.format(name)
Error:
NameError: name 'name' is not defined
Make sure you're supplying the argument correctly:
RunPython ("import Test; Test.sayhi('" & name & "')")
The text inside RunPython() should be a valid python script. So the comment from "Tim Williams" is a quick solution. You just need to be careful to avoid ' character inside the name variable to break the python script.
If you need to write UDF (User Defined Function) a lot, or need to pass in value to get the output and then handle the result via VBA, try use ExcelPython instead of Xlwings.
Note, ExcelPython and Xlwings can work together, you can have both without conflict.
I think it's better you play the example to understand the difference. My understanding is limited to my knowledge, which might not be correct.
To summarize the difference:
ExcelPython needs a installer to be installed, it is good in UDF, which helps if you want to pass arguments in and out, and the function is cached in memory, so later call will be very quick.
Xlwings is simpler by just add the VBA module, no installer needed. It trigger Python in the background each time to run the script (each call starts a new process), in the script you can manipulate (read/write) Excel via COM.
I have the same question in the beginning, but later I find out using VBA in Excel side (intellisense) plus ExcelPython on UDF (simply process and return the data back) is a nice combination, so I think ExcelPython is only what I need.
As mentioned earlier, the 2 components have no conflict, you can have both. If the 2 author agree too, it is a good idea to combine them.
I'm trying to get the list of meta-data associated to a file, using python in Ubuntu.
Without using python, the command "extract" works very well but I don't know how to use it with python, I always get a message saying that "extract" is not defined.
I assume you're asking about the metadata that appears in the Windows "Properties" dialogue under the "Summary" tab. (If not, just disregard this.) Here's how I managed it.
Download and install Python win32 extensions. This will put win32, win32com, etc. into your Python[ver]/Lib/site-packages folder. These bring the win32api, win32com, etc. For some reason, I couldn't get the version for Python 2.6 (in build 216) to work. I updated my system to Python 2.7 and used the 216 build for Python 2.7, and it worked. (To download & install, follow the link above, click the link reading 'pywin32', click the link for the latest build (currently 216), click the link for the .exe file that matches your system and Python installation (for me, it was pywin32-216.win32-py2.7.exe). Run the .exe file.)
Copy and paste the code from the "Get document summary information" page on Tim Golden's tutorial into a .py file on your own computer.
Tweak the code. You don't really have to tweak the code, but if you run this Tim's script as your main module, and if you don't supply a pathname as your first sys.argv, then you'll get an error. To make the tweak, scroll down to the bottom of the code, and omit the final block, which starts with if __name__ == '__main__':.
Save your file as something like property_reader.py, and call its property_sets(filepath) method. This method returns a generator object. You can iterate through the generator to see all the properties and their values. You could implement it like this:
# Assuming 'property_reader.py' is the name of the module/file in which you saved Tim Golden's code...
import property_reader
propgenerator = property_reader.property_sets('[your file path]')
for name, properties in propgenerator:
print name
for k, v in properties.items ():
print " ", k, "=>", v
The output of the above code will be something like the following:
DocSummaryInformation
PIDDSI_CATEGORY => qux
SummaryInformation
PIDSI_TITLE => foo
PIDSI_COMMENTS => flam
PIDSI_AUTHOR => baz
PIDSI_KEYWORDS => flim
PIDSI_SUBJECT => bar
extract is based on the libextractor library. You can access the library from Python by installing the python-extractor package on Ubuntu.
In case you're using Windows your question has been addressed on SO already.