openpyxl - Passen Sie die Spaltenbreite an


81

Ich habe folgendes Skript, das eine CSV-Datei in eine XLSX-Datei konvertiert, aber meine Spaltengröße ist sehr eng. Jedes Mal muss ich sie mit der Maus ziehen, um Daten zu lesen. Weiß jemand, wie man die Spaltenbreite einstellt openpyxl?

Hier ist der Code, den ich benutze.

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

Antworten:


84

Sie können dies schätzen (oder eine Schriftart mit Monobreite verwenden), um dies zu erreichen. Nehmen wir an, Daten sind ein verschachteltes Array wie [['a1', 'a2'], ['b1', 'b2']]

Wir können die maximalen Zeichen in jeder Spalte erhalten. Stellen Sie dann die Breite darauf ein. Die Breite ist genau die Breite einer Monospace-Schriftart (wenn nicht zumindest andere Stile geändert werden). Selbst wenn Sie eine Schriftart mit variabler Breite verwenden, ist dies eine anständige Schätzung. Dies funktioniert nicht mit Formeln.

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

Ein bisschen hacken, aber Ihre Berichte werden besser lesbar sein.


Könnten Sie eine Idee haben, was das Problem hier ist: stackoverflow.com/questions/32642026/…
Pyderman

1
Wenn ich int als Zellenwert habe, stößt dies auf einen Fehler, da int keine len-Eigenschaft hat. Gibt es eine Möglichkeit, dies zu vermeiden? Vielen Dank!
Kevin Zhao

1
@ KevinZhao Ein wenig spät - aber Ihre Frage wird hier angesprochen: stackoverflow.com/questions/2189800/…
jonyfries

53

Meine Variation von Bufkes Antwort. Vermeidet ein wenig Verzweigung mit dem Array und ignoriert leere Zellen / Spalten.

Jetzt behoben für Nicht-String-Zellenwerte.

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

Ab openpyxl Version 3.0.3 müssen Sie verwenden

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

Da die openpyxl-Bibliothek einen TypeError auslöst, wenn Sie column_dimensionseine Zahl anstelle eines Spaltenbuchstabens übergeben, kann alles andere gleich bleiben.


2
Zeile 6 kann verbessert werden, um den Spaltenbuchstaben zu verwenden: dims [cell.column_letter] = max ((dims.get (cell.column_letter, 0), len (str (cell.value)))
Jonathan L

36

Noch pythonischer, um die Breite aller Spalten festzulegen, die zumindest in openpyxl Version 2.4.0 funktioniert:

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

Die as_text-Funktion sollte etwas sein, das den Wert in eine Zeichenfolge mit der richtigen Länge konvertiert, wie bei 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 "") , keine Notwendigkeit für zusätzliche Funktionen
Irina Velikopolskaya

2
@ IrinaVelikopolskaya Wenn cell.value nicht implementiert wird __len__, wird dies eine Ausnahme intNoneType
auslösen

2
@IrinaVelikopolskaya datetime ist ein weiteres Beispiel dafür, wo man eine Ausnahme bekommt. Die as_text-Funktion scheint für mich am besten zu funktionieren.
Software-Propheten

6
Beachten Sie, dass mit openpyxl 2.6 dieser Code abstürzt TypeError: expected <class 'str'>. Man muss einen Spaltennamen angeben jetzt, dh ws.column_dimensions[openpyxl.utils.get_column_letter(column_cells[0].column)].width = length.Siehe bitbucket.org/openpyxl/openpyxl/issues/1240/...
phihag

10

Ich habe ein Problem mit Merged_Cells und Autosize funktioniert nicht richtig. Wenn Sie das gleiche Problem haben, können Sie es mit dem nächsten Code lösen:

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

Eine leichte Verbesserung der oben akzeptierten Antwort, die ich für pythonischer halte (um Vergebung zu bitten ist besser als um Erlaubnis zu bitten)

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 muss berücksichtigt werden, ob cell.value keine Zeichenfolge ist. Wenn beispielsweise cell.value vom Typ float ist, ist ein Typ-Casting erforderlich
wird

2
Wow, das war vor 4 Jahren. Sie sind richtig, obwohl ich bearbeitet habe, um zu beheben. Ich habe gerade eine Besetzung zum String hinzugefügt.
Shayst

4

Alle obigen Antworten erzeugen ein Problem, das darin besteht, dass col [0] .column eine Nummer zurückgibt, während worksheet.column_dimensions [column] nur Zeichen wie 'A', 'B', 'C' anstelle von column akzeptiert. Ich habe den Code von @ Virako geändert und er funktioniert jetzt einwandfrei.

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

Mit openpyxl 3.0.3 können Sie die Spalten am besten mit dem DimensionHolder- Objekt ändern . Hierbei handelt es sich um ein Wörterbuch, das jede Spalte einem ColumnDimension- Objekt zuordnet . ColumnDimension kann Parameter wie bestFit , auto_size (ein Alias ​​von bestFit) und width abrufen . Persönlich funktioniert auto_size nicht wie erwartet und ich musste width verwenden und stellte fest, dass die beste Breite für die Spalte ist len(cell_value) * 1.23.

Um den Wert jeder Zelle zu erhalten, muss jede einzelne durchlaufen werden, aber ich persönlich habe sie nicht verwendet, da ich in meinem Projekt nur Arbeitsblätter schreiben musste, sodass ich die längste Zeichenfolge in jeder Spalte direkt in meinen Daten erhielt.

Das folgende Beispiel zeigt nur, wie Sie die Spaltenabmessungen ändern:

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

Dies ist meine Version, die auf das Code-Snippet von @Virako verweist

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

Und wie man es benutzt, ist wie folgt:

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

3

Wir können Zahlen in ihre ASCII-Werte konvertieren und sie dem Parameter column_dimension geben

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

Ich musste @ User3759685 oben ändern, um darauf zu antworten, als das openpxyl aktualisiert wurde. Ich habe einen Fehler bekommen. Nun, @phihag hat dies auch in den Kommentaren gemeldet

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

Nach dem Update von openpyxl2.5.2a auf die neueste Version 2.6.4 (endgültige Version für die Unterstützung von Python 2.x) trat das gleiche Problem bei der Konfiguration der Spaltenbreite auf.

Grundsätzlich berechne ich immer die Breite für eine Spalte (dims ist ein Diktat, bei dem jede Spaltenbreite beibehalten wird):

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

Danach ändere ich die Skala auf etwas, das kurz größer als die ursprüngliche Größe ist, aber jetzt müssen Sie den "Buchstaben" -Wert einer Spalte und nicht mehr einen int-Wert angeben (Spalte unten ist der Wert und wird in den richtigen Buchstaben übersetzt):

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

Dies behebt den sichtbaren Fehler und weist Ihrer Spalte die richtige Breite zu;) Hoffe diese Hilfe.


2

Hier ist eine Antwort für Python 3.8 und OpenPyXL 3.0.0.

Ich habe versucht, die Verwendung der get_column_letterFunktion zu vermeiden, bin jedoch fehlgeschlagen.

Diese Lösung verwendet die neu eingeführten Zuweisungsausdrücke, auch bekannt als "Walrossoperator":

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

max(len(str(cell.value)) for cell in filter(None, column_cells))scheint mir klarer.
Nuno André

0

Dies ist eine schmutzige Lösung. Aber openpyxl unterstützt tatsächlich auto_fit. Es gibt jedoch keine Methode, um auf die Eigenschaft zuzugreifen.

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

Hat nicht funktioniert. Zumindest nicht für Microsoft Excel.
Sirmabus
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.