ホンモノのエンジニアになりたい

ITやビジネス、テクノロジーの話を中心とした雑記ブログです。

[Python] openpyxlでの複数セルの罫線引き

先日に引き続きopenpyxlで遊んでいた時に調べた内容のメモです。

・複数セルを選択した状態での罫線引き  
 
◇公式ドキュメントはここ
openpyxl - A Python library to read/write Excel 2010 xlsx/xlsm files — openpyxl 2.5.0 documentation

◇公式ソースコードはこっち
openpyxl / openpyxl — Bitbucket

 

複数セルを選択した状態での罫線引き

最終的にこういう線を引きたかったわけです。 罫線の範囲が入力によって変化するアプリ(おもちゃ)を作っていたので、罫線範囲が不定な状況で自動的に罫線引きたいなと。

f:id:kwnflog:20180308191534p:plain

 
エクセルを通常のGUIで操作する時は、5列選択して左右は実線、列間は点線を引いて5列分の書式をコピペ、行でも同じことをやる、って感じで操作すると思います。  
 
ただ調べても範囲指定したセル群の列間/行間の罫線引きが無さそうだったので、1行(1列)の範囲で指定して線を引きました。  
 
※公式ドキュメントからのコピペと、日曜プログラマーのコードです。

   
まずは公式にあるマージセルの操作関数を持ってくる。 https://openpyxl.readthedocs.io/en/stable/styles.html#cell-styles-and-named-styles

 

from openpyxl.styles import Border, Side, PatternFill, Font, GradientFill, Alignment
from openpyxl import Workbook

def style_range(ws, cell_range, border=Border(), fill=None, font=None, alignment=None):
    top = Border(top=border.top)
    left = Border(left=border.left)
    right = Border(right=border.right)
    bottom = Border(bottom=border.bottom)
    first_cell = ws[cell_range.split(":")[0]]
    if alignment:
        ws.merge_cells(cell_range)
        first_cell.alignment = alignment
    rows = ws[cell_range]
    if font:
        first_cell.font = font
    for cell in rows[0]:
        cell.border = cell.border + top
    for cell in rows[-1]:
        cell.border = cell.border + bottom
    for row in rows:
        l = row[0]
        r = row[-1]
        l.border = l.border + left
        r.border = r.border + right
        if fill:
            for c in row:
                c.fill = fill

フォントなど使わないのもありますが、とりあえずそのまま持ってきて使ってました。    
で、目的の罫線を引くには、列間と行間の線を引けないため外枠の罫線のみを使う。 まぁ対象範囲の全列/全行の処理をそれぞれ書いていく感じ。

## ワークブックとワークシートの定義
wb = Workbook()
ws = wb.active

## 数字からエクセル列アルファベットへの変換関数(1→A、27→AAみたいな変換)
def toAlpha(num):
    h=int((num-1-26)/(26*26))
    i=int((num-1-(h*26*26))/26)
    j=int(num-(i*26)-(h*26*26))
    Alpha=''
    for k in h,i,j:
        if k!=0:
            Alpha+=chr(k+64)
    return Alpha


##罫線の定義
# 罫線の線種と色の定義(solid:実線、dot:点線)
solid = Side(border_style="thin", color="000000")
dot   = Side(border_style="dotted", color="000000")

# 縦線の線種と位置の定義(縦線は列の左だけ線を引く)
border_ver_solid = Border(left=solid)
border_ver_dot   = Border(left=dot)

# 横線の線種と位置の定義(横線は行の上だけ線を引く)
border_hor_solid = Border(top=solid)
border_hor_dot   = Border(top=dot)



## 縦線を引いていく
# 5の倍数の時だけsolid(実線)で引く
ver_start='2'
ver_end='30'

for col in range(1,27):
    retuAlpha=toAlpha(3+col)
    # 罫線範囲を定義(ここでは1周目に'D2:D30'、2周目に'E2:E30'、・・)
    ver_range=retuAlpha+ver_start+':'+retuAlpha+ver_end
    if (col-1) % 5 ==0:
        style_range(ws,ver_range,border=border_ver_solid)
    else:
        style_range(ws,ver_range,border=border_ver_dot)


## 横線も引く
# こっちも5の倍数だけsolid
hor_start='B'
hor_end='AD'

for row in range(1,27):
    gyouNum=row+3
    #横線の範囲(ここでは1回目'B4:AD4'、2回目'B5:AD5'、・・・)
    hor_range=hor_start+str(gyouNum)+':'+hor_end+str(gyouNum)
    if (row-1) % 5 ==0:
        style_range(ws,hor_range,border=border_hor_solid)
    else:
        style_range(ws,hor_range,border=border_hor_dot)


## 列の幅を変更
for i in range(1,30+1):
    ws.column_dimensions[toAlpha(i)].width=3


## エクセル保存
wb.save('test.xlsx')

とりあえず、上で書いた公式docのスタイル関数と日曜プログラマーの産物を順次実行すれば目的である格子型の罫線が引けます。  
 

あ、ちなみにここで書いたコードの出力はLibre Officeでも開けました。 別のエントリでopenpyxlでのシートコピーのエントリも書いてますが、それもちゃんとできてました。

kwnflog.hatenablog.com

 
 

おわり。