openpyxl - điều chỉnh kích thước chiều rộng cột


83

Tôi có tập lệnh sau đang chuyển đổi tệp CSV thành tệp XLSX, nhưng kích thước cột của tôi rất hẹp. Mỗi lần tôi phải kéo chúng bằng chuột để đọc dữ liệu. Có ai biết cách đặt chiều rộng cột trong openpyxlkhông?

Đây là mã tôi đang sử dụng.

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

Câu trả lời:


84

Bạn có thể ước tính (hoặc sử dụng phông chữ có chiều rộng đơn sắc) để đạt được điều này. Giả sử dữ liệu là một mảng lồng nhau như [['a1', 'a2'], ['b1', 'b2']]

Chúng ta có thể lấy các ký tự tối đa trong mỗi cột. Sau đó đặt chiều rộng thành đó. Chiều rộng chính xác là chiều rộng của phông chữ monospace (nếu không thay đổi các kiểu khác ít nhất). Ngay cả khi bạn sử dụng phông chữ có chiều rộng thay đổi thì đó cũng là một ước tính tốt. Điều này sẽ không hoạt động với các công thức.

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

Hơi khó nhưng báo cáo của bạn sẽ dễ đọc hơn.


Có thể bạn biết vấn đề ở đây là gì: stackoverflow.com/questions/32642026/…
Pyderman,

1
khi tôi có int làm giá trị ô, điều này sẽ xảy ra lỗi vì int không có thuộc tính len, có cách nào để tránh điều này không? cảm ơn!
Kevin Zhao

1
@KevinZhao Hơi muộn - nhưng câu hỏi của bạn đã được giải quyết ở đây: stackoverflow.com/questions/2189800/…
jonyfries

53

Biến thể của tôi về câu trả lời của Bufke. Tránh một chút phân nhánh với mảng và bỏ qua các ô / cột trống.

Hiện đã được sửa cho các giá trị ô không phải là chuỗi.

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

Kể từ phiên bản openpyxl 3.0.3, bạn cần sử dụng

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

vì thư viện openpyxl sẽ tăng TypeError nếu bạn chuyển column_dimensionsmột số thay vì một ký tự cột, mọi thứ khác có thể giữ nguyên.


2
dòng 6 có thể được cải thiện để thư cột dụng: làm mờ [cell.column_letter] = max ((dims.get (cell.column_letter, 0), len (str (cell.value))))
Jonathan L

37

Thậm chí còn có một cách khó hiểu hơn để đặt chiều rộng của tất cả các cột hoạt động ít nhất trong phiên bản openpyxl 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

Hàm as_text phải là thứ chuyển đổi giá trị thành một chuỗi có độ dài thích hợp, như đối với 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 "") , không cần chức năng phụ
Irina Velikopolskaya

2
@IrinaVelikopolskaya nếu cell.value không triển khai __len__, điều này sẽ ném ra ngoại lệ ( inthoặc NoneTypeví dụ:)
thorhunter

2
@IrinaVelikopolskaya datetime là một ví dụ khác về trường hợp một ngoại lệ. Hàm as_text dường như hoạt động tốt nhất đối với tôi.
Phần mềm tiên tri

7
Lưu ý rằng với openpyxl 2.6, mã này sẽ gặp sự cố TypeError: expected <class 'str'>. Người ta phải chỉ định tên cột ngay bây giờ, tức là ws.column_dimensions[openpyxl.utils.get_column_letter(column_cells[0].column)].width = length.Xem bitbucket.org/openpyxl/openpyxl/issues/1240/…
phihag

10

Tôi gặp sự cố với merge_cells và autosize không hoạt động chính xác, nếu bạn gặp sự cố tương tự, bạn có thể giải quyết bằng mã tiếp theo:

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

Một chút cải thiện của câu trả lời được chấp nhận ở trên, mà tôi nghĩ là khó hiểu hơn (xin tha thứ tốt hơn là xin phép)

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

Cần xem xét nếu cell.value không phải là một chuỗi. Ví dụ: nếu cell.value thuộc loại float, thì sẽ cần truyền kiểu
wontleave

2
wow đó là 4 năm trước. Bạn đúng mặc dù tôi đã chỉnh sửa để sửa chữa. Chỉ cần thêm một diễn viên vào chuỗi.
shayst

4

Tất cả các câu trả lời ở trên đang tạo ra một vấn đề là col [0] .column đang trả về số trong khi worksheet.column_dimensions [column] chỉ chấp nhận các ký tự như 'A', 'B', 'C' thay cho cột. Tôi đã sửa đổi mã của @ Virako và hiện tại nó đang hoạt động tốt.

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

Với openpyxl 3.0.3, cách tốt nhất để sửa đổi các cột là với đối tượng DimensionHolder , đây là một từ điển ánh xạ từng cột với một đối tượng ColumnDimension . ColumnDimension có thể nhận các tham số như bestFit , auto_size (là bí danh của bestFit) và chiều rộng . Cá nhân tôi, auto_size không hoạt động như mong đợi và tôi đã phải sử dụng chiều rộng và phân tích rằng chiều rộng tốt nhất cho cột là len(cell_value) * 1.23.

Để nhận được giá trị của mỗi ô, cần phải lặp lại từng ô, nhưng cá nhân tôi không sử dụng nó vì trong dự án của tôi, tôi chỉ phải viết trang tính, vì vậy tôi có chuỗi dài nhất trong mỗi cột trực tiếp trên dữ liệu của mình.

Ví dụ bên dưới chỉ cho thấy cách sửa đổi kích thước cột:

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

Đây là phiên bản của tôi đề cập đến đoạn mã của @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

Và cách sử dụng như sau,

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

3

Chúng tôi có thể chuyển đổi các số thành giá trị ASCII của chúng và đưa nó vào tham số 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

Tôi đã phải thay đổi câu trả lời @ User3759685 ở trên cho điều này khi openpxyl cập nhật. Tôi đã nhận được một lỗi. Vâng @phihag cũng báo cáo điều này trong các bình luận

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

Sau khi cập nhật từ openpyxl2.5.2a lên 2.6.4 mới nhất (phiên bản cuối cùng để hỗ trợ python 2.x), tôi gặp vấn đề tương tự khi định cấu hình chiều rộng của cột.

Về cơ bản, tôi luôn tính toán chiều rộng cho một cột (dims là một lệnh duy trì chiều rộng mỗi cột):

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

Sau đó, tôi sẽ sửa đổi tỷ lệ thành một thứ gì đó lớn hơn kích thước ban đầu ngay lập tức, nhưng bây giờ bạn phải cung cấp giá trị "Chữ cái" của một cột chứ không phải giá trị int nữa (col bên dưới là giá trị và được dịch sang chữ cái bên phải):

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

Điều này sẽ sửa lỗi hiển thị và chỉ định chiều rộng phù hợp cho cột của bạn;) Hy vọng điều này sẽ giúp ích.


2

Đây là câu trả lời cho Python 3.8 và OpenPyXL 3.0.0.

Tôi đã cố gắng tránh sử dụng get_column_letter chức năng nhưng không thành công.

Giải pháp này sử dụng các biểu thức gán mới được giới thiệu hay còn gọi là "toán tử hải mã":

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))có vẻ rõ ràng hơn đối với tôi.
Nuno André

0

Đây là một bản sửa lỗi bẩn. Nhưng openpyxl thực sự hỗ trợ auto_fit. Nhưng không có phương pháp nào để truy cập tài sản.

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

Không hoạt động. Ít nhất là không dành cho Microsoft Excel.
Sirmabus
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.