Copying a worksheet with xlwings and python - python

I have been using xlwings in Python, but have not been able to figure out how to copy a worksheet. I want to treat a particular worksheet as a template, and copy that worksheet each time before making modifications.
I am using version 0.11.4 of xlwings. If such functionality is not built in, I am okay with going outside of xlwings to use pywin32 functions in order to accomplish this.

After poking around in several places and reading the pywin32 documentation, I found a solution to copy the worksheet:
import xlwings as xw
wb = xw.Book('filename.xlsx')
sheet = wb.sheets['Sheet1']
#copy within the same sheet
sheet.api.Copy(Before=sheet.api)
#copy to a new workbook
sheet.api.Copy()
#copy a third time at the beginning of the sheets
sheet2 = wb.sheets['sheet1 (2)']
sheet.api.Copy(Before=sheet2.api)
#copy to an existing workbook by putting it in front of a worksheet object
sheet.api.Copy(Before=existingSheet.api)
This does go outside of the native functionality provided by xlwings. Because xlwings is a wrapper around pywin32, the .api() call allows access to those pywin32 functions that are undocumented in xlwings.
Also note that the 'After' command does not work within the worksheet; it will open a new workbook with the sheet copied. This shouldn't pose too big of an issue, as I believe the indexes can be reordered if needed.

Note that in recent versions of xlwings you can now always use the simple copy() function instead of api.Copy() - even for copying between different workbooks:
# Create two books and add a value to the first sheet of the first book
first_book = xw.Book()
second_book = xw.Book()
first_book.sheets[0]['A1'].value = 'some value'
# Copy to same Book with the default location and name
first_book.sheets[0].copy()
# Copy to same Book with custom sheet name
first_book.sheets[0].copy(name='copied')
# Copy to second Book requires to use before or after
first_book.sheets[0].copy(after=second_book.sheets[0])
See the documentation here.

For anyone struggling on how to copy sheet to an another workbook, here is an example:
import xlwings as xw
wb = xw.Book("spravoc" + "/" + "spravoc.xlsx") # file FROM you want to copy sheet
sht = wb.sheets["Dictionary"] #select sheet you want to copy
new_wb = xw.Book("spravoc" + "/" + "spravoc_new.xlsx") # file where you want TO copy
new_wb.sheets.add("Temp", after=1) # add temp sheet, if you want to copy sheet after some other sheet (after=1 - after first sheet)
print(new_wb.sheets)
# copy needed sheet to the new_wb *before* Temp:
sht.api.Copy(Before=new_wb.sheets['Temp'].api)
# now, remove Temp from new_wb
new_wb.sheets['Temp'].delete()
new_wb.save("spravoc" + "/" + "spravoc_new.xlsx")

When copying to a different workbook you can use:
import xlwings as xw
wb_from = xw.Book(r"test1.xlsx")
wb_to = xw.Book(r"test2.xlsx")
ws_from = wb_from.sheets['Sheet1']
ws_to = wb_to.sheets['Sheet1']
# Copy the sheet BEFORE the sheet of another book.
ws_from.api.Copy(Before=ws_to.api)
# Copy the sheet AFTER the sheet of another book.
ws_from.api.Copy(None, After=ws_to.api)
If you want to copy the worksheet within the current workbook you can also use the built-in .copy() since xlwings version 0.22:
# Copy the sheet BEFORE ws_from.
sheet.copy(before=ws_from, name="name_of_copied_sheet")
# Copy the sheet AFTER ws_from.
sheet.copy(after=ws_from, name="name_of_copied_sheet")

I needed to copy multiple sheets so I gave up and used a super ugly hybrid macro approach...
Create a 'utility' sheet called 'my_macros.xlsm' with the following macro in Module1
Sub CopySheetsToAnotherSheet(fromName, toName)
Workbooks(fromName).Sheets(Array("SheetA", "SheetB")).Copy Before:=Workbooks(toName).Sheets(1)
End Sub
Then make sure the source and target sheet are open and execute the following:-
my_macros = xw.Book('my_macros.xlsm')
a_macro = my_macros.macro('CopySheetsToAnotherSheet')
a_macro('source_sheet.xlsm', 'target_sheet.xlsx')

Related

Xlwings - Delete similarly named sheets using loop

