openpyxl - ajustar el tamaño del ancho de la columna


82

Tengo el siguiente script que convierte un archivo CSV en un archivo XLSX, pero el tamaño de mi columna es muy estrecho. Cada vez que tengo que arrastrarlos con el mouse para leer los datos. ¿Alguien sabe cómo establecer el ancho de la columna openpyxl?

Aquí está el código que estoy usando.

#!/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)

Respuestas:


84

Puede estimar (o usar una fuente de ancho mono) para lograr esto. Supongamos que los datos son una matriz anidada como [['a1', 'a2'], ['b1', 'b2']]

Podemos obtener el máximo de caracteres en cada columna. Luego establezca el ancho a eso. El ancho es exactamente el ancho de una fuente monoespaciada (si no cambia otros estilos al menos). Incluso si usa una fuente de ancho variable, es una estimación decente. Esto no funcionará con fórmulas.

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):
    worksheet.column_dimensions[get_column_letter(i+1)].width = column_width

Un poco complicado, pero sus informes serán más legibles.


¿
Puede

1
cuando tengo int como valor de celda, esto generará un error ya que int no tiene propiedad len, ¿hay alguna manera de evitar esto? ¡Gracias!
Kevin Zhao

1
@KevinZhao Un poco tarde, pero su pregunta se aborda aquí: stackoverflow.com/questions/2189800/…
jonyfries

53

Mi variación de la respuesta de Bufke. Evita un poco de ramificación con la matriz e ignora las celdas / columnas vacías.

Ahora arreglado para valores de celda que no son cadenas.

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

A partir de la versión 3.0.3 de openpyxl, debe usar

 dims[cell.column_letter] = max((dims.get(cell.column_letter, 0), len(str(cell.value))))

como la biblioteca openpyxl generará un TypeError si pasa column_dimensionsun número en lugar de una letra de columna, todo lo demás puede permanecer igual.


2
La línea 6 se puede mejorar para usar la letra de columna: dims [cell.column_letter] = max ((dims.get (cell.column_letter, 0), len (str (cell.value))))
Jonathan L

36

Una forma aún más pitónica de establecer el ancho de todas las columnas que funciona al menos en la versión 2.4.0 de openpyxl:

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

La función as_text debería ser algo que convierta el valor en una cadena de longitud adecuada, como para Python 3:

def as_text(value):
    if value is None:
        return ""
    return str(value)

6
def as_text(value): return str(value) if value is not None else ""
thorhunter

4
@thorhunter len(cell.value or "") , no se necesitan funciones adicionales
Irina Velikopolskaya

2
@IrinaVelikopolskaya si cell.value no se implementa __len__, esto arrojará una excepción ( into NoneTypepor ejemplo)
thorhunter

2
@IrinaVelikopolskaya datetime es otro ejemplo de dónde se obtiene una excepción. La función as_text parece funcionar mejor para mí.
Software Prophets

6
Tenga en cuenta que con openpyxl 2.6, este código se bloqueará con TypeError: expected <class 'str'>. Uno tiene que especificar un nombre de columna ahora, es decir ws.column_dimensions[openpyxl.utils.get_column_letter(column_cells[0].column)].width = length.Ver bitbucket.org/openpyxl/openpyxl/issues/1240/...
phihag

10

Tengo un problema con merged_cells y el tamaño automático no funciona correctamente, si tienes el mismo problema, puedes resolverlo con el siguiente código:

for col in worksheet.columns:
    max_length = 0
    column = col[0].column # Get the column name
    for cell in col:
        if cell.coordinate in worksheet.merged_cells: # not check merge_cells
            continue
        try: # Necessary to avoid error on empty cells
            if len(str(cell.value)) > max_length:
                max_length = len(cell.value)
        except:
            pass
    adjusted_width = (max_length + 2) * 1.2
    worksheet.column_dimensions[column].width = adjusted_width

7

Una ligera mejora de la respuesta aceptada anterior, que creo que es más pitónica (pedir perdón es mejor que pedir permiso)

column_widths = []
for row in workSheet.iter_rows():
    for i, cell in enumerate(row):
        try:
            column_widths[i] = max(column_widths[i], len(str(cell.value)))
        except IndexError:
            column_widths.append(len(str(cell.value)))

for i, column_width in enumerate(column_widths):
    workSheet.column_dimensions[get_column_letter(i + 1)].width = column_width

Es necesario considerar si cell.value no es una cadena. Por ejemplo, si cell.value es de tipo flotante, se necesitará conversión de tipo
wontleave

2
wow eso fue hace 4 años. Tienes razón, aunque he editado para corregirlo. Acabo de agregar un yeso a la cuerda.
shayst

4

Todas las respuestas anteriores están generando un problema que es que la columna [0] .columna devuelve un número mientras que la hoja de cálculo.columna_dimensiones [columna] acepta solo caracteres como 'A', 'B', 'C' en lugar de columna. He modificado el código de @ Virako y ahora funciona bien.

