openpyxl - adjust column width size
I have following script which is converting a CSV file to an XLSX file, but my column size is very narrow. Each time I have to drag them with mouse to read data. Does anybody know how to set column width in openpyxl
?
Here is the code I am using.
#!/usr/bin/python2.6
import csv
from openpyxl import Workbook
from openpyxl.cell import get_column_letter
f = open('users_info_cvs.txt', "rU")
csv.register_dialect('colons', delimiter=':')
reader = csv.reader(f, dialect='colons')
wb = Workbook()
dest_filename = r"account_info.xlsx"
ws = wb.worksheets[0]
ws.title = "Users Account Information"
for row_index, row in enumerate(reader):
for column_index, cell in enumerate(row):
column_letter = get_column_letter((column_index + 1))
ws.cell('%s%s'%(column_letter, (row_index + 1))).value = cell
wb.save(filename = dest_filename)
You could estimate (or use a mono width font) to achieve this. Let's assume data is a nested array like
[['a1','a2'],['b1','b2']]
We can get the max characters in each column. Then set the width to that. Width is exactly the width of a monospace font (if not changing other styles at least). Even if you use a variable width font it is a decent estimation. This will not work with formulas.
from openpyxl.utils import get_column_letter
column_widths = []
for row in data:
for i, cell in enumerate(row):
if len(column_widths) > i:
if len(cell) > column_widths[i]:
column_widths[i] = len(cell)
else:
column_widths += [len(cell)]
for i, column_width in enumerate(column_widths,1): # ,1 to start at 1
worksheet.column_dimensions[get_column_letter(i)].width = column_width
A bit of a hack but your reports will be more readable.
My variation of Bufke's answer. Avoids a bit of branching with the array and ignores empty cells / columns.
Now fixed for non-string cell values.
ws = your current worksheet
dims = {}
for row in ws.rows:
for cell in row:
if cell.value:
dims[cell.column] = max((dims.get(cell.column, 0), len(str(cell.value))))
for col, value in dims.items():
ws.column_dimensions[col].width = value
As of openpyxl version 3.0.3 you need to use
dims[cell.column_letter] = max((dims.get(cell.column_letter, 0), len(str(cell.value))))
as the openpyxl library will raise a TypeError if you pass column_dimensions
a number instead of a column letter, everything else can stay the same.
Even more pythonic way to set the width of all columns that works at least in openpyxl version 2.4.0:
for column_cells in worksheet.columns:
length = max(len(as_text(cell.value)) for cell in column_cells)
worksheet.column_dimensions[column_cells[0].column].width = length
The as_text function should be something that converts the value to a proper length string, like for Python 3:
def as_text(value):
if value is None:
return ""
return str(value)
With openpyxl 3.0.3 the best way to modify the columns is with the DimensionHolder object, which is a dictionary that maps each column to a ColumnDimension object.
ColumnDimension can get parameters as bestFit, auto_size (which is an alias of bestFit) and width.
Personally, auto_size doesn't work as expected and I had to use width and figured out that the best width for the column is len(cell_value) * 1.23
.
To get the value of each cell it's necessary to iterate over each one, but I personally didn't use it because in my project I just had to write worksheets, so I got the longest string in each column directly on my data.
The example below just shows how to modify the column dimensions:
import openpyxl
from openpyxl.worksheet.dimensions import ColumnDimension, DimensionHolder
from openpyxl.utils import get_column_letter
wb = openpyxl.load_workbook("Example.xslx")
ws = wb["Sheet1"]
dim_holder = DimensionHolder(worksheet=ws)
for col in range(ws.min_column, ws.max_column + 1):
dim_holder[get_column_letter(col)] = ColumnDimension(ws, min=col, max=col, width=20)
ws.column_dimensions = dim_holder