I want to create a workbook and worksheet once using xlsxwriter outside my function, as I am calling my function from a robot framework to insert the items in excel.
However, my code gives me an error.
class writetoexcel:
workbook = xlsxwriter.Workbook('Example2.xlsx')
worksheet = workbook.add_worksheet()
def my_func(self, value, count):
print(value)
row = 0
column = 1
# worksheet.write_string(row, column, value)
for item in value:
worksheet.write_string(row, column, item)
worksheet.write_string(row, column, item)
row += 1
workbook.close()
You are getting an error because you are trying to initiate the xlsxwriter outside of the function or of the init method. This is your corrected code:
import xlsxwriter
class writetoexcel:
def my_func(self, value):
workbook = xlsxwriter.Workbook('Example2.xlsx')
worksheet = workbook.add_worksheet()
print(value)
row = 0
column = 1
for item in value:
worksheet.write_string(row, column, item)
row += 1
workbook.close()
Then you need to create an object:
myworkbook = writetoexcel()
And finally call its method we created:
myworkbook.my_func('abcd')
Output:
Finally i think this code is a better approach if you want to handle the workbooks as objects:
class writetoexcel:
def __init__(self):
self.workbook = xlsxwriter.Workbook('Example2.xlsx')
self.worksheet = self.workbook.add_worksheet()
def my_func(self, value):
row = 0
column = 1
for item in value:
self.worksheet.write_string(row, column, item)
row += 1
self.workbook.close()
Related
Basically the title. The thing is that I got an Excel file already (with a lot of formulas) and I have to use it as a template, but I have to copy certain column and paste it in another column.
Since I have to make some graphs in between I need the numeric data of the excel file so my plan is the following:
1.- load the file with data_only = False.
2.- Make the for loops needed to copy and paste info from one worksheet to another.
3.- Save the copied data as another Excel file.
4.- Open the new Excel created file, this time with data_only = True, so I can work with the numeric values.
The problem is that after doing this, it's like after putting data_only on the new created file it doesn't work, because when I made a list that filters NoneType values and strings in a column that have actual numerical values it gives me an empty list.
#I made the following
wb = load_workbook('file_name.xlsx', data_only = True)
S1 = wb['Sheet 1']
S2 = wb['Sheet 2']
#Determination of min and max cols and rows
col_min = S1.min_column
col_max = S1.max_column
row_min = S1.min_row
row_max = S1.max_row
for i in range(row_min + 2, row_max + 1):
for j in range(col_min + Value, Value + 2):
S2.cell(row = i+6, column = j+10-Value).value = S1.cell(row = i, column = j).value
Transition_file = wb.save('transition.xlsx')
wb1 = load_workbook('transition.xlsx', data_only = True) #To obtain only numerical values
S2 = wb1['Sheet 2'] #Re define my Sheet 2 values
I made a program that outputted values from a range of numbers. And I tried to complicate it, to create an interface where I had to display the values from the button press. But for some reason I only get 1 last value. At the same time, everything works as it should through the print.
def Excel123():
wb = openpyxl.reader.excel.load_workbook(filename="resd1.xlsx")
wb.active = 0
sheet = wb.active
for i in range(9, 14):
x1 = i, sheet.cell(row=i, column=23).value
print(i, sheet.cell(row=i, column=23).value)
v.set(x1)
v = StringVar()
Label(root, textvariable=v).pack()
Button(root, text="result", width=15, padx=15, pady=5, command=resd1).pack()
root.mainloop()
I was wondering if it was possible to write a StyleFrame to an arbitrary position in an existing excel worksheet while maintaining the original formatting and styling of pre-existing cells?
E.g In the example below, I'd like to set the output of the Styleframe to start from cell 'A9' while maintaining the formatting and coloring of the other cells (Cells 'A1','A2','A3' etc):
So user #MaxU has a helpful answer over here outlining a function that appends regular dataframes to arbitrary positions in an existing excel worksheet. With some minor changes I've been able to modify this function to work on styleframes. Note that while the modified function successfully writes most cell properties into existing worksheets for some reason it does not copy cell alignments over. So I have hard-coded the Alignments of appended cells to be top,left and wrapped. Please see the modified code below:
from pathlib import Path
from copy import copy
from typing import Union, Optional
import numpy as np
import pandas as pd
import openpyxl
from openpyxl import load_workbook
from openpyxl.utils import get_column_letter
from styleframe import StyleFrame
from openpyxl.styles.alignment import Alignment
def copy_excel_cell_range(
src_ws: openpyxl.worksheet.worksheet.Worksheet,
min_row: int = None,
max_row: int = None,
min_col: int = None,
max_col: int = None,
tgt_ws: openpyxl.worksheet.worksheet.Worksheet = None,
tgt_min_row: int = 1,
tgt_min_col: int = 1,
with_style: bool = True
) -> openpyxl.worksheet.worksheet.Worksheet:
if tgt_ws is None:
tgt_ws = src_ws
# https://stackoverflow.com/a/34838233/5741205
for row in src_ws.iter_rows(min_row=min_row, max_row=max_row,
min_col=min_col, max_col=max_col):
for cell in row:
tgt_cell = tgt_ws.cell(
row=cell.row + tgt_min_row - 1,
column=cell.col_idx + tgt_min_col - 1,
value=cell.value
)
if with_style and cell.has_style:
# tgt_cell._style = copy(cell._style)
tgt_cell.font = copy(cell.font)
tgt_cell.border = copy(cell.border)
tgt_cell.fill = copy(cell.fill)
tgt_cell.number_format = copy(cell.number_format)
tgt_cell.protection = copy(cell.protection)
tgt_cell.alignment = Alignment(horizontal='left', vertical='top',wrapText=True)
return tgt_ws
def append_sf_to_excel(
filename: Union[str, Path],
sf: StyleFrame,
sheet_name: str = 'Sheet1',
startrow: Optional[int] = None,
max_col_width: int = 30,
autofilter: bool = False,
fmt_int: str = "#,##0",
fmt_float: str = "#,##0.00",
fmt_date: str = "yyyy-mm-dd",
fmt_datetime: str = "yyyy-mm-dd hh:mm",
truncate_sheet: bool = False,
storage_options: Optional[dict] = None,
**to_excel_kwargs
) -> None:
def set_column_format(ws, column_letter, fmt):
for cell in ws[column_letter]:
cell.number_format = fmt
filename = Path(filename)
file_exists = filename.is_file()
# process parameters
# calculate first column number
# if the sf will be written using `index=True`, then `first_col = 2`, else `first_col = 1`
first_col = int(to_excel_kwargs.get("index", True)) + 1
# ignore [engine] parameter if it was passed
if 'engine' in to_excel_kwargs:
to_excel_kwargs.pop('engine')
# save content of existing sheets
if file_exists:
wb = load_workbook(filename)
sheet_names = wb.sheetnames
sheet_exists = sheet_name in sheet_names
sheets = {ws.title: ws for ws in wb.worksheets}
with StyleFrame.ExcelWriter(
filename.with_suffix(".xlsx"),
mode="a" if file_exists else "w",
if_sheet_exists="new" if file_exists else None,
date_format=fmt_date,
datetime_format=fmt_datetime,
storage_options=storage_options
) as writer:
if file_exists:
# try to open an existing workbook
writer.book = wb
# get the last row in the existing Excel sheet
# if it was not specified explicitly
# for row in wb['Sheet1'].iter_rows():
# for cell in row:
# print(f'{cell.alignment}\n\n')
if startrow is None and sheet_name in writer.book.sheetnames:
startrow = writer.book[sheet_name].max_row
# truncate sheet
if truncate_sheet and sheet_name in writer.book.sheetnames:
# index of [sheet_name] sheet
idx = writer.book.sheetnames.index(sheet_name)
# remove [sheet_name]
writer.book.remove(writer.book.worksheets[idx])
# create an empty sheet [sheet_name] using old index
writer.book.create_sheet(sheet_name, idx)
# copy existing sheets
writer.sheets = sheets
else:
# file doesn't exist, we are creating a new one
startrow = 0
# write out the DataFrame to an ExcelWriter
sf.to_excel(writer, sheet_name=sheet_name)
worksheet = writer.sheets[sheet_name]
if autofilter:
worksheet.auto_filter.ref = worksheet.dimensions
for xl_col_no, dtyp in enumerate(sf.data_df.dtypes, first_col):
col_no = xl_col_no - first_col
width = max(sf.iloc[:, col_no].astype(str).str.len().max(),
len(sf.columns[col_no]) + 6)
width = min(max_col_width, width)
column_letter = get_column_letter(xl_col_no)
worksheet.column_dimensions[column_letter].width = width
if np.issubdtype(dtyp, np.integer):
set_column_format(worksheet, column_letter, fmt_int)
if np.issubdtype(dtyp, np.floating):
set_column_format(worksheet, column_letter, fmt_float)
if file_exists and sheet_exists:
# move (append) rows from new worksheet to the `sheet_name` worksheet
wb = load_workbook(filename)
# retrieve generated worksheet name
new_sheet_name = set(wb.sheetnames) - set(sheet_names)
if new_sheet_name:
new_sheet_name = list(new_sheet_name)[0]
# copy rows written by `sf.to_excel(...)` to
copy_excel_cell_range(
src_ws=wb[new_sheet_name],
tgt_ws=wb[sheet_name],
tgt_min_row=startrow + 1,
with_style=True
)
# remove new (generated by Pandas) worksheet
del wb[new_sheet_name]
wb.save(filename)
wb.close()
Credit to Maxu for writing this function, and thanks to Deepspace for making me aware of this solution.
I am having issues copying cells from excel workbook and pasting as values to another workbook.
I get an error on line rowSelected.append(sheet.cell(row = i, column = j).value) with the message AttributeError: 'str' object has no attribute 'cell'
Can anyone help with this?
import openpyxl
#Prepare the spreadsheets to copy from and paste too.
#File to be copied
wb = openpyxl.load_workbook(r"") #Add file name
sheet = wb["BusinessDetails"] #Add Sheet name
#File to be pasted into
template = openpyxl.load_workbook(r"") #Add file name
temp_sheet = template["Sheet1"] #Add Sheet name
#Copy range of cells as a nested list
#Takes: start cell, end cell, and sheet you want to copy from.
def copyRange(startCol, startRow, endCol, endRow, sheet):
rangeSelected = []
#Loops through selected Rows
for i in range(startRow,endRow + 1,1):
#Appends the row to a RowSelected list
rowSelected = []
for j in range(startCol,endCol+1,1):
rowSelected.append(sheet.cell(row = i, column = j).value)
#Adds the RowSelected List and nests inside the rangeSelected
rangeSelected.append(rowSelected)
return rangeSelected
#Paste range
#Paste data from copyRange into template sheet
def pasteRange(startCol, startRow, endCol, endRow, sheetReceiving,copiedData):
countRow = 0
for i in range(startRow,endRow+1,1):
countCol = 0
for j in range(startCol,endCol+1,1):
sheetReceiving.cell(row = i, column = j).value = copiedData[countRow][countCol]
countCol += 1
countRow += 1
def createData():
print("Processing...")
selectedRange = copyRange(1,2,4,14,sheet) #Change the 4 number values
pastingRange = pasteRange(1,3,4,15,temp_sheet,selectedRange) #Change the 4 number values
#You can save the template as another file to create a new file here too.s
template.save(r"")
print("Range copied and pasted!")
copyRange(2,4,30,78,"BusinessDetails")
pasteRange(2,4,30,78,"Sheet1")
It must be:
copyRange(2,4,30,78,sheet)
pasteRange(2,4,30,78,temp_sheet)
i.e. you need to pass the sheet objects, not the sheet names to your functions.
Update as per comment:
rangeSelected = copyRange(2,4,30,78,sheet)
pasteRange(2,4,30,78,temp_sheet, rangeSelected)
I have error in python code. I am trying split workbook to different sheets based on column value, below is the code.
import pandas as pd
import os
from xlwings import Book, Range, Sheet
path = ('C:\Dell')
worksheet = ('FILE.xlsx')
sheet =('Temporary_Table')
column = ('SERIAL_NUMBER')
workbook = os.path.join(path, worksheet)
wb = Book(workbook)
data = pd.DataFrame(pd.read_excel(workbook, sheet, index_col=None, na_values=[0]))
data.sort_values(column, axis = 0, inplace = True)
data = pd.DataFrame(pd.read_excel(workbook, sheet, index_col=None, na_values=[0]))
data.sort_values(column, axis = 0, inplace = True)
split = data.groupby(column)
for i in split.groups:
Sheet.add()
Range('A1', index = False).value = split.get_group(i)
it keeps giving me
type object 'Sheet' has no attribute 'add'