I have .xlsm file with a Macro function. I\'m loading it using openpyxl and write some data to the file and finally want t
it's better to use xlwings to work with xlsm documents. I have tested it.
import xlwings as xw
wb = xw.Book(DATA.xlsm)
I had the same issue when editing xlsm files using openpyxl. I tried most of the solutions/workarounds available in stackoverflow and in other forums. But none of them worked. Then I found xlwings, this python library handles xlsm document, it preserve all the macros.
import xlwings as xw
wb = xw.Book('macro_xl.xlsm')
sheet = wb.sheets['Sheet1']
sheet.range('A1').value = 'From Script'
wb.save('result_file_name.xlsm')
I don't know if this is still relevant for the person that asked the question, but I am dealing with the same problem and found a possible solution.
1.xlsm
) and do magic with openpyxl;2.xlsx
;xlsx
files: one of the files is the macro (vbaProject.bin
) and 2 of the files are necessary because they describe the type of the file among other things;xlsx
directory back into a zip file and rename this from zip
to xlsm
. This file contains the original macro and has been edited with openpyxl
;2.xlsx
file.Example code:
import openpyxl
import zipfile
from shutil import copyfile
from shutil import rmtree
import os
PAD = os.getcwd()
wb = openpyxl.load_workbook('1.xlsm')
#####
# do magic with openpyxl here and save
ws = wb.worksheets[0]
ws.cell(row=2, column=3).value = 'Edited' # example
#####
wb.save('2.xlsx')
with zipfile.ZipFile('1.xlsm', 'r') as z:
z.extractall('./xlsm/')
with zipfile.ZipFile('2.xlsx', 'r') as z:
z.extractall('./xlsx/')
copyfile('./xlsm/[Content_Types].xml','./xlsx/[Content_Types].xml')
copyfile('./xlsm/xl/_rels/workbook.xml.rels','./xlsx/xl/_rels/workbook.xml.rels')
copyfile('./xlsm/xl/vbaProject.bin','./xlsx/xl/vbaProject.bin')
z = zipfile.ZipFile('2.zip', 'w')
os.chdir('./xlsx')
for root, dirs, files in os.walk('./'):
for file in files:
z.write(os.path.join(root, file))
z.close()
#clean
os.chdir(PAD)
rmtree('./xlsm/')
rmtree('./xlsx/')
os.remove('./2.xlsx')
os.rename('2.zip', '2.xlsm')
That's right, openpyxl cannot read and write VBA code.
According to this thread:
I think you should not give the xlsM extension, because the file will contain no VBA code. openpyxl is used to build xlsX files only.
Give a try to this fork instead: if you pass keep_vba=True
parameter to load_workbook
it should do the job.
Hope that helps.
For me this worked, together with version openpyxl==2.3.0-b2
wb = load_workbook(filename='original.xlsm', read_only=False, keep_vba=True)
..
wb.save('outfile.xlsm')
It is also mentioend in the documentation here: http://openpyxl.readthedocs.org/en/latest/usage.html?highlight=keep_vba#write-a-workbook-from-xltm-as-xlsm
if your initial excel was created with python you might need to add the workbook.xml as well and parse the sheet xlms:
for sheet in range(1,4):
with open('sheetX.xml', 'r') as myfile: 'r') as myfile:
my_str=myfile.read()
substr = "<dimension ref="
inserttxt = "<sheetPr codeName=\"Sheet"+str(sheet)+"\"/>"
idx = my_str.index(substr)
my_str = my_str[:idx] + inserttxt + my_str[idx:]
with open('sheetX.xml', "w") as text_file:
text_file.write(my_str)