How to run Excel Macro on MacOS using XLWINGS? - python

I have an API file that takes in dates, and then when the macro is run pulls in data for said date range.
I can run this on a Windows computer without use of Python, however I am automating a data clean up process and would like to be able to run this macro from MacOS.
I am using XLWINGS.
My logic is :
Open the workbook.
Update the date values.
Execute the macro.
Save workbook as a new file.
Close workbook.
Eventually set it on a loop to update itself
Current Code :
import xlwings as xw
start = dt.datetime(2022,2,1)
end = dt.datetime(2022,3,31)
excel_app = xw.App(visible=False)
wb = excel_app.books.open('sheet.xlsm')
sheet = wb.sheets['Start']
sheet.range('E5').value = start
sheet.range('E7').value = end
update = wb.macro("'sheet.xlsm'!Code.update_report")
update()
wb.save('PATH')
wb.close()
I get prompted with no errors, the script opens the workbook, updates the values, then saves it in the new location I defined with the new name.
However when I open the .xlsm file after its run its course - it does not have the update data for the inputted date range. Essentially, the macro does not run.
Other ways I've tried to run the macro (instead of running the macro on the original file, I save the original file once the date range has been updated and then try to execute the macro):
import xlwings as xw
if os.path.exists('sheet.xlsm'):
excel_app = xlwings.App(visible=False)
wb = xw.Book('sheet.xlsm')
app = wb.app
update = app.macro("!Code.update_report")
update()
wb.save()
Still - runs fine, no errors, except it does not execute the macro.
Any help welcomed! Thank you.

Related

Edit xlsm file containing macro and activex objects using python

The final goal is to append data into an xlsm file which is containing vba script and a activex listbox. Using python/pandas is preferred. For starters i would like to just open the xlsm file, add an empty sheet and close it again without the VBA code giving me errors due to not having a listbox.
The activex listbox gives me troubles, since it is not "transfered" and the VBA script yields an error when trying to use the listbox. The VBA code looks just fine, after running the python script, but it crashes when setting the 'xLstBox' variable.
Possible fix i can see but not able to implement are:
having python edit the file without messing with the activex objects
if the VBA script could generate the listbox.
My python code:
from openpyxl import load_workbook
xlsx_path = 'excel_file.xlsm'
wb = load_workbook(filename=xlsx_path, keep_vba=True)
wb.create_sheet('sheetname')
wb.save(xlsx_path)
My VBA code in the prefabricated xlsm file:
Public PreviousActiveCell As Range
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim xSelLst As Variant, I As Integer
Set xLstBox = ActiveSheet.ListBox1
Static pPrevious As Range
Set PreviousActiveCell = pPrevious
Set pPrevious = ActiveCell
If Not Intersect(Target, Range("A2:A999999")) Is Nothing Then
If xLstBox.Visible = False Then
xLstBox.Visible = True
xLstBox.Top = ActiveCell.Row * 15
xLstBox.Left = 0
End If
Else
If xLstBox.Visible = True Then
xLstBox.Visible = False
For I = xLstBox.ListCount - 1 To 0 Step -1
If xLstBox.Selected(I) = True Then
xSelLst = xLstBox.List(I) & "," & xSelLst
End If
Next I
If xSelLst <> "" Then
PreviousActiveCell = Mid(xSelLst, 1, Len(xSelLst) - 1)
End If
For I = xLstBox.ListCount - 1 To 0 Step -1
ListBox1.Selected(I) = False
Next I
End If
End If
End Sub
Openpyxl isn't guaranteed to manage vba components so you may have issues with xlsm sheets that contain these objects.
If you are running on Windows (or perhaps Mac) and can use Xlwings this may help. Xlwings needs Excel installed locally.
Your first test for example, adding a sheet should not impact your listbox.
import xlwings as xw
xlsx_path = 'excel_file.xlsm'
with xw.App() as app:
wb = xw.Book(xlsx_path)
wb.sheets.add('sheetname', after='Sheet1')
wb.save(xlsx_path)
wb.close()

