[{"data":1,"prerenderedAt":2568},["ShallowReactive",2],{"doc:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-gantt-chart-in-excel-with-python":3,"surround:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-gantt-chart-in-excel-with-python":2534,"ancestors:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-gantt-chart-in-excel-with-python":2543,"children:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-gantt-chart-in-excel-with-python":2548,"related:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-gantt-chart-in-excel-with-python":2549},{"id":4,"title":5,"body":6,"dateModified":2500,"datePublished":2500,"description":2501,"extension":2502,"faq":2503,"keyword":2512,"kind":2513,"meta":2514,"navigation":226,"path":2522,"seo":2523,"slug":2526,"stem":2527,"topics":2528,"type":2532,"__hash__":2533},"docs\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-gantt-chart-in-excel-with-python\u002Findex.md","Create a Gantt Chart in Excel with Python",{"type":7,"value":8,"toc":2488},"minimark",[9,19,138,143,171,177,181,184,400,403,407,414,865,868,872,875,1110,1143,1581,1585,1593,2006,2025,2139,2142,2346,2352,2356,2406,2410,2413,2417,2423,2427,2433,2439,2445,2451,2455,2484],[10,11,12,13,18],"p",{},"A Gantt chart — one horizontal bar per task, placed along a calendar — is the standard way to show a plan: a project, a month-end close, a release schedule. Excel has no Gantt chart type, but the classic workaround turns a stacked bar chart into one, and openpyxl can build it natively, so the chart lives in the workbook next to the task list and can be adjusted by anyone who opens it. This page, part of ",[14,15,17],"a",{"href":16},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002F","Creating Charts in Excel with openpyxl",", builds a close timeline from a task list with start and end dates, including progress shading and a cell-based alternative.",[20,21,29,30,29,34,29,38,29,46,29,56,29,67,29,74,29,79,29,85,29,90,29,95,29,102,29,107,29,111,29,115,29,118,29,122,29,129,29,134],"svg",{"viewBox":22,"role":23,"ariaLabelledBy":24,"xmlns":27,"style":28},"84 0 592 383","img",[25,26],"gantt-trick-t","gantt-trick-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:592px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[31,32,33],"title",{"id":25},"How a stacked bar chart becomes a Gantt chart",[35,36,37],"desc",{"id":26},"Each task's bar is made of stacked segments. The first segment runs from the chart's zero to the task's start date and is invisible. The next segment is the days already done, in a dark colour, and the last is the days remaining, in a light colour. Together the visible segments start on the start date and end on the end date.",[39,40],"rect",{"x":41,"y":42,"width":43,"height":44,"fill":45},"84","0","592","383","#ffffff",[39,47],{"x":48,"y":49,"width":50,"height":51,"rx":52,"fill":53,"stroke":54,"style":55},"100.0","16.0","560.0","93.0","12","#e7ebef","var(--line,#cdd5e6)","stroke-width:2px",[57,58,60],"g",{"transform":59},"translate(368.0,29.0) scale(1.000)",[61,62],"path",{"d":63,"fill":64,"stroke":65,"style":66},"M12 3a9 9 0 1 0 .01 0z M12 7v5l3.5 2","none","var(--text,#172033)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[68,69,73],"text",{"x":70,"y":71,"style":72},"380.0","75.5","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Days remaining",[68,75,78],{"x":70,"y":76,"style":77},"95.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","light segment",[80,81],"line",{"x1":70,"y1":82,"x2":70,"y2":83,"stroke":84,"style":55},"114.0","136.0","var(--brand,#5b5cf0)",[86,87],"polygon",{"points":88,"fill":89},"380.0,140.0 375.0,131.0 385.0,131.0","#5b5cf0",[39,91],{"x":48,"y":92,"width":50,"height":51,"rx":52,"fill":93,"stroke":94,"style":55},"145.0","#d9f4f1","var(--teal,#0f9488)",[57,96,98],{"transform":97},"translate(368.0,158.0) scale(1.000)",[61,99],{"d":100,"fill":64,"stroke":101,"style":66},"M5 12.5l4.5 4.5L19 7","var(--teal-ink,#0b6157)",[68,103,106],{"x":70,"y":104,"style":105},"204.5","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Days done",[68,108,110],{"x":70,"y":109,"style":77},"224.0","dark segment",[80,112],{"x1":70,"y1":113,"x2":70,"y2":114,"stroke":84,"style":55},"243.0","265.0",[86,116],{"points":117,"fill":89},"380.0,269.0 375.0,260.0 385.0,260.0",[39,119],{"x":48,"y":120,"width":50,"height":51,"rx":52,"fill":121,"stroke":84,"style":55},"274.0","#f0f4ff",[57,123,125],{"transform":124},"translate(368.0,287.0) scale(1.000)",[61,126],{"d":127,"fill":64,"stroke":128,"style":66},"M4 5h16v15H4z M4 10h16 M8 3v4 M16 3v4","var(--brand-strong,#4338ca)",[68,130,133],{"x":70,"y":131,"style":132},"333.5","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Start date",[68,135,137],{"x":70,"y":136,"style":77},"353.0","invisible — no fill, no line",[139,140,142],"h2",{"id":141},"prerequisites","Prerequisites",[144,145,150],"pre",{"className":146,"code":147,"language":148,"meta":149,"style":149},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl pandas\n","bash","",[151,152,153],"code",{"__ignoreMap":149},[154,155,157,161,165,168],"span",{"class":80,"line":156},1,[154,158,160],{"class":159},"sMTad","pip",[154,162,164],{"class":163},"srMev"," install",[154,166,167],{"class":163}," openpyxl",[154,169,170],{"class":163}," pandas\n",[10,172,173,176],{},[151,174,175],{},"close_plan.xlsx"," lists six month-end close tasks with a start date, an end date and how far each has progressed.",[139,178,180],{"id":179},"step-1-turn-dates-into-start-and-duration-columns","Step 1 — Turn dates into start and duration columns",[10,182,183],{},"The chart needs, for each task, where the bar starts and how long it is. End dates are inclusive — a task that starts and ends on 12 October takes one day — so the duration is the difference plus one. Split the duration into done and remaining days for the progress shading:",[144,185,189],{"className":186,"code":187,"language":188,"meta":149,"style":149},"language-python shiki shiki-themes github-light github-dark-high-contrast","from datetime import date\nimport pandas as pd\n\nplan = pd.read_excel(\"close_plan.xlsx\")\nplan[\"Days\"] = (plan[\"End\"] - plan[\"Start\"]).dt.days + 1\nplan[\"Done\"] = (plan[\"Days\"] * plan[\"Done %\"] \u002F 100).round(1)\nplan[\"Remaining\"] = plan[\"Days\"] - plan[\"Done\"]\nprint(plan[[\"Task\", \"Start\", \"Days\", \"Done\", \"Remaining\"]].to_string(index=False))\n","python",[151,190,191,207,221,228,246,287,329,356],{"__ignoreMap":149},[154,192,193,197,201,204],{"class":80,"line":156},[154,194,196],{"class":195},"s-kum","from",[154,198,200],{"class":199},"skGVy"," datetime ",[154,202,203],{"class":195},"import",[154,205,206],{"class":199}," date\n",[154,208,210,212,215,218],{"class":80,"line":209},2,[154,211,203],{"class":195},[154,213,214],{"class":199}," pandas ",[154,216,217],{"class":195},"as",[154,219,220],{"class":199}," pd\n",[154,222,224],{"class":80,"line":223},3,[154,225,227],{"emptyLinePlaceholder":226},true,"\n",[154,229,231,234,237,240,243],{"class":80,"line":230},4,[154,232,233],{"class":199},"plan ",[154,235,236],{"class":195},"=",[154,238,239],{"class":199}," pd.read_excel(",[154,241,242],{"class":163},"\"close_plan.xlsx\"",[154,244,245],{"class":199},")\n",[154,247,249,252,255,258,260,263,266,268,271,274,277,280,283],{"class":80,"line":248},5,[154,250,251],{"class":199},"plan[",[154,253,254],{"class":163},"\"Days\"",[154,256,257],{"class":199},"] ",[154,259,236],{"class":195},[154,261,262],{"class":199}," (plan[",[154,264,265],{"class":163},"\"End\"",[154,267,257],{"class":199},[154,269,270],{"class":195},"-",[154,272,273],{"class":199}," plan[",[154,275,276],{"class":163},"\"Start\"",[154,278,279],{"class":199},"]).dt.days ",[154,281,282],{"class":195},"+",[154,284,286],{"class":285},"sP0c6"," 1\n",[154,288,290,292,295,297,299,301,303,305,308,310,313,315,318,321,324,327],{"class":80,"line":289},6,[154,291,251],{"class":199},[154,293,294],{"class":163},"\"Done\"",[154,296,257],{"class":199},[154,298,236],{"class":195},[154,300,262],{"class":199},[154,302,254],{"class":163},[154,304,257],{"class":199},[154,306,307],{"class":195},"*",[154,309,273],{"class":199},[154,311,312],{"class":163},"\"Done %\"",[154,314,257],{"class":199},[154,316,317],{"class":195},"\u002F",[154,319,320],{"class":285}," 100",[154,322,323],{"class":199},").round(",[154,325,326],{"class":285},"1",[154,328,245],{"class":199},[154,330,332,334,337,339,341,343,345,347,349,351,353],{"class":80,"line":331},7,[154,333,251],{"class":199},[154,335,336],{"class":163},"\"Remaining\"",[154,338,257],{"class":199},[154,340,236],{"class":195},[154,342,273],{"class":199},[154,344,254],{"class":163},[154,346,257],{"class":199},[154,348,270],{"class":195},[154,350,273],{"class":199},[154,352,294],{"class":163},[154,354,355],{"class":199},"]\n",[154,357,359,362,365,368,371,373,375,377,379,381,383,385,388,392,394,397],{"class":80,"line":358},8,[154,360,361],{"class":285},"print",[154,363,364],{"class":199},"(plan[[",[154,366,367],{"class":163},"\"Task\"",[154,369,370],{"class":199},", ",[154,372,276],{"class":163},[154,374,370],{"class":199},[154,376,254],{"class":163},[154,378,370],{"class":199},[154,380,294],{"class":163},[154,382,370],{"class":199},[154,384,336],{"class":163},[154,386,387],{"class":199},"]].to_string(",[154,389,391],{"class":390},"sa561","index",[154,393,236],{"class":195},[154,395,396],{"class":285},"False",[154,398,399],{"class":199},"))\n",[10,401,402],{},"The durations are calendar days, which is what a chart along a calendar axis needs: a task from Friday to Monday spans four days on the chart even if only two are working days.",[139,404,406],{"id":405},"step-2-build-the-stacked-bar-chart","Step 2 — Build the stacked bar chart",[10,408,409,410,413],{},"Write the task table, then make a horizontal (",[151,411,412],{},"type = \"bar\"",") stacked chart of three series — start, done, remaining — and hide the first one by removing its fill and outline:",[144,415,417],{"className":186,"code":416,"language":188,"meta":149,"style":149},"from openpyxl import Workbook\nfrom openpyxl.chart import BarChart, Reference\n\nwb = Workbook(); ws = wb.active; ws.title = \"Plan\"\nws.append([\"Task\", \"Start\", \"Done\", \"Remaining\", \"End\"])\nfor r in plan.itertuples(index=False):\n    ws.append([r.Task, r.Start.date(), r.Done, r.Remaining, r.End.date()])\nfor row in ws.iter_rows(min_row=2, min_col=2, max_col=5):\n    row[0].number_format = row[3].number_format = \"dd mmm\"\nws.column_dimensions[\"A\"].width = 18\nfor col in \"BCDE\":\n    ws.column_dimensions[col].width = 10\n\nn = len(plan) + 1\nchart = BarChart()\nchart.type = \"bar\"\nchart.grouping = \"stacked\"\nchart.overlap = 100\nchart.gapWidth = 40\nchart.add_data(Reference(ws, min_col=2, max_col=4, min_row=1, max_row=n), titles_from_data=True)\nchart.set_categories(Reference(ws, min_col=1, min_row=2, max_row=n))\n\nstart_series, done_series, remaining_series = chart.series\nstart_series.graphicalProperties.noFill = True\nstart_series.graphicalProperties.line.noFill = True\ndone_series.graphicalProperties.solidFill = \"0B6157\"\nremaining_series.graphicalProperties.solidFill = \"B7D7D2\"\nprint([s.tx.strRef.f for s in chart.series])\n",[151,418,419,431,443,447,467,493,516,521,562,588,605,621,632,637,655,666,677,688,699,710,759,788,793,804,815,825,836,847],{"__ignoreMap":149},[154,420,421,423,426,428],{"class":80,"line":156},[154,422,196],{"class":195},[154,424,425],{"class":199}," openpyxl ",[154,427,203],{"class":195},[154,429,430],{"class":199}," Workbook\n",[154,432,433,435,438,440],{"class":80,"line":209},[154,434,196],{"class":195},[154,436,437],{"class":199}," openpyxl.chart ",[154,439,203],{"class":195},[154,441,442],{"class":199}," BarChart, Reference\n",[154,444,445],{"class":80,"line":223},[154,446,227],{"emptyLinePlaceholder":226},[154,448,449,452,454,457,459,462,464],{"class":80,"line":230},[154,450,451],{"class":199},"wb ",[154,453,236],{"class":195},[154,455,456],{"class":199}," Workbook(); ws ",[154,458,236],{"class":195},[154,460,461],{"class":199}," wb.active; ws.title ",[154,463,236],{"class":195},[154,465,466],{"class":163}," \"Plan\"\n",[154,468,469,472,474,476,478,480,482,484,486,488,490],{"class":80,"line":248},[154,470,471],{"class":199},"ws.append([",[154,473,367],{"class":163},[154,475,370],{"class":199},[154,477,276],{"class":163},[154,479,370],{"class":199},[154,481,294],{"class":163},[154,483,370],{"class":199},[154,485,336],{"class":163},[154,487,370],{"class":199},[154,489,265],{"class":163},[154,491,492],{"class":199},"])\n",[154,494,495,498,501,504,507,509,511,513],{"class":80,"line":289},[154,496,497],{"class":195},"for",[154,499,500],{"class":199}," r ",[154,502,503],{"class":195},"in",[154,505,506],{"class":199}," plan.itertuples(",[154,508,391],{"class":390},[154,510,236],{"class":195},[154,512,396],{"class":285},[154,514,515],{"class":199},"):\n",[154,517,518],{"class":80,"line":331},[154,519,520],{"class":199},"    ws.append([r.Task, r.Start.date(), r.Done, r.Remaining, r.End.date()])\n",[154,522,523,525,528,530,533,536,538,541,543,546,548,550,552,555,557,560],{"class":80,"line":358},[154,524,497],{"class":195},[154,526,527],{"class":199}," row ",[154,529,503],{"class":195},[154,531,532],{"class":199}," ws.iter_rows(",[154,534,535],{"class":390},"min_row",[154,537,236],{"class":195},[154,539,540],{"class":285},"2",[154,542,370],{"class":199},[154,544,545],{"class":390},"min_col",[154,547,236],{"class":195},[154,549,540],{"class":285},[154,551,370],{"class":199},[154,553,554],{"class":390},"max_col",[154,556,236],{"class":195},[154,558,559],{"class":285},"5",[154,561,515],{"class":199},[154,563,565,568,570,573,575,578,581,583,585],{"class":80,"line":564},9,[154,566,567],{"class":199},"    row[",[154,569,42],{"class":285},[154,571,572],{"class":199},"].number_format ",[154,574,236],{"class":195},[154,576,577],{"class":199}," row[",[154,579,580],{"class":285},"3",[154,582,572],{"class":199},[154,584,236],{"class":195},[154,586,587],{"class":163}," \"dd mmm\"\n",[154,589,591,594,597,600,602],{"class":80,"line":590},10,[154,592,593],{"class":199},"ws.column_dimensions[",[154,595,596],{"class":163},"\"A\"",[154,598,599],{"class":199},"].width ",[154,601,236],{"class":195},[154,603,604],{"class":285}," 18\n",[154,606,608,610,613,615,618],{"class":80,"line":607},11,[154,609,497],{"class":195},[154,611,612],{"class":199}," col ",[154,614,503],{"class":195},[154,616,617],{"class":163}," \"BCDE\"",[154,619,620],{"class":199},":\n",[154,622,624,627,629],{"class":80,"line":623},12,[154,625,626],{"class":199},"    ws.column_dimensions[col].width ",[154,628,236],{"class":195},[154,630,631],{"class":285}," 10\n",[154,633,635],{"class":80,"line":634},13,[154,636,227],{"emptyLinePlaceholder":226},[154,638,640,643,645,648,651,653],{"class":80,"line":639},14,[154,641,642],{"class":199},"n ",[154,644,236],{"class":195},[154,646,647],{"class":285}," len",[154,649,650],{"class":199},"(plan) ",[154,652,282],{"class":195},[154,654,286],{"class":285},[154,656,658,661,663],{"class":80,"line":657},15,[154,659,660],{"class":199},"chart ",[154,662,236],{"class":195},[154,664,665],{"class":199}," BarChart()\n",[154,667,669,672,674],{"class":80,"line":668},16,[154,670,671],{"class":199},"chart.type ",[154,673,236],{"class":195},[154,675,676],{"class":163}," \"bar\"\n",[154,678,680,683,685],{"class":80,"line":679},17,[154,681,682],{"class":199},"chart.grouping ",[154,684,236],{"class":195},[154,686,687],{"class":163}," \"stacked\"\n",[154,689,691,694,696],{"class":80,"line":690},18,[154,692,693],{"class":199},"chart.overlap ",[154,695,236],{"class":195},[154,697,698],{"class":285}," 100\n",[154,700,702,705,707],{"class":80,"line":701},19,[154,703,704],{"class":199},"chart.gapWidth ",[154,706,236],{"class":195},[154,708,709],{"class":285}," 40\n",[154,711,713,716,718,720,722,724,726,728,731,733,735,737,739,741,744,746,749,752,754,757],{"class":80,"line":712},20,[154,714,715],{"class":199},"chart.add_data(Reference(ws, ",[154,717,545],{"class":390},[154,719,236],{"class":195},[154,721,540],{"class":285},[154,723,370],{"class":199},[154,725,554],{"class":390},[154,727,236],{"class":195},[154,729,730],{"class":285},"4",[154,732,370],{"class":199},[154,734,535],{"class":390},[154,736,236],{"class":195},[154,738,326],{"class":285},[154,740,370],{"class":199},[154,742,743],{"class":390},"max_row",[154,745,236],{"class":195},[154,747,748],{"class":199},"n), ",[154,750,751],{"class":390},"titles_from_data",[154,753,236],{"class":195},[154,755,756],{"class":285},"True",[154,758,245],{"class":199},[154,760,762,765,767,769,771,773,775,777,779,781,783,785],{"class":80,"line":761},21,[154,763,764],{"class":199},"chart.set_categories(Reference(ws, ",[154,766,545],{"class":390},[154,768,236],{"class":195},[154,770,326],{"class":285},[154,772,370],{"class":199},[154,774,535],{"class":390},[154,776,236],{"class":195},[154,778,540],{"class":285},[154,780,370],{"class":199},[154,782,743],{"class":390},[154,784,236],{"class":195},[154,786,787],{"class":199},"n))\n",[154,789,791],{"class":80,"line":790},22,[154,792,227],{"emptyLinePlaceholder":226},[154,794,796,799,801],{"class":80,"line":795},23,[154,797,798],{"class":199},"start_series, done_series, remaining_series ",[154,800,236],{"class":195},[154,802,803],{"class":199}," chart.series\n",[154,805,807,810,812],{"class":80,"line":806},24,[154,808,809],{"class":199},"start_series.graphicalProperties.noFill ",[154,811,236],{"class":195},[154,813,814],{"class":285}," True\n",[154,816,818,821,823],{"class":80,"line":817},25,[154,819,820],{"class":199},"start_series.graphicalProperties.line.noFill ",[154,822,236],{"class":195},[154,824,814],{"class":285},[154,826,828,831,833],{"class":80,"line":827},26,[154,829,830],{"class":199},"done_series.graphicalProperties.solidFill ",[154,832,236],{"class":195},[154,834,835],{"class":163}," \"0B6157\"\n",[154,837,839,842,844],{"class":80,"line":838},27,[154,840,841],{"class":199},"remaining_series.graphicalProperties.solidFill ",[154,843,236],{"class":195},[154,845,846],{"class":163}," \"B7D7D2\"\n",[154,848,850,852,855,857,860,862],{"class":80,"line":849},28,[154,851,361],{"class":285},[154,853,854],{"class":199},"([s.tx.strRef.f ",[154,856,497],{"class":195},[154,858,859],{"class":199}," s ",[154,861,503],{"class":195},[154,863,864],{"class":199}," chart.series])\n",[10,866,867],{},"The start series is still there — it pushes the visible segments along the axis — but without a fill or an outline it cannot be seen. Excel stores dates as numbers, so stacking a date with durations works: the bar for \"Reconcile bank\" starts at the serial number of 2 October and extends by three days.",[139,869,871],{"id":870},"step-3-put-the-first-task-at-the-top-and-fix-the-date-range","Step 3 — Put the first task at the top and fix the date range",[10,873,874],{},"Two defaults make the chart read wrongly. Bar charts draw the first category at the bottom, so the plan appears upside down; reverse the category axis. And the value axis starts at zero — the year 1900 — so the bars sit far to the right; set its limits to the plan's first and last dates, as serial numbers:",[144,876,878],{"className":186,"code":877,"language":188,"meta":149,"style":149},"def excel_serial(d):\n    return (d - date(1899, 12, 30)).days\n\nfirst, last = plan[\"Start\"].min().date(), plan[\"End\"].max().date()\nchart.x_axis.scaling.orientation = \"maxMin\"\nchart.y_axis.crosses = \"max\"\nchart.y_axis.scaling.min = excel_serial(first)\nchart.y_axis.scaling.max = excel_serial(last) + 1\nchart.y_axis.majorUnit = 2\nchart.y_axis.number_format = \"dd mmm\"\nchart.x_axis.delete = False\nchart.y_axis.delete = False\nchart.legend = None\nchart.title = \"Month-end close — October 2026 (dark: done, light: remaining)\"\nchart.width, chart.height = 22, 9\nws.add_chart(chart, \"G2\")\nwb.save(\"close_gantt.xlsx\")\nprint(\"axis\", chart.y_axis.scaling.min, \"to\", chart.y_axis.scaling.max, \"=\", first, \"to\", last)\n",[151,879,880,892,920,924,943,953,963,973,987,997,1006,1016,1025,1035,1045,1060,1070,1080],{"__ignoreMap":149},[154,881,882,885,889],{"class":80,"line":156},[154,883,884],{"class":195},"def",[154,886,888],{"class":887},"s_Opv"," excel_serial",[154,890,891],{"class":199},"(d):\n",[154,893,894,897,900,902,905,908,910,912,914,917],{"class":80,"line":209},[154,895,896],{"class":195},"    return",[154,898,899],{"class":199}," (d ",[154,901,270],{"class":195},[154,903,904],{"class":199}," date(",[154,906,907],{"class":285},"1899",[154,909,370],{"class":199},[154,911,52],{"class":285},[154,913,370],{"class":199},[154,915,916],{"class":285},"30",[154,918,919],{"class":199},")).days\n",[154,921,922],{"class":80,"line":223},[154,923,227],{"emptyLinePlaceholder":226},[154,925,926,929,931,933,935,938,940],{"class":80,"line":230},[154,927,928],{"class":199},"first, last ",[154,930,236],{"class":195},[154,932,273],{"class":199},[154,934,276],{"class":163},[154,936,937],{"class":199},"].min().date(), plan[",[154,939,265],{"class":163},[154,941,942],{"class":199},"].max().date()\n",[154,944,945,948,950],{"class":80,"line":248},[154,946,947],{"class":199},"chart.x_axis.scaling.orientation ",[154,949,236],{"class":195},[154,951,952],{"class":163}," \"maxMin\"\n",[154,954,955,958,960],{"class":80,"line":289},[154,956,957],{"class":199},"chart.y_axis.crosses ",[154,959,236],{"class":195},[154,961,962],{"class":163}," \"max\"\n",[154,964,965,968,970],{"class":80,"line":331},[154,966,967],{"class":199},"chart.y_axis.scaling.min ",[154,969,236],{"class":195},[154,971,972],{"class":199}," excel_serial(first)\n",[154,974,975,978,980,983,985],{"class":80,"line":358},[154,976,977],{"class":199},"chart.y_axis.scaling.max ",[154,979,236],{"class":195},[154,981,982],{"class":199}," excel_serial(last) ",[154,984,282],{"class":195},[154,986,286],{"class":285},[154,988,989,992,994],{"class":80,"line":564},[154,990,991],{"class":199},"chart.y_axis.majorUnit ",[154,993,236],{"class":195},[154,995,996],{"class":285}," 2\n",[154,998,999,1002,1004],{"class":80,"line":590},[154,1000,1001],{"class":199},"chart.y_axis.number_format ",[154,1003,236],{"class":195},[154,1005,587],{"class":163},[154,1007,1008,1011,1013],{"class":80,"line":607},[154,1009,1010],{"class":199},"chart.x_axis.delete ",[154,1012,236],{"class":195},[154,1014,1015],{"class":285}," False\n",[154,1017,1018,1021,1023],{"class":80,"line":623},[154,1019,1020],{"class":199},"chart.y_axis.delete ",[154,1022,236],{"class":195},[154,1024,1015],{"class":285},[154,1026,1027,1030,1032],{"class":80,"line":634},[154,1028,1029],{"class":199},"chart.legend ",[154,1031,236],{"class":195},[154,1033,1034],{"class":285}," None\n",[154,1036,1037,1040,1042],{"class":80,"line":639},[154,1038,1039],{"class":199},"chart.title ",[154,1041,236],{"class":195},[154,1043,1044],{"class":163}," \"Month-end close — October 2026 (dark: done, light: remaining)\"\n",[154,1046,1047,1050,1052,1055,1057],{"class":80,"line":657},[154,1048,1049],{"class":199},"chart.width, chart.height ",[154,1051,236],{"class":195},[154,1053,1054],{"class":285}," 22",[154,1056,370],{"class":199},[154,1058,1059],{"class":285},"9\n",[154,1061,1062,1065,1068],{"class":80,"line":668},[154,1063,1064],{"class":199},"ws.add_chart(chart, ",[154,1066,1067],{"class":163},"\"G2\"",[154,1069,245],{"class":199},[154,1071,1072,1075,1078],{"class":80,"line":679},[154,1073,1074],{"class":199},"wb.save(",[154,1076,1077],{"class":163},"\"close_gantt.xlsx\"",[154,1079,245],{"class":199},[154,1081,1082,1084,1087,1090,1093,1096,1099,1102,1105,1107],{"class":80,"line":690},[154,1083,361],{"class":285},[154,1085,1086],{"class":199},"(",[154,1088,1089],{"class":163},"\"axis\"",[154,1091,1092],{"class":199},", chart.y_axis.scaling.min, ",[154,1094,1095],{"class":163},"\"to\"",[154,1097,1098],{"class":199},", chart.y_axis.scaling.max, ",[154,1100,1101],{"class":163},"\"=\"",[154,1103,1104],{"class":199},", first, ",[154,1106,1095],{"class":163},[154,1108,1109],{"class":199},", last)\n",[10,1111,1112,1113,1117,1118,1121,1122,1125,1126,1129,1130,1133,1134,1137,1138,1142],{},"In a bar chart the ",[1114,1115,1116],"em",{},"x"," axis is the vertical category axis and the ",[1114,1119,1120],{},"y"," axis the horizontal value axis — openpyxl keeps the names of a column chart. Reversing the categories moves the date axis to the top; ",[151,1123,1124],{},"crosses = \"max\""," moves it back to the bottom, where readers expect a timeline. ",[151,1127,1128],{},"1899-12-30"," is the base date that makes the serial numbers match Excel's: ",[151,1131,1132],{},"excel_serial(date(2026, 10, 1))"," is 46296. The legend is removed because it would list the invisible start series too; the title explains the two colours instead. The extra day on the maximum leaves room for the last task's bar, which ends at the end of its final day. The ",[151,1135,1136],{},"delete = False"," lines keep both axes visible in Excel; ",[14,1139,1141],{"href":1140},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fformat-chart-axes-with-openpyxl\u002F","Format Chart Axes with openpyxl"," explains why they are needed.",[20,1144,29,1150,29,1153,29,1156,29,1161,29,1166,29,1173,29,1177,29,1183,29,1187,29,1191,29,1194,29,1198,29,1201,29,1205,29,1208,29,1212,29,1215,29,1219,29,1222,29,1226,29,1229,29,1233,29,1236,29,1240,29,1243,29,1247,29,1250,29,1254,29,1257,29,1261,29,1264,29,1268,29,1272,29,1276,29,1278,29,1282,29,1284,29,1286,29,1288,29,1290,29,1292,29,1294,29,1296,29,1298,29,1300,29,1302,29,1304,29,1307,29,1309,29,1312,29,1314,29,1317,29,1319,29,1322,29,1324,29,1327,29,1329,29,1332,29,1334,29,1336,29,1338,29,1341,29,1343,29,1347,29,1350,29,1354,29,1356,29,1358,29,1360,29,1362,29,1364,29,1366,29,1368,29,1370,29,1372,29,1374,29,1376,29,1378,29,1381,29,1384,29,1386,29,1389,29,1391,29,1393,29,1395,29,1397,29,1399,29,1401,29,1403,29,1405,29,1407,29,1409,29,1411,29,1413,29,1415,29,1417,29,1419,29,1422,29,1425,29,1427,29,1430,29,1432,29,1434,29,1436,29,1438,29,1440,29,1442,29,1444,29,1447,29,1449,29,1451,29,1453,29,1455,29,1457,29,1459,29,1462,29,1465,29,1467,29,1470,29,1472,29,1474,29,1476,29,1478,29,1480,29,1482,29,1485,29,1487,29,1489,29,1491,29,1493,29,1495,29,1497,29,1499,29,1501,29,1504,29,1507,29,1509,29,1512,29,1514,29,1516,29,1518,29,1520,29,1522,29,1524,29,1526,29,1528,29,1530,29,1532,29,1534,29,1536,29,1538,29,1540,29,1542,29,1544,29,1547,29,1550,29,1552,29,1555,29,1557,29,1559,29,1561,29,1563,29,1565,29,1567,29,1569,29,1571,29,1573,29,1575,29,1577,29,1579],{"viewBox":1145,"role":23,"ariaLabelledBy":1146,"xmlns":27,"style":1149},"14 0 731 284",[1147,1148],"gantt-sheet-t","gantt-sheet-d","width:100%;max-width:731px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[31,1151,1152],{"id":1147},"The close timeline as the chart shows it",[35,1154,1155],{"id":1148},"A Gantt layout for the October close. Collect exports runs 1 to 2 October and is complete. Reconcile bank runs 2 to 4 October and is complete. Post accruals runs 5 to 6 October and is half done. Variance review runs 6 to 8 October, 20 percent done. Management pack runs 8 to 11 October and Pack to CFO is on 12 October, neither started.",[39,1157],{"x":1158,"y":42,"width":1159,"height":1160,"fill":45},"14","731","284",[68,1162,1165],{"x":70,"y":1163,"style":1164},"31.0","font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Plan — one row per task, days across",[39,1167],{"x":1168,"y":1169,"width":1170,"height":1171,"rx":42,"fill":53,"stroke":54,"style":1172},"30.9","48.0","32.0","24.0","stroke-width:1px",[39,1174],{"x":1175,"y":1169,"width":1176,"height":1171,"rx":42,"fill":53,"stroke":54,"style":1172},"62.9","138.1",[68,1178,1182],{"x":1179,"y":1180,"style":1181},"132.0","65.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[39,1184],{"x":1185,"y":1169,"width":1186,"height":1171,"rx":42,"fill":53,"stroke":54,"style":1172},"201.1","44.0",[68,1188,1190],{"x":1189,"y":1180,"style":1181},"223.1","B",[39,1192],{"x":1193,"y":1169,"width":1186,"height":1171,"rx":42,"fill":53,"stroke":54,"style":1172},"245.1",[68,1195,1197],{"x":1196,"y":1180,"style":1181},"267.1","C",[39,1199],{"x":1200,"y":1169,"width":1186,"height":1171,"rx":42,"fill":53,"stroke":54,"style":1172},"289.1",[68,1202,1204],{"x":1203,"y":1180,"style":1181},"311.1","D",[39,1206],{"x":1207,"y":1169,"width":1186,"height":1171,"rx":42,"fill":53,"stroke":54,"style":1172},"333.1",[68,1209,1211],{"x":1210,"y":1180,"style":1181},"355.1","E",[39,1213],{"x":1214,"y":1169,"width":1186,"height":1171,"rx":42,"fill":53,"stroke":54,"style":1172},"377.1",[68,1216,1218],{"x":1217,"y":1180,"style":1181},"399.1","F",[39,1220],{"x":1221,"y":1169,"width":1186,"height":1171,"rx":42,"fill":53,"stroke":54,"style":1172},"421.1",[68,1223,1225],{"x":1224,"y":1180,"style":1181},"443.1","G",[39,1227],{"x":1228,"y":1169,"width":1186,"height":1171,"rx":42,"fill":53,"stroke":54,"style":1172},"465.1",[68,1230,1232],{"x":1231,"y":1180,"style":1181},"487.1","H",[39,1234],{"x":1235,"y":1169,"width":1186,"height":1171,"rx":42,"fill":53,"stroke":54,"style":1172},"509.1",[68,1237,1239],{"x":1238,"y":1180,"style":1181},"531.1","I",[39,1241],{"x":1242,"y":1169,"width":1186,"height":1171,"rx":42,"fill":53,"stroke":54,"style":1172},"553.1",[68,1244,1246],{"x":1245,"y":1180,"style":1181},"575.1","J",[39,1248],{"x":1249,"y":1169,"width":1186,"height":1171,"rx":42,"fill":53,"stroke":54,"style":1172},"597.1",[68,1251,1253],{"x":1252,"y":1180,"style":1181},"619.1","K",[39,1255],{"x":1256,"y":1169,"width":1186,"height":1171,"rx":42,"fill":53,"stroke":54,"style":1172},"641.1",[68,1258,1260],{"x":1259,"y":1180,"style":1181},"663.1","L",[39,1262],{"x":1263,"y":1169,"width":1186,"height":1171,"rx":42,"fill":53,"stroke":54,"style":1172},"685.1",[68,1265,1267],{"x":1266,"y":1180,"style":1181},"707.1","M",[39,1269],{"x":1168,"y":1270,"width":1170,"height":1271,"rx":42,"fill":53,"stroke":54,"style":1172},"72.0","28.0",[68,1273,326],{"x":1274,"y":1275,"style":1181},"46.9","91.0",[39,1277],{"x":1175,"y":1270,"width":1176,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1279,1281],{"x":1179,"y":1275,"style":1280},"font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Task",[39,1283],{"x":1185,"y":1270,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1285,326],{"x":1189,"y":1275,"style":1280},[39,1287],{"x":1193,"y":1270,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1289,540],{"x":1196,"y":1275,"style":1280},[39,1291],{"x":1200,"y":1270,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1293,580],{"x":1203,"y":1275,"style":1280},[39,1295],{"x":1207,"y":1270,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1297,730],{"x":1210,"y":1275,"style":1280},[39,1299],{"x":1214,"y":1270,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1301,559],{"x":1217,"y":1275,"style":1280},[39,1303],{"x":1221,"y":1270,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1305,1306],{"x":1224,"y":1275,"style":1280},"6",[39,1308],{"x":1228,"y":1270,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1310,1311],{"x":1231,"y":1275,"style":1280},"7",[39,1313],{"x":1235,"y":1270,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1315,1316],{"x":1238,"y":1275,"style":1280},"8",[39,1318],{"x":1242,"y":1270,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1320,1321],{"x":1245,"y":1275,"style":1280},"9",[39,1323],{"x":1249,"y":1270,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1325,1326],{"x":1252,"y":1275,"style":1280},"10",[39,1328],{"x":1256,"y":1270,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1330,1331],{"x":1259,"y":1275,"style":1280},"11",[39,1333],{"x":1263,"y":1270,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1335,52],{"x":1266,"y":1275,"style":1280},[39,1337],{"x":1168,"y":48,"width":1170,"height":1271,"rx":42,"fill":53,"stroke":54,"style":1172},[68,1339,540],{"x":1274,"y":1340,"style":1181},"119.0",[39,1342],{"x":1175,"y":48,"width":1176,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1344,1346],{"x":1179,"y":1340,"style":1345},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","Collect exports",[39,1348],{"x":1185,"y":48,"width":1186,"height":1271,"rx":42,"fill":93,"stroke":94,"style":1349},"stroke-width:1.5px",[68,1351,1353],{"x":1189,"y":1340,"style":1352},"font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","█",[39,1355],{"x":1193,"y":48,"width":1186,"height":1271,"rx":42,"fill":93,"stroke":94,"style":1349},[68,1357,1353],{"x":1196,"y":1340,"style":1352},[39,1359],{"x":1200,"y":48,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1361],{"x":1207,"y":48,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1363],{"x":1214,"y":48,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1365],{"x":1221,"y":48,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1367],{"x":1228,"y":48,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1369],{"x":1235,"y":48,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1371],{"x":1242,"y":48,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1373],{"x":1249,"y":48,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1375],{"x":1256,"y":48,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1377],{"x":1263,"y":48,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1379],{"x":1168,"y":1380,"width":1170,"height":1271,"rx":42,"fill":53,"stroke":54,"style":1172},"128.0",[68,1382,580],{"x":1274,"y":1383,"style":1181},"147.0",[39,1385],{"x":1175,"y":1380,"width":1176,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1387,1388],{"x":1179,"y":1383,"style":1345},"Reconcile bank",[39,1390],{"x":1185,"y":1380,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1392],{"x":1193,"y":1380,"width":1186,"height":1271,"rx":42,"fill":93,"stroke":94,"style":1349},[68,1394,1353],{"x":1196,"y":1383,"style":1352},[39,1396],{"x":1200,"y":1380,"width":1186,"height":1271,"rx":42,"fill":93,"stroke":94,"style":1349},[68,1398,1353],{"x":1203,"y":1383,"style":1352},[39,1400],{"x":1207,"y":1380,"width":1186,"height":1271,"rx":42,"fill":93,"stroke":94,"style":1349},[68,1402,1353],{"x":1210,"y":1383,"style":1352},[39,1404],{"x":1214,"y":1380,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1406],{"x":1221,"y":1380,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1408],{"x":1228,"y":1380,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1410],{"x":1235,"y":1380,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1412],{"x":1242,"y":1380,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1414],{"x":1249,"y":1380,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1416],{"x":1256,"y":1380,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1418],{"x":1263,"y":1380,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1420],{"x":1168,"y":1421,"width":1170,"height":1271,"rx":42,"fill":53,"stroke":54,"style":1172},"156.0",[68,1423,730],{"x":1274,"y":1424,"style":1181},"175.0",[39,1426],{"x":1175,"y":1421,"width":1176,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1428,1429],{"x":1179,"y":1424,"style":1345},"Post accruals",[39,1431],{"x":1185,"y":1421,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1433],{"x":1193,"y":1421,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1435],{"x":1200,"y":1421,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1437],{"x":1207,"y":1421,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1439],{"x":1214,"y":1421,"width":1186,"height":1271,"rx":42,"fill":93,"stroke":94,"style":1349},[68,1441,1353],{"x":1217,"y":1424,"style":1352},[39,1443],{"x":1221,"y":1421,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1445,1446],{"x":1224,"y":1424,"style":1345},"░",[39,1448],{"x":1228,"y":1421,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1450],{"x":1235,"y":1421,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1452],{"x":1242,"y":1421,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1454],{"x":1249,"y":1421,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1456],{"x":1256,"y":1421,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1458],{"x":1263,"y":1421,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1460],{"x":1168,"y":1461,"width":1170,"height":1271,"rx":42,"fill":53,"stroke":54,"style":1172},"184.0",[68,1463,559],{"x":1274,"y":1464,"style":1181},"203.0",[39,1466],{"x":1175,"y":1461,"width":1176,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1468,1469],{"x":1179,"y":1464,"style":1345},"Variance review",[39,1471],{"x":1185,"y":1461,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1473],{"x":1193,"y":1461,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1475],{"x":1200,"y":1461,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1477],{"x":1207,"y":1461,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1479],{"x":1214,"y":1461,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1481],{"x":1221,"y":1461,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1483,1484],{"x":1224,"y":1464,"style":1345},"▓",[39,1486],{"x":1228,"y":1461,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1488,1446],{"x":1231,"y":1464,"style":1345},[39,1490],{"x":1235,"y":1461,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1492,1446],{"x":1238,"y":1464,"style":1345},[39,1494],{"x":1242,"y":1461,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1496],{"x":1249,"y":1461,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1498],{"x":1256,"y":1461,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1500],{"x":1263,"y":1461,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1502],{"x":1168,"y":1503,"width":1170,"height":1271,"rx":42,"fill":53,"stroke":54,"style":1172},"212.0",[68,1505,1306],{"x":1274,"y":1506,"style":1181},"231.0",[39,1508],{"x":1175,"y":1503,"width":1176,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1510,1511],{"x":1179,"y":1506,"style":1345},"Management pack",[39,1513],{"x":1185,"y":1503,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1515],{"x":1193,"y":1503,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1517],{"x":1200,"y":1503,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1519],{"x":1207,"y":1503,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1521],{"x":1214,"y":1503,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1523],{"x":1221,"y":1503,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1525],{"x":1228,"y":1503,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1527],{"x":1235,"y":1503,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1529,1446],{"x":1238,"y":1506,"style":1345},[39,1531],{"x":1242,"y":1503,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1533,1446],{"x":1245,"y":1506,"style":1345},[39,1535],{"x":1249,"y":1503,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1537,1446],{"x":1252,"y":1506,"style":1345},[39,1539],{"x":1256,"y":1503,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1541,1446],{"x":1259,"y":1506,"style":1345},[39,1543],{"x":1263,"y":1503,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1545],{"x":1168,"y":1546,"width":1170,"height":1271,"rx":42,"fill":53,"stroke":54,"style":1172},"240.0",[68,1548,1311],{"x":1274,"y":1549,"style":1181},"259.0",[39,1551],{"x":1175,"y":1546,"width":1176,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1553,1554],{"x":1179,"y":1549,"style":1345},"Pack to CFO",[39,1556],{"x":1185,"y":1546,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1558],{"x":1193,"y":1546,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1560],{"x":1200,"y":1546,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1562],{"x":1207,"y":1546,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1564],{"x":1214,"y":1546,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1566],{"x":1221,"y":1546,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1568],{"x":1228,"y":1546,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1570],{"x":1235,"y":1546,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1572],{"x":1242,"y":1546,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1574],{"x":1249,"y":1546,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1576],{"x":1256,"y":1546,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[39,1578],{"x":1263,"y":1546,"width":1186,"height":1271,"rx":42,"fill":45,"stroke":54,"style":1172},[68,1580,1446],{"x":1266,"y":1549,"style":1345},[139,1582,1584],{"id":1583},"step-4-a-gantt-grid-with-conditional-formatting","Step 4 — A Gantt grid with conditional formatting",[10,1586,1587,1588,1592],{},"A chart is best for presenting a plan. For a plan people edit day to day, a ",[1589,1590,1591],"strong",{},"grid"," is often more practical: one column per day, cells shaded when the task is running. It is built entirely from a conditional format, so it updates as soon as a start or end date is changed:",[144,1594,1596],{"className":186,"code":1595,"language":188,"meta":149,"style":149},"from openpyxl.formatting.rule import FormulaRule\nfrom openpyxl.styles import PatternFill\nfrom openpyxl.utils import get_column_letter\n\ngrid = wb.create_sheet(\"Grid\")\ngrid.append([\"Task\", \"Start\", \"End\"])\ndays = pd.date_range(first, last)\nfor i, d in enumerate(days, start=4):\n    grid.cell(row=1, column=i, value=d.date()).number_format = \"d\"\n    grid.column_dimensions[get_column_letter(i)].width = 4\nfor r in plan.itertuples(index=False):\n    grid.append([r.Task, r.Start.date(), r.End.date()])\nfor row in grid.iter_rows(min_row=2, min_col=2, max_col=3):\n    for c in row:\n        c.number_format = \"dd mmm\"\ngrid.column_dimensions[\"A\"].width = 18\n\nlast_col = get_column_letter(3 + len(days))\ngrid.conditional_formatting.add(f\"D2:{last_col}{n}\", FormulaRule(\n    formula=[\"AND(D$1>=$B2,D$1\u003C=$C2)\"], fill=PatternFill(\"solid\", start_color=\"0B6157\", end_color=\"0B6157\")))\ngrid.freeze_panes = \"D2\"\nwb.save(\"close_gantt.xlsx\")\nprint(len(days), \"day columns;\", \"rule range\", f\"D2:{last_col}{n}\")\n",[151,1597,1598,1610,1622,1634,1638,1653,1670,1680,1704,1739,1749,1767,1772,1807,1820,1829,1842,1846,1866,1899,1948,1958,1966],{"__ignoreMap":149},[154,1599,1600,1602,1605,1607],{"class":80,"line":156},[154,1601,196],{"class":195},[154,1603,1604],{"class":199}," openpyxl.formatting.rule ",[154,1606,203],{"class":195},[154,1608,1609],{"class":199}," FormulaRule\n",[154,1611,1612,1614,1617,1619],{"class":80,"line":209},[154,1613,196],{"class":195},[154,1615,1616],{"class":199}," openpyxl.styles ",[154,1618,203],{"class":195},[154,1620,1621],{"class":199}," PatternFill\n",[154,1623,1624,1626,1629,1631],{"class":80,"line":223},[154,1625,196],{"class":195},[154,1627,1628],{"class":199}," openpyxl.utils ",[154,1630,203],{"class":195},[154,1632,1633],{"class":199}," get_column_letter\n",[154,1635,1636],{"class":80,"line":230},[154,1637,227],{"emptyLinePlaceholder":226},[154,1639,1640,1643,1645,1648,1651],{"class":80,"line":248},[154,1641,1642],{"class":199},"grid ",[154,1644,236],{"class":195},[154,1646,1647],{"class":199}," wb.create_sheet(",[154,1649,1650],{"class":163},"\"Grid\"",[154,1652,245],{"class":199},[154,1654,1655,1658,1660,1662,1664,1666,1668],{"class":80,"line":289},[154,1656,1657],{"class":199},"grid.append([",[154,1659,367],{"class":163},[154,1661,370],{"class":199},[154,1663,276],{"class":163},[154,1665,370],{"class":199},[154,1667,265],{"class":163},[154,1669,492],{"class":199},[154,1671,1672,1675,1677],{"class":80,"line":331},[154,1673,1674],{"class":199},"days ",[154,1676,236],{"class":195},[154,1678,1679],{"class":199}," pd.date_range(first, last)\n",[154,1681,1682,1684,1687,1689,1692,1695,1698,1700,1702],{"class":80,"line":358},[154,1683,497],{"class":195},[154,1685,1686],{"class":199}," i, d ",[154,1688,503],{"class":195},[154,1690,1691],{"class":285}," enumerate",[154,1693,1694],{"class":199},"(days, ",[154,1696,1697],{"class":390},"start",[154,1699,236],{"class":195},[154,1701,730],{"class":285},[154,1703,515],{"class":199},[154,1705,1706,1709,1712,1714,1716,1718,1721,1723,1726,1729,1731,1734,1736],{"class":80,"line":564},[154,1707,1708],{"class":199},"    grid.cell(",[154,1710,1711],{"class":390},"row",[154,1713,236],{"class":195},[154,1715,326],{"class":285},[154,1717,370],{"class":199},[154,1719,1720],{"class":390},"column",[154,1722,236],{"class":195},[154,1724,1725],{"class":199},"i, ",[154,1727,1728],{"class":390},"value",[154,1730,236],{"class":195},[154,1732,1733],{"class":199},"d.date()).number_format ",[154,1735,236],{"class":195},[154,1737,1738],{"class":163}," \"d\"\n",[154,1740,1741,1744,1746],{"class":80,"line":590},[154,1742,1743],{"class":199},"    grid.column_dimensions[get_column_letter(i)].width ",[154,1745,236],{"class":195},[154,1747,1748],{"class":285}," 4\n",[154,1750,1751,1753,1755,1757,1759,1761,1763,1765],{"class":80,"line":607},[154,1752,497],{"class":195},[154,1754,500],{"class":199},[154,1756,503],{"class":195},[154,1758,506],{"class":199},[154,1760,391],{"class":390},[154,1762,236],{"class":195},[154,1764,396],{"class":285},[154,1766,515],{"class":199},[154,1768,1769],{"class":80,"line":623},[154,1770,1771],{"class":199},"    grid.append([r.Task, r.Start.date(), r.End.date()])\n",[154,1773,1774,1776,1778,1780,1783,1785,1787,1789,1791,1793,1795,1797,1799,1801,1803,1805],{"class":80,"line":634},[154,1775,497],{"class":195},[154,1777,527],{"class":199},[154,1779,503],{"class":195},[154,1781,1782],{"class":199}," grid.iter_rows(",[154,1784,535],{"class":390},[154,1786,236],{"class":195},[154,1788,540],{"class":285},[154,1790,370],{"class":199},[154,1792,545],{"class":390},[154,1794,236],{"class":195},[154,1796,540],{"class":285},[154,1798,370],{"class":199},[154,1800,554],{"class":390},[154,1802,236],{"class":195},[154,1804,580],{"class":285},[154,1806,515],{"class":199},[154,1808,1809,1812,1815,1817],{"class":80,"line":639},[154,1810,1811],{"class":195},"    for",[154,1813,1814],{"class":199}," c ",[154,1816,503],{"class":195},[154,1818,1819],{"class":199}," row:\n",[154,1821,1822,1825,1827],{"class":80,"line":657},[154,1823,1824],{"class":199},"        c.number_format ",[154,1826,236],{"class":195},[154,1828,587],{"class":163},[154,1830,1831,1834,1836,1838,1840],{"class":80,"line":668},[154,1832,1833],{"class":199},"grid.column_dimensions[",[154,1835,596],{"class":163},[154,1837,599],{"class":199},[154,1839,236],{"class":195},[154,1841,604],{"class":285},[154,1843,1844],{"class":80,"line":679},[154,1845,227],{"emptyLinePlaceholder":226},[154,1847,1848,1851,1853,1856,1858,1861,1863],{"class":80,"line":690},[154,1849,1850],{"class":199},"last_col ",[154,1852,236],{"class":195},[154,1854,1855],{"class":199}," get_column_letter(",[154,1857,580],{"class":285},[154,1859,1860],{"class":195}," +",[154,1862,647],{"class":285},[154,1864,1865],{"class":199},"(days))\n",[154,1867,1868,1871,1874,1877,1881,1884,1887,1890,1893,1896],{"class":80,"line":701},[154,1869,1870],{"class":199},"grid.conditional_formatting.add(",[154,1872,1873],{"class":195},"f",[154,1875,1876],{"class":163},"\"D2:",[154,1878,1880],{"class":1879},"sSjpA","{",[154,1882,1883],{"class":199},"last_col",[154,1885,1886],{"class":1879},"}{",[154,1888,1889],{"class":199},"n",[154,1891,1892],{"class":1879},"}",[154,1894,1895],{"class":163},"\"",[154,1897,1898],{"class":199},", FormulaRule(\n",[154,1900,1901,1904,1906,1909,1912,1915,1918,1920,1923,1926,1928,1931,1933,1936,1938,1941,1943,1945],{"class":80,"line":712},[154,1902,1903],{"class":390},"    formula",[154,1905,236],{"class":195},[154,1907,1908],{"class":199},"[",[154,1910,1911],{"class":163},"\"AND(D$1>=$B2,D$1\u003C=$C2)\"",[154,1913,1914],{"class":199},"], ",[154,1916,1917],{"class":390},"fill",[154,1919,236],{"class":195},[154,1921,1922],{"class":199},"PatternFill(",[154,1924,1925],{"class":163},"\"solid\"",[154,1927,370],{"class":199},[154,1929,1930],{"class":390},"start_color",[154,1932,236],{"class":195},[154,1934,1935],{"class":163},"\"0B6157\"",[154,1937,370],{"class":199},[154,1939,1940],{"class":390},"end_color",[154,1942,236],{"class":195},[154,1944,1935],{"class":163},[154,1946,1947],{"class":199},")))\n",[154,1949,1950,1953,1955],{"class":80,"line":761},[154,1951,1952],{"class":199},"grid.freeze_panes ",[154,1954,236],{"class":195},[154,1956,1957],{"class":163}," \"D2\"\n",[154,1959,1960,1962,1964],{"class":80,"line":790},[154,1961,1074],{"class":199},[154,1963,1077],{"class":163},[154,1965,245],{"class":199},[154,1967,1968,1970,1972,1975,1978,1981,1983,1986,1988,1990,1992,1994,1996,1998,2000,2002,2004],{"class":80,"line":795},[154,1969,361],{"class":285},[154,1971,1086],{"class":199},[154,1973,1974],{"class":285},"len",[154,1976,1977],{"class":199},"(days), ",[154,1979,1980],{"class":163},"\"day columns;\"",[154,1982,370],{"class":199},[154,1984,1985],{"class":163},"\"rule range\"",[154,1987,370],{"class":199},[154,1989,1873],{"class":195},[154,1991,1876],{"class":163},[154,1993,1880],{"class":1879},[154,1995,1883],{"class":199},[154,1997,1886],{"class":1879},[154,1999,1889],{"class":199},[154,2001,1892],{"class":1879},[154,2003,1895],{"class":163},[154,2005,245],{"class":199},[10,2007,2008,2009,2012,2013,317,2016,2019,2020,2024],{},"The formula compares each column's date in row 1 with the row's start and end dates. The anchoring is what makes one rule work for the whole grid: ",[151,2010,2011],{},"D$1"," keeps the row fixed so every cell looks at the date header, and ",[151,2014,2015],{},"$B2",[151,2017,2018],{},"$C2"," keep the columns fixed so every cell looks at its own task's dates. ",[14,2021,2023],{"href":2022},"\u002Fadvanced-data-transformation-and-cleaning\u002Fapplying-conditional-formatting-with-openpyxl\u002Fhighlight-differences-between-two-columns-with-openpyxl\u002F","Highlight Differences Between Two Columns with openpyxl"," covers formula rules in more depth.",[20,2026,29,2032,29,2035,29,2038,29,2042,29,2046,29,2053,29,2058,29,2062,29,2068,29,2074,29,2078,29,2083,29,2087,29,2094,29,2097,29,2103,29,2108,29,2112,29,2116,29,2121,29,2124,29,2129,29,2132],{"viewBox":2027,"role":23,"ariaLabelledBy":2028,"xmlns":27,"style":2031},"4 0 752 207",[2029,2030],"gantt-choice-t","gantt-choice-d","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[31,2033,2034],{"id":2029},"Chart or grid for a Gantt view",[35,2036,2037],{"id":2030},"Two panels. A stacked bar chart is a presentation object: it looks polished, shows progress shading and prints well, but changes need the data table edited. A conditional-formatting grid lives in the cells: it updates the moment a date changes and is easy to annotate, but it is less polished and gets wide for long plans.",[39,2039],{"x":730,"y":42,"width":2040,"height":2041,"fill":45},"752","207",[39,2043],{"x":2044,"y":49,"width":2045,"height":1424,"rx":1158,"fill":121,"stroke":84,"style":55},"20.0","351.0",[57,2047,2049],{"transform":2048},"translate(182.5,29.0) scale(1.083)",[61,2050],{"d":2051,"fill":64,"stroke":128,"style":2052},"M4 20h16 M7 17v-5 M12 17V7 M17 17v-8","stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[68,2054,2057],{"x":2055,"y":2056,"style":1164},"195.5","78.0","Stacked bar chart",[80,2059],{"x1":2060,"y1":1275,"x2":2061,"y2":1275,"stroke":84,"style":1172},"36.0","355.0",[68,2063,2067],{"x":2064,"y":2065,"style":2066},"60.0","111.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:start","polished, prints well",[57,2069,2071],{"transform":2070},"translate(38.5,99.5) scale(0.625)",[61,2072],{"d":100,"fill":64,"stroke":94,"style":2073},"stroke-width:2.88px;stroke-linecap:round;stroke-linejoin:round",[68,2075,2077],{"x":2064,"y":2076,"style":2066},"137.0","progress shading",[57,2079,2081],{"transform":2080},"translate(38.5,125.5) scale(0.625)",[61,2082],{"d":100,"fill":64,"stroke":94,"style":2073},[68,2084,2086],{"x":2064,"y":2085,"style":2066},"163.0","edit the table to change it",[57,2088,2090],{"transform":2089},"translate(38.5,151.5) scale(0.625)",[61,2091],{"d":2092,"fill":64,"stroke":2093,"style":2073},"M12 3.5l9.5 17h-19z M12 10v5 M12 17.8v.4","var(--gold,#b4740a)",[39,2095],{"x":2096,"y":49,"width":2045,"height":1424,"rx":1158,"fill":93,"stroke":94,"style":55},"389.0",[57,2098,2100],{"transform":2099},"translate(551.5,29.0) scale(1.083)",[61,2101],{"d":2102,"fill":64,"stroke":101,"style":2052},"M4 4h16v16H4z M4 9h16 M4 14h16 M10 4v16",[68,2104,2107],{"x":2105,"y":2056,"style":2106},"564.5","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Conditional-format grid",[80,2109],{"x1":2110,"y1":1275,"x2":2111,"y2":1275,"stroke":94,"style":1172},"405.0","724.0",[68,2113,2115],{"x":2114,"y":2065,"style":2066},"429.0","updates as dates change",[57,2117,2119],{"transform":2118},"translate(407.5,99.5) scale(0.625)",[61,2120],{"d":100,"fill":64,"stroke":94,"style":2073},[68,2122,2123],{"x":2114,"y":2076,"style":2066},"easy to annotate",[57,2125,2127],{"transform":2126},"translate(407.5,125.5) scale(0.625)",[61,2128],{"d":100,"fill":64,"stroke":94,"style":2073},[68,2130,2131],{"x":2114,"y":2085,"style":2066},"wide for long plans",[57,2133,2135],{"transform":2134},"translate(407.5,151.5) scale(0.625)",[61,2136],{"d":2137,"fill":64,"stroke":2138,"style":2073},"M6 6l12 12 M18 6L6 18","var(--accent,#f43f8f)",[10,2140,2141],{},"The grid takes two more rules well: grey weekend columns, so non-working days stand out, and a highlighted column for today, so the sheet shows at a glance which tasks should be under way. Add them after the task rule; openpyxl applies rules in the order they are added, and the task fill takes priority where they overlap because it was added first:",[144,2143,2145],{"className":186,"code":2144,"language":188,"meta":149,"style":149},"from openpyxl.styles import Font\n\ngrid.conditional_formatting.add(f\"D1:{last_col}{n}\", FormulaRule(\n    formula=[\"WEEKDAY(D$1,2)>5\"], fill=PatternFill(\"solid\", start_color=\"E6E6E6\", end_color=\"E6E6E6\")))\ngrid.conditional_formatting.add(f\"D1:{last_col}1\", FormulaRule(\n    formula=[\"D$1=TODAY()\"], font=Font(bold=True, color=\"C00000\")))\nwb.save(\"close_gantt.xlsx\")\nprint(sum(1 for d in days if d.weekday() >= 5), \"weekend days shaded;\", len(grid.conditional_formatting), \"rule ranges\")\n",[151,2146,2147,2158,2162,2185,2225,2244,2284,2292],{"__ignoreMap":149},[154,2148,2149,2151,2153,2155],{"class":80,"line":156},[154,2150,196],{"class":195},[154,2152,1616],{"class":199},[154,2154,203],{"class":195},[154,2156,2157],{"class":199}," Font\n",[154,2159,2160],{"class":80,"line":209},[154,2161,227],{"emptyLinePlaceholder":226},[154,2163,2164,2166,2168,2171,2173,2175,2177,2179,2181,2183],{"class":80,"line":223},[154,2165,1870],{"class":199},[154,2167,1873],{"class":195},[154,2169,2170],{"class":163},"\"D1:",[154,2172,1880],{"class":1879},[154,2174,1883],{"class":199},[154,2176,1886],{"class":1879},[154,2178,1889],{"class":199},[154,2180,1892],{"class":1879},[154,2182,1895],{"class":163},[154,2184,1898],{"class":199},[154,2186,2187,2189,2191,2193,2196,2198,2200,2202,2204,2206,2208,2210,2212,2215,2217,2219,2221,2223],{"class":80,"line":230},[154,2188,1903],{"class":390},[154,2190,236],{"class":195},[154,2192,1908],{"class":199},[154,2194,2195],{"class":163},"\"WEEKDAY(D$1,2)>5\"",[154,2197,1914],{"class":199},[154,2199,1917],{"class":390},[154,2201,236],{"class":195},[154,2203,1922],{"class":199},[154,2205,1925],{"class":163},[154,2207,370],{"class":199},[154,2209,1930],{"class":390},[154,2211,236],{"class":195},[154,2213,2214],{"class":163},"\"E6E6E6\"",[154,2216,370],{"class":199},[154,2218,1940],{"class":390},[154,2220,236],{"class":195},[154,2222,2214],{"class":163},[154,2224,1947],{"class":199},[154,2226,2227,2229,2231,2233,2235,2237,2239,2242],{"class":80,"line":248},[154,2228,1870],{"class":199},[154,2230,1873],{"class":195},[154,2232,2170],{"class":163},[154,2234,1880],{"class":1879},[154,2236,1883],{"class":199},[154,2238,1892],{"class":1879},[154,2240,2241],{"class":163},"1\"",[154,2243,1898],{"class":199},[154,2245,2246,2248,2250,2252,2255,2257,2260,2262,2265,2268,2270,2272,2274,2277,2279,2282],{"class":80,"line":289},[154,2247,1903],{"class":390},[154,2249,236],{"class":195},[154,2251,1908],{"class":199},[154,2253,2254],{"class":163},"\"D$1=TODAY()\"",[154,2256,1914],{"class":199},[154,2258,2259],{"class":390},"font",[154,2261,236],{"class":195},[154,2263,2264],{"class":199},"Font(",[154,2266,2267],{"class":390},"bold",[154,2269,236],{"class":195},[154,2271,756],{"class":285},[154,2273,370],{"class":199},[154,2275,2276],{"class":390},"color",[154,2278,236],{"class":195},[154,2280,2281],{"class":163},"\"C00000\"",[154,2283,1947],{"class":199},[154,2285,2286,2288,2290],{"class":80,"line":331},[154,2287,1074],{"class":199},[154,2289,1077],{"class":163},[154,2291,245],{"class":199},[154,2293,2294,2296,2298,2301,2303,2305,2308,2311,2313,2316,2319,2322,2325,2328,2331,2334,2336,2338,2341,2344],{"class":80,"line":358},[154,2295,361],{"class":285},[154,2297,1086],{"class":199},[154,2299,2300],{"class":285},"sum",[154,2302,1086],{"class":199},[154,2304,326],{"class":285},[154,2306,2307],{"class":195}," for",[154,2309,2310],{"class":199}," d ",[154,2312,503],{"class":195},[154,2314,2315],{"class":199}," days ",[154,2317,2318],{"class":195},"if",[154,2320,2321],{"class":199}," d.weekday() ",[154,2323,2324],{"class":195},">=",[154,2326,2327],{"class":285}," 5",[154,2329,2330],{"class":199},"), ",[154,2332,2333],{"class":163},"\"weekend days shaded;\"",[154,2335,370],{"class":199},[154,2337,1974],{"class":285},[154,2339,2340],{"class":199},"(grid.conditional_formatting), ",[154,2342,2343],{"class":163},"\"rule ranges\"",[154,2345,245],{"class":199},[10,2347,2348,2351],{},[151,2349,2350],{},"WEEKDAY(date, 2)"," numbers Monday as 1 and Sunday as 7, so values above 5 are the weekend. The today rule recalculates every time the file is opened, which is exactly what a live plan needs.",[139,2353,2355],{"id":2354},"common-pitfalls","Common pitfalls",[2357,2358,2359,2370,2379,2385,2391,2397],"ul",{},[2360,2361,2362,2365,2366,2369],"li",{},[1589,2363,2364],{},"Tasks upside down."," Reverse the category axis with ",[151,2367,2368],{},"orientation = \"maxMin\"",".",[2360,2371,2372,2375,2376,2369],{},[1589,2373,2374],{},"Date axis at the top."," After reversing, set ",[151,2377,2378],{},"y_axis.crosses = \"max\"",[2360,2380,2381,2384],{},[1589,2382,2383],{},"Bars squashed to the right."," Set the value axis limits to the plan's dates as serial numbers.",[2360,2386,2387,2390],{},[1589,2388,2389],{},"Off-by-one durations."," With inclusive end dates, duration is end − start + 1.",[2360,2392,2393,2396],{},[1589,2394,2395],{},"A visible start segment."," Remove both the fill and the outline of the first series.",[2360,2398,2399,2402,2403,2405],{},[1589,2400,2401],{},"Missing axes in Excel."," Set ",[151,2404,1136],{}," on both axes.",[139,2407,2409],{"id":2408},"performance-notes","Performance notes",[10,2411,2412],{},"A Gantt chart of a few dozen tasks is tiny. For plans with hundreds of tasks, the chart becomes unreadable before it becomes slow; group tasks into phases, or use the grid, which scrolls.",[139,2414,2416],{"id":2415},"conclusion","Conclusion",[10,2418,2419,2420,2422],{},"Compute each task's start and inclusive duration, split into done and remaining days, and chart start, done and remaining as a stacked horizontal bar chart with the start series made invisible. Reverse the category axis so the first task is on top, move the date axis back to the bottom with ",[151,2421,1124],{},", and set its limits to the plan's dates as Excel serial numbers. For an editable view, shade a grid of day columns with one anchored conditional-formatting formula.",[139,2424,2426],{"id":2425},"frequently-asked-questions","Frequently asked questions",[10,2428,2429,2432],{},[1589,2430,2431],{},"Can Excel make a Gantt chart?","\nNot as a built-in chart type, but a stacked bar chart becomes one: the first series holds each task's start date and is made invisible, and the second holds its duration, so the visible bar starts on the start date and is as long as the task.",[10,2434,2435,2438],{},[1589,2436,2437],{},"Why are my Gantt tasks upside down?","\nA bar chart draws the first category at the bottom. Reverse the category axis with chart.x_axis.scaling.orientation = \"maxMin\" so the first task is at the top, and set chart.y_axis.crosses = \"max\" to keep the date axis at the bottom.",[10,2440,2441,2444],{},[1589,2442,2443],{},"How do I set the date range of the Gantt chart?","\nExcel stores dates as serial numbers, so the value axis limits must be numbers too. Convert the first and last dates with (d - date(1899, 12, 30)).days and assign them to chart.y_axis.scaling.min and max.",[10,2446,2447,2450],{},[1589,2448,2449],{},"How do I show progress on each task?","\nSplit each duration into two stacked series, days done and days remaining, and colour them differently. The done segment then fills from the start of the bar as work is completed.",[139,2452,2454],{"id":2453},"related","Related",[2357,2456,2457,2463,2470,2477],{},[2360,2458,2459,2460,2462],{},"Up one level: ",[14,2461,17],{"href":16}," — every chart type.",[2360,2464,2465,2469],{},[14,2466,2468],{"href":2467},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-stacked-column-chart-with-openpyxl\u002F","Create a Stacked Column Chart with openpyxl"," — stacking in general.",[2360,2471,2472,2476],{},[14,2473,2475],{"href":2474},"\u002Fautomating-reporting-workflows\u002Fscheduling-python-excel-scripts-with-cron\u002Frun-a-report-on-the-last-business-day-of-the-month\u002F","Run a Report on the Last Business Day of the Month"," — business days for the plan.",[2360,2478,2479,2483],{},[14,2480,2482],{"href":2481},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002F","Building Finance and Operations Reports"," — the close this plan tracks.",[2485,2486,2487],"style",{},"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 .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}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}",{"title":149,"searchDepth":209,"depth":209,"links":2489},[2490,2491,2492,2493,2494,2495,2496,2497,2498,2499],{"id":141,"depth":209,"text":142},{"id":179,"depth":209,"text":180},{"id":405,"depth":209,"text":406},{"id":870,"depth":209,"text":871},{"id":1583,"depth":209,"text":1584},{"id":2354,"depth":209,"text":2355},{"id":2408,"depth":209,"text":2409},{"id":2415,"depth":209,"text":2416},{"id":2425,"depth":209,"text":2426},{"id":2453,"depth":209,"text":2454},"2026-09-25","Draw a project or month-end close timeline as a native Excel Gantt chart from a task list. Build it from a stacked bar chart with an invisible start series, put the first task at the top and the dates along the bottom, fix the date range, shade progress, and — as an alternative — paint a Gantt grid with conditional formatting.","md",[2504,2506,2508,2510],{"q":2431,"a":2505},"Not as a built-in chart type, but a stacked bar chart becomes one: the first series holds each task's start date and is made invisible, and the second holds its duration, so the visible bar starts on the start date and is as long as the task.",{"q":2437,"a":2507},"A bar chart draws the first category at the bottom. Reverse the category axis with chart.x_axis.scaling.orientation = \"maxMin\" so the first task is at the top, and set chart.y_axis.crosses = \"max\" to keep the date axis at the bottom.",{"q":2443,"a":2509},"Excel stores dates as serial numbers, so the value axis limits must be numbers too. Convert the first and last dates with (d - date(1899, 12, 30)).days and assign them to chart.y_axis.scaling.min and max.",{"q":2449,"a":2511},"Split each duration into two stacked series, days done and days remaining, and colour them differently. The done segment then fills from the start of the bar as work is completed.","create a gantt chart in excel with python",null,{"breadcrumb":2515},[2516,2518,2521],{"name":2517,"item":317},"Home",{"name":2519,"item":2520},"Formatting and Charting Excel Reports with Python","\u002Fformatting-and-charting-excel-reports-with-python\u002F",{"name":17,"item":16},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-gantt-chart-in-excel-with-python",{"title":2524,"description":2525},"Build an Excel Gantt Chart from Python with openpyxl","Create a Gantt chart in Excel with openpyxl: stacked bar chart, invisible start series, reversed task order, date axis min and max as serial numbers, crosses max, done and remaining segments, and a conditional-formatting Gantt grid alternative.","create-a-gantt-chart-in-excel-with-python","formatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-gantt-chart-in-excel-with-python\u002Findex",[2529,2530,2531],"charts-and-images","report-layouts","openpyxl","how-to","HDfWifBEVZlq-6jPQpU0xXIW8BhmehM_bBqkTV9tGak",[2535,2539],{"title":2536,"path":2537,"stem":2538,"children":-1},"Create a Funnel Chart in Excel with Python","\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-funnel-chart-in-excel-with-python","formatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-funnel-chart-in-excel-with-python\u002Findex",{"title":2540,"path":2541,"stem":2542,"children":-1},"Create a Gauge Chart in Excel with Python","\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-gauge-chart-in-excel-with-python","formatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-gauge-chart-in-excel-with-python\u002Findex",[2544,2546],{"path":2545,"title":17},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl",{"path":2547,"title":2519},"\u002Fformatting-and-charting-excel-reports-with-python",[],[2550,2553,2556,2559,2562,2565],{"path":2551,"title":2552,"kind":2532},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fbuild-a-dashboard-sheet-with-charts-from-multiple-tabs","Build a Dashboard Sheet with Charts from Multiple Tabs",{"path":2554,"title":2555,"kind":2532},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fbuild-a-rolling-12-month-chart-on-an-excel-dashboard","Build a Rolling 12-Month Chart on an Excel Dashboard",{"path":2557,"title":2558,"kind":2532},"\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Fkeep-charts-and-images-when-filling-an-excel-template","Keep Charts and Images When Filling an Excel Template",{"path":2560,"title":2561,"kind":2532},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fadd-a-chart-to-an-excel-file-with-xlsxwriter","Add a Chart to an Excel File with xlsxwriter",{"path":2563,"title":2564,"kind":2532},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fadd-sparklines-to-an-excel-report-with-xlsxwriter","Add Sparklines to an Excel Report with xlsxwriter",{"path":2566,"title":2567,"kind":2532},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fbuild-a-waterfall-chart-in-excel-with-python","Build a Waterfall Chart in Excel with Python",1790972110957]