I'm trying to automate construction of an Excel 2007 spreadsheet that uses the Bloomberg plugin to pull down live prices. The problem is that when I open Excel through win32com the Bloomberg plugin does not load (so all of the formulas end up with "#NAME?" errors).
Manually uninstalling and reinstalling the plugin works, but copying the VBA code from the recorded macro leads to a "Run-time error '13': Type mismatch" error. I can click the End button and everything runs fine, but I want to have this fully automated.
My code is:
import win32com.client
xl = win32com.client.gencache.EnsureDispatch("Excel.Application")
xl.Visible = True
MainWorkBook = xl.Workbooks.Add(1)
xl.AddIns("Bloomberg Excel Tools").Installed = False
xl.AddIns("Bloomberg Excel Tools").Installed = True
Setting DisplayAlerts = False doesn't catch the runtime error.
you have to open the bloomberg.xla file with something like:
from win32com.client import DispatchEx
xl = DispatchEx('Excel.Application')
xl.Workbooks.Open('C:/blp/API/Office Tools/BloombergUI.xla')
see here for more info:
http://msdn.microsoft.com/en-us/library/microsoft.office.interop.excel.addin.aspx
Related
I'm trying to create a Python script (I'm using Python 3.7.3 with UTF-8 encoding on Windows 10 64-bit with Microsoft Office 365) that exports user selected worksheets to PDF, after the user has selected the Excel-files.
The Excel-files contain a lot of different settings for page setup and each worksheet in each Excel-file has a different page setup.
The task is therefore that I need to read all current variables regarding page setup to be able to assign them to the related variables for export.
The problem is when I'm trying to get Excel to return the current print area of the worksheet, which I can't figure out.
As far as I understand I need to be able to read the current print area, to be able to set it for the export.
The Excel-files are a mixture of ".xlxs" and ".xlsm".
I've tried using all kind of different methods from the Excel VBA documentation, but nothing has worked so far e.g. by adding ".Range" and ".Address" etc.
I've also tried the ".UsedRange", but there is no significant difference in the cells that I can search for and I can't format them in a specific way so I can't use this.
I've also tried using the "IgnorePrintAreas = False" variable in the "ExportAsFixedFormat"-function, but that didn't work either.
#This is some of the script.
#I've left out irrelevant parts (dialogboxes etc.) just to make it shorter
#Import pywin32 and open Excel and selected workbook.
import win32com.client as win32
excel = win32.gencache.EnsureDispatch("Excel.Application")
excel.Visible = False
wb = excel.Workbooks.Open(wb_path)
#Select the 1st worksheet in the workbook
#This is just used for testing
wb.Sheets([1]).Select()
#This is the line I can't get to work
ps_prar = wb.ActiveSheet.PageSetup.PrintArea
#This is just used to test if I get the print area
print(ps_prar)
#This is exporting the selected worksheet to PDF
wb.Sheets([1]).Select()
wb.ActiveSheet.ExportAsFixedFormat(0, pdf_path, Quality = 0, IncludeDocProperties = True, IgnorePrintAreas = False, OpenAfterPublish = True)
#This closes the workbook and the Excel-file (although Excel sometimes still exists in Task Manager
wb.Close()
wb = None
excel.Quit()
excel = None
If I leave the code as above and try and open a test Excel-file (.xlxs) with a small PrintArea (A1:H8) the print function just gives me a blank line.
If I add something to .PrintArea (as mentioned above) I get 1 of 2 errors:
"TypeError: 'str' object is not callable".
or
"ps_prar = wb.ActiveSheet.PageSetup.PrintArea.Range
AttributeError: 'str' object has no attribute 'Range'"
I'm hoping someone can help me in this matter - thanks, in advance.
try
wb = excel.Workbooks.OpenXML(wb_path)
insead of
wb = excel.Workbooks.Open(wb_path)
My problem was with a german version of ms-office. It works now. Check here https://social.msdn.microsoft.com/Forums/de-DE/3dce9f06-2262-4e22-a8ff-5c0d83166e73/excel-api-interne-namen?forum=officede
I am opening Excel 2016 from Python using the PyWin32 package using the following code
import win32com.client as win32
from win32com.client import Dispatch
def openWorkbook(filePath):
excelObj = win32.gencache.EnsureDispatch("Excel.Application")
excelObj.DisplayAlerts = False
excelObj.Visible = True
wbkObj = excelObj.Workbooks.Open(Filename=filePath)
return(excelObj, wbkObj)
When I open workbooks in this way, a number of the add-ins which I rely upon are not initialized, though they do initialize when I open Excel in the typical fashion.
While I understand that I can initialize them manually via their filepaths, I would much prefer to open Excel in such a way that all of the add-ins which typically initialize are included.
Thank you.
I have an Excel file with one worksheet that has sediment collection data. I am running a long Python script.
In the worksheet is a column titled “CollectionYear.” Say I want the year 2010. If the year 2010 exists in the “CollectionYear” column, I want the rest of the script to run, if not then I want the script to stop.
This seems like an easy enough task but for the life of me I cannot figure it out nor find any examples.
Any help would be greatly appreciated.
I use xlrd all the time and it works great for me. Something like this might be helpful
from xlrd import open_workbook
def main():
book = open_workbook('example.xlsx')
sheet = book.sheet_by_index(0)
collection_year_col = 2 #Just an example
test_year = 2010
for row in range(sheet.nrows):
if sheet.cell(row,collection_year_col).value == test_year:
runCode()
def runCode():
#your code
I hope this points you in the right direction. More help could be given if the details of your problem were known.
Here is what I learned from tackling a needle-in-a-haystack problem for a gigantic pile of .xls files. There are some things xlrd and friends can't (or won't) do, such as getting the formula of a cell. For that, you'll need to use the Microsoft Component Object Model (COM)1.
I recommend you find yourself a copy of Python Programming on Win32 by Mark Hammond. It's still useful 20 years later. Python Programming on Win32 covers the basics of the COM and how to access it using the pywin32 library (also from Mark Hammond).
In a nutshell, you can think of the COM as an API between a server (say, Excel) and a client (such as a Python script)2.
import win32com.client
# Connect to Excel server
xl = win32com.client.Dispatch("Excel.Application")
The COM API is reasonably well documented. Once you get used to the terminology, things become straight-forward albeit tedious. For example, an Excel file is technically a "Workbook". The "Workbooks" COM object has the Open method which provides a handle for Python to interact with the "Workbook". (Did you notice the different 's' endings on those?)
import win32com.client
# Connect to Excel server
xl = win32com.client.Dispatch("Excel.Application")
myfile = r'C:\temp\myworkbook.xls'
wb = xl.Workbooks.Open(Filename=myfile)
A "Workbook" contains a "Sheet", accessed here through the "Sheets" COM object:
import win32com.client
# Connect to Excel server
xl = win32com.client.Dispatch("Excel.Application")
myfile = r'C:\temp\myworkbook.xls'
wb = xl.Workbooks.Open(Filename=myfile)
sht1 = wb.Sheets.Item(1)
Finally, the 'Cells' property of a worksheet "returns a Range object that represents all the cells on the worksheet". The Range object then has a Find method which will search within the range. The LookIn parameter allows for searching cell values, formulas, and comments.
import win32com.client
# Connect to Excel server
xl = win32com.client.Dispatch("Excel.Application")
myfile = r'C:\temp\myworkbook.xls'
wb = xl.Workbooks.Open(Filename=myfile)
sht1 = wb.Sheets.Item(1)
match = sht1.Cells.Find('search string')
The result of Find is a Range object which has many useful properties, like Formula, GetAddress, Value, and Text. You'll also find, as with anything Microsoft, that it's good enough for government work.
Finally, don't forget to close the workbook and to quit Excel!
import win32com.client
# Connect to Excel server
xl = win32com.client.Dispatch("Excel.Application")
myfile = r'C:\temp\myworkbook.xls'
wb = xl.Workbooks.Open(Filename=myfile)
sht1 = wb.Sheets.Item(1)
match = sht1.Cells.Find('search string')
print(match.Formula)
wb.Close(SaveChanges=False)
xl.Quit()
You can extend these ideas with Sheets.Item and Sheets.Count and iterate over all sheets in a workbook (or all workbooks in a directory). You can have lots of fun!
The headaches you may encounter include VBA macros and embedded objects, as well as the various different alerts each can produce. Performance is also an issue. The following silence notifications and can dramatically improve performance:
Application
xl.DisplayAlerts (False)
xl.AutomationSecurity (msoAutomationSecurityForceDisable)
xl.Interactive (False)
xl.PrintCommunication (False)
xl.ScreenUpdating (False)
xl.StatusBar (False)
Workbook
wb.DoNotPromptForConvert (True)
wb.EnableAutoRecover (False)
wb.KeepChangeHistory (False)
Another potential issue is late/early binding. Basically, does Python have information about the COM object? This affects things like introspection and how COM objects are referenced. The win32com.client package uses late-bound automation by default.
With late-bound automation, Python doesn't know much about the COM object:
>> import win32com.client
>> xl = win32com.client.Dispatch("Excel.Application")
>> xl
<COMObject Excel.Application>
>> len(dir(xl))
55
With early-bound automation, Python has full knowledge of the object:
>> import win32com.client
>> xl = win32com.client.Dispatch("Excel.Application")
>> xl
<win32com.gen_py.Microsoft Excel 16.0 Object Library._Application instance at 0x2583562290680>
>> len(dir(xl))
125
To enable early binding, you must run makepy.py which is included with pywin32. Running makepy.py will prompt for the library to bind with.
(venv) c:\temp\venv\Lib\site-packages\win32com\client>python makepy.py
python makepy.py
The process creates a Python file (in Temp\) which maps the methods and properties of the COM object.
(venv) c:\temp\venv\Lib\site-packages\win32com\client>python makepy.py
python makepy.py
Generating to C:\Users\Lorem\AppData\Local\Temp\gen_py\3.6\00020813-0000-0000-C000-000000000046x0x1x9.py
Building definitions from type library...
Generating...
Importing module
Early binding also provides access to COM constants, such as msoAutomationSecurityForceDisable and xlAscending and is case-sensitive (whereas late-binding is not).
That should be enough info to implement a Python-to-Excel library (like xlwings), overkill notwithstanding.
1 Actually, xlwings works by utilizing the COM though pywin32. Here's to one less dependency!
2 This example uses win32com.client.Dispatch which requires processing happen through a single Excel instance. Use win32com.client.DispatchEx to create separate instances of Excel.
Try using xlwings library to interface with Excel from python
example from their docs:
from xlwings import Workbook, Sheet, Range, Chart
wb = Workbook() # Creates a connection with a new workbook
Range('A1').value = 'Foo 1'
Range('A1').value
>>> 'Foo 1'
Range('A1').value = [['Foo 1', 'Foo 2', 'Foo 3'], [10.0, 20.0, 30.0]]
I've seen from various questions on here that if an instance of Excel is opened from Python using:
xl = win32com.client.gencache.EnsureDispatch('Excel.Application')
xl.Visible = True
wb = xl.Workbooks.Open('Test.xlsx')
Then it does not load the default add-ins. I've tried forcing my add-in to load by instead running:
xl = win32com.client.gencache.EnsureDispatch('Excel.Application')
xl.Visible = True
addin = xl.Workbooks.Open('C:/path/addIn.xll')
wb = xl.Workbooks.Open('Test.xlsx')
However, when I do this an instance of Excel opens and I get a security message asking me to verify I want to open this add-in. If I click "Enable this add-in for this session only" Excel immediately closes and does not open my desired xlsx file.
Does anyone have any ideas how to force the add-in to load and then to allow me to open my file?
Thanks very much for your help!
I have actually managed to resolve this by borrowing something from this MSDN article relating to doing the same thing with VBA:
http://support.microsoft.com/default.aspx?scid=KB;en-us;q213489
The following now works perfectly:
xl = win32com.client.gencache.EnsureDispatch('Excel.Application')
xl.Visible = True
xl.RegisterXLL('C:/path/addin.xll')
wb = xl.Workbooks.Open('Test.xlsx')
I had the same problem, but couldn't use xl.RegisterXLL('C:/path/addin.xla') from the accepted answer, because it only works with .XLL files, and I had a .XLA file.
Instead, I found that this worked:
xl = win32com.client.gencache.EnsureDispatch('Excel.Application')
# Need to load the addins before opening the workbook
addin_path = r'C:\path\addin.xla'
xl.Workbooks.Open(addin_path)
xl.AddIns.Add(addin_path).Installed = True
wb = xl.Workbooks.Open(r"C:\my_workbook.xlsm")
I have had much better success using Excel via win32com than any of the other methods, but you might want to look at pyxll (https://www.pyxll.com/introduction.html). Here are a few other things:
Have you checked that addin.Installed == True?
Have you tried AddIns.Add("c:\windows\addins\TSXL\TSXL.xll").Installed = True?
Try xl.DisplayAlerts=False before opening the workbooks
Have you tried the four steps in last answer of Automating Excel via COM/Python - standard addins won't load at startup, I copy them here:
Open the XLA / XLL file representing the addin in question
Set addins(addin_name).Installed = False
Addins(addin_name).Add(addin_file_path)
Set addins(addin_name).Installed = True
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.