I'm using win32com to run macros in excel and openpyxl to modify cell values. In the process of debugging, I attempted to create a simplified version of existing code but still ran into the same
[Errno 13] Permission denied:'C:\\Users\\NAME\\Desktop\\old\\Book1.xlsx'.
I believe that the error is caused by the two packages (win32com and openpyxl) opening the same file and, when attempting to save/close, cannot close the instance open in the other package.
When I attempt to save/close with openpyxl before saving/closing with win32com, I run into the permission denied error. This makes sense; Openpyxl probably does not have permission to close the excel instance open through win32com. Code is below:
wb.save(r"C:\Users\NAME\Desktop\old\Book1.xlsx")
xel.Workbooks(1).Close(SaveChanges=True)
However, when I switch the order:
xel.Workbooks(1).Close(SaveChanges=True)
wb.save(r"C:\Users\NAME\Desktop\old\Book1.xlsx")
Excel attempts to save a backup file (randomly named "522FED10" or "35C0ED10", etc.) and when I press save, Excel crashes.
What's the workaround? I was thinking that you could use win32com to run the macros, save under a different filename, then use openpyxl to access that file and edit values. However, this is extremely inefficient (I'm dealing with excel files that have hundreds of thousands of rows of data). I could consider just using win32com, but that would require a revamp of a system.
Simple code:
import openpyxl as xl
import win32com.client
xel=win32com.client.Dispatch("Excel.Application")
xel.Workbooks.Open(Filename=r"C:\Users\NAME\Desktop\old\Book1.xlsx")
wb = xl.load_workbook(r"C:\Users\NAME\Desktop\old\Book1.xlsx")
ws = wb.active
xel.visible = False
xel.Cells(1,1).Value = 'Hello Excel'
ws.cell(row = 1,column = 2).value = "test"
xel.Workbooks(1).Close(SaveChanges=True)
wb.save(r"C:\Users\NAME\Desktop\old\Book1.xlsx")
Current issue
You should definitely not mix win32com and openpyxl operations.
The win32com statement xel.Workbooks.Open() loads the workbook contents into a memory space controlled by an Excel process. The openpyxl xl.load_workbook() statement on the other hand loads the workbook contents into a completely separate memory space controlled by a Python process.
Hence any subsequent win32com commands will do nothing to affect the workbook that's living inside the python-process-controlled memory, and vice versa any openpxyl commands will do nothing to affect the workbook that's living inside the Excel-process-controlled memory.
Solution
You mentioned that you have to run some excel macros. This rules out an openpyxl-only solution. My suggestion would be to use xlwings, which is in essence a powerful and user-friendly wrapper around the win32com API.
Here is a simple example of how you can execute Excel macros and manually update cell values within a single python script:
import xlwings as xw
# Start Excel app (invisibly in the background)
app = xw.App(visible=False)
# Load excel file into active Excel app
book = app.books.open(r"Book1.xlsm")
# Instruct Excel to execute the pre-existing excel macro named "CleanUpMacro"
book.macro("CleanUpMacro")()
# Instruct Excel to write a cell value in the first sheet
book.sheets["Sheet1"].range('A1').value = 42
# Save workbook and terminate Excel application
book.save()
book.close()
app.kill()
Related
I need to open and edit my Excel with openpyxl, store the excel as a dataframe, and close the excel without any changes. Are there any ways to kill the excel and disable the auto-recovery dialogue which may pop out later?
The reason I'm asking is that my code worked perfectly fine in Pycharm, however after I packed it into .exe with pyinstaller, the code stopped working, the error said "Excel cannot access the file, there are serval possible reasons, the file name or path does not exist, or the file is being used by another program, or the workbook you are saving has the same name as a currently open workbook.
I assume it is because the openpyxl did not really close the excel, and I exported it to a different folder with the same file name.
Here is my code:
wb1 = openpyxl.load_workbook(my_path, keep_vba=True)
ws1 = wb1["sheet name"]
making changes...
ws1_df = pd.DataFrame(ws1.values)
wb1.close()
Many thanks ahead :)
The following way you can do this. solution
from win32com.client import Dispatch
# Start excel application
xl = Dispatch('Excel.Application')
# Open existing excel file
book = xl.Workbooks.Open('workbook.xlsx')
# Some arbitrary excel operations ...
# Close excel application without saving file
book.Close(SaveChanges=False)
xl.Quit()
I have a python script that pastes values from one excel to another excel called 'Automate'. I have pasted the values to specific cells in sheet 1 of Automate so that sheet 2 in Automate can read the values and apply a formula. In Automate, I have a macro that uploads the values in the sheet 2 to SQL before saving. I have used openpyxl to work with excel and the function wb.save(Automate.xlsxm) doesn't run the macro.
I am able to run the below code that refreshes qand saves Automate and it runs the macro to upload the values to SQL. However, I have to manually run the script and when I use task scheduler to run the script the values do not upload values from Automate to SQL.
import win32com.client
xlapp = win32com.client.gencache.EnsureDispatch("Excel.Application")
wb = xlapp.Workbooks.Open('Automate.xlsm')
wb.RefreshAll()
xlapp.CalculateUntilAsyncQueriesDone()
wb.Save()
wb.Close()
wb = None
xlapp.Quit()
xlapp = None
help will be very much appreciated
Openpyxl doesn't evaluate excel formulas and macros, as it doesn't actually use excel and doesn't have that functionality built into it. Instead, I'd recommend using xlwings, which opens excel and will evaluate all formula on opening (if they are set up to automatically reevaluate). You can also use xlwings to run your macro, with some examples here. A rough outline of your code would then be:
import openpyxl as op
import xlwings as xw
# Openpyxl open and write
wb_path = "Your_workbook.xlsm"
wb = op.load_workbook(wb_path)
ws = wb["Sheet1"]
# Write your values etc
# Save
wb.save(wb_path)
wb.close()
# Open with xlwings
wb = xw.Book(wb_path)
app = xw.apps.active
# Run macro
macro = wb.macro("YourMacro")
macro()
# Save, close
wb.save(wb_path)
app.quit()
I am opening a .xlsm file using openpyxl abd updated some cells in it and then saved as .xlsm file.Now when I open the saved file I see that the cells which were merged in the original file are broken in new file.
the code which i am using is-
from openpyxl import Workbook
from openpyxl import load_workbook
wb = load_workbook('Excel.xlsm',read_only=False ,keep_vba=True)
ws = wb['K0 Reg Patch Util']
ws.cell(row=42,column=3).value = 25
ws.cell(row=43,column=3).value = 30
ws.cell(row=44,column=3).value = 24
wb.save('Test.xlsm')
Even on simple opening and saving file with openpyxl merged columns borders in the original file are broken. I have searched many times regarding this problem but none of the solutions were satisfying. I even came across a monkeypatch script which is to be included in the script after including the openpyxl library.The source for the script is-
https://bitbucket.org/openpyxl/openpyxl/issues/365/styling-merged-cells-isnt-working
The monkeypatch will overwrite the definition of merged cell from that present in the library.
can somebody tell me how to include this patch in the script and What does "self" means in the script.
I ran into a similar issue. But for me this only occurs with "protected" excel files. Removing that protection worked for me. No more "broken" merged cells. At least for files with .XLSX extension, I did not test .XLSM.
I have an .xlsm file with lots of macros and sheets, which provides some graphs when filled in. I want to open the .xlsm file in python and fill some values to the macros in order to get the graphs . I have tried the following code but it seems that when I open the new file a message that the file is corrupted pops-up.
I use python 2.74 and openpyxl version is 2.4.8
import openpyxl
from xlwt import easyxf
book = openpyxl.load_workbook('macroexcel.xlsm',keep_vba=True) #opens excel file
sheet = book.get_sheet_by_name('SheetName') #Get the sheet name
sheet['K34'] = 'Value' #Assign value to a cell connected to the macro
book.save('newexcel.xlsm') #Save
Is there a way to get access to the values in the macros, select them and extract the graphs using python.?
Thanks.
Try this - How To: Add and Call Excel Macro from Python Using Pandas, Win32 and PyMySQL.
It suggests Python to initiate a win32com.client.Dispatch('Excel.Application') to start Excel. Then, having access to the application layer, xl.Application.Run("Macro1").
For a process I am maintaining, I have a script that creates a csv file and then I copy the csv file into an Excel workbook with buttons that activate macros. This process works just fine.
I am trying to improve that process by writing a script that builds the workbook directly, thus eliminating a step. I thought the best way to do that was to create a template workbook where the first worksheet has the macro button. Then I would simply copy the template workbook, add in my data and save the new workbook under a new custom name. My test code is below:
import csv, os, sys, xlrd, xlwt, xlutils, shutil
from copy import deepcopy
from xlutils import save
from xlutils.copy import copy
templatefile = 'N:\Tools\Scripts-DEV\Testing_Template.xls'
Destfile = 'N:\Tools\Scripts-DEV\Testing_Dest.xls'
shutil.copy(templatefile,Destfile)
# Works fine up to here.
# If you look at the new file, it has the button that is in the template file.
rb = xlrd.open_workbook(Destfile)
rs = rb.sheet_by_index(0)
wb = copy(rb)
wb.get_sheet(0).write(3, 0, 'Due Date')
wb.get_sheet(0).write(3, 1, 'Name')
wb.get_sheet(0).write(3, 3, 'Category')
wb.get_sheet(0).write(3, 4, 'Number')
wb.save(Destfile)
Here is where the problem shows up. After you save, the macro button disappears. I've been looking for a couple days but I haven't (yet) found a way to save the updated Excel file without losing the macro button.
I've been looking at Preserving styles using python's xlrd,xlwt, and xlutils.copy but that doesn't quite meet my needs as I'm trying to preserve a button, not a style.
Does anyone know a way to do this?
I'm about to start looking at alternatives to xlutils, xlrd and xlwt as well, but I thought I'd ask here first.
From you comment part C:\Python27\ I deduce that you are on Windows. In that case you are probably better off with using pywin32 and a template .xls or .xlsm file.
Open the file using os.startfile(filename) then connect using workbook = win32com.client.GetObject(filename). The resulting workbook can be filled with the data and written to a new file with `workbook.SaveAs(newfilename).
Anything you do not touch explicitly is preserved. Excel is, of course, somewhat better at that than xlrd, xlwt and xlutils.