TL; DR
How do I load a spreadsheet template using OpenPyXL
workbook = openpyxl.load_workbook('template.xlsx')
then create a new one and fill the rows data with XlsxWriter?
xls_workbook = xlsxwriter.Workbook('proposal.xlsx')
# Missing step
xls_workbook = workbook.add_worksheet(workbook)
xls_workbook.write(CELL_COST, cost)
Details
I have a spreadsheet with several tabs with a quite complex formatting (used as a sales proposal), so there is a lot of formatting that must be preserved.
So, instead of programatically create the spreadsheet, I have to:
Open the template for reading using OpenPyXL
Extract the template and save it into a template.py file
Create the sales pitch spreadsheets using the template, creating new tables with XlsxWriter.
Is there a simple way of extracting the template or do really I need recreate the whole layout programmatically?
I have run into the same "problem" and have not found much about it. So finally I code a little function to copy the template sheet and some styling.
It's 2 years late but I hope it helps someone who comes to this question.
def openpyxl_to_xlsxwriter(openpyxl_workbook, xlsxwriter_workbook):
template_sheets = openpyxl_workbook.get_sheet_names()
# Copy all the sheets from the openpyxl template to the xlsxwriter workbook.
for sheet_name in template_sheets:
xlsxwriter_workbook.add_worksheet(sheet_name)
for sheet in template_sheets:
openpyxl_active_sheet = openpyxl_workbook.get_sheet_by_name(sheet)
xlsxwriter_active_sheet = xlsxwriter_workbook.get_worksheet_by_name(sheet)
for row in openpyxl_active_sheet.rows:
for cell in row:
# # Copy some STYLES # #
# Copy font color and Background color
cell_style = {}
theme = cell.fill.start_color.theme
tint = cell.fill.start_color.tint
font_theme = cell.font.color.theme
font_tint = cell.font.color.tint
font_color = theme_and_tint_to_rgb(openpyxl_workbook, font_theme, font_tint)
cell_style['font_color'] = font_color
try:
background_color = theme_and_tint_to_rgb(openpyxl_workbook, theme, tint)
cell_style['bg_color'] = background_color
except:
pass
cell_format = xlsxwriter_workbook.add_format(cell_style)
# Copy border
border_left = cell.border.left.border_style
border_right = cell.border.right.border_style
border_top = cell.border.top.border_style
border_bottom = cell.border.bottom.border_style
if border_left:
cell_format.set_left()
if border_right:
cell_format.set_right()
if border_top:
cell_format.set_top()
if border_bottom:
cell_format.set_bottom()
# Copy Cell Width and Height
cell_height = openpyxl_active_sheet.row_dimensions[cell.row].height
cell_width = openpyxl_active_sheet.column_dimensions[cell.column_letter].width
column_index = cell.column - 1
xlsxwriter_active_sheet.set_column(column_index, column_index, cell_width)
row_index = cell.row - 1
xlsxwriter_active_sheet.set_row(row_index, cell_height)
# Copy Cell Value
xlsxwriter_active_sheet.write(cell.coordinate, cell.value, cell_format)
Related
This is a section of my python code, this class retrieves a sheet from smartsheet and turns it into a df, which later uses to display on a table on my GUI. I've been trying to change row colors but cant seem to get it right, in the code I managed to change the color of columns, however when I pass self.table.rowcolors... it does nothing. Anyone has an idea of what else I could try?
class SmartsheetFrame(Frame):
def __init__(self, master):
super().__init__(master)
self.configure(bg='white')
# Get the sheet
sheet_id = 3839061875025796
ss_client = smartsheet.Smartsheet(access_token=os.environ.get('SMARTSHEET_ACCESS_TOKEN'))
sheet = ss_client.Sheets.get_sheet(sheet_id)
rows = sheet.rows
columns = sheet.columns
#Creating a dataframe from the sheet
df = pd.DataFrame(columns=[col.title for col in columns])
for row in rows:
df.loc[row.id] = [col.value for col in row.cells]
last_4_rows = df.tail(4)
# Create a Table widget and set its properties
self.table = pt.Table(self, dataframe=df, showstatusbar=True, height=600, width=1200)
# Specify the row index you want to color
self.table.columncolors['AEM ID'] = 'forestgreen'
self.table.columncolors['Intel ID'] = 'midnightblue'
self.table.columncolors['Site'] = 'darkslategrey'
self.table.columncolors['Mode'] = 'firebrick'
self.table.redraw()
self.table.show()
I've tried many methods that I found online, so far I've managed to chage the color of columns, but havent been able to do the same for rows
I am trying to perform excel export functionality in Django in which I am generating an excel file as per the selection of field list. In that, I want to format cells as per the field data. Currently, data for a date is not showing in the proper date format. I am attaching a screenshot for the same.
Date format not proper
Every time after downloading excel I need to change the cell format manually.
Format change mannually
But I want to set the format for the date field via code.
Here is my code for generating excel and downloading it.
checkedField = request.POST.getlist('excelfields[]')
FinalPath = ''
filename = 'media\ExcelExportClientReg\InitalRegistaration.xls'
work_book = xlwt.Workbook(encoding='utf-8')
uc = u"".join(chr(0x0410 + i) for i in range(32)) # some Cyrillic characters
u8 = uc.encode("UTF-8")
work_sheet = work_book.add_sheet('Client Registration')
style_head_row = xlwt.easyxf("""
align:
wrap off,
vert center,
horiz center;
borders:
left THIN,
right THIN,
top THIN,
bottom THIN;
font:
name Arial,
colour_index white,
bold on,
height 0xA0;
pattern:
pattern solid,
fore-colour 0x15;
"""
)
style_data_row = xlwt.easyxf("""
align:
wrap on,
vert center,
horiz left;
font:
name Arial,
bold off,
height 0XA0;
borders:
left THIN,
right THIN,
top THIN,
bottom THIN;
"""
)
# set columns value.
row_num = 0
columns = checkedField
for col_num in range(len(columns)):
work_sheet.write(row_num, col_num, columns[col_num],style_head_row)
# set rows value
if request.session['AdminID'] == 1:
rows = ClientRegistration.objects.values_list(*checkedField).all()
else:
rows = ClientRegistration.objects.values_list(*checkedField).filter(CreatedBy=request.session['AdminID'],IsDeleted=0)
for row in rows:
row_num += 1
for col_num in range(len(row)):
work_sheet.write(row_num, col_num, row[col_num],style_data_row)
work_book.save(filename)
FinalPath = os.path.join(settings.BASE_DIR,filename)
path = open(FinalPath, 'rb')
mime_type, _ = mimetypes.guess_type(FinalPath)
response = HttpResponse(path,content_type=mime_type)
response['Content-Disposition'] = 'attachment; filename="InitalRegistaration.xls"'
os.remove(FinalPath)
return response
Here checkedField is the checkbox value for which fields need to add to the excel file.
Please help me with this. Thank you in advance.
I want to add columns to an existing table in an excel file.
Therefore I wan't to use python and the openpyxl library.
Right now I use a class when it is initialising, it is connecting to the file.
Afterwards I call the check_for_column function and when the column is not existing it should create it. And in the end of the script I save the file.
import os
from openpyxl import load_workbook
from openpyxl.worksheet.table import Table, TableColumn, range_boundaries
from openpyxl.utils.cell import get_column_letter
class ExcelHandler:
_wb_name = None
_table = None
_wb = None
def __init__(self):
self._wb_name = os.getenv('EXCEL_FULLPATH')
self._wb = load_workbook(filename=self._wb_name, keep_vba=True)
sheet = self._wb['DataInbox']
self._table = sheet.tables['WebPageForms']
return
def check_for_column(self, column_name):
if not column_name in self._table.column_names:
lst_ids = [my_object.id for my_object in self._table.tableColumns]
new_id = lst_ids[-1]+1
# change range of table
min_col, min_row, max_col, max_row = range_boundaries(
self._table.ref)
max_col += 1
mx = get_column_letter(max_col)
mn = get_column_letter(min_col)
self._table.ref = '{}{}:{}{}'.format(mn, min_row, mx, max_row)
# add column to table
tc = TableColumn(id=new_id, name=column_name)
self._table.tableColumns.append(tc)
return
def save_wb(self):
self._wb.save(self._wb_name)
return
The code runs fine as shown. Although when I then try to open the file with excel it gives me an alert saying:
We found a problem with some content in ’file.xlsm’. Do you want us to try to recover as much as we can? If you trust the source of this workbook, click Yes.
This is the repair result of excel when I press yes
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<recoveryLog xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><logFileName>Repair Result to file.xml</logFileName><summary>Errors were detected in file ’*path*/file.xlsm’</summary><repairedRecords summary="Following is a list of repairs:"><repairedRecord>Repaired Records: Table from /xl/tables/table1.xml part (Table)</repairedRecord></repairedRecords></recoveryLog>
I would highly appreciate If anyone could help me
Ok, I found the problem why the excel file is corrupt, my bad.
when I create the column in the table, I also have to write the name in the respective cell:
def check_for_column(self, column_name):
***
# write name in cell of new column header
self._ws.cell(row=min_row, column=max_col).value = column_name
***
return
If I add this to the code, my table is modified correctly
i have an issue that i can't show my second record in mysql table. The report just showing 1 record in a row and the second one isn't show on pdf file. i'm using reportlab for report generator on python 2.7
this is my code that i can' fix yet :
def nilaisql():
rpt = raw_input("input NPM : ")
sql = "select nama, tanggal, jamMasuk from t_"+rpt
curs.execute(sql)
result = curs.fetchall()
for row in result:
c = canvas.Canvas("Sampelone.pdf")
c.drawString(250, 700, str(row[0]))
c.save()
os.system("Sampelone.pdf")
this is my record on mysql. I want to show the second row record but the pdf just showing the first row record
it should showing the second row record
and this is the result on my pdf file
i'm getiing stuck in here and if you know something i'm really grateful that you can share the solution in here
for row in result:
c = canvas.Canvas("Sampelone.pdf")
c.drawString(250, 700, str(row[0]))
c.save()
what your code snippet is doing is, creating a new file and writing the content of your variable row into the pdf file and c.save saves it. In the next iteration, this same file is recreated which is blank and the original file is overwritten by this blank file and the content of row is printed that is why you will always see only first row record in it.
This should work fine. Increment or Decrement the value of y according to your use and document height.
c = canvas.Canvas("Sampelone.pdf") #creates a pdf
for row in result:
c.drawString(250, y, str(row[0])) #writes data at given co-ordinates
c.save() #saves the pdf
This way you can see all row records in the pdf.
But this practice is not considered good, you should always put your data in flowable like this. .
from reportlab.lib import styles
from reportlab.platypus import SimpleDocTemplate, Paragraph
def nilaisql():
pdfname = 'mydoc.pdf'
doc = SimpleDocTemplate(
pdfname
)
style = styles["Normal"]
story = []
rpt = raw_input("input NPM : ")
sql = "select nama, tanggal, jamMasuk from t_" + rpt
curs.execute(sql)
result = curs.fetchall()
for row in result:
story.append(Paragraph(row, style))
doc.build(
story
)
os.system("Sampelone.pdf")
Read more about flowables in reportlab-userguide
So, given:
dttm = datetime.datetime.strptime("2014-06-23 13:56:30", "%Y-%m-%d %H:%M:%S")
ws['A1'] = dttm
The result in excel is that the correct date-time is written to the cell (you can see it where you'd input formulas). BUT, the cell display format is only MM/DD/YYYY.
I need the cell to display like "6/23/2014 13:56" instead of just "6/23/2014".
How can I explicitly format the cell to accomplish this?
Thanks!
Edit
#alecxe This solution works and is exactly what I asked for. I would like to be able to save styles like the solution by #Woodham. Unfortunately it raises a typeError (see comment). Any suggestions?
The simplest way to format a cell is using .number_format = "format" as in:
value = datetime.datetime.strptime("2014-06-23 13:56:30", "%Y-%m-%d %H:%M:%S")
cell = ws['A1']
cell.value = value
cell.number_format = 'YYYY MMM DD'
This is tested in openpyxl (2.2.2)
For openpyxl 2.4.5 you'll no longer have access to NumberFormat and Style and will have to use NamedStyle. Here's some sample usage:
from openpyxl.styles import NamedStyle
date_style = NamedStyle(name='datetime', number_format='DD/MM/YYYY HH:MM:MM')
ws['A1'].style = date_style
Alternatively with the new NamedStyle class, you can set the style by the string name once NamedStyle has been instantiated:
from openpyxl.styles import NamedStyle
NamedStyle(name='custom_datetime', number_format='DD/MM/YYYY HH:MM:MM')
ws['A1'].style = 'custom_datetime'
Documentation here: https://openpyxl.readthedocs.io/en/stable/styles.html
I believe you will need to set a openpyxl.styles.Style on the cell(s) that you want to format.
Looking at the documentation here, something like this should work:
dttm = datetime.datetime.strptime("2014-06-23 13:56:30", "%Y-%m-%d %H:%M:%S")
s = Style(number_format=NumberFormat('dd-mm-yyyy h:mm:ss'))
ws['A1'] = dttm
ws['A1'].styles = s
Update:
Style class is no longer used, for the solution refer to this answer.
For openpyxl 2.3.4 the NumberFormat cannot be imported, but this code works to set the style:
from openpyxl.styles import Style
…
date_style = Style(number_format="DD/MM/YYYY HH:MM:MM")
ws['A1'].style = date_style
from openpyxl import load_workbook
from openpyxl.styles import NamedStyle
xlsx_file = args.xlsx_file.name
# openning:
wb = load_workbook(filename = xlsx_file)
# create date style:
date_style = NamedStyle(name='date_style', number_format='DD.MM.YYYY HH:MM:MM')
# apply the style to the column H of the default sheet:
ws = wb.active
for row in ws[2:ws.max_row]: # skip the header
cell = row[7] # column H
cell.style = date_style
# saving:
wb.save(xlsx_file)
Edit: the above works for me, but somehow does not work on my coleagues machine. Converting the cell to string fixed that:
import datetime
from openpyxl import load_workbook
from openpyxl.styles import Alignment
xlsx_file = 'file.xlsx'
date_format = '%Y-%b-%d'
# openning:
wb = load_workbook(filename = xlsx_file)
# we also center align that column:
alignment = Alignment(horizontal='center')
# apply python date format to column H of the default sheet, and convert the column to Excel text:
ws = wb.active
for row in ws[2:ws.max_row]: # skip the header
cell = row[7] # column H
if isinstance(cell.value, datetime.datetime):
cell.value = cell.value.strftime(date_format)
cell.alignment = alignment
# saving:
wb.save(xlsx_file)
The same wrapped in a script:
#!/usr/bin/env python3
import argparse
import datetime
from openpyxl import load_workbook
from openpyxl.styles import Alignment
# ==============
## parsing args:
desc="""
Applies python date format to a given column of the xlsx file (default sheet) and converts the column to a Excel text format.
Dependencies:
pip3 install --user --upgrade openpyxl
"""
parser = argparse.ArgumentParser(description=desc, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('--version', action='version', version='%(prog)s 0.01')
parser.add_argument('-f', '--file',
help = "xlsx file",
dest = 'xlsx_file',
type = argparse.FileType('r'),
)
parser.add_argument('-c', '--column',
help = "column (starting from A) (default to %(default)s)",
dest = 'column',
type = str,
default = "A",
)
parser.add_argument('-d', '--date-format',
help = "date format to use, e.g. %%d.%%m.%%Y (default to %(default)s)",
dest = 'date_format',
type = str,
default = '%Y-%b-%d',
)
args = parser.parse_args()
# =========
## program:
xlsx_file = args.xlsx_file.name
column_number = sum(
[ ord(char) - 97 + i*26 for i,char in enumerate(
list( args.column.lower() )
) ]
)
# openning:
wb = load_workbook(filename = xlsx_file)
# we also center align that column:
alignment = Alignment(horizontal='center')
# apply python date format to a given column of the default sheet, and convert the column to Excel text:
ws = wb.active
for row in ws[2:ws.max_row]: # skip the header
cell = row[column_number]
if isinstance(cell.value, datetime.datetime):
cell.value = cell.value.strftime(args.date_format)
cell.alignment = alignment
# saving:
wb.save(xlsx_file)
I found that this worked. Although number_format is used it seems to recognise the date format specified when put into the excel wb.
import datetime
date = datetime.date(2020, 2, 24) # python datetime format is yyyy mm dd
ws.cell(row=[row_ref], column=[col_ref], value=date)
ws.cell(row=[row_ref], column=[col_ref]).number_format = 'dd/mm/yy'