pythonでエクセル表をコピーしてパワポに張り付ける


pythonでエクセルの表をコピーしてパワポに貼り付けるプログラムを作ってみました。
本当は文字と上下左右中央寄せもコピーしたかったけど疲れたでここで

openpyxlで特定の範囲で下記情報をコピー
・テキスト
・背景塗りつぶし色
・文字色
・太字情報
・セルのマージ情報
・不完全だけど表サイズ

●実行例、このぐらいコピーできれば個人的には満足
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3638856/a598804a-a2e8-aab5-c7eb-7ee4a64b7f83.png)

```excel_to_pptxtable.py
import openpyxl
from openpyxl.styles import PatternFill, Font, Border
from openpyxl.utils import get_column_letter
import sys

def excel_get_cell_info(sheet, cellkey):
    '''
    cell情報を返す ( sheet , "B4" )
    #cell_obj = sheet["B2"] sheet["B2"].column_letter
    #cell_obj = sheet.cell(row=2, column=2)
    '''
    cell_obj = sheet[cellkey]
    # 塗りつぶし色
    fill_color = cell_obj.fill.start_color.index if cell_obj.fill else None
    # COLOR = "AARRGGBB" AA：不透明度00なら透明、RR：赤、GG：緑、BB：青
    if fill_color[0:2] == '00' or fill_color == None :
        font_color = '00FFFFFF'
        
    # 文字色
    font_color = cell_obj.font.color.index if cell_obj.font.color else None
    if font_color == 1 or font_color == None :
        font_color = '00000000'

    font_bold = cell_obj.font.bold
    
    # 枠線の有無
    borders = cell_obj.border
    border_info = {
        'top': bool(borders.top),
        'bottom': bool(borders.bottom),
        'left': bool(borders.left),
        'right': bool(borders.right),
    }

    # セルマージ情報
    merged_cells = sheet.merged_cells
    merged_range = [m.bounds for m in merged_cells if cellkey == str(m).split(":")[0]]
    if len(merged_range) > 0:
        #for m in merged_cells:
        #    print(str(m),cellkey == str(m).split(":")[0])
        ll = merged_range[0]
        #print("merged",ll[2] - ll[0],ll[3] - ll[1])

    # セルの値を取得
    value = cell_obj.value

    # 計算式の場合は計算結果を取得
    #if cell_obj.data_type == 'f':  # 'f' は計算式を示す
    #    formula_result = cell_obj.value  # 計算式の結果
    #    { 'type': 'formula', 'value': formula_result }
    
    # 文字列または数値の場合
    #if isinstance(value, (str, int, float)):
    #    { 'type': 'value', 'value': value }
    
    return {
        'fill_color': fill_color,
        'font_color': font_color,
        'font_bold' : font_bold,
        'value':value,
        'merged_range':merged_range,
        #'border_info': border_info,
    }
#enddef excel_get_cell_info

def tuple_to_a1(row, column):
    ## 使用例
    ##print(tuple_to_a1(3, 3))  # 出力: "C3"
    # 行番号を文字列に変換
    if row == 0 or column == 0 :
        print("excel row col >= 0",row,column)
        sys.exit()
    row_number = str(row)
    
    # 列番号を文字列に変換
    column_letter = ""
    while column > 0:
        column -= 1
        column_letter = chr(column % 26 + ord('A')) + column_letter
        column //= 26
    #
    return column_letter + row_number
#enddef

def excel_get_table_info(sheet, sr , sc , rowr , colr ):
    '''
    sheet:sheet
    sr:スタート行
    sc:スタート列
    rowr:読み込む行範囲
    colr:読み込む列範囲
    '''
    retcsv = []
    # 1行目、2行目、3行目と読んでいく
    for row_idy in range(sr,sr+rowr) :
        row = []
        # 1列目、2列目、3列目と
        for col_idx in range(sc,sc+colr) :
            cellkey = tuple_to_a1(row_idy,col_idx)
            #print("debug",cellkey,"row",row_idy,"col",col_idx)
            row.append( excel_get_cell_info(sheet, cellkey) )
        retcsv.append(row)
    #endfor
    
    # セルマージ情報print
    print("## cell marge start")
    for mr in sheet.merged_cells.ranges :
        print(mr)
        print(mr.bounds)
    #endfor
    print("## cell marge end")

    # セルの幅(文字数)と高さ(ポイント)で取得※文字数×７＝ポイント
    pwidth = 0
    pheight = 0
    for rn in range( sr + rowr ) :
        pwidth += sheet.column_dimensions[get_column_letter(rn+1)].width
    for rn in range( sc + colr ) :
        pheight += sheet.row_dimensions[rn+1].height

    return retcsv , pwidth , pheight
#enddef

#############################
from pptx import Presentation
from pptx.util import Inches
from pptx.dml.color import RGBColor
from pptx.oxml.xmlchemy import OxmlElement

##　https://stackoverflow.com/questions/42610829/python-pptx-changing-table-style-or-adding-borders-to-cells
def SubElement(parent, tagname, **kwargs):
        element = OxmlElement(tagname)
        element.attrib.update(kwargs)
        parent.append(element)
        return element
def _set_cell_border(cell, border_color="000000", border_width='12700'):
    linStyle = 'solid'
    tc = cell._tc
    tcPr = tc.get_or_add_tcPr()
    for lines in ['a:lnL','a:lnR','a:lnT','a:lnB']:
        ln = SubElement(tcPr, lines, w=border_width, cap='flat', cmpd='sng', algn='ctr')
        solidFill = SubElement(ln, 'a:solidFill')
        srgbClr = SubElement(solidFill, 'a:srgbClr', val=border_color)
        prstDash = SubElement(ln, 'a:prstDash', val=linStyle) ## 線のスタイル
        round_ = SubElement(ln, 'a:round')
        headEnd = SubElement(ln, 'a:headEnd', type='none', w='med', len='med')
        tailEnd = SubElement(ln, 'a:tailEnd', type='none', w='med', len='med')

'''
ppt_table_cell_font
pptxのテーブルのcellの文字色、太字の有無の設定
'''
def ppt_table_cell_font(cell,color=(0,0,0),bold=False):
            for paragraph in cell.text_frame.paragraphs: # 段落のループ
                for run in paragraph.runs: # 文字のループ
                    run.font.color.rgb = RGBColor(*color)  # 黒色に設定
                    run.font.bold = bold  # 太字を解除
#enddef

'''
ppt_table_reset
pptxのテーブルを一律設定
黒枠設定、塗りつぶし無し、文字色黒、太字無し
'''
def ppt_table_reset(table):
    num_rows = len(table.rows)
    num_cols = len(table.columns)
    for row in range(num_rows):
        for col in range(num_cols):
            cell = table.cell(row, col)
            # セル背景塗りつぶし
            cell.fill.solid()
            cell.fill.fore_color.rgb = RGBColor(255, 255, 255)  # 白色
            cell.fill.background() ##noFill
            # 線の設定
            _set_cell_border(table.cell(row, col))
            # 文字の設定
            ppt_table_cell_font(cell, (0,0,0),False)
#enddef

#############################
'''
argb_to_rgb('AARRGGBB')
AA は透明度
RGBを(129,44,00) みたいなのに変換
'''
def argb_to_rgb(argb_color):
    # 先頭の2文字を除去してRGBを取得
    alfa = int(argb_color[0:2],16)
    red = int(argb_color[2:4],16)
    green = int(argb_color[4:6],16)
    blue = int(argb_color[6:8],16)
    return (red, green, blue) , alfa
#enddef

'''
etcsv_to_ppt_table
エクセルから取得したtable情報をpptのテーブルで作成
'''
def etcsv_to_ppt_table( etcsv , slide ,pwidth,pheight) :
    # テーブルの作成
    rows, cols = len(etcsv),len(etcsv[0]), 
    left = Inches(1)
    top = Inches(1)
    width = Inches(pwidth*3/73) # エクセルのwidthは文字数なのでpointに変換するために2~7×
    height = Inches(pheight/73)
    table = slide.shapes.add_table(rows,cols,  left, top, width, height).table
    ppt_table_reset(table)

    #print("debug range",rows,cols)
    # 1行目、2行目、3行目と読んでいく
    for row in range(rows):
        for col in range(cols):
            ptablecell = table.cell(row,col,)
            etcell = etcsv[row][col]
            
            # value
            if etcell['value'] != None :
                ptablecell.text = etcell['value']

            #print("debug","r",row,"c",col,"value",etcell['value'])
            
            # y行x列をマージ
            merge_cell = etcell['merged_range']
            if len(merge_cell) > 0:
                merge_cell_range = merge_cell[0]
                mergeend_col = col + merge_cell_range[2]-merge_cell_range[0] # +1
                mergeend_row = row + merge_cell_range[3]-merge_cell_range[1] # +1
                #print("debug","r",row,"c",col,"merge","mr",mergeend_row,"mc",mergeend_col)
                ptablecell.merge(table.cell(mergeend_row,mergeend_col,))

            # セル背景塗りつぶし
            if etcell['fill_color'][0:2] != '00' :
                ptablecell.fill.solid()
                #ptablecell.fill.background() ##noFill
                fcolor , alfa = argb_to_rgb(etcell['fill_color'])
                ptablecell.fill.fore_color.rgb = RGBColor( *fcolor ) ## 塗りつぶし

            # 文字の設定
            fcolor , alfa = argb_to_rgb(etcell['font_color'])
            ppt_table_cell_font(ptablecell,fcolor,etcell['font_bold'])
#############


if __name__ == '__main__':
    # 使用例
    file_path = 'sss2.xlsx'  # Excelファイルのパス

    # Excelファイルを読み込む
    wb = openpyxl.load_workbook(file_path)
    print("## sheetname start")
    for sname in wb.sheetnames :
        print(sname)
    print("## sheetname end")

    # 指定した範囲をコピー
    # sheets,sr行,sc列,rowｒ行,colr列
    # B2:E10 , (2,2)-(5-9) , (2,2)->[4,8]
    excellinfo_csv ,pwidth ,pheight = excel_get_table_info(wb['22'],2,2,8,4)

    if False : # debug
        for row_idy , valy in enumerate(excellinfo_csv) :
            for col_idx , val in enumerate(valy) :
                print( row_idy , col_idx , val )

    # プレゼンテーションを作成
    prs = Presentation()
    # スライドを追加
    slide_layout = prs.slide_layouts[5]  # 空白のスライド
    slide = prs.slides.add_slide(slide_layout)

    # エクセルからコピーしてきたデータをスライドに張り付け
    etcsv_to_ppt_table(excellinfo_csv,slide,pwidth,pheight)
    # プレゼンテーションを保存
    prs.save('table_presentation.pptx')
```