import re
import openpyxl
..
for col in _ws.columns:
    max_lenght = 0
    print(col[0])
    col_name = re.findall('\w\d', str(col[0]))
    col_name = col_name[0]
    col_name = re.findall('\w', str(col_name))[0]
    print(col_name)
    for cell in col:
        try:
            if len(str(cell.value)) > max_lenght:
                max_lenght = len(cell.value)
        except:
            pass
    adjusted_width = (max_lenght+2)
    _ws.column_dimensions[col_name].width = adjusted_width

4

Con openpyxl 3.0.3, la mejor manera de modificar las columnas es con el objeto DimensionHolder , que es un diccionario que asigna cada columna a un objeto ColumnDimension . ColumnDimension puede obtener parámetros como bestFit , auto_size (que es un alias de bestFit) y ancho . Personalmente, auto_size no funciona como se esperaba y tuve que usar el ancho y me di cuenta de que el mejor ancho para la columna es len(cell_value) * 1.23.

Para obtener el valor de cada celda es necesario iterar sobre cada una, pero yo personalmente no lo usé porque en mi proyecto solo tenía que escribir hojas de trabajo, así que obtuve la cadena más larga en cada columna directamente en mis datos.

El siguiente ejemplo solo muestra cómo modificar las dimensiones de la columna:

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

3

Esta es mi versión que hace referencia al fragmento de código de @Virako

def adjust_column_width_from_col(ws, min_row, min_col, max_col):

        column_widths = []

        for i, col in \
                enumerate(
                    ws.iter_cols(min_col=min_col, max_col=max_col, min_row=min_row)
                ):

            for cell in col:
                value = cell.value
                if value is not None:

                    if isinstance(value, str) is False:
                        value = str(value)

                    try:
                        column_widths[i] = max(column_widths[i], len(value))
                    except IndexError:
                        column_widths.append(len(value))

        for i, width in enumerate(column_widths):

            col_name = get_column_letter(min_col + i)
            value = column_widths[i] + 2
            ws.column_dimensions[col_name].width = value

Y cómo usarlo es el siguiente,

adjust_column_width_from_col(ws, 1,1, ws.max_column)

3

Podemos convertir números a sus valores ASCII y darle al parámetro column_dimension

import openpyxl as xl

work_book = xl.load_workbook('file_location')
sheet = work_book['Sheet1']
column_number = 2
column = str(chr(64 + column_number))
sheet.column_dimensions[column].width = 20
work_book.save('file_location')

3

Tuve que cambiar @ User3759685 la respuesta anterior a esto cuando se actualizó openpxyl. Recibí un error. Bueno, @phihag también informó esto en los comentarios.

for column_cells in ws.columns:
    new_column_length = max(len(as_text(cell.value)) for cell in column_cells)
    new_column_letter = (openpyxl.utils.get_column_letter(column_cells[0].column))
    if new_column_length > 0:
        ws.column_dimensions[new_column_letter].width = new_column_length + 1

2

Después de la actualización de openpyxl2.5.2a a la última 2.6.4 (versión final para el soporte de python 2.x), tuve el mismo problema al configurar el ancho de una columna.

Básicamente, siempre calculo el ancho de una columna (dims es un dictado que mantiene el ancho de cada columna):

dims[cell.column] = max((dims.get(cell.column, 0), len(str(cell.value))))

Luego estoy modificando la escala a algo un poco más grande que el tamaño original, pero ahora tienes que dar el valor de "Letra" de una columna y ya no un valor int (la columna de abajo es el valor y se traduce a la letra correcta):

worksheet.column_dimensions[get_column_letter(col)].width = value +1 

Esto solucionará el error visible y asignará el ancho correcto a su columna;) Espero que esto ayude.


2

Aquí hay una respuesta para Python 3.8 y OpenPyXL 3.0.0.

Intenté evitar el uso de la get_column_letterfunción pero fallé.

Esta solución utiliza las expresiones de asignación recientemente introducidas, también conocidas como "operador de morsa":

import openpyxl
from openpyxl.utils import get_column_letter

workbook = openpyxl.load_workbook("myxlfile.xlsx")

worksheet = workbook["Sheet1"]

MIN_WIDTH = 10
for i, column_cells in enumerate(worksheet.columns, start=1):
    width = (
        length
        if (length := max(len(str(cell_value) if (cell_value := cell.value) is not None else "")
                          for cell in column_cells)) >= MIN_WIDTH
        else MIN_WIDTH
    )
    worksheet.column_dimensions[get_column_letter(i)].width = width

1
max(len(str(cell.value)) for cell in filter(None, column_cells))me parece más claro.
Nuno André

0

Esta es una solución sucia. Pero openpyxl realmente admite auto_fit. Pero no hay ningún método para acceder a la propiedad.

import openpyxl
from openpyxl.utils import get_column_letter

wb = openpyxl.load_workbook("Example.xslx")
ws = wb["Sheet1"]
for i in range(1, ws.max_column+1):
    ws.column_dimensions[get_column_letter(i)].bestFit = True
    ws.column_dimensions[get_column_letter(i)].auto_size = True

No funcionó. Al menos no para Microsoft Excel.
Sirmabus
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.