解決xlsxwriter的format覆蓋問題


 最近在用Python寫一個生成Excel日歷的腳本, 功能上實現沒多大問題, 倒是在xlsxwriter的格式寫入上遇到了一個大坑.

 舉個例子:

import xlsxwriter wb = xlsxwriter.Workbook("test.xlsx") ws = wb.add_worksheet() # 加粗和字體樣式
bold = wb.add_format({"bold":True}) fontsize = wb.add_format({"font_size":15}) ws.write("A1", "test", bold) ws.write("A2", "test", fontsize)

 可以預見A1格是粗體, A2格字體則大了幾號. 但我們無法同時給一個單元格套用兩種樣式, 或者說, 單元格只接受最后套用的樣式:

ws.write("A1", "test", bold) ws.write("A1", "test", fontsize)

 這一格的文字只會變大而不會變粗 .

 另一個問題是, 無法分別給一個單元格寫入值和樣式. 對於單個單元格, 必須在寫入值的同時為單元格寫入樣式. 關於這個, 可以用 set_column 和 set_row 解決, 它們的實質是設置該列/行的默認樣式:

ws.set_column("A:A", None, bold) ws.set_row(2, None, fontsize) # 第三行
ws.write("A1", "test1") ws.write("A2", "test2", fontsize) ws.write("A3", "test3")

 運行代碼可以發現A1繼承了A列的默認樣式(加粗), 而A2只有字體變大了, 樣式覆蓋問題依然存在; 另外, A3只繼承了行默認樣式: 不管 set_column 和 set_row 的先后順序如何, 行默認樣式會覆蓋列默認樣式. 

 對於規模極小的表格, 可以分別為單元格設置單獨而完全的格式, 但我們依舊希望能夠分別/分次為單元格追加新樣式, 例如先對一些單元格設置字體, 然后再對一些單元格設置背景色, 最后再給整體添加框線等.

 Xlsxwriter被設計為只能寫入xlsx文件而不能讀取或修改, 但問題在於, 我們xlsxwriter寫入的數據會暫存在內存中, 直到最后close()時才會寫入到文件中, 那么理應能夠從緩存中修改, 最起碼讀出某單元格的值和樣式. 而官方目前沒有這樣的設計, 所以我們可以先將設置的樣式緩存下來, 如果某單元格已經有了樣式那就合並, 最后再調用xlsxwriter的方法寫入到表格中.

 由於樣式和值需要同時寫入到單元格中, 所以值的寫入也需要緩存. 我為我的Excel日歷生成腳本寫的緩存機制代碼如下:

cells_format = {} cells_value = {} def write_format(row, col, append_format: dict): cell = xlsxwriter.worksheet.xl_rowcol_to_cell_fast(row, col) # 將行列號轉換成A1這樣的格式 fmt = cells_format[cell].copy() if cell in cells_format else {} fmt.update(append_format) cells_format[cell] = fmt # 批量套用樣式 def write_formats(s_row, s_col, e_row, e_col, append_format: dict): for row in range(s_row, e_row + 1): for col in range(s_col, e_col + 1): write_format(row, col, append_format) def write_value(row, col, value): cell = xlsxwriter.worksheet.xl_rowcol_to_cell_fast(row, col) cells_value[cell] = value def write_finish(wb: xlsxwriter.Workbook, ws: xlsxwriter.Workbook.worksheet_class): values, formats = set(cells_value.keys()), set(cells_format.keys()) for c in values.difference(formats): ws.write(c, cells_value[c]) for c in values.intersection(formats): ws.write(c, cells_value[c], wb.add_format(cells_format[c])) for c in formats.difference(values): ws.write_blank(c, None, wb.add_format(cells_format[c]))

 首先通過write_value和write_format(s)將值和樣式寫入緩存字典中, 如果已存在樣式, 就拷貝並追加新樣式, 在最后調用write_finish完成寫入. 有興趣的讀者可以將它模塊化, 再整合進一些其他功能, 方便xlsxwriter的寫入.

 然后就可以歡快地寫樣式了, 最后是一個簡單的小日歷:

# 每周前5天顏色較深
write_formats(0, 0, 4, 4, {"bg_color":"#C8C8C8"}) write_formats(0, 5, 4, 6, {"bg_color":"#D9D9D9"}) # 外框線
write_formats(0, 0,
4, 0, {"left":1}) write_formats(0, 6, 4, 6, {"right":1}) write_formats(0, 0, 0, 6, {"top":1}) write_formats(4, 0, 4, 6, {"bottom":1}) for i in range(1, 32): write_value(i//7, i%7, i) wb = xlsxwriter.Workbook("test.xlsx") ws = wb.add_worksheet() write_finish(wb, ws) wb.close()

效果如下:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM