【问题标题】:openpyxl - adjust column width sizeopenpyxl - 调整列宽大小
【发布时间】:2022-03-23 18:53:05
【问题描述】:

我有以下脚本将 CSV 文件转换为 XLSX 文件,但我的列大小非常窄。每次我必须用鼠标拖动它们来读取数据。有人知道如何在openpyxl 中设置列​​宽吗?

这是我正在使用的代码。

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

【问题讨论】:

标签: python openpyxl


【解决方案1】:

您可以估计(或使用单宽字体)来实现这一点。假设 data 是一个嵌套数组,例如

[['a1','a2'],['b1','b2']]

我们可以得到每列的最大字符数。然后将宽度设置为该值。宽度正是等宽字体的宽度(如果至少不改变其他样式)。即使您使用可变宽度字体,这也是一个不错的估计。这不适用于公式。

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

有点小技巧,但您的报告将更具可读性。

【讨论】:

  • 您可能知道这里的问题是什么:stackoverflow.com/questions/32642026/…
  • 当我将 int 作为单元格值时,由于 int 没有 len 属性,这会遇到错误,有没有办法避免这种情况?谢谢!
  • @KevinZhao 有点晚了 - 但你的问题在这里得到解决:stackoverflow.com/questions/2189800/…
  • 可能有用的是添加您仍然需要使用wb.save(filename = dest_filename)保存工作簿
  • 这在 len(cell) 的 3.0.9 版(可能之前)中失败,TypeError object of type 'Cell' has no len()
【解决方案2】:

我对 Bufke 回答的变体。避免与数组发生一些分支并忽略空单元格/列。

现在修复了非字符串单元格值。

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

从 openpyxl 版本 3.0.3 开始,您需要使用

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

如果您传递 column_dimensions 一个数字而不是列字母,openpyxl 库将引发 TypeError,因此其他所有内容都可以保持不变。

【讨论】:

  • 第 6 行可以改进为使用列字母: dims[cell.column_letter] = max((dims.get(cell.column_letter, 0), len(str(cell.value))))
【解决方案3】:

设置至少在 openpyxl 版本 2.4.0 中有效的所有列的宽度的更 Pythonic 方式:

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

as_text 函数应该将值转换为适当长度的字符串,例如 Python 3:

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

