Cell.Valueだけでしたら可能です。PythonでRangeを読んでみると2次元Tupleで返ってきました。つまり2次元配列で書いてやれば、時間のかかるCOM通信を一回で行えるのではないか?と推察しました。それに対して、Interior.Colorとかは、明示的な構造体・配列を示唆する情報が返ってきません。(print(xx)で見ました。)
なので、とりあえず、Valueは行けるのではないかと思って書いたのが次のソースです。これで、体感数十倍速にはなりました。
残念なことにこの技は、Interior.Colorには使えません。Rangeで一括は、定数では可能です。ですから初期化には使えます。例えば,次のソースです。
ws.UsedRange.Interior.ColorIndex=0 #Clear Colors
VBAでは、UnionでプログラムでRangeに追加できますが、Pythonではそうしたことが可能になる例は見つけられませんでした。やむを得ず、色付の場合、一個ずづ処理するので時間がかかりますよ。いいですか? のメッセージを出すのが精一杯ということになります。(無理やりPythonでやろうとすれば、Pythonソースをダイナミックに生成することになると思いますが..)
さて、求解すると次のところで停止します。pdb.set_trace()の次の行で停止します。
デバッグしたい開始点に、この行を設置することになります。ソースエディタで設定したブレークポイントは、set_traceで読み込まれます。また、当該エディタ上で停止しているときに設置・解除することが出来ます。実行ボタンを押すと進みます。
色なしを選択した場合、2-3秒
xl.Visible = True#False makes debug harder..
FalseにするとExcelが現れません。で、プログラム開発途中でExcelがプロセスとして残っていたりすると、そのExcelに対して操作が行われます。がExcelそのものは見えないと、原因不明のCOMエラーになったりします。Excelが現れていれば、その辺は、すぐに分かります。Excelが現れて、余計なキー操作でエラーになったりすることはありますが、その事自体は気付けるので、程度が良いです。プロセスが残っているかどうかは、タスクマネージャで見ることが出来ます。起動していないのに存在する!、という場合は、死んでもらってから求解するようにしましょう。
以下、Pythonソースです。ScheduleNurse固有命令sc3...は一切使っていませんが、GUIの集合定義は参照しています。これ位の規模になるとデバッガが欲しくなった次第です。最初は、print()文の実装で済まそうと思っていたのが、やはり欲しくなって155Aから内蔵しています。import os import datetime import locale import ctypes import win32gui import win32com.client import re import sc3 import pdb #--------------------------- xlHairline = 1 xlThin = 2 xlThick = 4 xlMedium = -4138 # ------------------------------------------------------------------ # Excel Enum XlFileFormat # ------------------------------------------------------------------ xlCSV = 6 xlHtml = 44 xlWorkbookDefault = 51 xlOpenXMLWorkbook = 51 xlOpenXMLWorkbookMacroEnabled = 52 xlWorkbookNormal = -4143 xlCurrentPlatformText = -4158 # ------------------------------------------------------------------ # Excel Enum XlLineStyle # ------------------------------------------------------------------ xlContinuous = 1 xlDashDot = 4 xlDashDotDot = 5 xlSlantDashDot = 13 xlDash = -4115 xldot = -4118 xlDouble = -4119 xlLineStyleNone = -4142 # ------------------------------------------------------------------ # Excel Enum XlOrientation # ------------------------------------------------------------------ xlHorizontal = -4128 xlVertical = -4166 xlDownward = -4170 xlUpward = -4171 # ------------------------------------------------------------------ #https://docs.microsoft.com/en-us/office/vba/api/excel.xlformatconditionoperator xlCellValue=1 xlExpression=2 xlBetween=1# Between. Can be used only if two formulas are provided. xlEqual=3# Equal. xlGreater=5# Greater than. xlGreaterEqual=7# Greater than or equal to. xlLess=6# Less than. xlLessEqual=8# Less than or equal to. xlNotBetween=2# Not between. Can be used only if two formulas are provided. xlNotEqual=4# Not equal. xlUnderlineStyleSingle = 2 xlCenter = -4108 MB_OK=0x00000000#The message box contains one push button: OK. This is the default. MB_OKCANCEL=0x00000001#The message box contains two push buttons: OK and Cancel. MB_RETRYCANCEL=0x00000005#The message box contains two push buttons: Retry and Cancel. MB_YESNO=0x00000004#The message box contains two push buttons: Yes and No. MB_YESNOCANCEL=0x00000003#The message box contains three push buttons: Yes, No, and MB_TOPMOST=0x00040000#The message box is created with the WS_EX_TOPMOST window style. IDABORT=3#The Abort button was selected. IDCANCEL=2#The Cancel button was selected. IDCONTINUE=11#The Continue button was selected. IDIGNORE=5#The Ignore button was selected. IDNO=7#The No button was selected. IDOK=1#The OK button was selected. IDRETRY=4#The Retry button was selected. IDTRYAGAIN=10#The Try Again button was selected. IDYES=6#The Yes button was selected. def post_main(): pdb.set_trace() print(locale.getlocale(locale.LC_CTYPE)) print("Hello post main world!") print('\n\n*********Processing Post operation *************\n') phases=len(dayphase_list) # #Launch Excel try : xl = win32com.client.Dispatch("Excel.Application") except: print("can not invoke excel") exit() #Show working xl.Visible = True#False makes debug harder.. os.chdir(project_file_path) file_name=project_file_name+".xlsx"; file=os.path.join(project_file_path,file_name) exists=os.path.exists(file) if exists: wb = xl.Workbooks.Open(file) else: wb=xl.Workbooks.Add() # Excelsheet object ws = wb.Worksheets(1) with_color=False if not exists: day_str=daydef[今月[0]] day=int(day_str[8:])#Day one_month_mode=False if day==1 and len(今月)<=31: one_month_mode=True make_initial_format_improved(ws,one_month_mode) sc_hwnd=get_sc3_handle() print("hwnd=",sc_hwnd) if "Japan" in locale.getlocale(locale.LC_CTYPE)[0]:#('Japanese_Japan', '932')('English_United States', '932') MessageBox = ctypes.windll.user32.MessageBoxW res=MessageBox(sc3_hwnd, '本当に色付き出力にしますか?色付きにするとすごく時間がかかります。', '色付き出力選択', MB_TOPMOST|MB_YESNOCANCEL) else: MessageBox = ctypes.windll.user32.MessageBoxW res=MessageBox(sc3_hwnd, 'Are sure you want the results with colored?\nIt takes far longer time with colored.', 'With/Without Color Selection',MB_TOPMOST|MB_YESNOCANCEL) if res==IDYES:# with_color=True elif res==IDCANCEL:#Cancel wb.SaveAs(f"{os.getcwd()}\\"+file_name, FileFormat = xlOpenXMLWorkbook) wb.Close(True)# True:Save False=Default:Not Save xl.Quit() return else:#No with_color=False make_data_improved(ws,phases,with_color)#Output the SC3 Data on the formatted sheet. if not exists: wb.SaveAs(f"{os.getcwd()}\\"+file_name, FileFormat = xlOpenXMLWorkbook) wb.Close(True)# True:Save False=Default:Not Save # Excel Quit xl.Quit() print('\n\n*********Processed Post Operation.*************\n') def clear(ws): ws.UsedRange.ClearContents() ws.UsedRange.ClearFormats() def set_font(ws,addr,value): ws.Range(addr).Value=value ws.Range(addr).Font.Name = "Yu Gothic UI" # Set Cell font size ws.Range(addr).Font.Size = 14 # Set Bold ws.Range(addr).Font.Bold = True def make_initial_format_improved(ws,one_month_mode=False): if one_month_mode==True: print("one_month mode formatting..") day_str=daydef[今月[0]] day=int(day_str[8:])#Day if day !=1 : print("Fatal Error. 今月 must start with 1.") exit() if len(今月)>31: print("Fatal Error. Excessive 今月 Length.") exit() else: print("nominal mode formatting..") 今月2=list(今月)#Deep Copy if one_month_mode==True: for m in range(31)[len(今月):]: 今月2.append(m+今月[0]) clear(ws) set_font(ws,"D2","Work Schedule") set_font(ws,"AW2","") #ws.Range("AW2").Formula='=Year(E7)' ws.Range("AW2","AY2").Merge() ws.Range("AW2").Font.Underline = xlUnderlineStyleSingle ws.Range("AW1","AY1").Merge() set_font(ws,"AW1",'Year') #ws.Range("AZ2").Formula='=Month(E7)' ws.Range("AZ2").Font.Underline = xlUnderlineStyleSingle ws.Range("AZ2","BB2").Merge() set_font(ws,"AZ1",'Month') ws.Range("AZ2","BB2").Merge() #ws.Range("BC2").Formula='=Month(E7)' ws.Range("BC2").Font.Underline = xlUnderlineStyleSingle ws.Range("BC2","BD2").Merge() set_font(ws,"BC1",'Day') ws.Range("BC1","BD1").Merge() ws.Columns(2).EntireColumn.ColumnWidth = 20 ws.Columns(3).EntireColumn.ColumnWidth = 10 ws.Columns(4).EntireColumn.ColumnWidth = 5 row_start=8 row=row_start col_start=2 col=col_start day_col_offset=3 persons=len(staffdef) days=len(今月2) phases=len(dayphase_list) cols=phases*days day_processed=0 row=row_start-2 #Draw Top Header for day in 今月2: col=col_start+day_col_offset+day_processed for ph in range(phases): ws.Columns(col+ph).EntireColumn.ColumnWidth = 2 if day_processed==0: day_str=daydef[day] #print(day_str,int(day_str[8:])) ws.Range("AW2").Formula='='+day_str[0:4]#Year ws.Range("AZ2").Formula='='+day_str[5:7]#Month ws.Range("BC2").Formula='='+day_str[8:]#Day print(get_relative_addr_str(ws,row-1,col)) ws.Cells(row-1,col).Formula='=Date(AW2,AZ2,BC2)'# ws.Cells(row ,col).Formula='=Day('+get_relative_addr_str(ws,row-1,col)+')' day_color_format2(ws,row,col,-1) merge_and_align(ws,row ,col,row,col+phases-1) ws.Cells(row+1 ,col).Formula='='+get_relative_addr_str(ws,row-1,col) ws.Cells(row+1,col).NumberFormatLocal='aaa' day_color_format2(ws,row+1,col,-2) merge_and_align(ws,row+1,col,row+1,col+phases-1) elif day_processed==phases: ws.Cells(row-1,col).Formula='='+get_relative_addr_str(ws,row-1,col-phases)+'+1' if one_month_mode: ws.Cells(row ,col).Formula='=if($AZ$2 <> Month('+get_relative_addr_str(ws,row-1,col)+'),"",Day('+get_relative_addr_str(ws,row-1,col)+'))' else: ws.Cells(row ,col).Formula='=Day('+get_relative_addr_str(ws,row-1,col)+')' day_color_format2(ws,row,col,-1,one_month_mode) merge_and_align(ws,row ,col,row,col+phases-1) if one_month_mode: #print("") ws.Cells(row+1 ,col).Formula='=if($AZ$2 <> Month('+get_relative_addr_str(ws,row-1,col)+'),"",'+get_relative_addr_str(ws,row-1,col)+')' else: ws.Cells(row+1 ,col).Formula='='+get_relative_addr_str(ws,row-1,col) #'=Day('+get_relative_addr_str(ws,row-1,col)+')' ws.Cells(row+1,col).NumberFormatLocal='aaa' day_color_format2(ws,row+1,col,-2,one_month_mode) merge_and_align(ws,row+1,col,row+1,col+phases-1) else: ws.Range(ws.Cells(row-1,col-phases),ws.Cells(row-1,col-1)).Copy(ws.Range(ws.Cells(row-1,col),ws.Cells(row-1,col))) ws.Range(ws.Cells(row,col-phases),ws.Cells(row+1,col-1)).Copy(ws.Range(ws.Cells(row,col),ws.Cells(row,col))) day_processed +=phases #Draw Person Property and Data Area row=row_start for person in All_Staff: day_processed=0 row =row_start+person*4 if person==All_Staff[0]: #name=staffdef[person] #ws.Cells(row,col_start).Value=name col=col_start 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) for day in 今月2: #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) ws.Range(ws.Cells(row,col),ws.Cells(row+3,col+phases-1)).Borders.LineStyle = xlContinuous ws.Range(ws.Cells(row,col),ws.Cells(row+3,col+phases-1)).Borders.weight=xlThin day_processed +=phases else: data_start_col=col_start data_end_col=data_start_col+day_col_offset+phases*len(今月2)-1 ws.Range(ws.Cells(row-4,data_start_col),ws.Cells(row-1,data_end_col)).Copy(ws.Range(ws.Cells(row,data_start_col),ws.Cells(row,data_start_col))) #Fill in Staff names list2 = [['' for i in range(1)] for j in range(len(All_Staff)*4)]#Constructor #wk=ws.Range(ws.Cells(row_start,col_start),ws.Cells(row_start+4*len(All_Staff)-1,col_start)).Value#Values ->2 dim array print("list2=",len(list2)) #[[],[],[]... for person in All_Staff: list2[person*4][0]=staffdef[person] ws.Range(ws.Cells(row_start,col_start),ws.Cells(row_start+4*len(All_Staff)-1,col_start)).Value=list2#Restore it ws.Rows(5).EntireRow.Hidden = True#Hide intemidiate Results def get_relative_addr_str(ws,row,col): str=ws.Cells(row,col).address str=str.replace('$','') return str def day_color_format2(ws,row,col,row_offset,one_month_mode=False): ws.Cells(row,col).FormatConditions.Delete()#Add(xlCellValue, xlGreaterEqual, '=85') if one_month_mode: ws.Cells(row,col).FormatConditions.Add(xlExpression, xlGreater,'=Month('+get_relative_addr_str(ws,row+row_offset,col)+')>$AZ$2') ws.Cells(row,col).FormatConditions.Add(xlExpression, xlEqual, '=Weekday('+get_relative_addr_str(ws,row+row_offset,col)+')=7') #SAT:7 Sun:1 ws.Cells(row,col).FormatConditions.Add(xlExpression, xlEqual, '=Weekday('+get_relative_addr_str(ws,row+row_offset,col)+')=1') #SAT:7 Sun:1 ws.Cells(row,col).FormatConditions(1).Interior.ColorIndex=2 #White ws.Cells(row,col).FormatConditions(2).Interior.ColorIndex=20 #LightBlue ws.Cells(row,col).FormatConditions(3).Interior.ColorIndex=22 # else: ws.Cells(row,col).FormatConditions.Add(xlExpression, xlEqual, '=Weekday('+get_relative_addr_str(ws,row+row_offset,col)+')=7') #SAT:7 Sun:1 ws.Cells(row,col).FormatConditions.Add(xlExpression, xlEqual, '=Weekday('+get_relative_addr_str(ws,row+row_offset,col)+')=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): print(row_start1,col_start1,row_end1,col_end1) 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 #Sub rountines def get_shift_label(shift,scheduled_shift_label): for key in shift_label_color_map.keys(): if key ==shift: item_array=shift_label_color_map[key] i=0 for item in item_array: if i==0: solution_shift_label=item[0] solution_label_color=item[1] else: if len(scheduled_shift_label)>=1 and item[0]==scheduled_shift_label: return item[0],item[1] i+=1 return solution_shift_label,solution_label_color print("Fatal Error",shift,scheduled_shift_label) exit() def get_task_label(task): if task in task_label_color_map: return task_label_color_map[task][0][0] else: print("Invalid Task", task) exit() def rgb_to_hex(rgb): ''' ws.Cells(1, i).Interior.color uses bgr in hex ''' bgr = (int(rgb[2]),int(rgb[1]),int(rgb[0])) strValue = '%02x%02x%02x' % bgr # print(strValue) iValue = int(strValue, 16) return iValue def get_task_label_color(task_label): if len(task_label)==0 or task_label=='.': return 0xffffff #空白,Dummy DummyDay for item_array in task_label_color_map.values():#task集合から探して for item in item_array:#taskは複数のラベルを持つ task_label_candidate=item[0] if task_label==task_label_candidate:#ラベル名が一致したなら color_str=item[1] rgb = re.findall(r"\d+",color_str) if len(rgb)==3: return rgb_to_hex(rgb) else: return 0xffffff #rgb形式でなければWhiteを返す for item in task_collections_def.values():#無かったらtask aggregatesから探す task_label_candidate=item[0] #if len(task_label)>=1: # print("list",task_label,task_label_candidate) if task_label==task_label_candidate: #print("matched",task_label_candidate) color_str=item[1] rgb = re.findall(r"\d+",color_str) if len(rgb)==3: return rgb_to_hex(rgb) else: return 0xffffff #rgb形式でなければWhiteを返す #print("unmatched",task_label) return 0xffffff #なければWhiteを返す def get_shift_label_color(shift_label): if len(shift_label)==0 or shift_label=='・': return 0xffffff #空白ならWhiteを返す for item_array in shift_label_color_map.values():#task集合から探して for item in item_array:#taskは複数のラベルを持つ shift_label_candidate=item[0] #print("label ",shift_label,shift_label_candidate) if shift_label==shift_label_candidate:#ラベル名が一致したなら color_str=item[1] rgb = re.findall(r"\d+",color_str) if len(rgb)==3: return rgb_to_hex(rgb) else: return 0xffffff #rgb形式でなければWhiteを返す #print(shift_label," line511") for item in shift_collections_def.values():#無かったらtask aggregatesから探す shift_label_candidate=item[0] #if len(task_label)>=1: # print("list",task_label,task_label_candidate) if shift_label==shift_label_candidate: #print("matched",task_label_candidate) color_str=item[1] rgb = re.findall(r"\d+",color_str) if len(rgb)==3: return rgb_to_hex(rgb) else: return 0xffffff #rgb形式でなければWhiteを返す for item in phase_objects_def.values(): shift_label_candidate=item[0] if shift_label==shift_label_candidate: color_str=item[1] rgb = re.findall(r"\d+",color_str) if len(rgb)==3: return rgb_to_hex(rgb) else: return 0xffffff #rgb形式でなければWhiteを返す for item in phase_aggregate_object_def.values(): shift_label_candidate=item[0] if shift_label==shift_label_candidate: color_str=item[1] rgb = re.findall(r"\d+",color_str) if len(rgb)==3: return rgb_to_hex(rgb) else: return 0xffffff #print("unmatched",task_label) return 0xffffff #なければWhiteを返す def get_task_color(task): if len(task)==0 or task=='Dummy' or task=='DummyDay': return 0xffffff if task in task_label_color_map: color_str=task_label_color_map[task][0][1] rgb = re.findall(r"\d+",color_str) if len(rgb)==3: return rgb_to_hex(rgb) else: return 255 + 255*256 + 255*256*256 else: return 255 + 255*256 + 255*256*256 def rgb_str_to_hex(color_str): rgb = re.findall(r"\d+",color_str) if len(rgb)==3: return rgb_to_hex(rgb) else: return 0xffffff def make_data_improved(ws,phases,color_enabled=False): ws.UsedRange.Interior.ColorIndex=0 #Clear Colors day_cols=phases*len(今月) person_rows=4*len(All_Staff) list2_val = [['' for i in range(day_cols)] for j in range(person_rows)]#2 Dim Array Constructor for shift/task list2_color = [[0xfffffff for i in range(day_cols)] for j in range(person_rows)]#2 Dim Array Constructor for label color Default White(0xffffff) for person in All_Staff: staff_name=staffdef[person] c=ws.Range("B1:B151").Find(staff_name) print('Excel Processing Data ',staff_name,c.Row,c.Column) col_offset=3 if person==0: row_start=c.Row col_start=c.Column+col_offset for day in 今月: D=day-制約開始日 #print(day) tph=D*phases #print("day",day) shift_label=shift_schedules[person][day][2] #ws.Cells(c.Row,c.Column+3+col*phases).Value=shift_label#Scheduled Shift list2_val[person*4][tph]=shift_label shift=shift_schedules[person][day][0] shift_color=get_shift_label_color(shift_label) #ws.Cells(c.Row,c.Column+3+col*phases).Interior.Color=shift_color#Scheduled Shift Color list2_color[person*4][tph]=shift_color for m in range(phases): tl=task_schedules[person][day*phases+m][2]#label tl_color=get_task_label_color(tl) #ws.Cells(c.Row+1,c.Column+3+tph+m).Value=tl#task schedule #ws.Cells(c.Row+1,c.Column+3+tph+m).Interior.Color=tl_color list2_val [person*4+1][tph+m] =tl list2_color[person*4+1][tph+m] =tl_color shift=shift_solution[person][day] label_color=get_shift_label(shift,shift_label)#return (label,color_str) #ws.Cells(c.Row+2,c.Column+3+col*phases).Value=label_color[0] #ws.Cells(c.Row+2,c.Column+3+col*phases).Interior.Color=rgb_str_to_hex(label_color[1]) list2_val[person*4+2][tph] =label_color[0] list2_color[person*4+2][tph]=rgb_str_to_hex(label_color[1]) for m in range(phases): task0=task_solution[person][day*phases+m] if len(task0)>=1: t0_color=get_task_color(task0) #ws.Cells(c.Row+3,c.Column+3+tph+m).Interior.Color=t0_color list2_color[person*4+3][tph+m]=t0_color task_label=get_task_label(task0) #ws.Cells(c.Row+3,c.Column+3+tph+m).Value=task_label list2_val[person*4+3][tph+m]=task_label #else: #ws.Cells(c.Row+3,c.Column+3+tph+m).Interior.Color=0xffffff #ws.Cells(c.Row+3,c.Column+3+tph+m).Value="" col_end=col_start+len(今月)*phases-1 ws.Range(ws.Cells(row_start,col_start),ws.Cells(row_start+4*len(All_Staff)-1,col_end)).Value=list2_val #wk=ws.Range(ws.Cells(row_start,col_start),ws.Cells(row_start+4*len(All_Staff)-1,col_end)).Interior.Color#=list2_color #print(len(wk)) if color_enabled: print("Please be patient for 2 min.") for row_person in range(len(list2_color)): for day_col in range(len(list2_color[row_person])): if list2_color[row_person][day_col] !=0xffffff: row=row_start+row_person col=col_start+day_col ws.Cells(row,col).Interior.Color=list2_color[row_person][day_col] def get_sc3_handle(): global sc3_hwnd win32gui.EnumWindows( winEnumHandler, None ) sc3_hwnd=int(int(sc3_hwnd,0)) return sc3_hwnd def winEnumHandler( hwnd, ctx ): if win32gui.IsWindowVisible( hwnd ): text=win32gui.GetWindowText( hwnd ) print (hex(hwnd),text ) if ('スケジュールナース' in text or "Schedule Nurse" in text) and project_file_name in text: global sc3_hwnd sc3_hwnd=hex(hwnd) print("Detected",sc3_hwnd)
0 件のコメント:
コメントを投稿