Is there a way to set a breakpoint and programmatically an excel vba module using pywin32? So far, I've been able to connect to an open instance of excel, add a workbook, add a vba module and then inject from a text file into this module.
import win32com.client as win32
excel = win32.Dispatch("Excel.Application") # generates a com instance
excel.Visible = True
workbook = excel.Workbooks.Add()
xlmodule = workbook.VBProject.VBComponents.Add(1)
with open('test.bas') as fo:
xlmodule.CodeModule.AddFromString(fo.read())
print('don')
I'm not sure how to then go about adding a breakpoint and then debug the module line by line. I know that XL.Application.Run can run the module for me, but I actually want to debug it through python. Any ideas?
Edit
Upon requests in the comments, here's my unfortunate, but nevertheless very common use case:
I have a legacy macro-enabled spreadsheet which I would like to convert to python using some form of test-driven approach. I use pywin32 to connect to the COM representations of the modules and run them. I use pytest to compare against my own python-based functions to ensure I am replicating the results accurately.
The missing, yet crucial, piece is code coverage. According to this link there are currently no code coverage tools for VBA modules. I need a way to circumvent this. If I can run my pywin32 based test suite and track which lines are being run by connecting to the VBA debugger, I may be able to do some crude, albeit slow, code coverage assessments.
Related
I want to use python to find what the address or coordinates of the currently active or selected cell in an excel spreadsheets currently active sheet.
So far all I've been able to do is the latter. Perhaps I'm just using the wrong words to search. However, this is the first time in two years of writing first VBA and now Python that I haven't been able to just search and find the answer. Even if it took me half a day.
I've crawled through the code at readthedocs (http://openpyxl.readthedocs.org/en/latest/_modules/index.html)
and looked through the openpyxl.cell.cell, openpyxl.worksheet.worksheet, openpyxl.worksheet.views code. The last seemed to have some promise and led me to writing the code below. Still, no joy, and I don't seem to be able to phrase my online searches to be able to pinpoint results that talk about finding the actual active/selected cell. Perhaps this is because openpyxl is really looking at the saved spreadsheet which might not include any data on the last cell to be selected.
I've tried it both in Python 3.4.3 and 2.7.11. Using openpyxl 2.4.0.
Here's the code that got me the closest to my goal. I was running it in Python3.
from openpyxl.worksheet.views import Selection
import openpyxl
wb = openpyxl.load_workbook('example.xlsx')
ws = wb.active
print(wb.get_sheet_names())
print(ws)
print(Selection.activeCell)
Which gives me the below.
['Sheet1', 'Sheet2', 'Sheet3']
<Worksheet "Sheet3">
Values must be of type <class 'str'>
I put in the first two prints just to prove to myself that I'm actually accessing the workbook/sheet.
If I change the last line to:
print(Selection.activeCellId)
I get:
Values must be of type <class 'int'>
I assume this is because these are only for writing not querying. I've toyed with the idea of writing a VBA macro and just running it from python. However, this code will be used with spreadsheets I don't control. By people who aren't necessarily capable of fixing any problems. I don't think I'm capable of writing something good enough to handle any problems that might crop up either.
Any help will be greatly appreciated.
It's difficult to see the purpose of an active cell for a library like openpyxl as it is effectively a GUI artefact. Nevertheless, because openpyxl works hard to implement the OOXML specification it should be possible to read the value stored by the previous application, or write it.
ws.views.sheetView[0].selection[0].activeCell
Consider the win32com library to replicate the Excel VBA property, ActiveCell. Openpyxl might have a limited method for this property while wind32com allows Python to fully utilize the COM libraries of Windows programs including the MS Office Suite (Excel, Word, Access, etc.). You can even manipulate files as a child process as if your were directly writing VBA.
import win32com.client
# OPEN EXCEL APP AND SPREADSHEET
xlApp = win32com.client.Dispatch("Excel.Application")
xlApp.Workbooks.Open('example.xlsx')
xlApp.ActiveWorkbook.Worksheets('Sheet1').Activate
print(xlApp.ActiveCell)
xlApp.ActiveWorkbook.Close(False)
xlApp.Quit
xlApp = None
This question is for calling Python from Excel. In VBA you'd do your RunPython("import mymodule; mymodule.my_function()")
In Python you would have something like,
from xlwings import Workbook, Sheet, Range
def my_function():
wb = Workbook.caller() # Create reference to calling Excel file
Range('A1:C3').clear_contents() # Clear some cells
My question is that this will work for your first instance of Excel. But in the event you have two instances open, and you're trying to run the code in the second instance, you will get a raised exception saying, "Can't establish connection! Make sure the calling workbook is the active one and is opened in the first instance of Excel."
So it seems like this is designed to work on only the first instance. Is there a way around this? Can you identify which instance you're in in the Python script? The user was hoping to run VBA macros that call Excel across multiple instances.
In principle, xlwings can deal with 2 instances. However, depending on your security settings, it might treat files downloaded from the internet or stored on a network drive as unsecure and "sandbox" them. These files are only accessible to xlwings if they run in the first instance.
If this sounds like your issue, then lowering the security settings could potentially solve the issue. See also this answer here.
I am trying to run Excels Solver add-in via pywin32 in python with:
import win32com.client
from win32com.client import constants as c
app = Dispatch("Excel.Application")
app.Visible = True
app.Workbooks.Open(r'C:\path\to\testsolver.xlsm')
app.Run("runsolver")
..but get the following error:
"Cannot run the macro 'runsolver'. The macro may not be available in this workbook or all macros may be disabled"
I realise this is a year old, but in case this is still of value the problem here may be that Solver is not automatically loaded when an Excel OLE object is created. By design, add-ins must be loaded manually before they can be used in an Excel OLE object.
You can confirm that it isn't loaded by examing the OLE created instance and going to the "Data" tab and observing that Solver doesn't appear there, like it does when you open Excel from the Windows GUI.
I discovered this while trying to solve a similar problem in Delphi code as described here:
CreateOleObject opened workbook won't run macro containing Application.Run "Solver.xlam!..." causes error 400
I expect the Python syntax will be similar.
Hi I intend to draw a chart with data in an xlsx file.
In order to keep the style, I HAVE TO draw it within excel.
I found a package named win32com, which can give a support to manipulate excel file with python on win32 platform, but I don't know where is the doc.....
Another similar question is how to change the style of cells, such as font, back-color ?
So maybe all I wanna know is the doc, you know how to fish is more useful than fishes.... and an example is better.
Check this lib, almost native excel graph can be generated here.
xlsxwriter line chart example
The only catch here is that you can't update the already existing sheet, means you can modify the sheet, with this lib you can create all most all the charts.
Documentation for win32com is next to non-existent as far I know. However, I use the following method to understand the commands.
MS-Excel
In Excel, record a macro of whatever action you intend to, say plotting a chart. Then go to the Macro menu and use View Macro to get the underlying commands. More often than not, the commands used would guide you to the corresponding commands in python that you need to use.
Pythonwin
You can use pythonwin to browse the underlying win32com defined objects (in your case Microsoft Excel Objects). In pythonwin (which can be found at \Lib\site-packages\pythonwin\ in your python installation), go to Tools -> COM Makepy Utility, select your required Library (in this case, Microsoft Excel 14.0 Object Library) and press Ok. Then when the process is complete, go to Tools -> COM Browser and open the required library under Registered Libraries. Note the ID no. as this would correspond to the source file. You can browse the various components of the library in the COM Browser.
Source
Go to \Lib\site-packages\win32com\ in your python installation folder. Run makepy.py and choose the required library. After this, the source file of the library can be found at \Lib\site-packages\win32com\gen_py . It is one of those files with the wacky name. The name corresponds to that found in Pythonwin. Open the file, and search for the commands you saw in the Excel Macro. (#2 and #3 maybe redundant, I am not sure)
AFAIK, win32com is an interface to Microsoft's COM technology, so you'd have to look there for an explanation of what objects and methods are available for the different COM-enabled applications. Your task will probably be easier if you don't start with an empty spreadsheet and try to create everything using win32com from scratch, but to create a template in Excel which only needs the actual data filled in. Actualy, since Excel can use ODBC data sources, maybe a database is an even better interface?
import win32com.client as w32
oe = w32.Dispatch("Excel.Application")
ow = oe.Workbooks.Add()
osh = ow.Sheets(1)
osh.Select()
osh.Activate()
osh.Cells(1,1).Font.Bold = True # set A1 to bold
osh.Range("A1").Interior.Color = RGB(255,0,0) # set to red background color
Does anyone know of a way of accessing MS Excel from Python? Specifically I am looking to create new sheets and fill them with data, including formulae.
Preferably I would like to do this on Linux if possible, but can do it from in a VM if there is no other way.
xlwt and xlrd can read and write Excel files, without using Excel itself:
http://www.python-excel.org/
Long time after the original question, but last answer pushed it top of feed again. Others might benefit from my experience using python and excel.
I am using excel and python quite bit. Instead of using the xlrd, xlwt modules directly, I normally use pandas. I think pandas uses these modules as imports, but i find it much easier using the pandas provided framework to create and read the spreadsheets. Pandas's Dataframe structure is very "spreadsheet-like" and makes life a lot easier in my opinion.
The other option that I use (not in direct answer to your problem) is DataNitro. It allows you to use python directly within excel. Different use case, but you would use it where you would normally have to write VBA code in Excel.
there is Python library to read/write Excel 2007 xlsx/xlsm files http://pythonhosted.org/openpyxl/
I wrote python class that allows working with Excel via COM interface in Windows http://sourceforge.net/projects/excelcomforpython/
The class uses win32com to interact with Excel. You can use class directly or use it as example. A lot of options implemented like array formulas, conditional formatting, charts etc.
It's surely possible through the Excel object model via COM: just use win32com modules for Python. Can't remember more but I once controlled the Media Player through COM from Python. It was piece of cake.
Its actually very simple. You can actually run anything from any program. Just see a way to reach command prompt from that program. In case of Excel, create a user defined function by pressing Alt+F11 and paste the following code.
Function call_cmd()
Shell "CMD /C Notepad", vbNormalFocus
End Function
Now press ctrl+s and go back to Excel, select a cell and run the function =call_cmd(). Here I ran Notepad. In the same way, you can see where python.exe is installed and run it. If you want to pass any inputs to python, then save the cells as file in local directory as csv file and read them in python using os.system().