I am using xlwings to place stock data I pull from the internet into worksheets. The workbook opens with a Sheet1, and upon running my program creates various sheets named according to the stock index. This leaves Sheet1 and causes problems with other methods I want to call. I want to test for any sheets that contain Sheet (plus an integer) and subsequently delete it similar to how you would test for the presence of a list element using the in operator. How would I go about doing this in xlwings? Current xlwings methods only allow sheets in which you manually name to be deleted.
My attempts have been rather lackluster. I've been trying loops to recognize the sheet names but to no avail. Here is my attempt to do so.
import xlwings as xw
wb = xw.Book('practice.xlsx')
for sheet in wb.sheets:
if 'Sheet' in sheet:
xw.Sheet[sheet].delete()
This works:
import xlwings as xw
wb = xw.Book('practice.xlsx')
for sheet in wb.sheets:
if 'Sheet' in sheet.name:
sheet.delete()
is the syntax more like sheet.api.Delete()? My xlwings is broken right now to check the exact syntax.
You can check, if the given sheet exist and you can delete the existing sheet and add a new one.
import xlwings as xw
def df_to_excel_util(excel,sheet_to_dataFrame_map):
with xw.App(visible=False) as app:
wb = app.books.open(excel)
current_sheets = [sheet.name for sheet in wb.sheets]
for sheet_name in sheet_to_dataFrame_map.keys():
if sheet_name in current_sheets:
wb.sheets[sheet_name].delete()
new_sheet = wb.sheets.add(after=wb.sheets.count)
new_sheet.range('A1').value = sheet_to_dataFrame_map.get(sheet_name)
new_sheet.name = sheet_name
wb.save()
if you have sheet object, you can delete as given below
sheet.delete()
if you want to delete a sheet with a given name.
wb.sheets['sheet_name'].delete

Overwriting existing cells in an XLSX file using Python

I am trying to find a library that overwrites an existing cell to change its contents using Python.
what I want to do:
read from .xlsx file
compare cell data determine if change is needed.
change data in cell Eg. overwrite date in cell 'O2'
save file.
I have tried the following libraries:
xlsxwriter
combination of:
xlrd
xlwt
xlutils
openpyxl
xlsxwriter only writes to a new excel sheet and file.
combination: works to read from .xlsx but only writes to .xls
openpyxl: reads from existing file but doesn't write to existing cells can only create new rows and cells, or can create entire new workbook
Any suggestions would greatly be appreciated. Other libraries? how to manipulate the libraries above to overwrite data in an existing file?
from win32com.client import Dispatch
import os
xl = Dispatch("Excel.Application")
xl.Visible = True # otherwise excel is hidden
# newest excel does not accept forward slash in path
wbs_path = r'C:\path\to\a\bunch\of\workbooks'
for wbname in os.listdir(wbs_path):
if not wbname.endswith(".xlsx"):
continue
wb = xl.Workbooks.Open(wbs_path + '\\' + wbname)
sh = wb.Worksheets("name of sheet")
sh.Range("A1").Value = "some new value"
wb.Save()
wb.Close()
xl.Quit()
Alternatively you can use xlwing, which (if I had to guess) seems to be using this approach under the hood.
>>> import xlwings as xw
>>> wb = xw.Book() # this will create a new workbook
>>> wb = xw.Book('FileName.xlsx') # connect to an existing file in the current working directory
>>> wb = xw.Book(r'C:\path\to\file.xlsx') # on Windows: use raw strings to escape backslashes

How Do I Use Excel's Format Painter Across a Whole Workbook