Python code not writing output in excel sheet but is able to take input from another sheet in same workbook

Background:
I am fetching Option chain for a symbol from web and then writing it to an excel sheet. I also have another excel sheet in the same workbook from which I take inputs for the program to run. All of this I am doing with excel 2016.
Sample of the code from program as the whole program is pretty long:
import xlwings as xw
excel_file = 'test.xlsx'
wb = xw.Book(excel_file)
wb.save()
# Fetching User input for Script/Ticker else it will be set to NIFTY as default
try:
Script_Input = pd.read_excel(excel_file, sheet_name = 'Input_Options', usecols = 'C')
script = Script_Input.iloc[0,0]
except:
script = 'NIFTY'
# Writing data in the sheet
sht_name = script + '_OC'
try:
wb.sheets.add(sht_name)
print('new sheet added')
wb.save()
except:
pass
# print('sheet already present')
# directing pointer towards current sheet to be written
sheet = wb.sheets(sht_name)
sheet.range('A4').options(index = False, header = False).value = df
sheet.range('B1').value = underlying
sheet.range('C1').value = underlying_Value
# sheet.range('A3').options(index = False, header = False).value = ce_data_final
# sheet.range('J3').options(index = False, header = False).value = pe_data_final
wb.save()
Problem: Since yesterday, I am able to open my excel workbook with excel 2016 and change inputs for my program but, I do not get any data written in the sheet that takes output from the program. The program runs perfectly as I can test the output on terminal. Also, once I delete the sheet no new sheet is being created as it should.
What I tried: I have uninstalled every other version of excel I had, so now only excel 2016 is present.
I have made sure that all the respective file formats use excel 2016 as the default app.
Also note that, 2 days ago I was able to write data perfectly in the respective sheet but now I am not able to do so.
Any help appreciated...
Sorry to everyone who tried to solve this question.
after #buran asked about 'df' I looked into my code and found that I had a return statement before writing 'df' into sheet (I have created a separate function to write data in excel). Now that I have moved that statement to its proper place the code is working fine. I am extremely sorry as I did not realise what the problem was in the 1st place and assumed it had to do with excel and python. Now the program runs perfectly and I am getting the output I want.

How to suppress "Update Links" Alert with xlwings

I am interfacing with Excel files in Python using the xlwings api. Some Excel files I am interacting with have old links which cause a prompt to appear when the file is opened asking if the user would like to update the links. This causes the code to hang indefinitely on the line that opened the book until this prompt is closed by a user. Is there a way to modify the settings of the Excel file so that this prompt will not appear or it will be automatically dismissed without opening the actual file?
I have tried using the xlwings method:
xlwings.App.display_alerts = False
to suppress the prompt, but as far as I can tell this can only be run for an instance of Excel after it has been opened. There are some Excel api's that do not require a file to be open in order to read data like xlrd, but they are not very convenient for reading and copying large amounts of data (Multiple/Entire sheets of data).
The following code demonstrates the issue:
import xlwings as xw
wb = xw.Book(r'C:\Path\To\File\Filename')
print('Done')
On a regular Excel file the code proceeds through and prints "Done" without the need of user interference, but on an Excel file where the "update links" prompt comes up, it will not proceed to the print statement until the prompt is dismissed by a user.
Expanding on your first attempt -- you're not handling an App instance, rather you're trying to assign to the xlwings.App class.
However, it seems that the display_alerts doesn't successfully suppress this alert in xlwings, try this:
import xlwings as xw
app = xw.App(add_book=False)
app.display_alerts = False
wb = app.books.api.Open(fullpath, UpdateLinks=False)
I believe there is an implementation in xlwings to avoid update links messages now. I was able to bypass these alerts by adding the following
app.books.open(fname, update_links=False, read_only=True, ignore_read_only_recommended=True)
You can see these arguments available in the documentation xlwings.Book.open(...)
I presently have 20+ source workbooks that I loop though to extract some rows of data. It was intolerable to respond to the update links prompt of each opened workbook. I tried the other solutions here but none worked for me. After reviewing the cited xlwings docs, this is the solution that worked for me:
for fname in workbook_list:
wb = xw.books.open(fname, update_links = False)
# Extract some data...
wb.close()
My environment is Win10Pro / Python 3.8.1 / pywin32 version: 303 / Excel 365 Subscription / xlwings 0.26.2

