2022年6月19日日曜日

Pywin32によるExcel Format操作その1

 Excelに書き込めるようにはなりましたが、フォーマットもPythonで書き込みたいということで試行錯誤しながらトライ中です。

下図は、Pywin32でフォーマットしたExcelですが、書くのに2分位かかっていました。フォーマット自体は、そのインスタンスについて1回しか使用しないので、遅くてもよいのですが、データ書き込みも1分以上、かかってしまっており、改善の必要があります。

Pywin32を用いたExcel操作は、あまり例が見当たらないと思いますので、使ったソースはそのまま公開します。ScheduleNurse3に依存する処理はないので、操作例として利用可能と思います。

以下は、現状でのフォーマットルーチンです。
難しいのは、土日に色を付けるところで、FormatConditionで記述しています。(恐らくは、仕様追加.追加..の産物なのでしょうが、VBAの言語仕様にたまげてしまいました。)
大抵は、VBAをそのままコンバート出来ますが、オブジェクト変数に落とす部分は、Pythonでは記述不能です。Unionで動的にオブジェクトを追加していくことは出来ないのは残念です。なので、VBAの究極の記述と比較すると、速度低下は避けられないと思います。

def make_initial_format(ws):

    
    print(locale.getdefaultlocale())
    dt = datetime.datetime(2018, 1, 1)
    print(dt)
# 2018-01-01 00:00:00

    print(dt.strftime('%A, %a, %B, %b'))
# Monday, Mon, January, Jan
    set_font(ws,"D2","Work Schedule")
    set_font(ws,"AW2","")
    ws.Range("AW2").Formula='=Year(E7)'
    ws.Range("AW2","AX2").Merge()
    ws.Range("AW2").Font.Underline = xlUnderlineStyleSingle
    ws.Range("AW1","AX1").Merge()
    set_font(ws,"AW1",'Year')
    set_font(ws,"AZ2","2022")#dummy
    ws.Range("AZ2").Formula='=Month(E7)'
    ws.Range("AZ2").Font.Underline = xlUnderlineStyleSingle
    ws.Range("AZ2","BB2").Merge()
    set_font(ws,"AZ1",'Month')
    ws.Range("AZ1","BB1").Merge()

    row_start=8
    row=row_start
    col_start=2
    col=col_start
    day_col_offset=3
    persons=len(staffdef)
    days=len(ThisMonth)
    phases=len(dayphase_list)
    cols=phases*days
