I'm using openpyxl to write data into excel files. I defined a function create_style to create named styles by passing a name and font, alignment, border styles etc. Once a style is created it'll be added to workbook and can be used anytime. In my case, NamedStyle is setting the alignment attributes to None. However, fonts,fills are working fine. I couldn't figure the problem.
Here's the code.
import os
from openpyxl import Workbook,load_workbook
from openpyxl.styles import Alignment, Font, NamedStyle
# to create a style passing workbook, name and optional arguments like font etc
def create_style(wb,stylename,**kwargs):
if stylename not in wb.named_styles:
style = NamedStyle(name=stylename)
for key,value in kwargs.items():
if value is not None:
setattr(style,key,value)
wb.add_named_style(style)
excelpath = '/path/to/my/master-excel-file.xlsx'
workbook = load_workbook(excelpath) if os.path.isfile(excelpath) else Workbook()
sheetname = 'sheet1'
sheets = workbook.get_sheet_names()
if sheetname in sheets:
print('loading existing sheet')
worksheet = workbook.get_sheet_by_name(sheetname)
else:
print('creating sheet...')
worksheet = workbook.create_sheet(sheetname)
font_1 = Font(name='Arial', size=18)
alignment_1 = Alignment(horizontal='general',
vertical='center')
create_style(workbook,'style1',font=font_1,alignment=alignment_1)
workbook.save(excelpath)
# My doubt is here
print(workbook._named_styles['style1'].font)
print('\n')
print(workbook._named_styles['style1'].alignment)
Let's assume there's no excel file in the path, then the code will create new excel file and create 'style1' and saves it in the workbook.
output1 :
creating sheet...
<openpyxl.styles.fonts.Font object>
Parameters:
name='Arial', charset=None, family=None, b=False, i=False, strike=None,
outline=None, shadow=None, condense=None, color=None, extend=None, sz=18.0,
u=None, vertAlign=None, scheme=None
<openpyxl.styles.alignment.Alignment object>
Parameters:
horizontal='general', vertical='center', textRotation=0, wrapText=None,
shrinkToFit=None, indent=0.0, relativeIndent=0.0, justifyLastLine=None,
readingOrder=0.0
When I run the same code again, None is assigned to all alignment parameters.
output 2:
loading existing sheet
<openpyxl.styles.fonts.Font object>
Parameters:
name='Arial', charset=None, family=None, b=False, i=False, strike=None,
outline=None, shadow=None, condense=None, color=None, extend=None, sz=18.0,
u=None, vertAlign=None, scheme=None
<openpyxl.styles.alignment.Alignment object>
Parameters:
horizontal=None, vertical=None, textRotation=0, wrapText=None,
shrinkToFit=None, indent=0.0, relativeIndent=0.0, justifyLastLine=None,
readingOrder=0.0
which means the style1 losing data regarding alignment on every run while font is working just fine. In other words, style1 isn't retaining the alignment. Why is it happening ?
Related
Background
I have a list of values in column A (which differ from worksheet to worksheet). I wanted to identify a subset of values to be matched and if a match was made between lists then the script will add the value from the tuple list to column P and moves downwards per value (see picture Script output).
Problem
The script kind of works, accept if the value in tuple is not found the Column A list, then it creates an empty cell in Excel and moves down to the next cell (see picture - Excel output).
Questions
What is the best way to avoid empty cells if the value from the tuple is not in the list?
Script
from openpyxl import Workbook
from openpyxl import load_workbook
from openpyxl.styles import Color, PatternFill, Font, Border
from openpyxl.styles import colors
from openpyxl.cell import Cell
import colorama
from colorama import init
init()
from colorama import Fore, Style, Back
import os
import shutil
import math
from decimal import Decimal
from openpyxl.styles import Alignment
from inspect import currentframe
import time
########################################
#########################################
src_path ="/Users/data/"
xlfileName = "test.xlsx"
xlfile = src_path + xlfileName
wb = Workbook()
wb = load_workbook(xlfile)
#########################################
#########################################
def get_linenumber():
cf = currentframe()
return cf.f_back.f_lineno
#########################################
#########################################
non_cash_tup = (
"Depreciation & amortisation", "Deferred income taxes", "Stock-based compensation", "Change in working capital",
"Accounts receivable", "Inventory", "Accounts payable", "Other non-cash items")
for xws in wb.sheetnames:
print(Style.RESET_ALL)
print(Fore.BLUE, "Looping Through Worksheet Name -- ", worksheet, "Script line is -- ", get_linenumber())
print(Style.RESET_ALL)
worksheet = wb[xws]
cell_list = []
for cell in worksheet['A']:
cell_list.append(cell.value)
for i, name in enumerate(non_cash_tup):
if name in cell_list:
print(Fore.LIGHTGREEN_EX, name, "is in the list")
column_cell = 16
worksheet.cell(column=column_cell, row=i+2, value=name)
else:
print(Fore.LIGHTRED_EX, name, "is not in the list")
wb.save(xlfile)
Use the set() function.
The set() function in Python uses to take an argument and convert it into a set object. It can take arguments like lists, tuples and dictionaries. The argument is called iterable. The output of elements might not be in the same order because items passed as list were not in order.
non_cash_tup = (
"Depreciation & amortisation", "Deferred income taxes", "Stock-based compensation", "Change in working capital",
"Accounts receivable", "Inventory", "Accounts payable", "Other non-cash items")
for xws in wb.sheetnames:
print(Style.RESET_ALL)
print(Fore.BLUE, "Looping Through Worksheet Name -- ", worksheet, "Script line is -- ", get_linenumber())
print(Style.RESET_ALL)
worksheet = wb[xws]
cell_list = []
for cell in worksheet['A']:
cell_list.append(cell.value)
matching = set(non_cash_tup) & set(cell_list)
#print(matching)
for i, name in enumerate(matching):
print(Fore.LIGHTGREEN_EX, name, "is in the list")
column_cell = 16
worksheet.cell(column=column_cell, row=i+2, value=name)
wb.save(xlfile)
i am trying to make an automated xlsx document creator, for this i want to make some of the cells to have bold font, this is part of the code i use
from openpyxl import load_workbook
from openpyxl.styles import Font
from openpyxl.styles.colors import Color
wb_orden = load_workbook(filename = 'resg.xlsx')
ws_orden = wb_orden["Resguardo"]
italic24Font = Font( size = 24, italic = True, bold = True)
ws_orden['A6'] = "hsadgfagahgafdhg"
ws_orden['A6'].font = italic24Font
NAME = "generated/"+"res_"+order+".xlsx"
wb_orden.save(filename = NAME)
but when i see the xlsx file the A6 cell is changed to hsadgfagahgafdhg, but it isn't bold. thanks for reading i hope you can help me whit this
i also tried this but still not working
ws_orden['A6'].font = Font(size=23, underline='single', color='FFBB00', bold=True, italic=True)
I am using python to do some simulations and using openpyxl to generate the reports. Now the simulation is results are to be divided into several sheets of an excel file. By the principles of OOP my structure should have a base simulator class which implements basic operations and several derived classes which implement modifications to simulator. Since functions related to a class should remain with the class I want the report sheets to be generated by the derived classes (with all its styling and formatting etc). Then maybe a driver class or function which takes all these report sheets and puts them in one work book. But as far as I can tell there is no way to copy a worksheet in openpyxl. Now it seems like I have broken the OOP models. Is there a way out of this?
Edit
Here is an example for my code. This is a trimmed fat free version, the real class is not that simple
from openpyxl import Workbook
from openpyxl.styles import Font
class sim1:#this will inherit from another class sim which has basic operations
def __init__(self):
#assume function calling and complex math here
self.x = 1
def f1OverRide(self):
#over rides some function in sim to implement custom method for sim1 (several of these)
return 23
def get_sheet(self):
wb = Workbook()
ws = wb.active
ws['A1'] = self.x
#example formatting real formatting is pretty complex
ws['A1'].font = Font(size=12,name='Calibri')
return ws
class sim2:#this will inherit from another class sim which has basic operations
def __init__(self):
#assume function calling and complex math here
self.x = 12
def f1OverRide(self):
#over rides some function in sim to implement custom method for sim1 (several of these)
return 42
def get_sheet(self):
wb = Workbook()
ws = wb.active
ws['A1'] = self.x
#example formatting, real formatting is pretty complex
ws['A1'].font = Font(size=14,name='Calibri',color='ff2223')
return ws
s1 = sim1()
s2 = sim2()
# now I want to get the sheets for sim1 and sim2 and combine in 1 workbook
wb = Workbook()
ws1 = s1.get_sheet()
ws2 = s2.get_sheet()
# dont know what to do now :( openpyxl can not copy sheet into this workbook
OOP copy Worksheets between Workbooks, for instance:
from openpyxl import Workbook
from openpyxl.styles import Font
from copy import copy
class sim():
def __init__(self, n):
self.n = n
def get_sheet(self):
#...
wb = Workbook()
ws = wb.active
ws['A1'] = 'sim'+str(self.n)
if self.n == 1:
ws['A1'].font = Font(size=12,name='Calibri')
else:
ws['A1'].font = Font(size=14, name='Calibri', color='ff2223')
return ws
class sim_Workbook(Workbook):
# overload Workbook.copy_worksheet
def copy_worksheet(self, from_worksheet):
# Create new empty sheet and append it to self(Workbook)
ws = self.create_sheet( title=from_worksheet.title )
for row, row_data in enumerate(from_worksheet.rows,1):
for column, from_cell in enumerate(row_data,1):
cell = ws.cell(row=row, column=column)
cell.value = from_cell.value
cell.font = copy(from_cell.font)
s1 = sim(1)
s2 = sim(2)
wb = sim_Workbook()
wb.copy_worksheet( s1.get_sheet() )
wb.copy_worksheet( s2.get_sheet() )
wb.save('../test/test.xlsx')
#example formatting real formatting is pretty complex
You have to copy your complex formatting, style by style as shown by font in the example. This could lead to huge workload, depending how many cells you have to copy.
Read this to get a hint about this, but you can't do it 1:1 as you copy from workbook to workbook copying-styles-from-a-range-to-another-range
Tested with Python:3.4.2 - openpyxl:2.4.1 - LibreOffice: 4.3.3.2
I am using openpyxl 2.2.6 from the bitbucket repository. I tried the simple example from the documentation
This however produces a blank workbook with a single sheet approproately named "MyTestSheet" . but values in any cell.
What am I missing to get write to a cell to work.
from openpyxl import Workbook
wb = Workbook(optimized_write = True)
ws = wb.create_sheet(title="MyTestSheet")
from openpyxl.writer.dump_worksheet import WriteOnlyCell
from openpyxl.comments import Comment
from openpyxl.styles import Style, Font
cell = WriteOnlyCell(ws, value="hello world")
cell.font = Font(name='Courrier', size=36)
cell.comment = Comment(text="A comment", author="Author's Name")
wb.save("testwr_so.xlsx")
You need to append the cell to the worksheet:
ws.append([cell]) # you must always append a sequence
While running following sample code I am getting error as "save is not defined."
That is why I added from xlsxcessive.xlsx import save.
But still it is not able to save on local machine.
Is this error of Python API or am I doing any mistake in Coding?
"""Just a simple example of XlsXcessive API usage."""
from xlsxcessive.xlsx import Workbook
from xlsxcessive.worksheet import Cell
**from xlsxcessive.xlsx import save**
import decimal
wb = Workbook()
sheet = wb.new_sheet('Test Sheet')
# a shared format
bigguy = wb.stylesheet.new_format()
bigguy.font(size=24)
bigguy.align('center')
# add a border
bigguy.border(top="medium", bottom="medium")
# set a builtin number format
bigguy.number_format('0.00')
# another shared format
boldfont = wb.stylesheet.new_format()
boldfont.font(bold=True)
# and another
highprec = wb.stylesheet.new_format()
# set a custom number format on the shared format
highprec.number_format("0.000")
# the API supports adding rows
row1 = sheet.row(1)
# rows support adding cells - cells can currently store strings, numbers
# and formulas.
a1 = row1.cell("A1", "Hello, World!", format=boldfont)
row1.cell("C1", 42.0, format=bigguy)
# cells can be merged with other cells - there is no checking on invalid merges
# though. merge at your own risk!
a1.merge(Cell('B1'))
# adding rows is easy
row2 = sheet.row(2)
row2.cell("B2", "Foo")
row2.cell("C2", 1, format=bigguy)
# formulas are written as strings and can have default values
shared_formula = sheet.formula("SUM(C1, C2)", 43.0, shared=True)
row3 = sheet.row(3)
row3.cell("C3", shared_formula, format=bigguy)
# you can work with cells directly on the sheet
sheet.cell('D1', 12.0005, format=highprec)
sheet.cell('D2', 11.9995, format=highprec)
sheet.cell('D3', shared_formula, format=highprec)
# and directly via row and column indicies
sheet.cell(coords=(0, 4), value=40)
sheet.cell(coords=(1, 4), value=2)
sheet.cell(coords=(2, 4), value=shared_formula)
# you can share a formula in a non-contiguous range of cells
times_two = sheet.formula('PRODUCT(A4, 2)', shared=True)
sheet.cell('A4', 12)
sheet.cell('B4', times_two)
sheet.cell('C4', 50)
sheet.cell('D4', times_two)
# iteratively adding data is easy now
for rowidx in xrange(5,10):
for colidx in xrange(5, 11, 2):
sheet.cell(coords=(rowidx, colidx), value=rowidx*colidx)
# set column widths
sheet.col(2, width=5)
# write unicode value
sheet.cell('G2', value=u"43\u00b0")
if __name__ == '__main__':
import os
import sys
from xlsxcessive.xlsx import save
if len(sys.argv) == 1:
print "USAGE: python sample.py NEWFILEPATH"
print "Writes a sample .xlsx file to NEWFILEPATH"
raise SystemExit(1)
if os.path.exists(sys.argv[1]):
print "Aborted. File %s already exists." % sys.argv[1]
raise SystemExit(1)
stream = None
if sys.argv[1] == '-':
stream = sys.stdout
# wb is the Workbook created above
save(wb, sys.argv[1], stream)
Note: Tried following also
# local file
save(workbook, 'financials.xlsx')
# stream
save(workbook, 'financials.xlsx', stream=sys.stdout)