Every week I generate a large excel sheet using Python/Pandas. However, the xls writer in Pandas does not allow one to format the excel sheets likely because of the proprietary format. Currently, I have to go worksheet by worksheet in the newly generated file and copy the formatting from the sheet the week before which is a little obnoxious.
Is there a way (in order of preference):
Copy all the formatting from one excel sheet to another in Python
Format Paint all sheets from a workbook to a second workbook
This would be making a sheet with formatting and links which I could update and than resave, but I'm hoping for a solution like (1) or (2).
I'd do it that way:
import win32com.client
xlPasteFormats = -4122
xlPasteSpecialOperationNone = -4142
excelInstance = win32com.client.gencache.EnsureDispatch ("Excel.Application")
workbook = excelInstance.Workbooks.Item(1)
worksheet = workbook.Worksheets(1)
worksheet2 = workbook.Worksheets(3)
cells1 = worksheet.UsedRange
cells2 = worksheet2.UsedRange
cells1.Copy()
cells2.PasteSpecial(xlPasteFormats, xlPasteSpecialOperationNone)
which is quite similar to solution in VBA, because uses the same functions, but does it via COM, so you stay completely in Python.
In this code I had workbook open. If you want to open workbook you should put:
filepath = r"path:\To\Excel\Workbook"
excelInstance.Workbooks.Open(filepath)
Here is the VBA way to do it (from sancho.s's answer in this question), assuming the sheets in each workbook are named the same. You may be able to use those objects in Python, or at least create this macro in the workbook you copy from.
Sub FormatMAC()
Dim wb1 As Workbook, wb2 As Workbook
Set wb1 = Workbooks("Results_2012 - Template - Master.xlsx")
Set wb2 = Workbooks("Copy of Results_2012 - Template1.xlsm")
Dim ws1 As Worksheet, ws2 As Worksheet
For Each ws1 In wb1.Worksheets
Set ws2 = wb2.Worksheets(ws1.Name)
ws1.Cells.Copy
ws2.Cells.PasteSpecial (xlPasteFormats)
Next ws1
End Sub

How to append to an existing excel sheet with XLWT in Python

I have created an excel sheet using XLWT plugin using Python. Now, I need to re-open the excel sheet and append new sheets / columns to the existing excel sheet. Is it possible by Python to do this?
After investigation today, (2014-2-18) I cannot see a way to read in a XLS file using xlwt. You can only write from fresh. I think it is better to use openpyxl. Here is a simple example:
from openpyxl import Workbook, load_workbook
wb = Workbook()
ws = wb.create_sheet()
ws.title = 'Pi'
ws.cell('F5').value = 3.14156265
wb.save(filename=r'C:\book2.xls')
# Re-opening the file:
wb_re_read = load_workbook(filename=r'C:\book2.xls')
sheet = wb_re_read.get_sheet_by_name('Pi')
print sheet.cell('F5').value
See other examples here: http://pythonhosted.org/openpyxl/usage.html (where this modified example is taken from)
You read in the file using xlrd, and then 'copy' it to an xlwt Workbook using xlutils.copy.copy().
Note that you'll need to install both xlrd and xlutils libraries.
Note also that not everything gets copied over. Things like images and print settings are not copied, for example, and have to be reset.

Accessing worksheets using xlwt 'get_sheet' method

I would like to access worksheets of a spreadsheet. I've copied the main workbook to another workbook using xlutils.copy(). But don't know the right way to access worksheets using xlwt module.
My sample code:
import xlrd
import xlwt
from xlutils.copy import copy
wb1 = xlrd.open_workbook('workbook1.xls', formatting_info=True)
wb2 = copy(master_wb)
worksheet_name = 'XYZ' (worksheet_name is a iterative parameter)
worksheet = wb2.get_sheet(worksheet_name)
Could someone please tell me what's the right command line to access the existing worksheets in a workbook using xlwt module? I know we can use 'add_sheet' method to add a worksheet in the existing workbook using xlwt module.
Any help, appreciated.
You can do sheets = wb1.sheets() to get a list of sheet objects, then call .name on each to get their names. To find the index of your sheet, use
[s.name for s in sheets].index(sheetname)
The sheets() method is curiously absent from the xlwt.Workbook class, so the other answer using that method will not work - only xlrd.book (for reading XLS files) has a sheets() method. Because all the class attributes are private, you have to do something like this:
def get_sheet_by_name(book, name):
"""Get a sheet by name from xlwt.Workbook, a strangely missing method.
Returns None if no sheet with the given name is present.
"""
# Note, we have to use exceptions for flow control because the
# xlwt API is broken and gives us no other choice.
try:
for idx in itertools.count():
sheet = book.get_sheet(idx)
if sheet.name == name:
return sheet
except IndexError:
return None
If you don't need it to return None for a non-existent sheet then just remove the try/except block. If you want to access multiple sheets by name repeatedly it would be more efficient to put them in a dictionary, like this:
sheets = {}
try:
for idx in itertools.count():
sheet = book.get_sheet(idx)
sheets[sheet.name] = sheet
except IndexError:
pass
Well, here is my answer. Let me take it step-by-step.
Considerting previous answers, xlrd is the right module to get the worksheets.
xlrd.Book object is returned by open_workbook.
rb = open_workbook('sampleXLS.xls',formatting_info=True)
nsheets is an attribute integer which returns the total number of sheets in the workbook.
numberOfSheets=rb.nsheets
Since you have copied this to a new workbook wb -> basically to write things, wb to modify excel
wb = copy(rb)
there are two ways to get the sheet information,
a. if you just want to read the sheets, use sheet=rb.sheet_by_index(sheetNumber)
b. if you want to edit the sheet, use ws = wb.get_sheet(sheetNumber) (this is required in this context to the asked question)
you know how many number of sheets in excel workbook now and how to get them individually,
putting all of them together,
Sample Code:
reference: http://www.simplistix.co.uk/presentations/python-excel.pdf
from xlrd import open_workbook
from xlutils.copy import copy
from xlwt import Workbook
rb = open_workbook('sampleXLS.xls',formatting_info=True)
numberOfSheets=rb.nsheets
wb = copy(rb)
for each in range(sheetsCount):
sheet=rb.sheet_by_index(each)
ws = wb.get_sheet(each)
## both prints will give you the same thing
print sheet.name
print ws.name

Categories

Resources