Fill cells with colors using openpyxl? - python

I am currently using openpyxl v2.2.2 for Python 2.7 and i wanted to set colors to cells. I have used the following imports
import openpyxl,
from openpyxl import Workbook
from openpyxl.styles import Color, PatternFill, Font, Border
from openpyxl.styles import colors
from openpyxl.cell import Cell
and the following is the code I tried using:
wb = openpyxl.Workbook()
ws = wb.active
redFill = PatternFill(start_color='FFFF0000',
end_color='FFFF0000',
fill_type='solid')
ws['A1'].style = redFill
but I get the following error:
Traceback (most recent call last)
self.font = value.font.copy()
AttributeError: 'PatternFill' object has no attribute 'font'
Any idea on how to set cell A1 (or any other cells) with colors using openpyxl?

I believe the issue is that you're trying to assign a fill object to a style.
ws['A1'].fill = redFill should work fine.

The API for styles changed once again. What worked for me was
my_red = openpyxl.styles.colors.Color(rgb='00FF0000')
my_fill = openpyxl.styles.fills.PatternFill(patternType='solid', fgColor=my_red)
cell.fill = my_fill
Color is an alpha RGB hex color. You can pass it in as 'rrggbb' with a default alpha of 00 or specify the alpha with 'aarrggbb'. A bunch of colors are defined as constants in openpyxl.styles.colors if you need to grab one quickly.

This worked for me. They changed things and most of the help you see on the internet is for older versions of the openpyxl library from what I am seeing.
# Change background color
xls_cell.style = Style(fill=PatternFill(patternType='solid',
fill_type='solid',
fgColor=Color('C4C4C4')))

in python 3.x
wb = openpyxl.Workbook()
ws = wb.active
redFill = PatternFill(start_color='FFFF0000',
end_color='FFFF0000',
fill_type='solid')
ws['A1'].fill = redFill
that working but i dont know in python 2.x i hope working
just put ws['A1'].fill=redFill

from openpyxl import Workbook, load_workbook
from openpyxl.styles import PatternFill
_file_name = "Test.xlsx"
_sheet_name = "Test_Sheet"
def new_workbook(_file_name, _sheet_name):
wb = Workbook() # Workbook Object
ws = wb.active # Gets the active worksheet
ws.title = _sheet_name # Name the active worksheet
# Writing the header columns
ws['A1'] = 'Name'
ws['B1'] = 'Class'
ws['C1'] = 'Section'
ws['D1'] = 'Marks'
ws['E1'] = 'Age'
col_range = ws.max_column # get max columns in the worksheet
# formatting the header columns, filling red color
for col in range(1, col_range + 1):
cell_header = ws.cell(1, col)
cell_header.fill = PatternFill(start_color='FF0000', end_color='FF0000', fill_type="solid") #used hex code for red color
wb.save(_file_name) # save the workbook
wb.close() # close the workbook
if __name__ == '__main__':
new_workbook(_file_name, _sheet_name)
Result -

What I would do for Excel is this:
from openpyxl import Workbook, load_workbook
from openpyxl.styles import PatternFill
wb = load_workbook("test.xlsx")
ws = wb.active
ws["A1"].fill = PatternFill("solid", start_color="FFA500")
You can replace "A1" with another cell and start_color has to be a hex color.

To fill a range of rows/columns, do this
for cell in ws['A1:A100']:
cell[0].fill = redFill
To fill all rows of a column
for cell in ws['A1:{}'.format(ws.max_row)]:
cell[0].fill = redFill

Use a nested for loop to fill a two dimensional range.
Python 3.9
import openpyxl as op
fill_gen = op.styles.PatternFill(fill_type='solid',
start_color='FFFFFF',
end_color='FFFFFF')
for row in ws["A1:BB50"]:
for cell in row:
cell.fill = fill_gen

Related

Store cell value of first few characters