How to put a value to an excel cell?

You'll probably laugh at me, but I am sitting on this for two weeks. I'm using python with pandas.
All I want to do, is to put a calculated value in a pre-existing excel file to a specific cell without changing the rest of the file. That's it.
Openpyxl makes my file unusable (means, I can not open because it's "corrupted" or something) or it plainly delets the whole content of the file. Xlsxwriter cannot read or modify pre-existing files. So it has to be pandas.
And for some reason I can't use worksheet = writer.sheets['Sheet1'], because that leads to an "unhandled exception".
Guys. Help.
I tried a bunch of packages but (for a lot of reasons) I ended up using xlwings. You can do pretty much anything with it in python that you can do in Excel.
Documentation link
So with xlwings you'd have:
import xlwings as xw
# open app_excel
app_excel = xw.App(visible = False)
# open excel template
wbk = xw.Book( r'stuff.xlsx' )
# write to a cell
wbk.sheets['Sheet1'].range('B5').value = 15
# save in the same place with the same name or not
wbk.save()
wbk.save( r'things.xlsx' )
# kill the app_excel
app_excel.kill()
del app_excel
Let me know how it goes.

XLWings data link warning not caught by display_alerts [duplicate]

I am interfacing with Excel files in Python using the xlwings api. Some Excel files I am interacting with have old links which cause a prompt to appear when the file is opened asking if the user would like to update the links. This causes the code to hang indefinitely on the line that opened the book until this prompt is closed by a user. Is there a way to modify the settings of the Excel file so that this prompt will not appear or it will be automatically dismissed without opening the actual file?
I have tried using the xlwings method:
xlwings.App.display_alerts = False
to suppress the prompt, but as far as I can tell this can only be run for an instance of Excel after it has been opened. There are some Excel api's that do not require a file to be open in order to read data like xlrd, but they are not very convenient for reading and copying large amounts of data (Multiple/Entire sheets of data).
The following code demonstrates the issue:
import xlwings as xw
wb = xw.Book(r'C:\Path\To\File\Filename')
print('Done')
On a regular Excel file the code proceeds through and prints "Done" without the need of user interference, but on an Excel file where the "update links" prompt comes up, it will not proceed to the print statement until the prompt is dismissed by a user.
Expanding on your first attempt -- you're not handling an App instance, rather you're trying to assign to the xlwings.App class.
However, it seems that the display_alerts doesn't successfully suppress this alert in xlwings, try this:
import xlwings as xw
app = xw.App(add_book=False)
app.display_alerts = False
wb = app.books.api.Open(fullpath, UpdateLinks=False)
I believe there is an implementation in xlwings to avoid update links messages now. I was able to bypass these alerts by adding the following
app.books.open(fname, update_links=False, read_only=True, ignore_read_only_recommended=True)
You can see these arguments available in the documentation xlwings.Book.open(...)
I presently have 20+ source workbooks that I loop though to extract some rows of data. It was intolerable to respond to the update links prompt of each opened workbook. I tried the other solutions here but none worked for me. After reviewing the cited xlwings docs, this is the solution that worked for me:
for fname in workbook_list:
wb = xw.books.open(fname, update_links = False)
# Extract some data...
wb.close()
My environment is Win10Pro / Python 3.8.1 / pywin32 version: 303 / Excel 365 Subscription / xlwings 0.26.2

Categories