#Clear All
    ws.Range(ws.Cells(row_start,col_start+day_col_offset), ws.Cells(row_start+4*persons-1,col_start+day_col_offset+cols-1)).Value=""
    ws.Range(ws.Cells(row_start,col_start+day_col_offset), ws.Cells(row_start+4*persons-1,col_start+day_col_offset+cols-1)).Interior.Color=0xffffff

    ws.Columns(2).EntireColumn.ColumnWidth = 30
    ws.Columns(3).EntireColumn.ColumnWidth = 10
    ws.Columns(4).EntireColumn.ColumnWidth = 8

    for name in staffdef:
        ws.Cells(row,col_start).Value=name   
        merge_and_align(ws,row,col,row+3,col)
        ws.Cells(row,col+1).Value='Preferred'
        merge_and_align(ws,row,col+1,row+1,col+1)

        ws.Cells(row+2,col+1).Value='Sc3 Solution'
        merge_and_align(ws,row+2,col+1,row+3,col+1)

        ws.Cells(row,col+2).Value='Shift'
        merge_and_align(ws,row,col+2,row,col+2)

        ws.Cells(row+2,col+2).Value='Shift'
        merge_and_align(ws,row+2,col+2,row+2,col+2)
        
        ws.Cells(row+1,col+2).Value='Task'
        merge_and_align(ws,row+1,col+2,row+1,col+2)
        
        ws.Cells(row+3,col+2).Value='Task'
        merge_and_align(ws,row+3,col+2,row+3,col+2)
        row +=4
    day_processed=0
    
    row=row_start-2
    #import pdb
    #pdb.set_trace()
    #print("set trace")
    
    pdb.set_trace()
    for day in ThisMonth:
        day_str=daydef[day]
        
        print(day_str,int(day_str[8:]))
        col=col_start+day_col_offset+day_processed
        for ph in range(phases):
            ws.Columns(col+ph).EntireColumn.ColumnWidth = 2

        #ws.Cells(row,col).Value=int(day_str[8:])
        ws.Cells(row,col).Formula='=Day(Date('+day_str[0:4]+','+day_str[5:7]+','+day_str[8:]+'))'
        day_color_format(ws,row,col,day_str)
        merge_and_align(ws,row ,col,row,col+phases-1)

        ws.Cells(row+1,col).Formula='=Date('+day_str[0:4]+','+day_str[5:7]+','+day_str[8:]+')'
        ws.Cells(row+1,col).NumberFormatLocal='aaa'
        day_color_format(ws,row+1,col,day_str)

        merge_and_align(ws,row+1,col,row+1,col+phases-1)

        day_processed +=phases

    row=row_start
    for person in All_Staff:
        day_processed=0
        row =row_start+person*4
        for day in ThisMonth:
            #print("row=",row,"row_start=",row_start)
            col=col_start+day_col_offset+day_processed
            merge_and_align(ws,row,col,row,col+phases-1)
            merge_and_align(ws,row+2,col,row+2,col+phases-1)

            day_processed +=phases
        ws.Range(ws.Cells(row,col_start), ws.Cells(row+3,col_start+day_col_offset+len(ThisMonth)*phases-1)).Borders.LineStyle = xlContinuous
        ws.Range(ws.Cells(row,col_start), ws.Cells(row+3,col_start+day_col_offset+len(ThisMonth)*phases-1)).Borders.weight=xlThin

#Sub rountines
def get_weekday(day_str):
    date=datetime.day(int(day_str[0:4]),int(day_str[5:7]),int(day_str[8:]))
    return date.weekday()#return integer Monday=0

def day_color_format(ws,row,col,day_str):
    ws.Cells(row,col).FormatConditions.Delete()#Add(xlCellValue, xlGreaterEqual, '=85')

    ws.Cells(row,col).FormatConditions.Add(xlExpression, xlEqual, '=Weekday(Date('+day_str[0:4]+','+day_str[5:7]+','+day_str[8:]+'))=7') #SAT:7 Sun:1
    ws.Cells(row,col).FormatConditions.Add(xlExpression, xlEqual, '=Weekday(Date('+day_str[0:4]+','+day_str[5:7]+','+day_str[8:]+'))=1') #SAT:7 Sun:1
        
    ws.Cells(row,col).FormatConditions(1).Interior.ColorIndex=20 #LightBlue
    ws.Cells(row,col).FormatConditions(2).Interior.ColorIndex=22 #

def merge_and_align(ws,row_start1,col_start1,row_end1,col_end1,weight=xlThin):
        
#        if row_start<=0:
        print(row_start1,col_start1,row_end1,col_end1)
#            pdb.set_trace()
        ws.Range(ws.Cells(row_start1,col_start1), ws.Cells(row_end1,col_end1)).Merge()
        ws.Range(ws.Cells(row_start1,col_start1), ws.Cells(row_end1,col_end1)).VerticalAlignment = xlCenter
        ws.Range(ws.Cells(row_start1,col_start1), ws.Cells(row_end1,col_end1)).HorizontalAlignment = xlCenter
        ws.Range(ws.Cells(row_start1,col_start1), ws.Cells(row_end1,col_end1)).Borders.LineStyle = xlContinuous
        ws.Range(ws.Cells(row_start1,col_start1), ws.Cells(row_end1,col_end1)).Borders.Weight = weight

0 件のコメント:

コメントを投稿