openpyxl - dostosuj rozmiar szerokości kolumny

86

Mam następujący skrypt, który konwertuje plik CSV do pliku XLSX, ale rozmiar mojej kolumny jest bardzo wąski. Za każdym razem muszę przeciągać je myszą, aby odczytać dane. Czy ktoś wie, jak ustawić szerokość kolumny w openpyxl?

Oto kod, którego używam.

#!/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)
Satish
źródło

Odpowiedzi:

86

Aby to osiągnąć, możesz oszacować (lub użyć czcionki o stałej szerokości). Załóżmy, że dane są zagnieżdżoną tablicą, taką jak [['a1', 'a2'], ['b1', 'b2']]

Możemy uzyskać maksymalną liczbę znaków w każdej kolumnie. Następnie ustaw na to szerokość. Szerokość to dokładnie szerokość czcionki o stałej szerokości (jeśli nie zmienia się przynajmniej innych stylów). Nawet jeśli używasz czcionki o zmiennej szerokości, jest to przyzwoita ocena. To nie zadziała w przypadku formuł.

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

Trochę hack, ale Twoje raporty będą bardziej czytelne.

Bufke
źródło
Może masz pomysł, o co chodzi tutaj: stackoverflow.com/questions/32642026/ ...
Pyderman
1
kiedy jako wartość komórki mam int, spowoduje to błąd, ponieważ int nie ma właściwości len, czy istnieje sposób, aby tego uniknąć? dzięki!
Kevin Zhao
1
@KevinZhao Trochę późno - ale twoje pytanie zostało omówione tutaj: stackoverflow.com/questions/2189800/…
jonyfries
56

Moja odmiana odpowiedzi Bufkego. Unika rozgałęziania się z tablicą i ignoruje puste komórki / kolumny.

Teraz naprawiono dla wartości komórek niebędących ciągami.

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

Od wersji 3.0.3 openpyxl musisz użyć

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

ponieważ biblioteka openpyxl zgłosi błąd TypeError, jeśli podasz column_dimensionsliczbę zamiast litery w kolumnie, wszystko inne może pozostać takie samo.

velis
źródło
2
wiersz 6 można ulepszyć, aby użyć litery kolumny: dims [cell.column_letter] = max ((dims.get (cell.column_letter, 0), len (str (cell.value))))
Jonathan L
38

Jeszcze bardziej pythonowy sposób na ustawienie szerokości wszystkich kolumn, który działa przynajmniej w openpyxl w wersji 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

Funkcja as_text powinna być czymś, co konwertuje wartość na ciąg o odpowiedniej długości, jak w Pythonie 3:

def as_text(value):
    if value is None:
        return ""
    return str(value)
Użytkownik3759685
źródło
6
def as_text(value): return str(value) if value is not None else ""
thorhunter
4
@thorhunter len(cell.value or "") , nie potrzeba dodatkowych funkcji
Irina Velikopolskaya
2
@IrinaVelikopolskaya jeśli cell.value nie implementuje __len__, zgłosi wyjątek ( intlub NoneTypena przykład)
thorhunter
2
@IrinaVelikopolskaya datetime to kolejny przykład sytuacji, w której otrzymuje się wyjątek. Wydaje mi się, że funkcja as_text działa najlepiej.
Software Prophets,
11
Zauważ, że w openpyxl 2.6 ten kod ulegnie awarii z TypeError: expected <class 'str'>. Należy teraz podać nazwę kolumny, np. ws.column_dimensions[openpyxl.utils.get_column_letter(column_cells[0].column)].width = lengthPatrz bitbucket.org/openpyxl/openpyxl/issues/1240/…
phihag
10

Mam problem z komórkami scalonymi i autosize nie działają poprawnie, jeśli masz ten sam problem, możesz rozwiązać następnym kodem:

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
Virako
źródło
7