【讨论】:

  • def as_text(value): return str(value) if value is not None else ""
  • @thorhunter len(cell.value or "") ,不需要额外的功能
  • @IrinaVelikopolskaya 如果 cell.value 没有实现__len__,这将抛出异常(例如intNoneType
  • @IrinaVelikopolskaya datetime 是另一个出现异常的例子。 as_text 函数似乎最适合我。
  • 请注意,使用 openpyxl 2.6 时,此代码将与 TypeError: expected <class 'str'> 一起崩溃。现在必须指定一个列名,即ws.column_dimensions[openpyxl.utils.get_column_letter(column_cells[0].column)].width = length。参见bitbucket.org/openpyxl/openpyxl/issues/1240/…
【解决方案4】:

使用 openpyxl 3.0.3 修改列的最佳方法是使用 DimensionHolder 对象,这是一个将每列映射到 ColumnDimension 对象的字典。 ColumnDimension 可以获取参数为bestFitauto_size(即bestFit 的别名)和width。 就个人而言,auto_size 没有按预期工作,我不得不使用 width 并发现该列的最佳宽度是 len(cell_value) * 1.23

要获取每个单元格的值,需要对每个单元格进行迭代,但我个人没有使用它,因为在我的项目中我只需要编写工作表,所以我直接在我的数据上获得了每列中最长的字符串.

下面的例子只是展示了如何修改列尺寸:

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

【讨论】:

    【解决方案5】:

    merge_cells 有问题,autosize 不能正常工作,如果你有同样的问题,你可以用下一个代码解决:

    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
    

    【讨论】:

      【解决方案6】:

      对上述接受的答案略有改进,我认为它更符合 Python 风格(请求宽恕比请求许可更好)

      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
      

      【讨论】:

      • 需要考虑cell.value是否不是字符串。例如,如果 cell.value 是 float 类型,则需要进行类型转换
      • 哇,那是 4 年前的事了。尽管我进行了编辑以修复,但您是正确的。刚刚向字符串添加了演员表。
      • 使用defaultdict 而不是iftry/except IndexError.items() 而不是enumerate 是更pythonic。
      【解决方案7】:

      我们可以将数字转换为它们的 ASCII 值,并把它交给 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')
      

      【讨论】:

        【解决方案8】:

        这是我的版本,引用了@Virako 的代码 sn-p

        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
        

        以及如何使用如下,

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

        【讨论】:

          【解决方案9】:

          以上所有答案都产生了一个问题,即 col[0].column 正在返回数字,而 worksheet.column_dimensions[column] 仅接受诸如“A”、“B”、“C”之类的字符来代替列.我修改了@Virako 的代码,现在运行良好。

          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
          

          【讨论】:

          【解决方案10】:

          当 openpxyl 更新时,我必须更改上面的 @User3759685 答案。我遇到了一个错误。好吧,@phihag 也在 cmets 中报告了这一点

          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
          

          【讨论】:

            【解决方案11】:

            这是一个肮脏的修复。但是openpyxl实际上支持auto_fit。但是没有方法可以访问该属性。

            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
            

            【讨论】:

              【解决方案12】:

              对于刚接触该主题的用户来说,这是一个更通用、更简化的解决方案(未针对该问题指定)。

              如果您想更改openpyxl(3.0.9 版)中单元格的宽度或高度,只需将单元格的属性指定为row_dimensionscolumn_dimensions

              import openpyxl
              wb = openpyxl.Workbook()
              sheet = wb["Sheet"]
              
              
              sheet["A1"] = "Tall row"
              sheet["B2"] = "Wide column"
              
              # Change height of row A1
              sheet.row_dimensions[1].height = 100
              # Change width of column B
              sheet.column_dimensions["B"].width = 50
              
              wb.save("StackOverflow.xlsx")
              

              【讨论】:

                【解决方案13】:

                从 openpyxl2.5.2a 更新到最新的 2.6.4(python 2.x 支持的最终版本)后,我在配置列宽时遇到了同样的问题。

                基本上我总是计算一列的宽度(dims 是一个保持每列宽度的字典):

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

                之后我将比例修改为比原始大小稍大的东西,但现在你必须给列的“字母”值而不是 int 值(下面的 col 是值,并被转换为正确的字母):

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

                这将修复可见错误并为您的列分配正确的宽度;) 希望对您有所帮助。

                【讨论】:

                  【解决方案14】:

                  编译并应用上述多个建议,并将合并单元格检测扩展到仅水平合并单元格,我可以提供以下代码:

                  def adjust_width(ws):
                      """
                      Adjust width of the columns
                      @param ws: worksheet
                      @return: None
                      """
                  
                      def is_merged_horizontally(cell):
                          """
                          Checks if cell is merged horizontally with an another cell
                          @param cell: cell to check
                          @return: True if cell is merged horizontally with an another cell, else False
                          """
                          cell_coor = cell.coordinate
                          if cell_coor not in ws.merged_cells:
                              return False
                          for rng in ws.merged_cells.ranges:
                              if cell_coor in rng and len(list(rng.cols)) > 1:
                                  return True
                          return False
                  
                      for col_number, col in enumerate(ws.columns, start=1):
                          col_letter = get_column_letter(col_number)
                  
                          max_length = max(
                              len(str(cell.value or "")) for cell in col if not is_merged_horizontally(cell)
                          )
                          adjusted_width = (max_length + 2) * 0.95
                          ws.column_dimensions[col_letter].width = adjusted_width
                  

                  【讨论】:

                    【解决方案15】:

                    另一种不存储任何状态的方法可能是这样的:

                    from itertools import chain
                    # Using `ws` as the Worksheet
                    for cell in chain.from_iterable(ws.iter_cols()):
                        if cell.value:
                            ws.column_dimensions[cell.column_letter].width = max(
                                ws.column_dimensions[cell.column_letter].width,
                                len(f"{cell.value}"),
                            )
                    

                    【讨论】:

                      【解决方案16】:

                      这是 Python 3.8 和 OpenPyXL 3.0.0 的答案。

                      我试图避免使用get_column_letter 函数但失败了。

                      此解决方案使用新引入的assignment expressions aka “海象算子”:

                      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)) 对我来说似乎更清楚。
                      • 这个(length if length >= MIN_WIDTH else MIN_WIDTH)可以简化为max(length, MIN_WIDTH),这样你就可以去掉海象运算符了。
                      【解决方案17】:

                      从openpyxl 2.6.1开始,设置宽度时需要列字母,而不是列号。

                       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
                      

                      【讨论】:

                        【解决方案18】:

                        我创建了一个处理大型 Excel 文件的函数,因为它使用 pandas.read_excel

                        import pandas as pd
                        from openpyxl import load_workbook
                        from openpyxl.utils import get_column_letter
                        
                        def auto_adjust_column_width(file_path, sheet_name=0):
                            column_widths = []
                        
                            df = pd.read_excel(file_path, sheet_name=sheet_name, header=None)
                            for col in df.columns:
                                max_length = int(df[col].astype(str).str.len().max() * 1.2)
                                column_widths.append(max_length)
                        
                            wb = load_workbook(file_path)
                            if isinstance(sheet_name, int):
                                sheet_name = wb.sheetnames[sheet_name]
                        
                            worksheet = wb[sheet_name]
                            for i, column_width in enumerate(column_widths):
                                column = get_column_letter(i+1)
                                worksheet.column_dimensions[column].width = column_width
                            wb.save(file_path)
                        
                        

                        【讨论】:

                          【解决方案19】:

                          当我遇到这个问题时,我只是用 openpyxl 完成了我想做的所有事情,保存了工作簿,然后用 pywin32 再次打开它。 Pywin32 内置了自动调整功能,无需制定一堆规则/条件。

                          编辑:我应该注意 pywin32 仅适用于 Windows。

                          from win32com.client import Dispatch
                          
                          excel = Dispatch('Excel.Application')
                          wb = excel.Workbooks.Open("excelFile.xlsx")
                          
                          excel.Worksheets(1).Activate()
                          excel.ActiveSheet.Columns.AutoFit()
                          
                          wb.Save()
                          wb.Close()
                          excel.Quit()
                          

                          不过,我确实添加了一条规则,因为我有一个文本列,其中包含一些不需要显示的长值。我将任何列限制为 75 个字符。

                          excel = Dispatch('Excel.Application')
                          wb = excel.Workbooks.Open("excelFile.xlsx")
                          
                          excel.Worksheets(1).Activate()
                          excel.ActiveSheet.Columns.AutoFit()
                          
                          for col in excel.ActiveSheet.Columns:
                              if col.ColumnWidth > 75:
                                  col.ColumnWidth = 75
                          
                          wb.Save()
                          wb.Close()
                          excel.Quit()
                           
                          

                          【讨论】:

                          • 这是迄今为止最好的方法(就像一个魅力),但这不适用于 Linux 对吧?
                          • @Ángel 没错。不幸的是,pywin32 仅适用于 Windows。我应该对此添加一个注释,但当时没有想到。
                          猜你喜欢
                          • 2022-08-17
                          • 1970-01-01
                          • 1970-01-01
                          • 2012-03-24
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 2011-05-29
                          • 2018-03-24
                          相关资源
                          最近更新 更多