I am trying to change the name of a sheet according to the value of a cell.
here is the code I am using.
from openpyxl import load_workbook
wb = load_workbook('file_name.xlsx')
ws = wb['Sheet 1']
sheet_name = ws['B2']
ws.title = f'Marketing {sheet_name}'
This code works, but
my problem is I only need to extract the first 3 characters from the cell ws['B2'].
How can I do that.
Use slicing:
sheet_name = ws['B2'].value[:3]
You can use the characters in the string.
Edit the parameters to get the desired result.
from openpyxl import load_workbook
wb = load_workbook('file_name.xlsx')
ws = wb['Sheet 1']
sheet_name = ws['B2'].value
ws.title = f'Marketing {sheet_name[0:3]}' # First character to third
Start reading the tabs by number so you don't have to change the tab name in the future.
Final code:
from openpyxl import load_workbook
wb = load_workbook('file_name.xlsx')
ws = wb.worksheets[0] # First tab
sheet_name = ws['B2'].value
ws.title = f'Marketing {sheet_name[0:3]}' # First character to third

adjust column width size using openpyxl

I'm facing trouble in adjusting column width of the below excel file. I'm using this code.
from openpyxl.utils import get_column_letter
ws.delete_cols(1) #remove col 'A' which has pandas index no.
for column in ws.iter_cols():
name = get_column_letter(column[0].column)
new_col_length = max(len(str(cell.value)) for cell in column)
#ws.column_dimensions[name].bestFit = True #I tried this but the result is same
ws.column_dimensions[name].width = new_col_length
excel sheet:
the output im getting.:
Something like this should manage the deletion of column A using Openpyxl
from openpyxl import load_workbook
from openpyxl.utils import get_column_letter
path = 'col_width.xlsx'
wb = load_workbook(path)
ws = wb['Sheet1']
remerge_cells_list = []
# Remove existing merged cells, and
# Add the calculated new cell coords to remerge_cells_list to re-add after
# the deletion of column A.
for unmerge in range(len(ws.merged_cells.ranges)):
current_merge = ws.merged_cells.ranges[0]
new_min_col = get_column_letter(current_merge.min_col-1)
new_max_col = get_column_letter(current_merge.max_col-1)
remerge_cells_list.append(new_min_col + str(current_merge.min_row) + ':'
+ new_max_col + str(current_merge.max_row))
print("Removing merge: " + current_merge.coord)
ws.unmerge_cells(current_merge.coord)
print("\nDeleting column A\n")
ws.delete_cols(1) #remove col 'A' which has pandas index no.
# Set the column width dimenions
for column in ws.iter_cols():
name = get_column_letter(column[0].column)
new_col_length = max(len(str(cell.value)) for cell in column)
# ws.column_dimensions[name].bestFit = True #I tried this but the result is same
ws.column_dimensions[name].width = new_col_length+2 # Added a extra bit for padding
# Re-merge the cells from the remerge_cells_list
# Don't think it matters if this is done before or after resizing the columns
for merge in remerge_cells_list:
print("Add adjusted cell merge: " + merge)
ws.merge_cells(merge)
wb.save('col_width_out.xlsx')
After many hours of research finally, I found it.
NOTE : In the below code, sheet is the worksheet name. Usually in the documentation, we can see it as ws. Please don't forget to change the worksheet name.
# Imorting the necessary modules
try:
from openpyxl.cell import get_column_letter
except ImportError:
from openpyxl.utils import get_column_letter
from openpyxl.utils import column_index_from_string
from openpyxl import load_workbook
import openpyxl
from openpyxl import Workbook
for column_cells in sheet.columns:
new_column_length = max(len(str(cell.value)) for cell in column_cells)
new_column_letter = (get_column_letter(column_cells[0].column))
if new_column_length > 0:
sheet.column_dimensions[new_column_letter].width = new_column_length*1.23
UPDATE : This code doesn't work for all, but don't hesitate to try it..

How to write text in a cell from top to bottom in openpyxl