Nieznaczna poprawa powyższej akceptowanej odpowiedzi, która moim zdaniem jest bardziej pytoniczna (prośba o wybaczenie jest lepsza niż prośba o pozwolenie)

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
shayst
źródło
Należy rozważyć, czy cell.value nie jest ciągiem. Na przykład, jeśli cell.value jest typu float, wymagane będzie rzutowanie typu
opuści
2
wow, to było 4 lata temu. Masz rację, chociaż poprawiłem. Właśnie dodałem rzut do struny.
shayst
5

W openpyxl 3.0.3 najlepszym sposobem modyfikacji kolumn jest obiekt DimensionHolder , który jest słownikiem mapującym każdą kolumnę na obiekt ColumnDimension . ColumnDimension może pobierać parametry takie jak bestFit , auto_size (co jest aliasem bestFit) i width . Osobiście auto_size nie działa zgodnie z oczekiwaniami i musiałem użyć szerokości i zamazałem, że najlepsza szerokość kolumny to len(cell_value) * 1.23.

Aby uzyskać wartość każdej komórki, konieczne jest iterowanie po każdej z nich, ale osobiście jej nie użyłem, ponieważ w moim projekcie musiałem tylko napisać arkusze, więc uzyskałem najdłuższy ciąg w każdej kolumnie bezpośrednio na moich danych.

Poniższy przykład pokazuje, jak zmodyfikować wymiary kolumny:

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
GI Jack
źródło
4

To jest moja wersja odnosząca się do fragmentu kodu @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

Sposób użycia jest następujący:

adjust_column_width_from_col(ws, 1,1, ws.max_column)
samotni
źródło
4

Wszystkie powyższe odpowiedzi generują problem, który polega na tym, że kolumna [0] .kolumna zwraca liczbę, podczas gdy arkusz roboczy.column_dimensions [kolumna] akceptuje tylko znaki takie jak „A”, „B”, „C” zamiast kolumny. Zmodyfikowałem kod @ Virako i teraz działa dobrze.

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
Ssubrat Rrudra
źródło
3

Możemy przekonwertować liczby na ich wartości ASCII i przekazać to parametrowi 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')
Prashant Tiwari
źródło
3

Musiałem zmienić powyższą odpowiedź @ User3759685 na to, gdy aktualizacja openpxyl. Otrzymałem błąd. @Phihag również zgłosił to w komentarzach

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
Monte Jones
źródło
2

Po aktualizacji z openpyxl2.5.2a do najnowszej wersji 2.6.4 (ostateczna wersja obsługująca python 2.x), mam ten sam problem z konfiguracją szerokości kolumny.

Zasadniczo zawsze obliczam szerokość kolumny (dims to dykt, który zachowuje szerokość każdej kolumny):

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

Następnie modyfikuję skalę do czegoś nieco większego niż rozmiar oryginalny, ale teraz musisz podać wartość „Letter” kolumny, a nie wartość int (kolumna poniżej jest wartością i jest tłumaczona na prawą literę):

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

To naprawi widoczny błąd i przypisanie odpowiedniej szerokości do twojej kolumny;) Mam nadzieję, że to pomoże.

Marco smdm
źródło
2

Oto odpowiedź na Python 3.8 i OpenPyXL 3.0.0.

Próbowałem uniknąć używania tej get_column_letterfunkcji, ale nie udało mi się.

W tym rozwiązaniu używane są nowo wprowadzone wyrażenia przypisania znane jako „operator 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
dmmfll
źródło
1
max(len(str(cell.value)) for cell in filter(None, column_cells))wydaje mi się jaśniejsze.
Nuno André
0

To jest brudna poprawka. Ale openpyxl faktycznie obsługuje auto_fit. Ale nie ma metody dostępu do nieruchomości.

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
Han Luo
źródło
0

Ponieważ w openpyxl 2.6.1 wymaga litery kolumny, a nie numeru kolumny, podczas ustawiania szerokości.

 for column in sheet.columns:
    length = max(len(str(cell.value)) for cell in column)
    length = length if length <= 16 else 16
    sheet.column_dimensions[column[0].column_letter].width = length
DàChún
źródło