Python xlwt - 访问现有单元格内容,自动调整列宽

33
我想创建一个Excel工作簿,在保存工作簿之前可以自动设置或自动调整列的宽度。
我一直在阅读Python-Excel教程,希望找到一些在xlwt中模拟xlrd函数的方法(例如sheet_names()cellname(row, col)cell_typecell_value等)。例如,假设我有以下内容:
from xlwt import Workbook    
wb = Workbook()
sh1 = wb.add_sheet('sheet1' , cell_overwrite_ok = True)    
sh2 = wb.get_sheet(0)

wb.get_sheet(0)和xlrd中提供的rb.sheet_by_index(0)函数类似,但前者允许您修改内容(前提是用户已设置cell_overwrite_ok = True)。

假设xlwt提供我需要的功能,我打算再次遍历每个工作表,但这次要跟踪特定列占用最多空间的内容,并根据该内容设置列宽。当然,我也可以在写入工作表时跟踪特定列的最大宽度,但我觉得在所有数据已经写入后设置宽度会更加清晰简洁。

有人知道我能否这样做吗?如果不能,您建议采取什么方法来调整列宽度?

6个回答

50

我刚刚实现了一个包装类,可以在您输入项时跟踪它们的宽度。看起来它的工作效果还不错。

import arial10

class FitSheetWrapper(object):
    """Try to fit columns to max size of any entry.
    To use, wrap this around a worksheet returned from the 
    workbook's add_sheet method, like follows:

        sheet = FitSheetWrapper(book.add_sheet(sheet_name))

    The worksheet interface remains the same: this is a drop-in wrapper
    for auto-sizing columns.
    """
    def __init__(self, sheet):
        self.sheet = sheet
        self.widths = dict()

    def write(self, r, c, label='', *args, **kwargs):
        self.sheet.write(r, c, label, *args, **kwargs)
        width = arial10.fitwidth(label)
        if width > self.widths.get(c, 0):
            self.widths[c] = width
            self.sheet.col(c).width = width

    def __getattr__(self, attr):
        return getattr(self.sheet, attr)

所有的魔法都在John Yeung的arial10模块中。这个模块为Arial 10设置了很好的宽度,这是默认的Excel字体。如果你想使用其他字体编写工作表,你需要改变fitwidth函数,最好考虑传递给FitSheetWrapper.write的style参数。


7
非常棒的解决方案。由于 xlwt-0.7.5 会引发 ValueError,因此我不得不在 width = arial10.fitwidth(label) 外面加上 int(..)。如果你得到的单元格宽度略小于所需宽度,请尝试使用 math.ceil(int(..)) - dset0x
7
我找不到arial10模块的链接,但在github上找到了它:https://github.com/GeekPeduli/Sahana-Eden/blob/master/modules/arial10.py。请注意,这是一个英文回答,我尽力使其通俗易懂。 - Humphrey
不幸的是,对于较长的标签,这将无法工作,因为总宽度可能会超过65535(这是Excel的最大值)。 - Jerzyk
2
汉弗莱的链接也已经失效了,但是Arial10似乎在这里得到了保留:https://github.com/juanpex/django-model-report/blob/master/model_report/arial10.py - btown
1
除了由zmode创建的int()包装外,我还必须编辑arial10.py,在第148行用str(..)包装数据。 - CCKx

14

如果您不想使用另一个类(FitSheetWrapper),那么可以使用WorkSheet列方法来实现。

work = xlwt.WorkBook()
sheet = work.add_sheet('Sheet1')
for row_index in range(0,max_row):
   for column_index in range(0,max_col) :
      cwidth = sheet.col(column_index).width
      if (len(column_data)*367) > cwidth:  
          sheet.col(column_index).width = (len(column_data)*367) #(Modify column width to match biggest data in that column)

      sheet.write(row_index,column_index,column_data,style)

宽度的默认值为2962个单位,而Excel将其指定为8.11个单位。因此,我将数据的长度乘以367。

这是从Kevin的FitSheetWrapper改编过来的。


5
xlwt中没有自动实现这个功能的方法。您需要按照您所描述的一般模式,即在写入时跟踪最大宽度,并在所有数据都已查看但在保存工作簿之前的某个时间设置列宽。
请注意,当处理Excel文件时,这是可用的最清洁和最有效的方法。如果您所说的“在数据已经被写入后”意味着“在您已经提交单元格值(“写入”)但尚未实际保存工作簿之前”,那么上述方法正是这样做的。如果您的意思是“在已保存工作簿之后,您想要重新读取它以获取最大宽度,然后使用新的列宽再次保存它”,那么这将会更慢,并且需要同时使用xlwt和xlrd(可能还有xlutils)。此外,请注意,当您使用真正的Microsoft Excel时,不存在“更新”文件的概念。从用户的角度来看,可能会出现这种情况,但在幕后发生的事情是每次保存时,Excel都会清除现有文件并从头开始编写全新的文件。

2
FitSheetWrapper在3.3.4版本中应该对xlwt3进行一些修改
第19行:
更改:
width = arial10.fitwidth(label)

to:

width = int(arial10.fitwidth(label))  

原因: \Python\3.3.3\Lib\site-packages\xlwt3\biffrecords.py
1624 def __init__(self, first_col, last_col, width, xf_index, options):
1625        self._rec_data = pack('<6H', first_col, last_col, width, xf_index, options, 0)

宽度必须是整数。


1
这可能有点晚了,但我创建了一个可以一次处理整个表格的方法。它很快且能完成任务。如果你认为 256 计算不准确(如果你有更长的文本字段),则需要额外添加缓冲参数。
from xlrd import *
from xlwt import *

def autoAdjustColumns(workbook, path, writerSheet, writerSheet_index, extraCushion):
    readerSheet = open_workbook(path).sheet_by_index(writerSheet_index)
    for row in range(readerSheet.nrows):
            for column in range(readerSheet.ncols):
                    thisCell = readerSheet.cell(row, column)
                    neededWidth = int((1 + len(str(thisCell.value))) * 256) 
                    if writerSheet.col(column).width < neededWidth:
                            writerSheet.col(column).width = neededWidth + extraCushion
    workbook.save(path)

1
我使用这个方法:

wb = Workbook()
ws = wb.add_sheet('Sheet1')
columnwidth = {}
row = 0
for rowdata in data:
    column = 0
    for colomndata in rowdata:
        if column in columnwidth:
            if len(colomndata) > columnwidth[column]:
                columnwidth[column] = len(colomndata)
        else:
            columnwidth[column] = len(colomndata)
        ws.write(row, column, colomndata, style0)
        column = column + 1
    row = row + 1
for column, widthvalue in columnwidth.items():
    ws.col(column).width = (widthvalue + 4) * 367

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接