When one opens Excel workbook with Excel COM object
app = gencache.EnsureDispatch("Excel.Application")
doc = app.Workbooks.Open(filepath)
all print areas are dropped, but they're accessible via VBA when the file is opened normally.
Localized versions of MS Excel ignore print areas and titles when accessed as a COM object, so one must explicitly specify PageSetup.PrintArea|PrintTitleColumns|PrintTitleRows for each worksheet if needed.
for sh in self.doc.Worksheets: #explicitly specify print areas and titles
for name in sh.Names:
if name.Name.endswith("!Print_Area"):
sh.PageSetup.PrintArea = name.RefersTo
elif name.Name.endswith("!Print_Titles"):
#protect comma symbol in sheet name
chunks = name.RefersTo.replace(sheet.Name, "[sheet_name]").split(",")
chunks = [i.replace("[sheet_name]", sheet.Name) for i in chunks]
if len(chunks) == 1:
try: sh.PageSetup.PrintTitleColumns = chunks[0]
except: sh.PageSetup.PrintTitleRows = chunks[0]
else: sh.PageSetup.PrintTitleColumns, sh.PageSetup.PrintTitleRows = chunks
Source: Excel -> PDF (ExportAsFixedFormat)
UPD: Support sheet names with commas
Related
I have a qtablewidet that has data and when the user clicks the export button a dialog will appear asking for filename to save as excel, how do i do that using openpyxl?
here is my code
self.exportbtn.clicked.connect(self.export)
def export(self):
try:
filename = QtWidgets.QFileDialog.getSaveFileName(self, 'Save file', '','Excel files(*.xlsx)')
wb = Workbook()
sheet = wb.active
for column in range(self.tableWidget.columnCount()):
for row in range(self.tableWidget.rowCount()):
try:
text = str(self.tableWidget.item(row, column).text())
sheet.write(row, column, text)
wb.save(filename)
except Exception as e:
print("Error Writing file.")
except Exception as e:
print("Error Saving file.")
when i try to click save from the dialog, the output right now is this
how do i save the whole data including the headers from qtablewidget to an excel file using openpyxl?
update:
i edited my code now and i am able to create the file but the data from the qtablewidget is still not written in the excel file
def export(self):
filename, filter = QtWidgets.QFileDialog.getSaveFileName(self, 'Save file', '','Excel files (*.xlsx)')
wb = Workbook()
sheet = wb.active
if not filename:
for column in range(self.tableWidget.columnCount()):
for row in range(self.tableWidget.rowCount()):
try:
text = str(self.tableWidget.item(row, column).text())
sheet.write(row, column, text)
except AttributeError:
pass
wb.save(filename)
i tried printing the data from the qtablewidget and it shows, it just doesn't save in the excel file, is there still something missing?
So your line wb.save(filename) is inside your for loop, thereby the same filename is being saved on every loop.
Move that line outside and after the for loops at the outer indentation and therefore save it only once.
Also, ensure that the filename does not already exist, else you may get a pop dialog "Are you sure?" and you then need to force save it.
Almost all static methods of QFileDialog (except for getExistingDirectory() and getExistingDirectoryUrl()) return a tuple in the form of (file(s), filter).
For getOpenFileNames() (note the plural) the first object is a list of strings indicating the selected files, in the other cases it's the file path as a string or URL.
Do note that if the dialog is canceled, the file string (or the list) is empty, so you should check that before going on with the file writing:
filename, filter = QtWidgets.QFileDialog.getSaveFileName(
self, 'Save file', '','Excel files(*.xlsx)')
if not filename:
return
# ...
Also, you should not try to save the file at each cycle of the for loop.
While 99% of the functions in Qt behave in the same way in PyQt, that's not always true, especially for situations for which C++ uses referenced variables. Whenever you are not sure about a function or get unexpeted behavior from it, you can check the PyQt documentation (the returned type(s) are specified at the end of the function definition, after the →) or just call help(class.function) in the python consolle. An even simpler solution is to just print the returned value, and you'll see that for yourself.
def imag_Export_Report(self):
self.cur.execute('''SELECT name, phone, image FROM photo''')
rows = self.cur.fetchall()
excel_file = QtWidgets.QFileDialog.getSaveFileName(self, "save file",'./','Excel Files (*.xlsx)')
workbook = xlsxwriter.Workbook(excel_file[0])
sheet1 = workbook.add_worksheet()
sheet1.write(0, 0, 'name ')
sheet1.write(0, 1, ' phone')
sheet1.write(0, 2, 'image ')
row_number = 1
for row in rows:
column_number = 0
for item in row:
sheet1.write(row_number, column_number, str(item))
column_number += 1
row_number += 1
workbook.close()
self.con.commit()
All I want to do is copy a worksheet from an excel workbook to another excel workbook in Python.
I want to maintain all formatting (coloured cells, tables etc.)
I have a number of excel files and I want to copy the first sheet from all of them into one workbook. I also want to be able to update the main workbook if changes are made to any of the individual workbooks.
It's a code block that will run every few hours and update the master spreadsheet.
I've tried pandas, but it doesn't maintain formatting and tables.
I've tried openpyxl to no avail
I thought xlwings code below would work:
import xlwings as xw
wb = xw.Book('individual_files\\file1.xlsx')
sht = wb.sheets[0]
new_wb = xw.Book('Master Spreadsheet.xlsx')
new_wb.sheets["Sheet1"] = sht
But I just get the error:
----> 4 new_wb.sheets["Sheet1"] = sht
AttributeError: __setitem__
"file1.xlsx" above is an example first excel file.
"Master Spreadsheet.xlsx" is my master spreadsheet with all individual files.
In the end I did this:
def copyExcelSheet(sheetName):
read_from = load_workbook(item)
#open(destination, 'wb').write(open(source, 'rb').read())
read_sheet = read_from.active
write_to = load_workbook("Master file.xlsx")
write_sheet = write_to[sheetName]
for row in read_sheet.rows:
for cell in row:
new_cell = write_sheet.cell(row=cell.row, column=cell.column,
value= cell.value)
write_sheet.column_dimensions[get_column_letter(cell.column)].width = read_sheet.column_dimensions[get_column_letter(cell.column)].width
if cell.has_style:
new_cell.font = copy(cell.font)
new_cell.border = copy(cell.border)
new_cell.fill = copy(cell.fill)
new_cell.number_format = copy(cell.number_format)
new_cell.protection = copy(cell.protection)
new_cell.alignment = copy(cell.alignment)
write_sheet.merge_cells('C8:G8')
write_sheet.merge_cells('K8:P8')
write_sheet.merge_cells('R8:S8')
write_sheet.add_table(newTable("table1","C10:G76","TableStyleLight8"))
write_sheet.add_table(newTable("table2","K10:P59","TableStyleLight9"))
write_to.save('Master file.xlsx')
read_from.close
With this to check if the sheet already exists:
#checks if sheet already exists and updates sheet if it does.
def checkExists(sheetName):
book = load_workbook("Master file.xlsx") # open an Excel file and return a workbook
if sheetName in book.sheetnames:
print ("Removing sheet",sheetName)
del book[sheetName]
else:
print ("No sheet ",sheetName," found, will create sheet")
book.create_sheet(sheetName)
book.save('Master file.xlsx')
with this to create new tables:
def newTable(tableName,ref,styleName):
tableName = tableName + ''.join(random.choices(string.ascii_uppercase + string.digits + string.ascii_lowercase, k=15))
tab = Table(displayName=tableName, ref=ref)
# Add a default style with striped rows and banded columns
tab.tableStyleInfo = TableStyleInfo(name=styleName, showFirstColumn=False,showLastColumn=False, showRowStripes=True, showColumnStripes=True)
return tab
Adapted from this solution, but note that in my (limited) testing (and as observed in the other Q&A), this does not support the After parameter of the Copy method, only Before. If you try to use After, it creates a new workbook instead.
import xlwings as xw
wb = xw.Book('individual_files\\file1.xlsx')
sht = wb.sheets[0]
new_wb = xw.Book('Master Spreadsheet.xlsx')
# copy this sheet into the new_wb *before* Sheet1:
sht.api.Copy(Before=new_wb.sheets['Sheet1'].api)
# now, remove Sheet1 from new_wb
new_wb.sheets['Sheet1'].delete()
This can be done using pywin32 directly. The Before or After parameter needs to be provided (see the api docs), and the parameter needs to be a worksheet <object>, not simply a worksheet Name or index value. So, for example, to add it to the end of an existing workbook:
def copy_sheet_within_excel_file(excel_filename, sheet_name_or_number_to_copy):
excel_app = win32com_client.gencache.EnsureDispatch('Excel.Application')
wb = excel_app.Workbooks.Open(excel_filename)
wb.Worksheets[sheet_name_or_number_to_copy].Copy(After=wb.Worksheets[wb.Worksheets.Count])
new_ws = wb.ActiveSheet
return new_ws
As most of my code runs on end-user machines, I don't like to make assumptions whether Excel is open or not so my code determines if Excel is already open (see GetActiveObject), as in:
try:
excel_app = win32com_client.GetActiveObject('Excel.Application')
except com_error:
excel_app = win32com_client.gencache.EnsureDispatch('Excel.Application')
And then I also check to see if the workbook is already loaded (see Workbook.FullName). Iterate through the Application.Workbooks testing the FullName to see if the file is already open. If so, grab that wb as your wb handle.
You might find this helpful for digging around the available Excel APIs directly from pywin32:
def show_python_interface_modules():
os.startfile(os.path.dirname(win32com_client.gencache.GetModuleForProgID('Excel.Application').__file__))
I want to merge multiple excel files with multiple sheets respectively using python. I do not want to lose any formatting from the sheets. It should copy all sheets and just create a single excel file.
I'm able to merge only the first sheet and also all formatting is lost.
This is my code:
import os
import os.path
import xlrd
import xlsxwriter
file_name = input("merge")
merged_file_name = file_name + ".xls"
dest_book = xlsxwriter.Workbook('m.xls')
dest_sheet_1 = dest_book.add_worksheet()
dest_row = 1
temp = 0
path = input("C:\\test")
out = os.path.isdir("")
print(out)
print("File path: " + path)
for root,dirs, files in os.walk("C:\\test"):
for xlsfile in files:
print ("File in mentioned folder is: " + xlsfile)
temp_book = xlrd.open_workbook(os.path.join(root,xlsfile))
temp_sheet = temp_book.sheet_by_index(0)
if temp == 0:
for col_index in range(temp_sheet.ncols):
str = temp_sheet.cell_value(0, col_index)
dest_sheet_1.write(0, col_index, str)
temp = temp + 1
for row_index in range(1, temp_sheet.nrows):
for col_index in range(temp_sheet.ncols):
str = temp_sheet.cell_value(row_index, col_index)
dest_sheet_1.write(dest_row, col_index, str)
dest_row = dest_row + 1
dest_book.close()
book = xlrd.open_workbook("m.xls")
sheet = book.sheet_by_index(0)
print ("number of rows in destination file are: "), sheet.nrows
print ("number of columns in destination file are: "), sheet.ncols
Since you require Excel specific needs like formatting, consider directly interfacing to the Excel object library with a COM interface. Of course this assumes you have Excel installed on machine. For Windows, Python can run COM with the win32com library and this connects beyond Excel but to most Windows apps and objects including Notepad, Paint, even ADODB.
Essentially, this mirrors VBA (which does a similar interface to the Excel object library) using Workbooks.Add, Sheets.Add, Range.Copy, and other methods. All other APIs such as xlrd and xlwriter do not directly use Excel methods and hence why you lose formatting even graphics but not data.
import os
import win32com.client as win32
path = input("C:\\test")
file_name = input("merge")
merged_file_name = file_name + ".xlsx"
try:
# INITIALIZE EXCEL COM APP
xlapp = win32.gencache.EnsureDispatch('Excel.Application')
# ASSIGN CONSTANTS
xlPasteValues = -4163; lPasteFormats = -4122; xlWorkbookDefault = 51
# CREATE NEW WOKRBOOK (PROMPTS IF EXISTS)
new_wb = xlapp.Workbooks.Add()
new_wb.SaveAs(Filename='MasterMerge.xlsx', FileFormat=xlWorkbookDefault)
# LOOP THROUGH WORKBOOKS
xl_files = [f for f in os.listdir(path) if f.endswith('.xls') or f.endswith('.xlsx')]
for wb in xl_files:
xlwb = xlapp.Workbooks.Open(os.path.join(path, wb))
# LOOP THROUGH EVERY WORKSHEET, COPYING TO NEW WORKSHEET
for xlsh in xlwb.Worksheets:
new_sh = new_wb.Worksheets.Add()
new_sh.Name = xlsh.Name
new_wb.Save()
new_sh.Move(After=new_wb.Worksheets(new_wb.Worksheets.Count))
xlsh.Cells.Copy(new_sh.Cells)
new_sh = None
xlwb.Close(False)
xlwb = None
# REMOVNIG DEFAULT SHEET AND LAUNCHING TO SCREEN
new_wb.Worksheets('Sheet1').Delete()
new_wb.Save()
xlapp.Visible = True
except Exception as e:
print(e)
finally:
# RELEASE RESOURCES
xlsh = None; new_sh = None;
xlwb = None; new_wb = None; xlapp = None
Regarding to the error faced by Sachin Ingle,
(-2147352567, 'Exception occurred.', (0, 'Microsoft Excel', 'That name is already taken. Try a different one.', 'xlmain11.chm', 0, -2146827284), None) facing this error
It's probably because you have created a file of same name before. Try create it with different name.
The answer by Parfait on
new_wb.SaveAs(Filename='MasterMerge.xlsx', FileFormat=xlWorkbookDefault)
will make the file named "MasterMerge.xlsx" and probably you have created the file already.
And btw can add in xlapp.Quit() at finally: block to solve the in-use problem
I done some changes on Parfait answer (Thanks mate)
def merge_excel_files(filepath_list,filename,delete_original_files=False):
import os, errno
import win32com.client as win32
try:
# INITIALIZE EXCEL COM APP
xlapp = win32.gencache.EnsureDispatch('Excel.Application')
# ASSIGN CONSTANTS
xlPasteValues = -4163; lPasteFormats = -4122; xlWorkbookDefault = 51
# CREATE NEW WOKRBOOK (PROMPTS IF EXISTS)
new_wb = xlapp.Workbooks.Add()
new_wb.SaveAs(Filename=filename, FileFormat=xlWorkbookDefault)
# Gain filename in a directory
# xl_files = [f for f in os.listdir(path) if f.endswith('.xls') or f.endswith('.xlsx')]
for wb in filepath_list:
xlwb = xlapp.Workbooks.Open(wb)
# LOOP THROUGH EVERY WORKSHEET, COPYING TO NEW WORKSHEET
for xlsh in xlwb.Worksheets:
new_sh = new_wb.Worksheets.Add()
new_sh.Name = xlsh.Name
new_wb.Save()
new_sh.Move(After=new_wb.Worksheets(new_wb.Worksheets.Count))
xlsh.Cells.Copy(new_sh.Cells)
new_sh = None
xlwb.Close(False)
xlwb = None
# REMOVNIG DEFAULT SHEET AND LAUNCHING TO SCREEN
new_wb.Worksheets('Sheet1').Delete()
new_wb.Save()
# xlapp.Visible = True
except Exception as e:
print(e)
finally:
# Close the Excel file since done writing
xlapp.Quit()
# RELEASE RESOURCES
xlsh = None; new_sh = None;
xlwb = None; new_wb = None; xlapp = None
# Delete the initial file
if delete_original_files:
for count,x in enumerate(filepath_list):
print(f"Deleting the {count+1}/{len(filepath_list)} original file(s)...")
try:
os.remove(x)
except OSError as e:
# No such file or directory
if e.errno != errno.ENOENT:
raise
else:
# If there's no exception
print(f"Deleted {x}")
## Merge Excel files into one workbook with keeping the sheets and styling/formatting
# => https://stackoverflow.com/questions/51986517/merge-two-excel-files-with-multiple-sheet-without-losing-formatting
# => https://stackoverflow.com/questions/44593705/how-to-copy-over-an-excel-sheet-to-another-workbook-in-python/44596301#44596301 [openpyxl (Can't keep formatting), pywin32, xlwings]
# => https://stackoverflow.com/questions/56687602/copy-excel-sheet-from-one-worksheet-to-another-in-python/56688138#56688138 [xlwings]
## Solve file in use problem with pywin32 solution from questions/51986517
# => https://stackoverflow.com/questions/6337595/python-win32-com-closing-excel-workbook/6338030
## Basic Python: Pythonic way to delete a files, running code if try statements were successful
# => https://stackoverflow.com/questions/10840533/most-pythonic-way-to-delete-a-file-which-may-not-exist
# => https://stackoverflow.com/questions/2792568/running-code-if-try-statements-were-successful-in-python
## Research on openpyxl copy_worksheet(); Conclusion: it can only copy and paste sheet within same workbook. =(
# => https://stackoverflow.com/questions/44593705/how-to-copy-over-an-excel-sheet-to-another-workbook-in-python/44596301
# => https://openpyxl.readthedocs.io/en/latest/tutorial.html?highlight=copy_worksheet#manipulating-a-workbook-in-memory
So i have the same excel workbook in several different different folders i.e. for each hotel I have a file and in that file there's an excel workbook. Now, I need to go in each file and change the contents of the cell 'B2' in worksheet "Set Up" to the hotel name (which is referred to as hotelname in my code). I try to run the code below but it gives me the error "C:\Python34\python-3.4.4.amd64\lib\site-packages\openpyxl\reader\worksheet.py:319: UserWarning: Data Validation extension is not supported and will be removed" and it doesn't change anything in my excel files?
from openpyxl import load_workbook
log = 'G:\Data\Hotels\hotelnames.txt' ##text file with a list of the hotel names
file = open(log, 'r')
hotelnames = []
line = file.readlines()
for a in line:
hotelnames.append(a.rstrip('\n'))
for hotel in hotelnames:
wb = load_workbook("G:\\Data\\Hotels\\"+hotel+"\\"+hotel+" - Meetings\\"+hotel+"_Action_Log.xlsx", data_only = True)
ws = wb["Set Up"]
ws['B2'] = hotel ### I want this cell to to have that particular hotel name
wb.save
Your code should be calling the method wb.save, not just referencing it. Do that by adding some parentheses and pass the file name to save the file to:
wb.save(filename)
wb.save only references the save method, but does not call it.
Also processing of the input file can be greatly simplified by iterating directly over the file object:
import os.path
with open(log) as f:
file_spec = os.path.join(r"G:\Data\Hotels", '{0}', '{0} - Meetings', '{0}_Action_Log.xlsx'
for hotel in f:
hotel = hotel.rstrip('\n') # probably hotel.rstrip() is sufficient
wb = load_workbook(file_spec.format(hotel), data_only = True)
ws = wb["Set Up"]
ws['B2'] = hotel
wb.save(file_spec.format(hotel)) # careful, this overwrites the original file, it would be safer to save it to a new file.
I am trying to write a matrix prepared with NumPy to an EXCEL file. I need to specify a range of cells in which the matrix must be written.
I need to write the matrix to cells A4:Z512 in sheet 4 of the EXCEL file.
Now, the standard EXCEL file has 3 sheets, so I need to first add a 4th sheet and then write the matrix to it.
Is there a way to do this in python 2.7? Is it possible to do this with pure NumPy or not?
I have not used NumPy, so I am not sure if you can manipulate excel files. But for working on excel files I recommend using the win32com library. Below is some code I have used in the past to make win32com API easier to use. Feel free to use the code yourself. Hope this helps!
import win32com.client as win32
excel = win32.gencache.EnsureDispatch('Excel.Application')
def openExcel(makeExcelVisible=True):
excel.Visible = makeExcelVisible
def closeExcel():
excel.Application.Quit()
class ExcelFile(object):
# opens up a workbook to work on, not selecting a file name creates a new one
def __init__(self, fileName=None):
if fileName == None:
self.wb = excel.Workbooks.Add()
else:
self.wb = excel.Workbooks.Open(fileName)
self.ws = None
def addWorksheet(self):
# adds a new worksheet to the workbook and makes it the current worksheet
self.ws = self.wb.Worksheets.Add()
def selectWorksheet(self, worksheetName):
# selects a worksheet to work on
self.ws = self.wb.Worksheets(worksheetName)
def renameWorksheet(self, worksheetName):
# renames current worksheet
self.ws.Name = worksheetName
def save(self):
# saves the workbook
self.wb.Save()
def saveAs(self, fileName):
# saves the workbook with the file name
self.wb.SaveAs(fileName)
def close(self):
# closes the workbook
self.wb.Close()
def insertIntoCell(self, cellRow, cellCol, data):
self.ws.Cells(cellRow,cellCol).Value = data
def clearCell(self, cellRow, cellCol):
self.ws.Cells(cellRow,cellCol).Value = None
Here is an example of how to use the code above. It creates a new excel file, renames the worksheets, adds information into the first cell on each worksheet and saves the file as "test.xlsx". Default save location is your home directory.
worksheets = ["Servers", "Printers", "Drives", "IT Phones"]
information = ["Server WS", "Printer WS", "Driver WS", "IT Phone WS"]
def changeCells(information):
excelFile = ExcelFile()
for index in xrange(len(worksheets)):
sheetNumber = index + 1
if sheetNumber == 4:
excelFile.addWorksheet()
excelFile.selectWorksheet("Sheet%d" % sheetNumber)
excelFile.renameWorksheet(worksheets[index])
excelFile.insertIntoCell(1,1,information[index])
excelFile.saveAs("test.xlsx")
excelFile.close()
openExcel()
changeCells(information)
closeExcel()
Also, I would recommend looking at the API for win32com yourself. It's a very nice and useful library.
I put together the actual code you would need for entering your matrix on Sheet4 to A4:Z512.
def addMatrix(matrix):
# use ExcelFile("fileName.xlsx") if you need to add to a specific file
excelFile = ExcelFile()
excelFile.addWorksheet()
# default excel files only have 3 sheets so had to add one
excelFile.selectWorksheet("Sheet4")
# xrange(4,513) since end index is exclusive
for row in xrange(4,513):
# 1 for A, 26 for Z
for col in xrange(1,27):
mRow = row - 4
mCol = col - 1
excelFile.insertIntoCell(row, col, matrix[mRow][mCol])
excelFile.saveAs("test.xlsx")
excelFile.close()
matrix = list()
for row in xrange(509):
matrix.append([])
for col in xrange(26):
matrix[row].append(0)
# the matrix is now filled with zeros
# use openExcel(False) to run faster, it won't open a window while running
openExcel()
addMatrix(matrix)
closeExcel()