[{"data":1,"prerenderedAt":2066},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fapply-the-same-change-to-every-sheet-in-a-workbook":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fapply-the-same-change-to-every-sheet-in-a-workbook":2034,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fapply-the-same-change-to-every-sheet-in-a-workbook":2042,"children:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fapply-the-same-change-to-every-sheet-in-a-workbook":2046,"related:\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fapply-the-same-change-to-every-sheet-in-a-workbook":2047},{"id":4,"title":5,"body":6,"dateModified":2001,"datePublished":2001,"description":2002,"extension":2003,"faq":2004,"keyword":2013,"kind":2014,"meta":2015,"navigation":124,"path":2023,"seo":2024,"slug":2027,"stem":2028,"topics":2029,"type":2032,"__hash__":2033},"docs\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fapply-the-same-change-to-every-sheet-in-a-workbook\u002Findex.md","Apply the Same Change to Every Sheet in a Workbook",{"type":7,"value":8,"toc":1989},"minimark",[9,19,24,50,79,83,461,471,610,614,1129,1147,1151,1348,1355,1554,1558,1578,1642,1646,1838,1854,1858,1893,1897,1900,1904,1915,1935,1947,1953,1957,1986],[10,11,12,13,18],"p",{},"A workbook with forty monthly sheets needs the same fix on each one: bold the headings, freeze the top row, show the amounts with thousands separators, put the sheet name and page number in the footer. In Excel that is forty repetitions — or grouping the sheets and hoping nothing goes wrong, since grouped edits silently apply to every selected sheet, including the ones that should have been left alone. With openpyxl it is one loop. The interesting part is not the loop but the guards around it: workbooks contain chart sheets, hidden lookup sheets and the odd summary sheet with a different layout, and a change applied blindly to those breaks them. This page applies a set of changes to every suitable sheet, skips the rest with a reason, logs what happened on each sheet, and makes the whole run safe to repeat. It belongs to ",[14,15,17],"a",{"href":16},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002F","Working with Multiple Excel Sheets in Python",".",[20,21,23],"h2",{"id":22},"prerequisites","Prerequisites",[25,26,31],"pre",{"className":27,"code":28,"language":29,"meta":30,"style":30},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl\n","bash","",[32,33,34],"code",{"__ignoreMap":30},[35,36,39,43,47],"span",{"class":37,"line":38},"line",1,[35,40,42],{"class":41},"sMTad","pip",[35,44,46],{"class":45},"srMev"," install",[35,48,49],{"class":45}," openpyxl\n",[10,51,52,53,57,58,57,61,57,64,57,67,70,71,74,75,78],{},"The workbook has nine monthly sheets with the same columns — ",[54,55,56],"em",{},"Date",", ",[54,59,60],{},"Region",[54,62,63],{},"Units",[54,65,66],{},"Revenue",[54,68,69],{},"Margin %"," — plus a ",[54,72,73],{},"Summary"," sheet with a different layout, a hidden ",[54,76,77],{},"Lookup"," sheet and a chart sheet. The changes should reach the nine monthly sheets and nothing else.",[20,80,82],{"id":81},"step-1-decide-which-sheets-to-touch","Step 1 — Decide which sheets to touch",[25,84,88],{"className":85,"code":86,"language":87,"meta":30,"style":30},"language-python shiki shiki-themes github-light github-dark-high-contrast","from openpyxl import load_workbook\nfrom openpyxl.worksheet.worksheet import Worksheet\n\nEXPECTED = [\"Date\", \"Region\", \"Units\", \"Revenue\", \"Margin %\"]\n\ndef eligible(sheet):\n    if not isinstance(sheet, Worksheet):\n        return False, \"chart sheet\"\n    if sheet.sheet_state != \"visible\":\n        return False, f\"{sheet.sheet_state} sheet\"\n    headers = [c.value for c in sheet[1]]\n    if headers[:len(EXPECTED)] != EXPECTED:\n        return False, f\"unexpected headings {headers[:3]}…\"\n    return True, \"\"\n\nwb = load_workbook(\"monthly_sales.xlsx\")\nfor s in wb.worksheets + wb.chartsheets:\n    ok, why = eligible(s)\n    print(f\"{s.title:10} {'apply' if ok else 'skip: ' + why}\")\n","python",[32,89,90,106,119,126,165,170,183,198,212,229,257,287,313,343,357,362,379,398,409],{"__ignoreMap":30},[35,91,92,96,100,103],{"class":37,"line":38},[35,93,95],{"class":94},"s-kum","from",[35,97,99],{"class":98},"skGVy"," openpyxl ",[35,101,102],{"class":94},"import",[35,104,105],{"class":98}," load_workbook\n",[35,107,109,111,114,116],{"class":37,"line":108},2,[35,110,95],{"class":94},[35,112,113],{"class":98}," openpyxl.worksheet.worksheet ",[35,115,102],{"class":94},[35,117,118],{"class":98}," Worksheet\n",[35,120,122],{"class":37,"line":121},3,[35,123,125],{"emptyLinePlaceholder":124},true,"\n",[35,127,129,133,136,139,142,144,147,149,152,154,157,159,162],{"class":37,"line":128},4,[35,130,132],{"class":131},"sP0c6","EXPECTED",[35,134,135],{"class":94}," =",[35,137,138],{"class":98}," [",[35,140,141],{"class":45},"\"Date\"",[35,143,57],{"class":98},[35,145,146],{"class":45},"\"Region\"",[35,148,57],{"class":98},[35,150,151],{"class":45},"\"Units\"",[35,153,57],{"class":98},[35,155,156],{"class":45},"\"Revenue\"",[35,158,57],{"class":98},[35,160,161],{"class":45},"\"Margin %\"",[35,163,164],{"class":98},"]\n",[35,166,168],{"class":37,"line":167},5,[35,169,125],{"emptyLinePlaceholder":124},[35,171,173,176,180],{"class":37,"line":172},6,[35,174,175],{"class":94},"def",[35,177,179],{"class":178},"s_Opv"," eligible",[35,181,182],{"class":98},"(sheet):\n",[35,184,186,189,192,195],{"class":37,"line":185},7,[35,187,188],{"class":94},"    if",[35,190,191],{"class":94}," not",[35,193,194],{"class":131}," isinstance",[35,196,197],{"class":98},"(sheet, Worksheet):\n",[35,199,201,204,207,209],{"class":37,"line":200},8,[35,202,203],{"class":94},"        return",[35,205,206],{"class":131}," False",[35,208,57],{"class":98},[35,210,211],{"class":45},"\"chart sheet\"\n",[35,213,215,217,220,223,226],{"class":37,"line":214},9,[35,216,188],{"class":94},[35,218,219],{"class":98}," sheet.sheet_state ",[35,221,222],{"class":94},"!=",[35,224,225],{"class":45}," \"visible\"",[35,227,228],{"class":98},":\n",[35,230,232,234,236,238,241,244,248,251,254],{"class":37,"line":231},10,[35,233,203],{"class":94},[35,235,206],{"class":131},[35,237,57],{"class":98},[35,239,240],{"class":94},"f",[35,242,243],{"class":45},"\"",[35,245,247],{"class":246},"sSjpA","{",[35,249,250],{"class":98},"sheet.sheet_state",[35,252,253],{"class":246},"}",[35,255,256],{"class":45}," sheet\"\n",[35,258,260,263,266,269,272,275,278,281,284],{"class":37,"line":259},11,[35,261,262],{"class":98},"    headers ",[35,264,265],{"class":94},"=",[35,267,268],{"class":98}," [c.value ",[35,270,271],{"class":94},"for",[35,273,274],{"class":98}," c ",[35,276,277],{"class":94},"in",[35,279,280],{"class":98}," sheet[",[35,282,283],{"class":131},"1",[35,285,286],{"class":98},"]]\n",[35,288,290,292,295,298,301,303,306,308,311],{"class":37,"line":289},12,[35,291,188],{"class":94},[35,293,294],{"class":98}," headers[:",[35,296,297],{"class":131},"len",[35,299,300],{"class":98},"(",[35,302,132],{"class":131},[35,304,305],{"class":98},")] ",[35,307,222],{"class":94},[35,309,310],{"class":131}," EXPECTED",[35,312,228],{"class":98},[35,314,316,318,320,322,324,327,329,332,335,338,340],{"class":37,"line":315},13,[35,317,203],{"class":94},[35,319,206],{"class":131},[35,321,57],{"class":98},[35,323,240],{"class":94},[35,325,326],{"class":45},"\"unexpected headings ",[35,328,247],{"class":246},[35,330,331],{"class":98},"headers[:",[35,333,334],{"class":131},"3",[35,336,337],{"class":98},"]",[35,339,253],{"class":246},[35,341,342],{"class":45},"…\"\n",[35,344,346,349,352,354],{"class":37,"line":345},14,[35,347,348],{"class":94},"    return",[35,350,351],{"class":131}," True",[35,353,57],{"class":98},[35,355,356],{"class":45},"\"\"\n",[35,358,360],{"class":37,"line":359},15,[35,361,125],{"emptyLinePlaceholder":124},[35,363,365,368,370,373,376],{"class":37,"line":364},16,[35,366,367],{"class":98},"wb ",[35,369,265],{"class":94},[35,371,372],{"class":98}," load_workbook(",[35,374,375],{"class":45},"\"monthly_sales.xlsx\"",[35,377,378],{"class":98},")\n",[35,380,382,384,387,389,392,395],{"class":37,"line":381},17,[35,383,271],{"class":94},[35,385,386],{"class":98}," s ",[35,388,277],{"class":94},[35,390,391],{"class":98}," wb.worksheets ",[35,393,394],{"class":94},"+",[35,396,397],{"class":98}," wb.chartsheets:\n",[35,399,401,404,406],{"class":37,"line":400},18,[35,402,403],{"class":98},"    ok, why ",[35,405,265],{"class":94},[35,407,408],{"class":98}," eligible(s)\n",[35,410,412,415,417,419,421,423,426,429,431,434,437,440,443,446,449,452,455,457,459],{"class":37,"line":411},19,[35,413,414],{"class":131},"    print",[35,416,300],{"class":98},[35,418,240],{"class":94},[35,420,243],{"class":45},[35,422,247],{"class":246},[35,424,425],{"class":98},"s.title",[35,427,428],{"class":94},":10",[35,430,253],{"class":246},[35,432,433],{"class":246}," {",[35,435,436],{"class":45},"'apply'",[35,438,439],{"class":94}," if",[35,441,442],{"class":98}," ok ",[35,444,445],{"class":94},"else",[35,447,448],{"class":45}," 'skip: '",[35,450,451],{"class":94}," +",[35,453,454],{"class":98}," why",[35,456,253],{"class":246},[35,458,243],{"class":45},[35,460,378],{"class":98},[10,462,463,466,467,470],{},[32,464,465],{},"wb.worksheets"," contains only ordinary sheets; chart sheets live in ",[32,468,469],{},"wb.chartsheets",", and they have no cells, so any cell operation on them would fail. Checking the type explicitly keeps the rule in one place. Hidden and very hidden sheets are skipped because they are usually lookups or calculations that should not be restyled. The most useful guard is the last one: a sheet is changed only if its first row has the expected headings. The summary sheet, which starts with three blank rows and has different columns, is left alone — exactly the sheet that would have been damaged by a grouped edit in Excel.",[472,473,481,482,481,486,481,490,481,498,481,508,481,515,481,520,481,524,481,530,481,535,481,540,481,546,481,550,481,554,481,557,481,560,481,564,481,566,481,570,481,573,481,576,481,580,481,584,481,587,481,591,481,594,481,599,481,604,481,607],"svg",{"viewBox":474,"role":475,"ariaLabelledBy":476,"xmlns":479,"style":480},"4 0 752 108","img",[477,478],"every-guard-t","every-guard-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[483,484,485],"title",{"id":477},"Which sheets get the change",[487,488,489],"desc",{"id":478},"Every sheet is checked in turn. Chart sheets are skipped because they have no cells, hidden sheets are skipped because they are lookups or calculations, and sheets whose first row does not have the expected headings are skipped. The remaining sheets get the change.",[491,492],"rect",{"x":493,"y":494,"width":495,"height":496,"fill":497},"4","0","752","108","#ffffff",[491,499],{"x":500,"y":501,"width":502,"height":503,"rx":504,"fill":505,"stroke":506,"style":507},"20.0","16.0","115.2","76.5","12","#e7ebef","var(--line,#cdd5e6)","stroke-width:2px",[509,510,514],"text",{"x":511,"y":512,"style":513},"77.6","41.5","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Every sheet",[509,516,519],{"x":511,"y":517,"style":518},"61.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","worksheets +",[509,521,523],{"x":511,"y":522,"style":518},"78.5","chartsheets",[37,525],{"x1":526,"y1":527,"x2":528,"y2":527,"stroke":529,"style":507},"140.2","54.2","162.2","var(--brand,#5b5cf0)",[531,532],"polygon",{"points":533,"fill":534},"166.2,54.2 157.2,59.2 157.2,49.2","#5b5cf0",[491,536],{"x":537,"y":501,"width":502,"height":503,"rx":504,"fill":538,"stroke":539,"style":507},"171.2","#fdefd8","var(--gold,#b4740a)",[509,541,545],{"x":542,"y":543,"style":544},"228.8","50.2","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Chart sheet?",[509,547,549],{"x":542,"y":548,"style":518},"69.8","skip",[37,551],{"x1":552,"y1":527,"x2":553,"y2":527,"stroke":529,"style":507},"291.4","313.4",[531,555],{"points":556,"fill":534},"317.4,54.2 308.4,59.2 308.4,49.2",[491,558],{"x":559,"y":501,"width":502,"height":503,"rx":504,"fill":538,"stroke":539,"style":507},"322.4",[509,561,563],{"x":562,"y":543,"style":544},"380.0","Hidden?",[509,565,549],{"x":562,"y":548,"style":518},[37,567],{"x1":568,"y1":527,"x2":569,"y2":527,"stroke":529,"style":507},"442.6","464.6",[531,571],{"points":572,"fill":534},"468.6,54.2 459.6,59.2 459.6,49.2",[491,574],{"x":575,"y":501,"width":502,"height":503,"rx":504,"fill":538,"stroke":539,"style":507},"473.6",[509,577,579],{"x":578,"y":512,"style":544},"531.2","Expected",[509,581,583],{"x":578,"y":582,"style":544},"59.0","headings?",[509,585,586],{"x":578,"y":522,"style":518},"no → skip",[37,588],{"x1":589,"y1":527,"x2":590,"y2":527,"stroke":529,"style":507},"593.8","615.8",[531,592],{"points":593,"fill":534},"619.8,54.2 610.8,59.2 610.8,49.2",[491,595],{"x":596,"y":501,"width":502,"height":503,"rx":504,"fill":597,"stroke":598,"style":507},"624.8","#d9f4f1","var(--teal,#0f9488)",[509,600,603],{"x":601,"y":512,"style":602},"682.4","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Apply the",[509,605,606],{"x":601,"y":582,"style":602},"change",[509,608,609],{"x":601,"y":522,"style":518},"and log it",[20,611,613],{"id":612},"step-2-the-change-itself-written-once","Step 2 — The change itself, written once",[25,615,617],{"className":85,"code":616,"language":87,"meta":30,"style":30},"from openpyxl.styles import Alignment, Border, Font, PatternFill, Side\n\nFORMATS = {\"Date\": \"dd mmm yyyy\", \"Units\": \"#,##0\", \"Revenue\": \"£#,##0.00\", \"Margin %\": \"0.0%\"}\nheader_font, header_fill = Font(bold=True, color=\"FFFFFF\"), PatternFill(\"solid\", fgColor=\"1F3864\")\nrule = Border(bottom=Side(style=\"thin\", color=\"1F3864\"))\n\ndef standardise(ws):\n    changes = []\n    for cell in ws[1]:\n        cell.font, cell.fill, cell.border = header_font, header_fill, rule\n        cell.alignment = Alignment(horizontal=\"center\", vertical=\"center\")\n    changes.append(\"header styled\")\n    if ws.freeze_panes != \"A2\":\n        ws.freeze_panes = \"A2\"\n        changes.append(\"top row frozen\")\n    for cell in ws[1]:\n        fmt = FORMATS.get(cell.value)\n        if fmt:\n            for (c,) in ws.iter_rows(min_row=2, min_col=cell.column, max_col=cell.column):\n                c.number_format = fmt\n            changes.append(f\"{cell.value} → {fmt}\")\n        ws.column_dimensions[cell.column_letter].width = max(12, len(str(cell.value)) + 4)\n    ws.oddFooter.left.text = \"&A\"                       # sheet name\n    ws.oddFooter.right.text = \"Page &P of &N\"\n    ws.page_setup.orientation = \"landscape\"\n    ws.sheet_properties.pageSetUpPr.fitToPage = True\n    ws.page_setup.fitToHeight = 0\n    ws.print_title_rows = \"1:1\"\n    changes.append(\"footer and print setup\")\n    return changes\n",[32,618,619,631,635,682,729,766,770,780,790,808,818,847,857,871,881,891,905,918,926,965,976,1007,1041,1056,1067,1078,1089,1100,1111,1121],{"__ignoreMap":30},[35,620,621,623,626,628],{"class":37,"line":38},[35,622,95],{"class":94},[35,624,625],{"class":98}," openpyxl.styles ",[35,627,102],{"class":94},[35,629,630],{"class":98}," Alignment, Border, Font, PatternFill, Side\n",[35,632,633],{"class":37,"line":108},[35,634,125],{"emptyLinePlaceholder":124},[35,636,637,640,642,644,646,649,652,654,656,658,661,663,665,667,670,672,674,676,679],{"class":37,"line":121},[35,638,639],{"class":131},"FORMATS",[35,641,135],{"class":94},[35,643,433],{"class":98},[35,645,141],{"class":45},[35,647,648],{"class":98},": ",[35,650,651],{"class":45},"\"dd mmm yyyy\"",[35,653,57],{"class":98},[35,655,151],{"class":45},[35,657,648],{"class":98},[35,659,660],{"class":45},"\"#,##0\"",[35,662,57],{"class":98},[35,664,156],{"class":45},[35,666,648],{"class":98},[35,668,669],{"class":45},"\"£#,##0.00\"",[35,671,57],{"class":98},[35,673,161],{"class":45},[35,675,648],{"class":98},[35,677,678],{"class":45},"\"0.0%\"",[35,680,681],{"class":98},"}\n",[35,683,684,687,689,692,696,698,701,703,706,708,711,714,717,719,722,724,727],{"class":37,"line":128},[35,685,686],{"class":98},"header_font, header_fill ",[35,688,265],{"class":94},[35,690,691],{"class":98}," Font(",[35,693,695],{"class":694},"sa561","bold",[35,697,265],{"class":94},[35,699,700],{"class":131},"True",[35,702,57],{"class":98},[35,704,705],{"class":694},"color",[35,707,265],{"class":94},[35,709,710],{"class":45},"\"FFFFFF\"",[35,712,713],{"class":98},"), PatternFill(",[35,715,716],{"class":45},"\"solid\"",[35,718,57],{"class":98},[35,720,721],{"class":694},"fgColor",[35,723,265],{"class":94},[35,725,726],{"class":45},"\"1F3864\"",[35,728,378],{"class":98},[35,730,731,734,736,739,742,744,747,750,752,755,757,759,761,763],{"class":37,"line":167},[35,732,733],{"class":98},"rule ",[35,735,265],{"class":94},[35,737,738],{"class":98}," Border(",[35,740,741],{"class":694},"bottom",[35,743,265],{"class":94},[35,745,746],{"class":98},"Side(",[35,748,749],{"class":694},"style",[35,751,265],{"class":94},[35,753,754],{"class":45},"\"thin\"",[35,756,57],{"class":98},[35,758,705],{"class":694},[35,760,265],{"class":94},[35,762,726],{"class":45},[35,764,765],{"class":98},"))\n",[35,767,768],{"class":37,"line":172},[35,769,125],{"emptyLinePlaceholder":124},[35,771,772,774,777],{"class":37,"line":185},[35,773,175],{"class":94},[35,775,776],{"class":178}," standardise",[35,778,779],{"class":98},"(ws):\n",[35,781,782,785,787],{"class":37,"line":200},[35,783,784],{"class":98},"    changes ",[35,786,265],{"class":94},[35,788,789],{"class":98}," []\n",[35,791,792,795,798,800,803,805],{"class":37,"line":214},[35,793,794],{"class":94},"    for",[35,796,797],{"class":98}," cell ",[35,799,277],{"class":94},[35,801,802],{"class":98}," ws[",[35,804,283],{"class":131},[35,806,807],{"class":98},"]:\n",[35,809,810,813,815],{"class":37,"line":231},[35,811,812],{"class":98},"        cell.font, cell.fill, cell.border ",[35,814,265],{"class":94},[35,816,817],{"class":98}," header_font, header_fill, rule\n",[35,819,820,823,825,828,831,833,836,838,841,843,845],{"class":37,"line":259},[35,821,822],{"class":98},"        cell.alignment ",[35,824,265],{"class":94},[35,826,827],{"class":98}," Alignment(",[35,829,830],{"class":694},"horizontal",[35,832,265],{"class":94},[35,834,835],{"class":45},"\"center\"",[35,837,57],{"class":98},[35,839,840],{"class":694},"vertical",[35,842,265],{"class":94},[35,844,835],{"class":45},[35,846,378],{"class":98},[35,848,849,852,855],{"class":37,"line":289},[35,850,851],{"class":98},"    changes.append(",[35,853,854],{"class":45},"\"header styled\"",[35,856,378],{"class":98},[35,858,859,861,864,866,869],{"class":37,"line":315},[35,860,188],{"class":94},[35,862,863],{"class":98}," ws.freeze_panes ",[35,865,222],{"class":94},[35,867,868],{"class":45}," \"A2\"",[35,870,228],{"class":98},[35,872,873,876,878],{"class":37,"line":345},[35,874,875],{"class":98},"        ws.freeze_panes ",[35,877,265],{"class":94},[35,879,880],{"class":45}," \"A2\"\n",[35,882,883,886,889],{"class":37,"line":359},[35,884,885],{"class":98},"        changes.append(",[35,887,888],{"class":45},"\"top row frozen\"",[35,890,378],{"class":98},[35,892,893,895,897,899,901,903],{"class":37,"line":364},[35,894,794],{"class":94},[35,896,797],{"class":98},[35,898,277],{"class":94},[35,900,802],{"class":98},[35,902,283],{"class":131},[35,904,807],{"class":98},[35,906,907,910,912,915],{"class":37,"line":381},[35,908,909],{"class":98},"        fmt ",[35,911,265],{"class":94},[35,913,914],{"class":131}," FORMATS",[35,916,917],{"class":98},".get(cell.value)\n",[35,919,920,923],{"class":37,"line":400},[35,921,922],{"class":94},"        if",[35,924,925],{"class":98}," fmt:\n",[35,927,928,931,934,936,939,942,944,947,949,952,954,957,960,962],{"class":37,"line":411},[35,929,930],{"class":94},"            for",[35,932,933],{"class":98}," (c,) ",[35,935,277],{"class":94},[35,937,938],{"class":98}," ws.iter_rows(",[35,940,941],{"class":694},"min_row",[35,943,265],{"class":94},[35,945,946],{"class":131},"2",[35,948,57],{"class":98},[35,950,951],{"class":694},"min_col",[35,953,265],{"class":94},[35,955,956],{"class":98},"cell.column, ",[35,958,959],{"class":694},"max_col",[35,961,265],{"class":94},[35,963,964],{"class":98},"cell.column):\n",[35,966,968,971,973],{"class":37,"line":967},20,[35,969,970],{"class":98},"                c.number_format ",[35,972,265],{"class":94},[35,974,975],{"class":98}," fmt\n",[35,977,979,982,984,986,988,991,993,996,998,1001,1003,1005],{"class":37,"line":978},21,[35,980,981],{"class":98},"            changes.append(",[35,983,240],{"class":94},[35,985,243],{"class":45},[35,987,247],{"class":246},[35,989,990],{"class":98},"cell.value",[35,992,253],{"class":246},[35,994,995],{"class":45}," → ",[35,997,247],{"class":246},[35,999,1000],{"class":98},"fmt",[35,1002,253],{"class":246},[35,1004,243],{"class":45},[35,1006,378],{"class":98},[35,1008,1010,1013,1015,1018,1020,1022,1024,1026,1028,1031,1034,1036,1039],{"class":37,"line":1009},22,[35,1011,1012],{"class":98},"        ws.column_dimensions[cell.column_letter].width ",[35,1014,265],{"class":94},[35,1016,1017],{"class":131}," max",[35,1019,300],{"class":98},[35,1021,504],{"class":131},[35,1023,57],{"class":98},[35,1025,297],{"class":131},[35,1027,300],{"class":98},[35,1029,1030],{"class":131},"str",[35,1032,1033],{"class":98},"(cell.value)) ",[35,1035,394],{"class":94},[35,1037,1038],{"class":131}," 4",[35,1040,378],{"class":98},[35,1042,1044,1047,1049,1052],{"class":37,"line":1043},23,[35,1045,1046],{"class":98},"    ws.oddFooter.left.text ",[35,1048,265],{"class":94},[35,1050,1051],{"class":45}," \"&A\"",[35,1053,1055],{"class":1054},"s-wDw","                       # sheet name\n",[35,1057,1059,1062,1064],{"class":37,"line":1058},24,[35,1060,1061],{"class":98},"    ws.oddFooter.right.text ",[35,1063,265],{"class":94},[35,1065,1066],{"class":45}," \"Page &P of &N\"\n",[35,1068,1070,1073,1075],{"class":37,"line":1069},25,[35,1071,1072],{"class":98},"    ws.page_setup.orientation ",[35,1074,265],{"class":94},[35,1076,1077],{"class":45}," \"landscape\"\n",[35,1079,1081,1084,1086],{"class":37,"line":1080},26,[35,1082,1083],{"class":98},"    ws.sheet_properties.pageSetUpPr.fitToPage ",[35,1085,265],{"class":94},[35,1087,1088],{"class":131}," True\n",[35,1090,1092,1095,1097],{"class":37,"line":1091},27,[35,1093,1094],{"class":98},"    ws.page_setup.fitToHeight ",[35,1096,265],{"class":94},[35,1098,1099],{"class":131}," 0\n",[35,1101,1103,1106,1108],{"class":37,"line":1102},28,[35,1104,1105],{"class":98},"    ws.print_title_rows ",[35,1107,265],{"class":94},[35,1109,1110],{"class":45}," \"1:1\"\n",[35,1112,1114,1116,1119],{"class":37,"line":1113},29,[35,1115,851],{"class":98},[35,1117,1118],{"class":45},"\"footer and print setup\"",[35,1120,378],{"class":98},[35,1122,1124,1126],{"class":37,"line":1123},30,[35,1125,348],{"class":94},[35,1127,1128],{"class":98}," changes\n",[10,1130,1131,1132,1135,1136,1138,1139,1142,1143,1146],{},"All the changes live in one function that takes a worksheet and returns a list of what it did. Number formats are chosen by column ",[54,1133,1134],{},"heading",", not by letter, so a sheet where ",[54,1137,63],{}," sits in a different column still gets the right format. The print setup — landscape, fit to one page wide, heading row repeated on every page — and a footer with the sheet name (",[32,1140,1141],{},"&A",") and page numbers (",[32,1144,1145],{},"&P of &N",") make every month print the same way. Keeping the change in a function also makes it testable on its own, and reusable for a single sheet created later — next month's sheet can be standardised the moment it is added.",[20,1148,1150],{"id":1149},"step-3-apply-log-and-save-to-a-new-file","Step 3 — Apply, log and save to a new file",[25,1152,1154],{"className":85,"code":1153,"language":87,"meta":30,"style":30},"log = []\nfor ws in wb.worksheets:\n    ok, why = eligible(ws)\n    if not ok:\n        log.append({\"Sheet\": ws.title, \"Result\": \"skipped\", \"Detail\": why})\n        continue\n    log.append({\"Sheet\": ws.title, \"Result\": \"changed\", \"Detail\": \"; \".join(standardise(ws))})\nfor cs in wb.chartsheets:\n    log.append({\"Sheet\": cs.title, \"Result\": \"skipped\", \"Detail\": \"chart sheet\"})\nwb.save(\"monthly_sales_standardised.xlsx\")\n\nimport pandas as pd\nprint(pd.DataFrame(log).to_string(index=False, max_colwidth=70))\n",[32,1155,1156,1165,1177,1186,1195,1222,1227,1255,1266,1293,1303,1307,1320],{"__ignoreMap":30},[35,1157,1158,1161,1163],{"class":37,"line":38},[35,1159,1160],{"class":98},"log ",[35,1162,265],{"class":94},[35,1164,789],{"class":98},[35,1166,1167,1169,1172,1174],{"class":37,"line":108},[35,1168,271],{"class":94},[35,1170,1171],{"class":98}," ws ",[35,1173,277],{"class":94},[35,1175,1176],{"class":98}," wb.worksheets:\n",[35,1178,1179,1181,1183],{"class":37,"line":121},[35,1180,403],{"class":98},[35,1182,265],{"class":94},[35,1184,1185],{"class":98}," eligible(ws)\n",[35,1187,1188,1190,1192],{"class":37,"line":128},[35,1189,188],{"class":94},[35,1191,191],{"class":94},[35,1193,1194],{"class":98}," ok:\n",[35,1196,1197,1200,1203,1206,1209,1211,1214,1216,1219],{"class":37,"line":167},[35,1198,1199],{"class":98},"        log.append({",[35,1201,1202],{"class":45},"\"Sheet\"",[35,1204,1205],{"class":98},": ws.title, ",[35,1207,1208],{"class":45},"\"Result\"",[35,1210,648],{"class":98},[35,1212,1213],{"class":45},"\"skipped\"",[35,1215,57],{"class":98},[35,1217,1218],{"class":45},"\"Detail\"",[35,1220,1221],{"class":98},": why})\n",[35,1223,1224],{"class":37,"line":172},[35,1225,1226],{"class":94},"        continue\n",[35,1228,1229,1232,1234,1236,1238,1240,1243,1245,1247,1249,1252],{"class":37,"line":185},[35,1230,1231],{"class":98},"    log.append({",[35,1233,1202],{"class":45},[35,1235,1205],{"class":98},[35,1237,1208],{"class":45},[35,1239,648],{"class":98},[35,1241,1242],{"class":45},"\"changed\"",[35,1244,57],{"class":98},[35,1246,1218],{"class":45},[35,1248,648],{"class":98},[35,1250,1251],{"class":45},"\"; \"",[35,1253,1254],{"class":98},".join(standardise(ws))})\n",[35,1256,1257,1259,1262,1264],{"class":37,"line":200},[35,1258,271],{"class":94},[35,1260,1261],{"class":98}," cs ",[35,1263,277],{"class":94},[35,1265,397],{"class":98},[35,1267,1268,1270,1272,1275,1277,1279,1281,1283,1285,1287,1290],{"class":37,"line":214},[35,1269,1231],{"class":98},[35,1271,1202],{"class":45},[35,1273,1274],{"class":98},": cs.title, ",[35,1276,1208],{"class":45},[35,1278,648],{"class":98},[35,1280,1213],{"class":45},[35,1282,57],{"class":98},[35,1284,1218],{"class":45},[35,1286,648],{"class":98},[35,1288,1289],{"class":45},"\"chart sheet\"",[35,1291,1292],{"class":98},"})\n",[35,1294,1295,1298,1301],{"class":37,"line":231},[35,1296,1297],{"class":98},"wb.save(",[35,1299,1300],{"class":45},"\"monthly_sales_standardised.xlsx\"",[35,1302,378],{"class":98},[35,1304,1305],{"class":37,"line":259},[35,1306,125],{"emptyLinePlaceholder":124},[35,1308,1309,1311,1314,1317],{"class":37,"line":289},[35,1310,102],{"class":94},[35,1312,1313],{"class":98}," pandas ",[35,1315,1316],{"class":94},"as",[35,1318,1319],{"class":98}," pd\n",[35,1321,1322,1325,1328,1331,1333,1336,1338,1341,1343,1346],{"class":37,"line":315},[35,1323,1324],{"class":131},"print",[35,1326,1327],{"class":98},"(pd.DataFrame(log).to_string(",[35,1329,1330],{"class":694},"index",[35,1332,265],{"class":94},[35,1334,1335],{"class":131},"False",[35,1337,57],{"class":98},[35,1339,1340],{"class":694},"max_colwidth",[35,1342,265],{"class":94},[35,1344,1345],{"class":131},"70",[35,1347,765],{"class":98},[10,1349,1350,1351,1354],{},"The loop goes through the sheets, applies the function where the guards allow and records a line for every sheet either way. The log answers the question someone will ask later — ",[54,1352,1353],{},"did the change reach every month?"," — and it shows the skipped sheets with their reason, so a monthly sheet that was skipped because someone retyped a heading is noticed rather than silently left unformatted. The result is saved to a new file, which keeps the original as a fallback; overwriting in place is only safe once the script has been run against a copy and checked.",[472,1356,481,1362,481,1365,481,1368,481,1373,481,1379,481,1383,481,1389,481,1393,481,1397,481,1401,481,1405,481,1409,481,1412,481,1416,481,1420,481,1422,481,1425,481,1427,481,1430,481,1433,481,1436,481,1438,481,1441,481,1443,481,1447,481,1449,481,1452,481,1455,481,1458,481,1460,481,1463,481,1465,481,1468,481,1470,481,1473,481,1476,481,1479,481,1481,481,1484,481,1486,481,1488,481,1490,481,1493,481,1496,481,1500,481,1502,481,1505,481,1507,481,1509,481,1511,481,1513,481,1516,481,1520,481,1522,481,1524,481,1526,481,1528,481,1530,481,1533,481,1536,481,1540,481,1542,481,1545,481,1547,481,1549,481,1551],{"viewBox":1357,"role":475,"ariaLabelledBy":1358,"xmlns":479,"style":1361},"154 0 452 252",[1359,1360],"every-log-t","every-log-d","width:100%;max-width:452px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[483,1363,1364],{"id":1359},"The change log",[487,1366,1367],{"id":1360},"Each of the nine monthly sheets is changed — header styled, top row frozen, four number formats set and the footer and print setup added. The Summary sheet is skipped for unexpected headings, the Lookup sheet because it is hidden, and the Chart sheet because it is a chart sheet.",[491,1369],{"x":1370,"y":494,"width":1371,"height":1372,"fill":497},"154","452","252",[491,1374],{"x":1375,"y":501,"width":1376,"height":1377,"rx":494,"fill":505,"stroke":506,"style":1378},"170.3","32.0","24.0","stroke-width:1px",[491,1380],{"x":1381,"y":501,"width":1382,"height":1377,"rx":494,"fill":505,"stroke":506,"style":1378},"202.3","83.2",[509,1384,1388],{"x":1385,"y":1386,"style":1387},"243.9","33.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[491,1390],{"x":1391,"y":501,"width":1392,"height":1377,"rx":494,"fill":505,"stroke":506,"style":1378},"285.5","77.8",[509,1394,1396],{"x":1395,"y":1386,"style":1387},"324.5","B",[491,1398],{"x":1399,"y":501,"width":1400,"height":1377,"rx":494,"fill":505,"stroke":506,"style":1378},"363.4","226.3",[509,1402,1404],{"x":1403,"y":1386,"style":1387},"476.5","C",[491,1406],{"x":1375,"y":1407,"width":1376,"height":1408,"rx":494,"fill":505,"stroke":506,"style":1378},"40.0","28.0",[509,1410,283],{"x":1411,"y":582,"style":1387},"186.3",[491,1413],{"x":1381,"y":1407,"width":1382,"height":1408,"rx":494,"fill":1414,"stroke":529,"style":1415},"#f0f4ff","stroke-width:1.5px",[509,1417,1419],{"x":1385,"y":582,"style":1418},"font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Sheet",[491,1421],{"x":1391,"y":1407,"width":1392,"height":1408,"rx":494,"fill":1414,"stroke":529,"style":1415},[509,1423,1424],{"x":1395,"y":582,"style":1418},"Result",[491,1426],{"x":1399,"y":1407,"width":1400,"height":1408,"rx":494,"fill":1414,"stroke":529,"style":1415},[509,1428,1429],{"x":1403,"y":582,"style":1418},"Detail",[491,1431],{"x":1375,"y":1432,"width":1376,"height":1408,"rx":494,"fill":505,"stroke":506,"style":1378},"68.0",[509,1434,946],{"x":1411,"y":1435,"style":1387},"87.0",[491,1437],{"x":1381,"y":1432,"width":1382,"height":1408,"rx":494,"fill":497,"stroke":506,"style":1378},[509,1439,73],{"x":1385,"y":1435,"style":1440},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle",[491,1442],{"x":1391,"y":1432,"width":1392,"height":1408,"rx":494,"fill":538,"stroke":539,"style":1415},[509,1444,1446],{"x":1395,"y":1435,"style":1445},"font-size:12px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","skipped",[491,1448],{"x":1399,"y":1432,"width":1400,"height":1408,"rx":494,"fill":497,"stroke":506,"style":1378},[509,1450,1451],{"x":1403,"y":1435,"style":1440},"unexpected headings",[491,1453],{"x":1375,"y":1454,"width":1376,"height":1408,"rx":494,"fill":505,"stroke":506,"style":1378},"96.0",[509,1456,334],{"x":1411,"y":1457,"style":1387},"115.0",[491,1459],{"x":1381,"y":1454,"width":1382,"height":1408,"rx":494,"fill":497,"stroke":506,"style":1378},[509,1461,1462],{"x":1385,"y":1457,"style":1440},"Jan 2026",[491,1464],{"x":1391,"y":1454,"width":1392,"height":1408,"rx":494,"fill":497,"stroke":506,"style":1378},[509,1466,1467],{"x":1395,"y":1457,"style":1440},"changed",[491,1469],{"x":1399,"y":1454,"width":1400,"height":1408,"rx":494,"fill":497,"stroke":506,"style":1378},[509,1471,1472],{"x":1403,"y":1457,"style":1440},"header, freeze, 4 formats, footer",[491,1474],{"x":1375,"y":1475,"width":1376,"height":1408,"rx":494,"fill":505,"stroke":506,"style":1378},"124.0",[509,1477,493],{"x":1411,"y":1478,"style":1387},"143.0",[491,1480],{"x":1381,"y":1475,"width":1382,"height":1408,"rx":494,"fill":497,"stroke":506,"style":1378},[509,1482,1483],{"x":1385,"y":1478,"style":1440},"…",[491,1485],{"x":1391,"y":1475,"width":1392,"height":1408,"rx":494,"fill":497,"stroke":506,"style":1378},[509,1487,1467],{"x":1395,"y":1478,"style":1440},[491,1489],{"x":1399,"y":1475,"width":1400,"height":1408,"rx":494,"fill":497,"stroke":506,"style":1378},[509,1491,1492],{"x":1403,"y":1478,"style":1440},"same for each month",[491,1494],{"x":1375,"y":1495,"width":1376,"height":1408,"rx":494,"fill":505,"stroke":506,"style":1378},"152.0",[509,1497,1499],{"x":1411,"y":1498,"style":1387},"171.0","5",[491,1501],{"x":1381,"y":1495,"width":1382,"height":1408,"rx":494,"fill":497,"stroke":506,"style":1378},[509,1503,1504],{"x":1385,"y":1498,"style":1440},"Sep 2026",[491,1506],{"x":1391,"y":1495,"width":1392,"height":1408,"rx":494,"fill":497,"stroke":506,"style":1378},[509,1508,1467],{"x":1395,"y":1498,"style":1440},[491,1510],{"x":1399,"y":1495,"width":1400,"height":1408,"rx":494,"fill":497,"stroke":506,"style":1378},[509,1512,1472],{"x":1403,"y":1498,"style":1440},[491,1514],{"x":1375,"y":1515,"width":1376,"height":1408,"rx":494,"fill":505,"stroke":506,"style":1378},"180.0",[509,1517,1519],{"x":1411,"y":1518,"style":1387},"199.0","6",[491,1521],{"x":1381,"y":1515,"width":1382,"height":1408,"rx":494,"fill":497,"stroke":506,"style":1378},[509,1523,77],{"x":1385,"y":1518,"style":1440},[491,1525],{"x":1391,"y":1515,"width":1392,"height":1408,"rx":494,"fill":538,"stroke":539,"style":1415},[509,1527,1446],{"x":1395,"y":1518,"style":1445},[491,1529],{"x":1399,"y":1515,"width":1400,"height":1408,"rx":494,"fill":497,"stroke":506,"style":1378},[509,1531,1532],{"x":1403,"y":1518,"style":1440},"hidden sheet",[491,1534],{"x":1375,"y":1535,"width":1376,"height":1408,"rx":494,"fill":505,"stroke":506,"style":1378},"208.0",[509,1537,1539],{"x":1411,"y":1538,"style":1387},"227.0","7",[491,1541],{"x":1381,"y":1535,"width":1382,"height":1408,"rx":494,"fill":497,"stroke":506,"style":1378},[509,1543,1544],{"x":1385,"y":1538,"style":1440},"Chart",[491,1546],{"x":1391,"y":1535,"width":1392,"height":1408,"rx":494,"fill":538,"stroke":539,"style":1415},[509,1548,1446],{"x":1395,"y":1538,"style":1445},[491,1550],{"x":1399,"y":1535,"width":1400,"height":1408,"rx":494,"fill":497,"stroke":506,"style":1378},[509,1552,1553],{"x":1403,"y":1538,"style":1440},"chart sheet",[20,1555,1557],{"id":1556},"choosing-the-guard","Choosing the guard",[10,1559,1560,1561,1563,1564,1567,1568,1570,1571,1573,1574,1577],{},"The heading check is the guard that matters most, and it is worth thinking about how strict it should be. Requiring the first five headings to match exactly, as here, is strict: a sheet where someone renamed ",[54,1562,66],{}," to ",[54,1565,1566],{},"Sales"," is skipped and shows up in the log. A looser check — say, that the first row contains ",[54,1569,56],{}," and ",[54,1572,66],{}," somewhere — would format more sheets, but it would also format a sheet that merely happens to contain those words. For a one-off clean-up the looser check is often fine; for a script that runs every month on files other people edit, the strict check and a log that is actually read are the safer pair. Either way, put the rule in the ",[32,1575,1576],{},"eligible"," function so there is one place to change it.",[472,1579,481,1584,481,1587,481,1590,481,1593,481,1598,481,1604,481,1609,481,1613,481,1617,481,1621,481,1624,481,1629,481,1633,481,1636,481,1639],{"viewBox":1580,"role":475,"ariaLabelledBy":1581,"xmlns":479,"style":480},"4 0 752 173",[1582,1583],"every-guard-choice-t","every-guard-choice-d",[483,1585,1586],{"id":1582},"Strict or loose sheet selection",[487,1588,1589],{"id":1583},"A strict check that the first headings match exactly skips any sheet that has been edited and reports it, which suits scripts that run regularly. A loose check that certain headings appear somewhere formats more sheets but can also change sheets it should not, which suits one-off clean-ups.",[491,1591],{"x":493,"y":494,"width":495,"height":1592,"fill":497},"173",[491,1594],{"x":500,"y":501,"width":1595,"height":1596,"rx":1597,"fill":597,"stroke":598,"style":507},"351.0","141.0","14",[509,1599,1603],{"x":1600,"y":1601,"style":1602},"195.5","44.0","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Strict — exact headings",[37,1605],{"x1":1606,"y1":1607,"x2":1608,"y2":1607,"stroke":598,"style":1378},"36.0","57.0","355.0",[509,1610,1612],{"x":1600,"y":1611,"style":1440},"77.0","skips edited sheets",[509,1614,1616],{"x":1600,"y":1615,"style":1440},"103.0","reports them in the log",[509,1618,1620],{"x":1600,"y":1619,"style":1440},"129.0","safe for monthly runs",[491,1622],{"x":1623,"y":501,"width":1595,"height":1596,"rx":1597,"fill":538,"stroke":539,"style":507},"389.0",[509,1625,1628],{"x":1626,"y":1601,"style":1627},"564.5","font-size:14px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Loose — headings present",[37,1630],{"x1":1631,"y1":1607,"x2":1632,"y2":1607,"stroke":539,"style":1378},"405.0","724.0",[509,1634,1635],{"x":1626,"y":1611,"style":1440},"formats more sheets",[509,1637,1638],{"x":1626,"y":1615,"style":1440},"may touch the wrong ones",[509,1640,1641],{"x":1626,"y":1619,"style":1440},"fine for a one-off clean-up",[20,1643,1645],{"id":1644},"step-4-run-it-again-safely-and-across-many-files","Step 4 — Run it again safely, and across many files",[25,1647,1649],{"className":85,"code":1648,"language":87,"meta":30,"style":30},"wb2 = load_workbook(\"monthly_sales_standardised.xlsx\")\nagain = [standardise(ws) for ws in wb2.worksheets if eligible(ws)[0]]\nprint(\"second run, first sheet:\", again[0])\n\nfrom pathlib import Path\ndef standardise_file(path, out_dir):\n    book = load_workbook(path)\n    changed = sum(1 for ws in book.worksheets if eligible(ws)[0] and standardise(ws))\n    Path(out_dir).mkdir(exist_ok=True)\n    book.save(Path(out_dir) \u002F Path(path).name)\n    return changed\nprint(\"sheets changed:\", standardise_file(\"monthly_sales.xlsx\", \"standardised\"))\n",[32,1650,1651,1664,1693,1710,1714,1726,1736,1746,1785,1799,1810,1817],{"__ignoreMap":30},[35,1652,1653,1656,1658,1660,1662],{"class":37,"line":38},[35,1654,1655],{"class":98},"wb2 ",[35,1657,265],{"class":94},[35,1659,372],{"class":98},[35,1661,1300],{"class":45},[35,1663,378],{"class":98},[35,1665,1666,1669,1671,1674,1676,1678,1680,1683,1686,1689,1691],{"class":37,"line":108},[35,1667,1668],{"class":98},"again ",[35,1670,265],{"class":94},[35,1672,1673],{"class":98}," [standardise(ws) ",[35,1675,271],{"class":94},[35,1677,1171],{"class":98},[35,1679,277],{"class":94},[35,1681,1682],{"class":98}," wb2.worksheets ",[35,1684,1685],{"class":94},"if",[35,1687,1688],{"class":98}," eligible(ws)[",[35,1690,494],{"class":131},[35,1692,286],{"class":98},[35,1694,1695,1697,1699,1702,1705,1707],{"class":37,"line":121},[35,1696,1324],{"class":131},[35,1698,300],{"class":98},[35,1700,1701],{"class":45},"\"second run, first sheet:\"",[35,1703,1704],{"class":98},", again[",[35,1706,494],{"class":131},[35,1708,1709],{"class":98},"])\n",[35,1711,1712],{"class":37,"line":128},[35,1713,125],{"emptyLinePlaceholder":124},[35,1715,1716,1718,1721,1723],{"class":37,"line":167},[35,1717,95],{"class":94},[35,1719,1720],{"class":98}," pathlib ",[35,1722,102],{"class":94},[35,1724,1725],{"class":98}," Path\n",[35,1727,1728,1730,1733],{"class":37,"line":172},[35,1729,175],{"class":94},[35,1731,1732],{"class":178}," standardise_file",[35,1734,1735],{"class":98},"(path, out_dir):\n",[35,1737,1738,1741,1743],{"class":37,"line":185},[35,1739,1740],{"class":98},"    book ",[35,1742,265],{"class":94},[35,1744,1745],{"class":98}," load_workbook(path)\n",[35,1747,1748,1751,1753,1756,1758,1760,1763,1765,1767,1770,1772,1774,1776,1779,1782],{"class":37,"line":200},[35,1749,1750],{"class":98},"    changed ",[35,1752,265],{"class":94},[35,1754,1755],{"class":131}," sum",[35,1757,300],{"class":98},[35,1759,283],{"class":131},[35,1761,1762],{"class":94}," for",[35,1764,1171],{"class":98},[35,1766,277],{"class":94},[35,1768,1769],{"class":98}," book.worksheets ",[35,1771,1685],{"class":94},[35,1773,1688],{"class":98},[35,1775,494],{"class":131},[35,1777,1778],{"class":98},"] ",[35,1780,1781],{"class":94},"and",[35,1783,1784],{"class":98}," standardise(ws))\n",[35,1786,1787,1790,1793,1795,1797],{"class":37,"line":214},[35,1788,1789],{"class":98},"    Path(out_dir).mkdir(",[35,1791,1792],{"class":694},"exist_ok",[35,1794,265],{"class":94},[35,1796,700],{"class":131},[35,1798,378],{"class":98},[35,1800,1801,1804,1807],{"class":37,"line":231},[35,1802,1803],{"class":98},"    book.save(Path(out_dir) ",[35,1805,1806],{"class":94},"\u002F",[35,1808,1809],{"class":98}," Path(path).name)\n",[35,1811,1812,1814],{"class":37,"line":259},[35,1813,348],{"class":94},[35,1815,1816],{"class":98}," changed\n",[35,1818,1819,1821,1823,1826,1829,1831,1833,1836],{"class":37,"line":289},[35,1820,1324],{"class":131},[35,1822,300],{"class":98},[35,1824,1825],{"class":45},"\"sheets changed:\"",[35,1827,1828],{"class":98},", standardise_file(",[35,1830,375],{"class":45},[35,1832,57],{"class":98},[35,1834,1835],{"class":45},"\"standardised\"",[35,1837,765],{"class":98},[10,1839,1840,1841,1844,1845,1848,1849,1853],{},"A change script should give the same result however many times it runs. Every operation here sets a value rather than adding to one — the footer is replaced, not appended to; the freeze is set to A2 — so a second run leaves the workbook as it was. The freeze check even reports nothing when the panes are already frozen, which makes the log of a repeat run shorter: the second run on the first sheet reports the styling and formats it re-applied, but no ",[54,1842,1843],{},"top row frozen",", because there was nothing to do. Run against the original file, the folder version reports nine sheets changed — the nine months — and leaves the summary, the lookup and the chart sheet as they were. Wrapping the whole thing in a function that takes a path makes it easy to apply to every workbook in a folder, skipping the ",[32,1846,1847],{},"~$"," lock files Excel leaves behind, as described in ",[14,1850,1852],{"href":1851},"\u002Fautomating-reporting-workflows\u002Ferror-handling-and-logging-in-excel-automation\u002Fignore-excel-lock-files-when-processing-a-folder\u002F","Ignore Excel Lock Files When Processing a Folder",". Remember that openpyxl does not keep everything when it saves an existing file — pivot tables, slicers and some charts are lost or simplified — so test on a copy of any workbook that contains them.",[20,1855,1857],{"id":1856},"common-pitfalls","Common pitfalls",[1859,1860,1861,1869,1875,1881,1887],"ul",{},[1862,1863,1864,1868],"li",{},[1865,1866,1867],"strong",{},"Treating every sheet the same."," Skip chart sheets, hidden sheets and sheets with a different layout.",[1862,1870,1871,1874],{},[1865,1872,1873],{},"Formats by column letter."," Choose them by heading so moved columns still get the right format.",[1862,1876,1877,1880],{},[1865,1878,1879],{},"No record of what changed."," Log every sheet, including the skipped ones and why.",[1862,1882,1883,1886],{},[1865,1884,1885],{},"Changes that accumulate."," Set values instead of appending, so the script can run twice safely.",[1862,1888,1889,1892],{},[1865,1890,1891],{},"Overwriting the original."," Save to a new file until the script is trusted.",[20,1894,1896],{"id":1895},"conclusion","Conclusion",[10,1898,1899],{},"To apply one change to every sheet, put the change in a function that takes a worksheet, guard it with a check that skips chart sheets, hidden sheets and sheets without the expected headings, and loop over the workbook logging what happened on each sheet. Choose formats by heading, make every operation idempotent, save to a new file, and wrap it in a per-file function to run it across a folder.",[20,1901,1903],{"id":1902},"frequently-asked-questions","Frequently asked questions",[10,1905,1906,1909,1910,1912,1913,18],{},[1865,1907,1908],{},"How do I loop over all sheets in a workbook with openpyxl?","\nIterate over ",[32,1911,465],{}," for normal sheets; chart sheets are in ",[32,1914,469],{},[10,1916,1917,1920,1921,1924,1925,57,1928,1931,1932,18],{},[1865,1918,1919],{},"How do I skip hidden sheets in openpyxl?","\nCheck ",[32,1922,1923],{},"ws.sheet_state",", which is ",[32,1926,1927],{},"\"visible\"",[32,1929,1930],{},"\"hidden\""," or ",[32,1933,1934],{},"\"veryHidden\"",[10,1936,1937,1940,1941,1570,1944,18],{},[1865,1938,1939],{},"How do I add the sheet name and page number to a footer with openpyxl?","\nSet ",[32,1942,1943],{},"ws.oddFooter.left.text = \"&A\"",[32,1945,1946],{},"ws.oddFooter.right.text = \"Page &P of &N\"",[10,1948,1949,1952],{},[1865,1950,1951],{},"How do I make sure a formatting script can run twice?","\nHave every step set a value rather than add to one, so a second run leaves the workbook unchanged.",[20,1954,1956],{"id":1955},"related","Related",[1859,1958,1959,1965,1972,1979],{},[1862,1960,1961,1962,1964],{},"Up one level: ",[14,1963,17],{"href":16}," — many sheets and files.",[1862,1966,1967,1971],{},[14,1968,1970],{"href":1969},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Frename-reorder-and-delete-excel-sheets-with-openpyxl\u002F","Rename, Reorder and Delete Excel Sheets with openpyxl"," — changing the sheets themselves.",[1862,1973,1974,1978],{},[14,1975,1977],{"href":1976},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fsearch-every-sheet-and-workbook-for-a-value\u002F","Search Every Sheet and Workbook for a Value"," — reading across sheets.",[1862,1980,1981,1985],{},[14,1982,1984],{"href":1983},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fstyling-excel-cells-with-openpyxl\u002Ffreeze-header-row-in-excel-with-openpyxl\u002F","Freeze the Header Row in Excel with openpyxl"," — one of the changes.",[749,1987,1988],{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}",{"title":30,"searchDepth":108,"depth":108,"links":1990},[1991,1992,1993,1994,1995,1996,1997,1998,1999,2000],{"id":22,"depth":108,"text":23},{"id":81,"depth":108,"text":82},{"id":612,"depth":108,"text":613},{"id":1149,"depth":108,"text":1150},{"id":1556,"depth":108,"text":1557},{"id":1644,"depth":108,"text":1645},{"id":1856,"depth":108,"text":1857},{"id":1895,"depth":108,"text":1896},{"id":1902,"depth":108,"text":1903},{"id":1955,"depth":108,"text":1956},"2026-10-02","Make one change across every worksheet of a workbook — or of a folder of workbooks — with openpyxl: style the header row, freeze it, set number formats by column heading, add a footer and a print setup, while skipping chart sheets, hidden sheets and sheets that do not have the expected layout, logging what was changed on each sheet and making the run safe to repeat.","md",[2005,2007,2009,2011],{"q":1908,"a":2006},"Iterate over wb.worksheets for normal sheets; chart sheets are in wb.chartsheets.",{"q":1919,"a":2008},"Check ws.sheet_state, which is \"visible\", \"hidden\" or \"veryHidden\".",{"q":1939,"a":2010},"Set ws.oddFooter.left.text = \"&A\" and ws.oddFooter.right.text = \"Page &P of &N\".",{"q":1951,"a":2012},"Have every step set a value rather than add to one, so a second run leaves the workbook unchanged.","apply change to every sheet openpyxl",null,{"breadcrumb":2016},[2017,2019,2022],{"name":2018,"item":1806},"Home",{"name":2020,"item":2021},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":17,"item":16},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fapply-the-same-change-to-every-sheet-in-a-workbook",{"title":2025,"description":2026},"Apply the Same Change to Every Sheet in a Workbook with Python","Loop over every sheet with openpyxl: header style, freeze panes, number formats by heading, footers and print settings, skipping chart and hidden sheets, a change log per sheet and a safe rerun.","apply-the-same-change-to-every-sheet-in-a-workbook","getting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fapply-the-same-change-to-every-sheet-in-a-workbook\u002Findex",[2030,2031],"sheets-and-files","openpyxl","how-to","s9rkwxqgrI6g_MuCbpb3yh1ZxNwRHeUOmpnGMGoaqdQ",[2035,2038],{"title":17,"path":2036,"stem":2037,"children":-1},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python","getting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Findex",{"title":2039,"path":2040,"stem":2041,"children":-1},"Combine Multiple Excel Files into One in Python","\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fcombine-multiple-excel-files-into-one-python","getting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002Fcombine-multiple-excel-files-into-one-python\u002Findex",[2043,2045],{"path":2044,"title":2020},"\u002Fgetting-started-with-python-excel-automation",{"path":2036,"title":17},[],[2048,2051,2054,2057,2060,2063],{"path":2049,"title":2050,"kind":2032},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fconsolidate-timesheets-from-many-workbooks","Consolidate Timesheets from Many Workbooks with Python",{"path":2052,"title":2053,"kind":2032},"\u002Fadvanced-data-transformation-and-cleaning\u002Fextracting-data-from-messy-excel-sheets\u002Fcombine-sheets-with-different-layouts-into-one-table","Combine Sheets with Different Layouts into One Table",{"path":2055,"title":2056,"kind":2032},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmerging-and-joining-excel-dataframes\u002Fappend-new-rows-to-a-master-excel-file-without-duplicates","Append New Rows to a Master Excel File Without Duplicates",{"path":2058,"title":2059,"kind":2032},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmerging-and-joining-excel-dataframes\u002Fconcatenate-excel-sheets-with-different-columns","Concatenate Excel Sheets with Different Columns",{"path":2061,"title":2062,"kind":2032},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmerging-and-joining-excel-dataframes\u002Fmerge-excel-files-with-different-column-names","Merge Excel Files with Different Column Names in pandas",{"path":2064,"title":2065,"kind":2032},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmerging-and-joining-excel-dataframes\u002Fmerge-more-than-two-excel-files-on-a-key-column","Merge More Than Two Excel Files on a Key Column",1790972076042]