I'd like to write text in a cell from top to bottom just like the vertical text orientation in EXCEL using openpyxl packages in python.
But I can't do that.
Could anyone please help me?
I uploaded the exact image I want
Use the alignment attribute of a cell and set the textRotation to the needed angle (1-180). You can read more abot it in the openpyxl documentation - Here.
Code Example:
from openpyxl import Workbook
from openpyxl.styles import Alignment
wb = Workbook()
ws = wb.active
ws['A1'] = 'Example'
ws['A1'].alignment = Alignment(textRotation=180)
wb.save('Example.xlsx')
Output:
If you want the charecters to be written horizontally but the word verticaly just set textRotation to 255:
from openpyxl import Workbook
from openpyxl.styles import Alignment
wb = Workbook()
ws = wb.active
ws['A1'] = 'Hello'
ws['A1'].alignment = Alignment(textRotation=255)
wb.save('Example.xlsx')
Output:

Writing multi-line strings into cells using openpyxl

I'm trying to write data into a cell, which has multiple line breaks (I believe \n), the resulting .xlsx has line breaks removed.
Is there a way to keep these line breaks?
The API for styles changed for openpyxl >= 2. The following code demonstrates the modern API.
from openpyxl import Workbook
from openpyxl.styles import Alignment
wb = Workbook()
ws = wb.active # wb.active returns a Worksheet object
ws['A1'] = "Line 1\nLine 2\nLine 3"
ws['A1'].alignment = Alignment(wrapText=True)
wb.save("wrap.xlsx")
Disclaimer: This won't work in recent versions of Openpyxl. See other answers.
In openpyxl you can set the wrap_text alignment property to wrap multi-line strings:
from openpyxl import Workbook
workbook = Workbook()
worksheet = workbook.worksheets[0]
worksheet.title = "Sheet1"
worksheet.cell('A1').style.alignment.wrap_text = True
worksheet.cell('A1').value = "Line 1\nLine 2\nLine 3"
workbook.save('wrap_text1.xlsx')
This is also possible with the XlsxWriter module.
Here is a small working example:
from xlsxwriter.workbook import Workbook
# Create an new Excel file and add a worksheet.
workbook = Workbook('wrap_text2.xlsx')
worksheet = workbook.add_worksheet()
# Widen the first column to make the text clearer.
worksheet.set_column('A:A', 20)
# Add a cell format with text wrap on.
cell_format = workbook.add_format({'text_wrap': True})
# Write a wrapped string to a cell.
worksheet.write('A1', "Line 1\nLine 2\nLine 3", cell_format)
workbook.close()
Just an additional option, you can use text blocking """ my cell info here """ along with the text wrap Boolean in alignment and get the desired result as well.
from openpyxl import Workbook
from openpyxl.styles import Alignment
wb= Workbook()
sheet= wb.active
sheet.title = "Sheet1"
sheet['A1'] = """Line 1
Line 2
Line 3"""
sheet['A1'].alignment = Alignment(wrapText=True)
wb.save('wrap_text1.xlsx')
Just in case anyone is looking for an example where we iterate over all cells to apply wrapping:
Small working example:
import pandas as pd
from openpyxl import Workbook
from openpyxl.styles import Alignment
from openpyxl.utils.dataframe import dataframe_to_rows
# create a toy dataframe. Our goal is to replace commas (',') with line breaks and have Excel rendering \n as line breaks.
df = pd.DataFrame(data=[["Mark", "Student,26 y.o"],
["Simon", "Student,31 y.o"]],
columns=['Name', 'Description'])
# replace comma "," with '\n' in all cells
df = df.applymap(lambda v: v.replace(',', '\n') if isinstance(v, str) else v)
# Create an empty openpyxl Workbook. We will populate it by iteratively adding the dataframe's rows.
wb = Workbook()
ws = wb.active # to get the actual Worksheet object
# dataframe_to_rows allows to iterate over a dataframe with an interface
# compatible with openpyxl. Each df row will be added to the worksheet.
for r in dataframe_to_rows(df3, index=True, header=True):
ws.append(r)
# iterate over each row and row's cells and apply text wrapping.
for row in ws:
for cell in row:
cell.alignment = Alignment(wrapText=True)
# export the workbook as an excel file.
wb.save("wrap.xlsx")

