解決 openpyxl 垂直分頁符和水平分頁符同時添加的問題

前言

十天前知乎上有人提問 python:openpyxl模塊怎麼給表格添加分頁符?實現分頁打印功能?,看到問題以後,我很快的給他了一個如何添加垂直分頁符或水平分頁符的示例,你覺得問題就結束了?我是這麼覺得的,可是事實證實,我太天真了,就在我給出示例的幾分鐘後,他在個人回答下評論了,說是同時添加垂直分頁符和水平分頁符失敗了.
我當時的第一反應:

內心想着,確定是他的寫法有問題,堅決果斷的回覆到," 沒有試過同時添加兩種分頁符的操做,默認是水平分頁符,若是你先添加了垂直分頁符的話,應該後面須要從新聲明:openpyxl.worksheet.pagebreak.PageBreak.tagname = "rowBreaks",聽着本身飛快擊打鍵盤的聲音,本身不經有點飄飄然.就在沉浸在本身的YY當中,又過去了幾分鐘,他用正確的代碼錯誤的結果狠狠的摔在了個人臉上:python

col_break = openpyxl.worksheet.pagebreak.Break(5) #建立分頁符,參數5:在第5/6中間分頁
sheet1.page_breaks.tagname = 'rowBreaks' #分頁符屬性設置爲行分頁符
sheet1.page_breaks.append(col_break) #把分頁符對象添加到sheet對象裏

row_break = openpyxl.worksheet.pagebreak.Break(3) #建立分頁符
sheet1.page_breaks.tagname = 'colBreaks' #分頁符屬性設置爲列分頁符
sheet1.page_breaks.append(row_break) #把分頁符對象添加到sheet對象裏

"結果是在第3和5列添加了兩個垂直分頁符,是哪裏有問題?大師",他問道. 我看了一眼代碼好像沒有錯,個人心有點慌了,雙手開始微微顫抖起來,一遍又一遍地仔細地巡視着代碼,視圖找出錯誤反駁他,可是並無,抱着最後的但願,我把他的代碼複製到本身的文件中,而後敲下回車符, excel 文件靜靜的生成在目錄下,這多是我最後的但願了.拿鼠標的手不自覺的顫抖起來,那麼小的屏幕,那麼大的文件,鼠標怎麼半天都沒辦法移動上去,我深吸一口氣,控制住本身手,終於把鼠標一上去了,雙擊excel,閉上眼睛,再睜開,我知道,我錯了.可是就這麼放棄了嗎?毫不!我要把這個問題打到!app

亮劍

"是時候展現真正的技術了"測試

俗話說"解鈴還須繫鈴人",咱們還得本身看一下問題代碼:spa

# example.py

from openpyxl import Workbook
from openpyxl.compat import range
from openpyxl.utils import get_column_letter
from openpyxl.worksheet.pagebreak import Break, PageBreak

wb = Workbook()
ws = wb.active

for row in range(1, 20):
    for col in range(1,30):
        _ = ws.cell(column=col, row=row, value="{0}".format(get_column_letter(col)))

col_break = Break(5) #建立分頁符,參數5:在第5/6中間分頁
ws.page_breaks.tagname = 'rowBreaks' #分頁符屬性設置爲行分頁符
ws.page_breaks.append(col_break) #把分頁符對象添加到sheet對象裏

row_break = Break(3) #建立分頁符
ws.page_breaks.tagname = 'colBreaks' #分頁符屬性設置爲列分頁符
ws.page_breaks.append(row_break) #把分頁符對象添加到sheet對象裏

wb.save(filename = dest_filename)

從代碼上應該是後面的 page_breaks 把前面的覆蓋了, 那讓咱們看看 page_breaks 到底是什麼東西.excel

class Worksheet(_WorkbookChild):
   # 省略部分代碼
      def _setup(self):
        self.page_breaks = PageBreak() # 再看 PageBreak

class PageBreak(Serialisable):
    tagname = "rowBreaks"
    # 省略部分代碼
    def append(self, brk=None):
        """
        Add a page break
        """
        vals = list(self.brk)
        if not isinstance(brk, Break):
            brk = Break(id=self.count+1)
        vals.append(brk)
        self.brk = vals

從 example 中咱們不難發現,咱們是經過修改 page_breaks 的 tag_name 去決定插入的分頁符是垂直分頁符仍是水平分頁符的.可是 page_breaks 如今只有一個 PageBreak 這就難怪後聲明的會把前面的覆蓋了,那麼若是咱們把 page_breaks 變成 PageBreak 的列表呢?code

First Blood -- page_breaks

說改咱就改啊,首先嚐試修改 WorkSheet 類orm

class Worksheet(_WorkbookChild):
   # 省略部分代碼
      def _setup(self):
        self.page_breaks = [PageBreak()]

而後再修改一下 example.pyxml

from openpyxl import Workbook
from openpyxl.compat import range
from openpyxl.utils import get_column_letter
from openpyxl.worksheet.pagebreak import Break, PageBreak

wb = Workbook()
dest_filename = 'empty_book.xlsx'

ws = wb.active
for row in range(1, 20):
    for col in range(1,30):
        _ = ws.cell(column=col, row=row, value="{0}".format(get_column_letter(col)))

rowPageBreak = PageBreak()
rowPageBreak.tagname = 'rowBreaks'

colPageBreak = PageBreak()
colPageBreak.tagname = 'colBreaks'

ws.page_breaks = [rowPageBreak, colPageBreak] 

ws.page_breaks[0].append(Break(id=5))  
ws.page_breaks[1].append(Break(id=3))
wb.save(filename = dest_filename)

敲下回車,內心那個美滋滋,還沒高興幾秒鐘,就出問題了,果真作人仍是得低調一點對象

Traceback (most recent call last):
  File "test.py", line 24, in <module>
    wb.save(filename = dest_filename)
  File "F:\workspace\python\test_openpyxl\test_openpyxl\lib\site-packages\openpyxl\workbook\workbook.py", line 391, in save
    save_workbook(self, filename)
  File "F:\workspace\python\test_openpyxl\test_openpyxl\lib\site-packages\openpyxl\writer\excel.py", line 284, in save_workbook
    writer.save(filename)
  File "F:\workspace\python\test_openpyxl\test_openpyxl\lib\site-packages\openpyxl\writer\excel.py", line 266, in save
    self.write_data()
  File "F:\workspace\python\test_openpyxl\test_openpyxl\lib\site-packages\openpyxl\writer\excel.py", line 83, in write_data
    self._write_worksheets()
  File "F:\workspace\python\test_openpyxl\test_openpyxl\lib\site-packages\openpyxl\writer\excel.py", line 203, in _write_worksheets
    xml = ws._write()
  File "F:\workspace\python\test_openpyxl\test_openpyxl\lib\site-packages\openpyxl\worksheet\worksheet.py", line 893, in _write
    return write_worksheet(self)
  File "F:\workspace\python\test_openpyxl\test_openpyxl\lib\site-packages\openpyxl\writer\worksheet.py", line 151, in write_worksheet
    xf.write(ws.page_breaks.to_tree())
AttributeError: 'list' object has no attribute 'to_tree'

看了一眼錯誤信息,發現了從中做祟的傢伙再 worksheet.py 的 151 行, 讓咱們悄悄地看一眼,打槍的不要.blog

# worksheet.py
# 省略部分代碼
            if ws.page_breaks:
                    xf.write(ws.page_breaks.to_tree())

原來是咱們修改了 page_breaks 以後, page_breaks 有時候再也不是孤家寡人了,咱們須要考慮它有另外的 PageBreak 的狀況了.

Double Kill -- Worksheet

# worksheet.py
# 省略部分代碼
            if ws.page_breaks:
                if isinstance(ws.page_breaks,list):
                    for page_break_item in ws.page_breaks:
                        xf.write(page_break_item.to_tree())
                else:
                    xf.write(ws.page_breaks.to_tree())

回車,毫無問題,人生啊就是這麼寂寞如雪~~~

後記

已經在 openpyxl 提了相應的issue,目前再寫測試用例,過段時間就提交 PR 了

相關文章
相關標籤/搜索