Setting styles in Openpyxl

I need advice on setting styles in Openpyxl.
I see that the NumberFormat of a cell can be set, but I also require setting of font colors and attributes (bold etc). There is a style.py class but it seems I can't set the style attribute of a cell, and I don't really want to start tinkering with the openpyxl source code.
Has anyone found a solution to this?
As of openpyxl version 1.5.7, I have successfully applied the following worksheet style options...
from openpyxl.reader.excel import load_workbook
from openpyxl.workbook import Workbook
from openpyxl.styles import Color, Fill
from openpyxl.cell import Cell
# Load the workbook...
book = load_workbook('foo.xlsx')
# define ws here, in this case I pick the first worksheet in the workbook...
# NOTE: openpyxl has other ways to select a specific worksheet (i.e. by name
# via book.get_sheet_by_name('someWorksheetName'))
ws = book.worksheets[0]
## ws is a openpypxl worksheet object
_cell = ws.cell('C1')
# Font properties
_cell.style.font.color.index = Color.GREEN
_cell.style.font.name = 'Arial'
_cell.style.font.size = 8
_cell.style.font.bold = True
_cell.style.alignment.wrap_text = True
# Cell background color
_cell.style.fill.fill_type = Fill.FILL_SOLID
_cell.style.fill.start_color.index = Color.DARKRED
# You should only modify column dimensions after you have written a cell in
# the column. Perfect world: write column dimensions once per column
#
ws.column_dimensions["C"].width = 60.0
FYI, you can find the names of the colors in openpyxl/style.py... I sometimes I patch in extra colors from the X11 color names
class Color(HashableObject):
"""Named colors for use in styles."""
BLACK = 'FF000000'
WHITE = 'FFFFFFFF'
RED = 'FFFF0000'
DARKRED = 'FF800000'
BLUE = 'FF0000FF'
DARKBLUE = 'FF000080'
GREEN = 'FF00FF00'
DARKGREEN = 'FF008000'
YELLOW = 'FFFFFF00'
DARKYELLOW = 'FF808000'
For openpyxl version 2.4.1 and above use below code to set font color:
from openpyxl.styles import Font
from openpyxl.styles.colors import Color
ws1['A1'].font = Font(color = "FF0000")
hex codes for various colors can be found at:
http://dmcritchie.mvps.org/excel/colors.htm
As of openpyxl 2.0, styles are immutable.
If you have a cell, you can (e.g.) set bold text by:
cell.style = cell.style.copy(font=cell.style.font.copy(bold=True))
Yes, this is annoying.
As of openpyxl 2.0, setting cell styles is done by creating new style objects and by assigning them to properties of a cell.
There are several style objects: Font, PatternFill, Border, and Alignment. See the doc.
To change a style property of a cell, first you either have to copy the existing style object from the cell and change the value of the property or you have to create a new style object with the desired settings. Then, assign the new style object to the cell.
Example of setting the font to bold and italic of cell A1:
from openpyxl import Workbook
from openpyxl.styles import Font
# Create workbook
wb = Workbook()
# Select active sheet
ws = wb.active()
# Select cell A1
cell = ws['A1']
# Make the text of the cell bold and italic
cell.font = cell.font.copy(bold=True, italic=True)
This seems like a feature that has changed a few times. I am using openpyxl 2.5.0, and I was able to set the strike-through option this way:
new_font = copy(cell.font)
new_font.strike = True
cell.font = new_font
It seems like earlier versions (1.9 to 2.4?) had a copy method on the font that is now deprecated and raises a warning:
cell.font = cell.font.copy(strike=True)
Versions up to 1.8 had mutable fonts, so you could just do this:
cell.font.strike=True
That now raises an error.
New 2021 Updated Way of Changing FONT in OpenPyXl:
sheet.cell.font = Font(size=23, underline='single', color='FFBB00', bold=True, italic=True)
Full Code:
import openpyxl # Connect the library
from openpyxl import Workbook
from openpyxl.styles import PatternFill # Connect cell styles
from openpyxl.workbook import Workbook
from openpyxl.styles import Font, Fill # Connect styles for text
from openpyxl.styles import colors # Connect colors for text and cells
wb = openpyxl.Workbook() # Create book
work_sheet = wb.create_sheet(title='Testsheet') # Created a sheet with a name and made it active
work_sheet['A1'] = 'Test text'
work_sheet_a1 = work_sheet['A5'] # Created a variable that contains cell A1 with the existing text
work_sheet_a1.font = Font(size=23, underline='single', color='FFBB00', bold=True,
italic=True) # We apply the following parameters to the text: size - 23, underline, color = FFBB00 (text color is specified in RGB), bold, oblique. If we do not need a bold font, we use the construction: bold = False. We act similarly if we do not need an oblique font: italic = False.
# Important:
# if necessary, the possibility of using standard colors is included in the styles, but the code in this case will look different:
work_sheet_a1.font = Font(size=23, underline='single', color=colors.RED, bold=True,
italic=True) # what color = colors.RED — color prescribed in styles
work_sheet_a1.fill = PatternFill(fill_type='solid', start_color='ff8327',
end_color='ff8327') # This code allows you to do design color cells
Like openpyxl doc said:
This is an open source project, maintained by volunteers in their spare time. This may well mean that particular features or functions that you would like are missing.
I checked openpyxl source code, found that:
Till openpyxl 1.8.x, styles are mutable. Their attribute can be assigned directly like this:
from openpyxl.workbook import Workbook
from openpyxl.style import Color
wb = Workbook()
ws = wb.active
ws['A1'].style.font.color.index = Color.RED
However from of openpyxl 1.9, styles are immutable.
Styles are shared between objects and once they have been assigned they cannot be changed. This stops unwanted side-effects such as changing the style for lots of cells when instead of only one.
To create a new style object, you can assign it directly, or copy one from an existing cell's style with new attributes, answer to the question as an example(forgive my Chinese English):
from openpyxl.styles import colors
from openpyxl.styles import Font, Color
from openpyxl import Workbook
wb = Workbook()
ws = wb.active
a1 = ws['A1']
d4 = ws['D4']
# create a new style with required attributes
ft_red = Font(color=colors.RED)
a1.font = ft_red
# you can also do it with function copy
ft_red_bold = ft_red.copy(bold=True)
# you can copy from a cell's style with required attributes
ft_red_sigle_underline = a1.font.copy(underline="single")
d4.font = ft_red_bold
# apply style to column E
col_e = ws.column_dimensions['E']
col_e.font = ft_red_sigle_underline
A cell' style contains these attributes: font, fill, border, alignment, protection and number_format. Check openpyxl.styles.
They are similar and should be created as an object, except number_format, its value is string type.
Some pre-defined number formats are available, number formats can also be defined in string type. Check openpyxl.styles.numbers.
from openpyxl.styles import numbers
# use pre-defined values
ws.cell['T49'].number_format = numbers.FORMAT_GENERAL
ws.cell(row=2, column=4).number_format = numbers.FORMAT_DATE_XLSX15
# use strings
ws.cell['T57'].number_format = 'General'
ws.cell(row=3, column=5).number_format = 'd-mmm-yy'
ws.cell['E5'].number_format = '0.00'
ws.cell['E50'].number_format = '0.00%'
ws.cell['E100'].number_format = '_ * #,##0_ ;_ * -#,##0_ ;_ * "-"??_ ;_ #_ '
As of openpyxl-1.7.0 you can do this too:
cell.style.fill.start_color.index = "FF124191"
I've got a couple of helper functions which set a style on a given cell - things like headers, footers etc.
You can define a common style then you can apply the same to any cell or range.
Define Style:
Apply on a cell.
This worked for me (font colour + bold font):
from openpyxl.styles import colors
from openpyxl.styles import Font, Color
from openpyxl import Workbook
wb = Workbook()
ws = wb['SheetName']
ws.cell(row_number,column_number).font = Font(color = "0000FF",bold = True)

Categories