[{"data":1,"prerenderedAt":2037},["ShallowReactive",2],{"doc:\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards":3,"surround:\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards":2029},{"id":4,"title":5,"body":6,"description":2020,"extension":2021,"meta":2022,"navigation":69,"path":2023,"seo":2024,"stem":2027,"__hash__":2028},"docs\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Findex.md","Building Multi-Sheet Excel Dashboards",{"type":7,"value":8,"toc":2009},"minimark",[9,19,22,27,35,374,380,384,403,668,684,688,695,1014,1026,1030,1048,1462,1472,1476,1491,1650,1660,1664,1679,1884,1891,1895,1915,1928,1937,1950,1954,1961,1965,2005],[10,11,12,13,18],"p",{},"A dashboard is more than a pile of tabs. It is a workbook with a deliberate front page — a Summary sheet that shows the headline numbers and a chart — backed by detail sheets a reader can drill into. Building one in Python means writing several DataFrames into a single file, computing the KPIs that go on the front, embedding a chart bound to the underlying data, and wiring up navigation so the workbook feels designed rather than dumped. This cluster sits inside ",[14,15,17],"a",{"href":16},"\u002Fautomating-reporting-workflows\u002F","Automating Reporting Workflows",": once a script can produce a polished dashboard on its own, you can schedule it and email the result without a human ever opening Excel.",[10,20,21],{},"The narrative below builds one dashboard end to end. Each section adds a layer to the same workbook: structure, then data, then the summary, then the chart, then navigation, then the finishing touches that make a tab order feel intentional.",[23,24,26],"h2",{"id":25},"structure-the-workbook-as-summary-plus-detail","Structure the workbook as Summary plus detail",[10,28,29,30,34],{},"Decide the layout before you write a cell. A reliable pattern for a sales report is one ",[31,32,33],"strong",{},"Summary"," sheet at the front and one detail sheet per dimension — here, sales by region and sales by month. Build the source DataFrames once and keep them around; every later stage reads from these:",[36,37,42],"pre",{"className":38,"code":39,"language":40,"meta":41,"style":41},"language-python shiki shiki-themes github-light github-dark","import pandas as pd\n\nsales = pd.DataFrame({\n    \"region\": [\"North\", \"South\", \"North\", \"West\", \"South\", \"West\"],\n    \"month\":  [\"Jan\", \"Jan\", \"Feb\", \"Feb\", \"Mar\", \"Mar\"],\n    \"product\": [\"Widget\", \"Gadget\", \"Widget\", \"Gizmo\", \"Gadget\", \"Widget\"],\n    \"revenue\": [12500, 9800, 14200, 7600, 11100, 8300],\n    \"units\":   [125, 98, 142, 76, 111, 83],\n})\n\nby_region = sales.groupby(\"region\", as_index=False)[[\"revenue\", \"units\"]].sum()\nby_month = sales.groupby(\"month\", as_index=False)[[\"revenue\", \"units\"]].sum()\n\nprint(by_region)\nprint(by_month)\n","python","",[43,44,45,64,71,83,122,158,193,232,271,277,282,321,352,357,366],"code",{"__ignoreMap":41},[46,47,50,54,58,61],"span",{"class":48,"line":49},"line",1,[46,51,53],{"class":52},"szBVR","import",[46,55,57],{"class":56},"sVt8B"," pandas ",[46,59,60],{"class":52},"as",[46,62,63],{"class":56}," pd\n",[46,65,67],{"class":48,"line":66},2,[46,68,70],{"emptyLinePlaceholder":69},true,"\n",[46,72,74,77,80],{"class":48,"line":73},3,[46,75,76],{"class":56},"sales ",[46,78,79],{"class":52},"=",[46,81,82],{"class":56}," pd.DataFrame({\n",[46,84,86,90,93,96,99,102,104,106,108,111,113,115,117,119],{"class":48,"line":85},4,[46,87,89],{"class":88},"sZZnC","    \"region\"",[46,91,92],{"class":56},": [",[46,94,95],{"class":88},"\"North\"",[46,97,98],{"class":56},", ",[46,100,101],{"class":88},"\"South\"",[46,103,98],{"class":56},[46,105,95],{"class":88},[46,107,98],{"class":56},[46,109,110],{"class":88},"\"West\"",[46,112,98],{"class":56},[46,114,101],{"class":88},[46,116,98],{"class":56},[46,118,110],{"class":88},[46,120,121],{"class":56},"],\n",[46,123,125,128,131,134,136,138,140,143,145,147,149,152,154,156],{"class":48,"line":124},5,[46,126,127],{"class":88},"    \"month\"",[46,129,130],{"class":56},":  [",[46,132,133],{"class":88},"\"Jan\"",[46,135,98],{"class":56},[46,137,133],{"class":88},[46,139,98],{"class":56},[46,141,142],{"class":88},"\"Feb\"",[46,144,98],{"class":56},[46,146,142],{"class":88},[46,148,98],{"class":56},[46,150,151],{"class":88},"\"Mar\"",[46,153,98],{"class":56},[46,155,151],{"class":88},[46,157,121],{"class":56},[46,159,161,164,166,169,171,174,176,178,180,183,185,187,189,191],{"class":48,"line":160},6,[46,162,163],{"class":88},"    \"product\"",[46,165,92],{"class":56},[46,167,168],{"class":88},"\"Widget\"",[46,170,98],{"class":56},[46,172,173],{"class":88},"\"Gadget\"",[46,175,98],{"class":56},[46,177,168],{"class":88},[46,179,98],{"class":56},[46,181,182],{"class":88},"\"Gizmo\"",[46,184,98],{"class":56},[46,186,173],{"class":88},[46,188,98],{"class":56},[46,190,168],{"class":88},[46,192,121],{"class":56},[46,194,196,199,201,205,207,210,212,215,217,220,222,225,227,230],{"class":48,"line":195},7,[46,197,198],{"class":88},"    \"revenue\"",[46,200,92],{"class":56},[46,202,204],{"class":203},"sj4cs","12500",[46,206,98],{"class":56},[46,208,209],{"class":203},"9800",[46,211,98],{"class":56},[46,213,214],{"class":203},"14200",[46,216,98],{"class":56},[46,218,219],{"class":203},"7600",[46,221,98],{"class":56},[46,223,224],{"class":203},"11100",[46,226,98],{"class":56},[46,228,229],{"class":203},"8300",[46,231,121],{"class":56},[46,233,235,238,241,244,246,249,251,254,256,259,261,264,266,269],{"class":48,"line":234},8,[46,236,237],{"class":88},"    \"units\"",[46,239,240],{"class":56},":   [",[46,242,243],{"class":203},"125",[46,245,98],{"class":56},[46,247,248],{"class":203},"98",[46,250,98],{"class":56},[46,252,253],{"class":203},"142",[46,255,98],{"class":56},[46,257,258],{"class":203},"76",[46,260,98],{"class":56},[46,262,263],{"class":203},"111",[46,265,98],{"class":56},[46,267,268],{"class":203},"83",[46,270,121],{"class":56},[46,272,274],{"class":48,"line":273},9,[46,275,276],{"class":56},"})\n",[46,278,280],{"class":48,"line":279},10,[46,281,70],{"emptyLinePlaceholder":69},[46,283,285,288,290,293,296,298,302,304,307,310,313,315,318],{"class":48,"line":284},11,[46,286,287],{"class":56},"by_region ",[46,289,79],{"class":52},[46,291,292],{"class":56}," sales.groupby(",[46,294,295],{"class":88},"\"region\"",[46,297,98],{"class":56},[46,299,301],{"class":300},"s4XuR","as_index",[46,303,79],{"class":52},[46,305,306],{"class":203},"False",[46,308,309],{"class":56},")[[",[46,311,312],{"class":88},"\"revenue\"",[46,314,98],{"class":56},[46,316,317],{"class":88},"\"units\"",[46,319,320],{"class":56},"]].sum()\n",[46,322,324,327,329,331,334,336,338,340,342,344,346,348,350],{"class":48,"line":323},12,[46,325,326],{"class":56},"by_month ",[46,328,79],{"class":52},[46,330,292],{"class":56},[46,332,333],{"class":88},"\"month\"",[46,335,98],{"class":56},[46,337,301],{"class":300},[46,339,79],{"class":52},[46,341,306],{"class":203},[46,343,309],{"class":56},[46,345,312],{"class":88},[46,347,98],{"class":56},[46,349,317],{"class":88},[46,351,320],{"class":56},[46,353,355],{"class":48,"line":354},13,[46,356,70],{"emptyLinePlaceholder":69},[46,358,360,363],{"class":48,"line":359},14,[46,361,362],{"class":203},"print",[46,364,365],{"class":56},"(by_region)\n",[46,367,369,371],{"class":48,"line":368},15,[46,370,362],{"class":203},[46,372,373],{"class":56},"(by_month)\n",[10,375,376,379],{},[43,377,378],{},"as_index=False"," keeps the grouping column as an ordinary column so it writes cleanly to Excel without an index leaking in. You now have three tables to place: the two details and a summary you will compute next.",[23,381,383],{"id":382},"write-every-dataframe-to-one-file-with-a-single-excelwriter","Write every DataFrame to one file with a single ExcelWriter",[10,385,386,387,390,391,394,395,398,399,402],{},"One workbook means one ",[43,388,389],{},"pd.ExcelWriter",", opened once as a context manager. Each ",[43,392,393],{},"to_excel()"," call targets a distinct ",[43,396,397],{},"sheet_name","; the order of the calls becomes the left-to-right tab order. Opening a second writer on the same path would truncate the file and throw away the first writer's sheets, so keep everything inside one ",[43,400,401],{},"with"," block:",[36,404,406],{"className":38,"code":405,"language":40,"meta":41,"style":41},"import pandas as pd\n\nsales = pd.DataFrame({\n    \"region\": [\"North\", \"South\", \"North\", \"West\", \"South\", \"West\"],\n    \"month\":  [\"Jan\", \"Jan\", \"Feb\", \"Feb\", \"Mar\", \"Mar\"],\n    \"revenue\": [12500, 9800, 14200, 7600, 11100, 8300],\n})\nby_region = sales.groupby(\"region\", as_index=False)[\"revenue\"].sum()\nby_month = sales.groupby(\"month\", as_index=False)[\"revenue\"].sum()\n\nwith pd.ExcelWriter(\"dashboard.xlsx\", engine=\"openpyxl\") as writer:\n    by_region.to_excel(writer, sheet_name=\"By Region\", index=False)\n    by_month.to_excel(writer, sheet_name=\"By Month\", index=False)\n\nprint(\"Wrote two detail sheets to dashboard.xlsx\")\n",[43,407,408,418,422,430,460,490,520,524,550,574,578,606,630,652,656],{"__ignoreMap":41},[46,409,410,412,414,416],{"class":48,"line":49},[46,411,53],{"class":52},[46,413,57],{"class":56},[46,415,60],{"class":52},[46,417,63],{"class":56},[46,419,420],{"class":48,"line":66},[46,421,70],{"emptyLinePlaceholder":69},[46,423,424,426,428],{"class":48,"line":73},[46,425,76],{"class":56},[46,427,79],{"class":52},[46,429,82],{"class":56},[46,431,432,434,436,438,440,442,444,446,448,450,452,454,456,458],{"class":48,"line":85},[46,433,89],{"class":88},[46,435,92],{"class":56},[46,437,95],{"class":88},[46,439,98],{"class":56},[46,441,101],{"class":88},[46,443,98],{"class":56},[46,445,95],{"class":88},[46,447,98],{"class":56},[46,449,110],{"class":88},[46,451,98],{"class":56},[46,453,101],{"class":88},[46,455,98],{"class":56},[46,457,110],{"class":88},[46,459,121],{"class":56},[46,461,462,464,466,468,470,472,474,476,478,480,482,484,486,488],{"class":48,"line":124},[46,463,127],{"class":88},[46,465,130],{"class":56},[46,467,133],{"class":88},[46,469,98],{"class":56},[46,471,133],{"class":88},[46,473,98],{"class":56},[46,475,142],{"class":88},[46,477,98],{"class":56},[46,479,142],{"class":88},[46,481,98],{"class":56},[46,483,151],{"class":88},[46,485,98],{"class":56},[46,487,151],{"class":88},[46,489,121],{"class":56},[46,491,492,494,496,498,500,502,504,506,508,510,512,514,516,518],{"class":48,"line":160},[46,493,198],{"class":88},[46,495,92],{"class":56},[46,497,204],{"class":203},[46,499,98],{"class":56},[46,501,209],{"class":203},[46,503,98],{"class":56},[46,505,214],{"class":203},[46,507,98],{"class":56},[46,509,219],{"class":203},[46,511,98],{"class":56},[46,513,224],{"class":203},[46,515,98],{"class":56},[46,517,229],{"class":203},[46,519,121],{"class":56},[46,521,522],{"class":48,"line":195},[46,523,276],{"class":56},[46,525,526,528,530,532,534,536,538,540,542,545,547],{"class":48,"line":234},[46,527,287],{"class":56},[46,529,79],{"class":52},[46,531,292],{"class":56},[46,533,295],{"class":88},[46,535,98],{"class":56},[46,537,301],{"class":300},[46,539,79],{"class":52},[46,541,306],{"class":203},[46,543,544],{"class":56},")[",[46,546,312],{"class":88},[46,548,549],{"class":56},"].sum()\n",[46,551,552,554,556,558,560,562,564,566,568,570,572],{"class":48,"line":273},[46,553,326],{"class":56},[46,555,79],{"class":52},[46,557,292],{"class":56},[46,559,333],{"class":88},[46,561,98],{"class":56},[46,563,301],{"class":300},[46,565,79],{"class":52},[46,567,306],{"class":203},[46,569,544],{"class":56},[46,571,312],{"class":88},[46,573,549],{"class":56},[46,575,576],{"class":48,"line":279},[46,577,70],{"emptyLinePlaceholder":69},[46,579,580,582,585,588,590,593,595,598,601,603],{"class":48,"line":284},[46,581,401],{"class":52},[46,583,584],{"class":56}," pd.ExcelWriter(",[46,586,587],{"class":88},"\"dashboard.xlsx\"",[46,589,98],{"class":56},[46,591,592],{"class":300},"engine",[46,594,79],{"class":52},[46,596,597],{"class":88},"\"openpyxl\"",[46,599,600],{"class":56},") ",[46,602,60],{"class":52},[46,604,605],{"class":56}," writer:\n",[46,607,608,611,613,615,618,620,623,625,627],{"class":48,"line":323},[46,609,610],{"class":56},"    by_region.to_excel(writer, ",[46,612,397],{"class":300},[46,614,79],{"class":52},[46,616,617],{"class":88},"\"By Region\"",[46,619,98],{"class":56},[46,621,622],{"class":300},"index",[46,624,79],{"class":52},[46,626,306],{"class":203},[46,628,629],{"class":56},")\n",[46,631,632,635,637,639,642,644,646,648,650],{"class":48,"line":354},[46,633,634],{"class":56},"    by_month.to_excel(writer, ",[46,636,397],{"class":300},[46,638,79],{"class":52},[46,640,641],{"class":88},"\"By Month\"",[46,643,98],{"class":56},[46,645,622],{"class":300},[46,647,79],{"class":52},[46,649,306],{"class":203},[46,651,629],{"class":56},[46,653,654],{"class":48,"line":359},[46,655,70],{"emptyLinePlaceholder":69},[46,657,658,660,663,666],{"class":48,"line":368},[46,659,362],{"class":203},[46,661,662],{"class":56},"(",[46,664,665],{"class":88},"\"Wrote two detail sheets to dashboard.xlsx\"",[46,667,629],{"class":56},[10,669,670,671,674,675,679,680,683],{},"Use ",[43,672,673],{},"engine=\"openpyxl\""," for this cluster — it is the engine you can reopen later to add a chart, hyperlinks, and frozen panes. The dedicated long-tail ",[14,676,678],{"href":677},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fwrite-multiple-dataframes-to-one-excel-file\u002F","Write Multiple DataFrames to One Excel File"," covers same-sheet stacking with ",[43,681,682],{},"startrow",", append mode, and the sheet-name rules in depth.",[23,685,687],{"id":686},"add-a-summary-sheet-with-kpis","Add a Summary sheet with KPIs",[10,689,690,691,694],{},"The front page carries the numbers a reader wants before any detail: totals and a couple of derived ratios. Compute them with pandas, then place a small KPI table at the top of a sheet you write ",[31,692,693],{},"first"," so it lands as the leftmost tab. Writing the Summary first is the simplest way to control tab order:",[36,696,698],{"className":38,"code":697,"language":40,"meta":41,"style":41},"import pandas as pd\n\nsales = pd.DataFrame({\n    \"region\": [\"North\", \"South\", \"North\", \"West\"],\n    \"revenue\": [12500, 9800, 14200, 7600],\n    \"units\":   [125, 98, 142, 76],\n})\n\ntotal_rev = sales[\"revenue\"].sum()\ntotal_units = sales[\"units\"].sum()\navg_price = round(total_rev \u002F total_units, 2)\n\nkpis = pd.DataFrame({\n    \"Metric\": [\"Total Revenue\", \"Total Units\", \"Avg Unit Price\"],\n    \"Value\":  [total_rev, total_units, avg_price],\n})\nby_region = sales.groupby(\"region\", as_index=False)[\"revenue\"].sum()\n\nwith pd.ExcelWriter(\"dashboard.xlsx\", engine=\"openpyxl\") as writer:\n    kpis.to_excel(writer, sheet_name=\"Summary\", index=False, startrow=1)\n    by_region.to_excel(writer, sheet_name=\"By Region\", index=False)\n\nprint(kpis)\n",[43,699,700,710,714,722,744,766,788,792,796,810,823,847,851,860,882,890,895,920,925,948,980,1001,1006],{"__ignoreMap":41},[46,701,702,704,706,708],{"class":48,"line":49},[46,703,53],{"class":52},[46,705,57],{"class":56},[46,707,60],{"class":52},[46,709,63],{"class":56},[46,711,712],{"class":48,"line":66},[46,713,70],{"emptyLinePlaceholder":69},[46,715,716,718,720],{"class":48,"line":73},[46,717,76],{"class":56},[46,719,79],{"class":52},[46,721,82],{"class":56},[46,723,724,726,728,730,732,734,736,738,740,742],{"class":48,"line":85},[46,725,89],{"class":88},[46,727,92],{"class":56},[46,729,95],{"class":88},[46,731,98],{"class":56},[46,733,101],{"class":88},[46,735,98],{"class":56},[46,737,95],{"class":88},[46,739,98],{"class":56},[46,741,110],{"class":88},[46,743,121],{"class":56},[46,745,746,748,750,752,754,756,758,760,762,764],{"class":48,"line":124},[46,747,198],{"class":88},[46,749,92],{"class":56},[46,751,204],{"class":203},[46,753,98],{"class":56},[46,755,209],{"class":203},[46,757,98],{"class":56},[46,759,214],{"class":203},[46,761,98],{"class":56},[46,763,219],{"class":203},[46,765,121],{"class":56},[46,767,768,770,772,774,776,778,780,782,784,786],{"class":48,"line":160},[46,769,237],{"class":88},[46,771,240],{"class":56},[46,773,243],{"class":203},[46,775,98],{"class":56},[46,777,248],{"class":203},[46,779,98],{"class":56},[46,781,253],{"class":203},[46,783,98],{"class":56},[46,785,258],{"class":203},[46,787,121],{"class":56},[46,789,790],{"class":48,"line":195},[46,791,276],{"class":56},[46,793,794],{"class":48,"line":234},[46,795,70],{"emptyLinePlaceholder":69},[46,797,798,801,803,806,808],{"class":48,"line":273},[46,799,800],{"class":56},"total_rev ",[46,802,79],{"class":52},[46,804,805],{"class":56}," sales[",[46,807,312],{"class":88},[46,809,549],{"class":56},[46,811,812,815,817,819,821],{"class":48,"line":279},[46,813,814],{"class":56},"total_units ",[46,816,79],{"class":52},[46,818,805],{"class":56},[46,820,317],{"class":88},[46,822,549],{"class":56},[46,824,825,828,830,833,836,839,842,845],{"class":48,"line":284},[46,826,827],{"class":56},"avg_price ",[46,829,79],{"class":52},[46,831,832],{"class":203}," round",[46,834,835],{"class":56},"(total_rev ",[46,837,838],{"class":52},"\u002F",[46,840,841],{"class":56}," total_units, ",[46,843,844],{"class":203},"2",[46,846,629],{"class":56},[46,848,849],{"class":48,"line":323},[46,850,70],{"emptyLinePlaceholder":69},[46,852,853,856,858],{"class":48,"line":354},[46,854,855],{"class":56},"kpis ",[46,857,79],{"class":52},[46,859,82],{"class":56},[46,861,862,865,867,870,872,875,877,880],{"class":48,"line":359},[46,863,864],{"class":88},"    \"Metric\"",[46,866,92],{"class":56},[46,868,869],{"class":88},"\"Total Revenue\"",[46,871,98],{"class":56},[46,873,874],{"class":88},"\"Total Units\"",[46,876,98],{"class":56},[46,878,879],{"class":88},"\"Avg Unit Price\"",[46,881,121],{"class":56},[46,883,884,887],{"class":48,"line":368},[46,885,886],{"class":88},"    \"Value\"",[46,888,889],{"class":56},":  [total_rev, total_units, avg_price],\n",[46,891,893],{"class":48,"line":892},16,[46,894,276],{"class":56},[46,896,898,900,902,904,906,908,910,912,914,916,918],{"class":48,"line":897},17,[46,899,287],{"class":56},[46,901,79],{"class":52},[46,903,292],{"class":56},[46,905,295],{"class":88},[46,907,98],{"class":56},[46,909,301],{"class":300},[46,911,79],{"class":52},[46,913,306],{"class":203},[46,915,544],{"class":56},[46,917,312],{"class":88},[46,919,549],{"class":56},[46,921,923],{"class":48,"line":922},18,[46,924,70],{"emptyLinePlaceholder":69},[46,926,928,930,932,934,936,938,940,942,944,946],{"class":48,"line":927},19,[46,929,401],{"class":52},[46,931,584],{"class":56},[46,933,587],{"class":88},[46,935,98],{"class":56},[46,937,592],{"class":300},[46,939,79],{"class":52},[46,941,597],{"class":88},[46,943,600],{"class":56},[46,945,60],{"class":52},[46,947,605],{"class":56},[46,949,951,954,956,958,961,963,965,967,969,971,973,975,978],{"class":48,"line":950},20,[46,952,953],{"class":56},"    kpis.to_excel(writer, ",[46,955,397],{"class":300},[46,957,79],{"class":52},[46,959,960],{"class":88},"\"Summary\"",[46,962,98],{"class":56},[46,964,622],{"class":300},[46,966,79],{"class":52},[46,968,306],{"class":203},[46,970,98],{"class":56},[46,972,682],{"class":300},[46,974,79],{"class":52},[46,976,977],{"class":203},"1",[46,979,629],{"class":56},[46,981,983,985,987,989,991,993,995,997,999],{"class":48,"line":982},21,[46,984,610],{"class":56},[46,986,397],{"class":300},[46,988,79],{"class":52},[46,990,617],{"class":88},[46,992,98],{"class":56},[46,994,622],{"class":300},[46,996,79],{"class":52},[46,998,306],{"class":203},[46,1000,629],{"class":56},[46,1002,1004],{"class":48,"line":1003},22,[46,1005,70],{"emptyLinePlaceholder":69},[46,1007,1009,1011],{"class":48,"line":1008},23,[46,1010,362],{"class":203},[46,1012,1013],{"class":56},"(kpis)\n",[10,1015,1016,1017,1020,1021,1025],{},"The ",[43,1018,1019],{},"startrow=1"," leaves row 1 free for a title you can drop in with openpyxl afterward. For a fuller treatment — styling the KPI block, keeping it in sync with the detail, and making it the active tab — see ",[14,1022,1024],{"href":1023},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-summary-sheet-to-excel-report-python\u002F","Add a Summary Sheet to an Excel Report with Python",".",[23,1027,1029],{"id":1028},"embed-a-chart-on-the-dashboard-sheet","Embed a chart on the dashboard sheet",[10,1031,1032,1033,1036,1037,1040,1041,1044,1045,1047],{},"A KPI table tells; a chart shows. openpyxl's ",[43,1034,1035],{},"BarChart"," reads its values straight from cells on a data sheet via ",[43,1038,1039],{},"Reference",", so the chart updates whenever that data does. Reopen the finished workbook, build a chart from the ",[31,1042,1043],{},"By Region"," sheet, and anchor it onto the ",[31,1046,33],{}," sheet:",[36,1049,1051],{"className":38,"code":1050,"language":40,"meta":41,"style":41},"import pandas as pd\nfrom openpyxl import load_workbook\nfrom openpyxl.chart import BarChart, Reference\n\nby_region = pd.DataFrame({\n    \"region\": [\"North\", \"South\", \"West\"],\n    \"revenue\": [26700, 20900, 7600],\n})\nwith pd.ExcelWriter(\"dashboard.xlsx\", engine=\"openpyxl\") as writer:\n    pd.DataFrame({\"Metric\": [\"Total Revenue\"], \"Value\": [55200]}).to_excel(\n        writer, sheet_name=\"Summary\", index=False)\n    by_region.to_excel(writer, sheet_name=\"By Region\", index=False)\n\nwb = load_workbook(\"dashboard.xlsx\")\ndata_ws = wb[\"By Region\"]\nsummary_ws = wb[\"Summary\"]\n\nchart = BarChart()\nchart.title = \"Revenue by Region\"\nchart.type = \"col\"\nn = data_ws.max_row  # header + data rows\ndata = Reference(data_ws, min_col=2, min_row=1, max_row=n)   # revenue + header\ncats = Reference(data_ws, min_col=1, min_row=2, max_row=n)   # region labels\nchart.add_data(data, titles_from_data=True)\nchart.set_categories(cats)\n\nsummary_ws.add_chart(chart, \"D2\")  # anchor top-left at cell D2\nwb.save(\"dashboard.xlsx\")\nprint(\"Embedded a bar chart on the Summary sheet\")\n",[43,1052,1053,1063,1076,1088,1092,1100,1118,1138,1142,1164,1190,1211,1231,1235,1249,1264,1277,1281,1291,1301,1311,1325,1364,1398,1414,1420,1425,1440,1450],{"__ignoreMap":41},[46,1054,1055,1057,1059,1061],{"class":48,"line":49},[46,1056,53],{"class":52},[46,1058,57],{"class":56},[46,1060,60],{"class":52},[46,1062,63],{"class":56},[46,1064,1065,1068,1071,1073],{"class":48,"line":66},[46,1066,1067],{"class":52},"from",[46,1069,1070],{"class":56}," openpyxl ",[46,1072,53],{"class":52},[46,1074,1075],{"class":56}," load_workbook\n",[46,1077,1078,1080,1083,1085],{"class":48,"line":73},[46,1079,1067],{"class":52},[46,1081,1082],{"class":56}," openpyxl.chart ",[46,1084,53],{"class":52},[46,1086,1087],{"class":56}," BarChart, Reference\n",[46,1089,1090],{"class":48,"line":85},[46,1091,70],{"emptyLinePlaceholder":69},[46,1093,1094,1096,1098],{"class":48,"line":124},[46,1095,287],{"class":56},[46,1097,79],{"class":52},[46,1099,82],{"class":56},[46,1101,1102,1104,1106,1108,1110,1112,1114,1116],{"class":48,"line":160},[46,1103,89],{"class":88},[46,1105,92],{"class":56},[46,1107,95],{"class":88},[46,1109,98],{"class":56},[46,1111,101],{"class":88},[46,1113,98],{"class":56},[46,1115,110],{"class":88},[46,1117,121],{"class":56},[46,1119,1120,1122,1124,1127,1129,1132,1134,1136],{"class":48,"line":195},[46,1121,198],{"class":88},[46,1123,92],{"class":56},[46,1125,1126],{"class":203},"26700",[46,1128,98],{"class":56},[46,1130,1131],{"class":203},"20900",[46,1133,98],{"class":56},[46,1135,219],{"class":203},[46,1137,121],{"class":56},[46,1139,1140],{"class":48,"line":234},[46,1141,276],{"class":56},[46,1143,1144,1146,1148,1150,1152,1154,1156,1158,1160,1162],{"class":48,"line":273},[46,1145,401],{"class":52},[46,1147,584],{"class":56},[46,1149,587],{"class":88},[46,1151,98],{"class":56},[46,1153,592],{"class":300},[46,1155,79],{"class":52},[46,1157,597],{"class":88},[46,1159,600],{"class":56},[46,1161,60],{"class":52},[46,1163,605],{"class":56},[46,1165,1166,1169,1172,1174,1176,1179,1182,1184,1187],{"class":48,"line":279},[46,1167,1168],{"class":56},"    pd.DataFrame({",[46,1170,1171],{"class":88},"\"Metric\"",[46,1173,92],{"class":56},[46,1175,869],{"class":88},[46,1177,1178],{"class":56},"], ",[46,1180,1181],{"class":88},"\"Value\"",[46,1183,92],{"class":56},[46,1185,1186],{"class":203},"55200",[46,1188,1189],{"class":56},"]}).to_excel(\n",[46,1191,1192,1195,1197,1199,1201,1203,1205,1207,1209],{"class":48,"line":284},[46,1193,1194],{"class":56},"        writer, ",[46,1196,397],{"class":300},[46,1198,79],{"class":52},[46,1200,960],{"class":88},[46,1202,98],{"class":56},[46,1204,622],{"class":300},[46,1206,79],{"class":52},[46,1208,306],{"class":203},[46,1210,629],{"class":56},[46,1212,1213,1215,1217,1219,1221,1223,1225,1227,1229],{"class":48,"line":323},[46,1214,610],{"class":56},[46,1216,397],{"class":300},[46,1218,79],{"class":52},[46,1220,617],{"class":88},[46,1222,98],{"class":56},[46,1224,622],{"class":300},[46,1226,79],{"class":52},[46,1228,306],{"class":203},[46,1230,629],{"class":56},[46,1232,1233],{"class":48,"line":354},[46,1234,70],{"emptyLinePlaceholder":69},[46,1236,1237,1240,1242,1245,1247],{"class":48,"line":359},[46,1238,1239],{"class":56},"wb ",[46,1241,79],{"class":52},[46,1243,1244],{"class":56}," load_workbook(",[46,1246,587],{"class":88},[46,1248,629],{"class":56},[46,1250,1251,1254,1256,1259,1261],{"class":48,"line":368},[46,1252,1253],{"class":56},"data_ws ",[46,1255,79],{"class":52},[46,1257,1258],{"class":56}," wb[",[46,1260,617],{"class":88},[46,1262,1263],{"class":56},"]\n",[46,1265,1266,1269,1271,1273,1275],{"class":48,"line":892},[46,1267,1268],{"class":56},"summary_ws ",[46,1270,79],{"class":52},[46,1272,1258],{"class":56},[46,1274,960],{"class":88},[46,1276,1263],{"class":56},[46,1278,1279],{"class":48,"line":897},[46,1280,70],{"emptyLinePlaceholder":69},[46,1282,1283,1286,1288],{"class":48,"line":922},[46,1284,1285],{"class":56},"chart ",[46,1287,79],{"class":52},[46,1289,1290],{"class":56}," BarChart()\n",[46,1292,1293,1296,1298],{"class":48,"line":927},[46,1294,1295],{"class":56},"chart.title ",[46,1297,79],{"class":52},[46,1299,1300],{"class":88}," \"Revenue by Region\"\n",[46,1302,1303,1306,1308],{"class":48,"line":950},[46,1304,1305],{"class":56},"chart.type ",[46,1307,79],{"class":52},[46,1309,1310],{"class":88}," \"col\"\n",[46,1312,1313,1316,1318,1321],{"class":48,"line":982},[46,1314,1315],{"class":56},"n ",[46,1317,79],{"class":52},[46,1319,1320],{"class":56}," data_ws.max_row  ",[46,1322,1324],{"class":1323},"sJ8bj","# header + data rows\n",[46,1326,1327,1330,1332,1335,1338,1340,1342,1344,1347,1349,1351,1353,1356,1358,1361],{"class":48,"line":1003},[46,1328,1329],{"class":56},"data ",[46,1331,79],{"class":52},[46,1333,1334],{"class":56}," Reference(data_ws, ",[46,1336,1337],{"class":300},"min_col",[46,1339,79],{"class":52},[46,1341,844],{"class":203},[46,1343,98],{"class":56},[46,1345,1346],{"class":300},"min_row",[46,1348,79],{"class":52},[46,1350,977],{"class":203},[46,1352,98],{"class":56},[46,1354,1355],{"class":300},"max_row",[46,1357,79],{"class":52},[46,1359,1360],{"class":56},"n)   ",[46,1362,1363],{"class":1323},"# revenue + header\n",[46,1365,1366,1369,1371,1373,1375,1377,1379,1381,1383,1385,1387,1389,1391,1393,1395],{"class":48,"line":1008},[46,1367,1368],{"class":56},"cats ",[46,1370,79],{"class":52},[46,1372,1334],{"class":56},[46,1374,1337],{"class":300},[46,1376,79],{"class":52},[46,1378,977],{"class":203},[46,1380,98],{"class":56},[46,1382,1346],{"class":300},[46,1384,79],{"class":52},[46,1386,844],{"class":203},[46,1388,98],{"class":56},[46,1390,1355],{"class":300},[46,1392,79],{"class":52},[46,1394,1360],{"class":56},[46,1396,1397],{"class":1323},"# region labels\n",[46,1399,1401,1404,1407,1409,1412],{"class":48,"line":1400},24,[46,1402,1403],{"class":56},"chart.add_data(data, ",[46,1405,1406],{"class":300},"titles_from_data",[46,1408,79],{"class":52},[46,1410,1411],{"class":203},"True",[46,1413,629],{"class":56},[46,1415,1417],{"class":48,"line":1416},25,[46,1418,1419],{"class":56},"chart.set_categories(cats)\n",[46,1421,1423],{"class":48,"line":1422},26,[46,1424,70],{"emptyLinePlaceholder":69},[46,1426,1428,1431,1434,1437],{"class":48,"line":1427},27,[46,1429,1430],{"class":56},"summary_ws.add_chart(chart, ",[46,1432,1433],{"class":88},"\"D2\"",[46,1435,1436],{"class":56},")  ",[46,1438,1439],{"class":1323},"# anchor top-left at cell D2\n",[46,1441,1443,1446,1448],{"class":48,"line":1442},28,[46,1444,1445],{"class":56},"wb.save(",[46,1447,587],{"class":88},[46,1449,629],{"class":56},[46,1451,1453,1455,1457,1460],{"class":48,"line":1452},29,[46,1454,362],{"class":203},[46,1456,662],{"class":56},[46,1458,1459],{"class":88},"\"Embedded a bar chart on the Summary sheet\"",[46,1461,629],{"class":56},[10,1463,1464,1465,1467,1468,1025],{},"Because the chart references cells on ",[31,1466,1043],{},", regenerating that sheet with fresh numbers redraws the bars automatically. For deeper chart configuration — line charts, axis titles, multiple series — see ",[14,1469,1471],{"href":1470},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002F","Creating Charts in Excel with openpyxl",[23,1473,1475],{"id":1474},"link-between-sheets-for-navigation","Link between sheets for navigation",[10,1477,1478,1479,1482,1483,1486,1487,1490],{},"On the Summary sheet, give the reader a way to jump to the detail. Two approaches work. The Excel ",[43,1480,1481],{},"HYPERLINK"," formula is portable and recalculates as a live cell; openpyxl's ",[43,1484,1485],{},"cell.hyperlink"," attribute writes a stored link. Internal targets use the ",[43,1488,1489],{},"#'Sheet Name'!A1"," syntax — note the quotes around names with spaces:",[36,1492,1494],{"className":38,"code":1493,"language":40,"meta":41,"style":41},"from openpyxl import load_workbook\n\nwb = load_workbook(\"dashboard.xlsx\")\nws = wb[\"Summary\"]\n\n# Option A: a HYPERLINK formula (a live, recalculating cell)\nws[\"A10\"] = '=HYPERLINK(\"#\\'By Region\\'!A1\", \"Go to By Region\")'\n\n# Option B: openpyxl's stored hyperlink on a normal cell\nlink_cell = ws[\"A11\"]\nlink_cell.value = \"Go to By Month\"\nlink_cell.hyperlink = \"#'By Month'!A1\"\nlink_cell.style = \"Hyperlink\"   # built-in blue\u002Funderline style\n\nwb.save(\"dashboard.xlsx\")\nprint(\"Added cross-sheet navigation links\")\n",[43,1495,1496,1506,1510,1522,1535,1539,1544,1570,1574,1579,1594,1604,1614,1627,1631,1639],{"__ignoreMap":41},[46,1497,1498,1500,1502,1504],{"class":48,"line":49},[46,1499,1067],{"class":52},[46,1501,1070],{"class":56},[46,1503,53],{"class":52},[46,1505,1075],{"class":56},[46,1507,1508],{"class":48,"line":66},[46,1509,70],{"emptyLinePlaceholder":69},[46,1511,1512,1514,1516,1518,1520],{"class":48,"line":73},[46,1513,1239],{"class":56},[46,1515,79],{"class":52},[46,1517,1244],{"class":56},[46,1519,587],{"class":88},[46,1521,629],{"class":56},[46,1523,1524,1527,1529,1531,1533],{"class":48,"line":85},[46,1525,1526],{"class":56},"ws ",[46,1528,79],{"class":52},[46,1530,1258],{"class":56},[46,1532,960],{"class":88},[46,1534,1263],{"class":56},[46,1536,1537],{"class":48,"line":124},[46,1538,70],{"emptyLinePlaceholder":69},[46,1540,1541],{"class":48,"line":160},[46,1542,1543],{"class":1323},"# Option A: a HYPERLINK formula (a live, recalculating cell)\n",[46,1545,1546,1549,1552,1555,1557,1560,1563,1565,1567],{"class":48,"line":195},[46,1547,1548],{"class":56},"ws[",[46,1550,1551],{"class":88},"\"A10\"",[46,1553,1554],{"class":56},"] ",[46,1556,79],{"class":52},[46,1558,1559],{"class":88}," '=HYPERLINK(\"#",[46,1561,1562],{"class":203},"\\'",[46,1564,1043],{"class":88},[46,1566,1562],{"class":203},[46,1568,1569],{"class":88},"!A1\", \"Go to By Region\")'\n",[46,1571,1572],{"class":48,"line":234},[46,1573,70],{"emptyLinePlaceholder":69},[46,1575,1576],{"class":48,"line":273},[46,1577,1578],{"class":1323},"# Option B: openpyxl's stored hyperlink on a normal cell\n",[46,1580,1581,1584,1586,1589,1592],{"class":48,"line":279},[46,1582,1583],{"class":56},"link_cell ",[46,1585,79],{"class":52},[46,1587,1588],{"class":56}," ws[",[46,1590,1591],{"class":88},"\"A11\"",[46,1593,1263],{"class":56},[46,1595,1596,1599,1601],{"class":48,"line":284},[46,1597,1598],{"class":56},"link_cell.value ",[46,1600,79],{"class":52},[46,1602,1603],{"class":88}," \"Go to By Month\"\n",[46,1605,1606,1609,1611],{"class":48,"line":323},[46,1607,1608],{"class":56},"link_cell.hyperlink ",[46,1610,79],{"class":52},[46,1612,1613],{"class":88}," \"#'By Month'!A1\"\n",[46,1615,1616,1619,1621,1624],{"class":48,"line":354},[46,1617,1618],{"class":56},"link_cell.style ",[46,1620,79],{"class":52},[46,1622,1623],{"class":88}," \"Hyperlink\"",[46,1625,1626],{"class":1323},"   # built-in blue\u002Funderline style\n",[46,1628,1629],{"class":48,"line":359},[46,1630,70],{"emptyLinePlaceholder":69},[46,1632,1633,1635,1637],{"class":48,"line":368},[46,1634,1445],{"class":56},[46,1636,587],{"class":88},[46,1638,629],{"class":56},[46,1640,1641,1643,1645,1648],{"class":48,"line":892},[46,1642,362],{"class":203},[46,1644,662],{"class":56},[46,1646,1647],{"class":88},"\"Added cross-sheet navigation links\"",[46,1649,629],{"class":56},[10,1651,1652,1653,1655,1656,1659],{},"Prefer the ",[43,1654,1481],{}," formula when the link text or target is derived from data; prefer the stored hyperlink when you also want the built-in ",[43,1657,1658],{},"Hyperlink"," cell style applied in one step.",[23,1661,1663],{"id":1662},"order-tabs-set-the-active-sheet-and-freeze-the-header","Order tabs, set the active sheet, and freeze the header",[10,1665,1666,1667,1670,1671,1674,1675,1678],{},"The last layer is presentation. Reorder tabs by rearranging ",[43,1668,1669],{},"wb._sheets",", point the cursor at the Summary on open with ",[43,1672,1673],{},"wb.active",", and freeze the header row on each detail sheet so column titles stay visible while scrolling. ",[43,1676,1677],{},"freeze_panes = \"A2\""," pins everything above row 2:",[36,1680,1682],{"className":38,"code":1681,"language":40,"meta":41,"style":41},"from openpyxl import load_workbook\n\nwb = load_workbook(\"dashboard.xlsx\")\n\n# Force tab order: Summary first, then details\norder = [\"Summary\", \"By Region\", \"By Month\"]\nwb._sheets.sort(key=lambda ws: order.index(ws.title)\n                if ws.title in order else len(order))\n\n# Open the workbook on the Summary sheet\nwb.active = wb.sheetnames.index(\"Summary\")\n\n# Freeze the header row on each detail sheet\nfor name in (\"By Region\", \"By Month\"):\n    if name in wb.sheetnames:\n        wb[name].freeze_panes = \"A2\"\n\nwb.save(\"dashboard.xlsx\")\nprint(\"Tabs:\", wb.sheetnames, \"| active:\", wb.active.title)\n",[43,1683,1684,1694,1698,1710,1714,1719,1741,1755,1778,1782,1787,1801,1805,1810,1832,1844,1854,1858,1866],{"__ignoreMap":41},[46,1685,1686,1688,1690,1692],{"class":48,"line":49},[46,1687,1067],{"class":52},[46,1689,1070],{"class":56},[46,1691,53],{"class":52},[46,1693,1075],{"class":56},[46,1695,1696],{"class":48,"line":66},[46,1697,70],{"emptyLinePlaceholder":69},[46,1699,1700,1702,1704,1706,1708],{"class":48,"line":73},[46,1701,1239],{"class":56},[46,1703,79],{"class":52},[46,1705,1244],{"class":56},[46,1707,587],{"class":88},[46,1709,629],{"class":56},[46,1711,1712],{"class":48,"line":85},[46,1713,70],{"emptyLinePlaceholder":69},[46,1715,1716],{"class":48,"line":124},[46,1717,1718],{"class":1323},"# Force tab order: Summary first, then details\n",[46,1720,1721,1724,1726,1729,1731,1733,1735,1737,1739],{"class":48,"line":160},[46,1722,1723],{"class":56},"order ",[46,1725,79],{"class":52},[46,1727,1728],{"class":56}," [",[46,1730,960],{"class":88},[46,1732,98],{"class":56},[46,1734,617],{"class":88},[46,1736,98],{"class":56},[46,1738,641],{"class":88},[46,1740,1263],{"class":56},[46,1742,1743,1746,1749,1752],{"class":48,"line":195},[46,1744,1745],{"class":56},"wb._sheets.sort(",[46,1747,1748],{"class":300},"key",[46,1750,1751],{"class":52},"=lambda",[46,1753,1754],{"class":56}," ws: order.index(ws.title)\n",[46,1756,1757,1760,1763,1766,1769,1772,1775],{"class":48,"line":234},[46,1758,1759],{"class":52},"                if",[46,1761,1762],{"class":56}," ws.title ",[46,1764,1765],{"class":52},"in",[46,1767,1768],{"class":56}," order ",[46,1770,1771],{"class":52},"else",[46,1773,1774],{"class":203}," len",[46,1776,1777],{"class":56},"(order))\n",[46,1779,1780],{"class":48,"line":273},[46,1781,70],{"emptyLinePlaceholder":69},[46,1783,1784],{"class":48,"line":279},[46,1785,1786],{"class":1323},"# Open the workbook on the Summary sheet\n",[46,1788,1789,1792,1794,1797,1799],{"class":48,"line":284},[46,1790,1791],{"class":56},"wb.active ",[46,1793,79],{"class":52},[46,1795,1796],{"class":56}," wb.sheetnames.index(",[46,1798,960],{"class":88},[46,1800,629],{"class":56},[46,1802,1803],{"class":48,"line":323},[46,1804,70],{"emptyLinePlaceholder":69},[46,1806,1807],{"class":48,"line":354},[46,1808,1809],{"class":1323},"# Freeze the header row on each detail sheet\n",[46,1811,1812,1815,1818,1820,1823,1825,1827,1829],{"class":48,"line":359},[46,1813,1814],{"class":52},"for",[46,1816,1817],{"class":56}," name ",[46,1819,1765],{"class":52},[46,1821,1822],{"class":56}," (",[46,1824,617],{"class":88},[46,1826,98],{"class":56},[46,1828,641],{"class":88},[46,1830,1831],{"class":56},"):\n",[46,1833,1834,1837,1839,1841],{"class":48,"line":368},[46,1835,1836],{"class":52},"    if",[46,1838,1817],{"class":56},[46,1840,1765],{"class":52},[46,1842,1843],{"class":56}," wb.sheetnames:\n",[46,1845,1846,1849,1851],{"class":48,"line":892},[46,1847,1848],{"class":56},"        wb[name].freeze_panes ",[46,1850,79],{"class":52},[46,1852,1853],{"class":88}," \"A2\"\n",[46,1855,1856],{"class":48,"line":897},[46,1857,70],{"emptyLinePlaceholder":69},[46,1859,1860,1862,1864],{"class":48,"line":922},[46,1861,1445],{"class":56},[46,1863,587],{"class":88},[46,1865,629],{"class":56},[46,1867,1868,1870,1872,1875,1878,1881],{"class":48,"line":927},[46,1869,362],{"class":203},[46,1871,662],{"class":56},[46,1873,1874],{"class":88},"\"Tabs:\"",[46,1876,1877],{"class":56},", wb.sheetnames, ",[46,1879,1880],{"class":88},"\"| active:\"",[46,1882,1883],{"class":56},", wb.active.title)\n",[10,1885,1886,1887,1890],{},"Setting ",[43,1888,1889],{},"freeze_panes = \"B2\""," instead would pin both the header row and the first column — useful when a detail sheet has a wide label column you want anchored as the reader scrolls right.",[23,1892,1894],{"id":1893},"frequently-asked-questions","Frequently asked questions",[10,1896,1897,1900,1901,1903,1904,1906,1907,1910,1911,1914],{},[31,1898,1899],{},"Why does only my last sheet survive when I run the script twice?","\nYou almost certainly opened a fresh ",[43,1902,389],{}," on the same path in default write mode, which truncates the file. Keep all ",[43,1905,393],{}," calls inside one ",[43,1908,1909],{},"with pd.ExcelWriter(...)"," block, or reopen with ",[43,1912,1913],{},"load_workbook()"," to add to an existing file.",[10,1916,1917,1920,1921,1924,1925,1927],{},[31,1918,1919],{},"Can I embed a chart with the xlsxwriter engine instead of openpyxl?","\nYes, but the API differs (",[43,1922,1923],{},"workbook.add_chart","). This cluster standardizes on openpyxl because you can ",[43,1926,1913],{}," an existing file to add charts, hyperlinks, and frozen panes after pandas has written the data. Pick one engine per workbook and stay with it.",[10,1929,1930,1933,1934,1936],{},[31,1931,1932],{},"Will the embedded chart update when the data changes?","\nYes, as long as the data lives in cells the chart's ",[43,1935,1039],{}," points to. Rewrite the data sheet with new numbers and the bars redraw on open. A chart built from hard-coded values would not.",[10,1938,1939,1942,1943,1945,1946,1949],{},[31,1940,1941],{},"How do I make the dashboard open on the Summary tab?","\nSet ",[43,1944,1673],{}," to the integer index of the Summary sheet (",[43,1947,1948],{},"wb.sheetnames.index(\"Summary\")",") before saving. Excel remembers which tab was active and shows it on open.",[23,1951,1953],{"id":1952},"conclusion","Conclusion",[10,1955,1956,1957,1960],{},"A multi-sheet dashboard is built in layers: one ",[43,1958,1959],{},"ExcelWriter"," writes every DataFrame into a single file, pandas computes the KPIs for a Summary sheet you write first, openpyxl embeds a chart bound to a detail sheet, hyperlinks wire up navigation, and a final pass fixes tab order, the active sheet, and frozen headers. Because each layer reads from the data already in the workbook, the whole thing regenerates correctly every time the upstream numbers change — which is exactly what you want from a scheduled report.",[23,1962,1964],{"id":1963},"where-to-go-next","Where to go next",[1966,1967,1968,1974,1979,1984,1991,1998],"ul",{},[1969,1970,1971,1973],"li",{},[14,1972,17],{"href":16}," — the pillar this dashboard plugs into, end to end.",[1969,1975,1976,1978],{},[14,1977,678],{"href":677}," — same-sheet stacking, append mode, and sheet-name rules.",[1969,1980,1981,1983],{},[14,1982,1024],{"href":1023}," — compute, style, and order the KPI front page.",[1969,1985,1986,1990],{},[14,1987,1989],{"href":1988},"\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002F","Generating Excel Reports from Templates"," — start from a pre-styled workbook instead of building layout in code.",[1969,1992,1993,1997],{},[14,1994,1996],{"href":1995},"\u002Fautomating-reporting-workflows\u002Fexporting-excel-reports-to-pdf\u002F","Exporting Excel Reports to PDF"," — ship the finished dashboard as a read-only document.",[1969,1999,2000,2004],{},[14,2001,2003],{"href":2002},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-multiple-excel-sheets-in-python\u002F","Working with Multiple Excel Sheets in Python"," — the multi-sheet fundamentals underneath this build.",[2006,2007,2008],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}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 .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":41,"searchDepth":66,"depth":66,"links":2010},[2011,2012,2013,2014,2015,2016,2017,2018,2019],{"id":25,"depth":66,"text":26},{"id":382,"depth":66,"text":383},{"id":686,"depth":66,"text":687},{"id":1028,"depth":66,"text":1029},{"id":1474,"depth":66,"text":1475},{"id":1662,"depth":66,"text":1663},{"id":1893,"depth":66,"text":1894},{"id":1952,"depth":66,"text":1953},{"id":1963,"depth":66,"text":1964},"Build a multi-sheet Excel dashboard in Python: a Summary tab with KPIs and a chart, detail sheets behind it, cross-sheet hyperlinks, frozen headers, and tab order.","md",{},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards",{"title":2025,"description":2026},"Build Multi-Sheet Excel Dashboards in Python","Assemble an Excel dashboard with Python and openpyxl: one ExcelWriter, a KPI summary sheet, an embedded bar chart, cross-sheet hyperlinks, frozen panes, and tab order.","automating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Findex","oYvONpy4tJd2f1lnGL3EKf5A06SWbaLzyq3eggPZHKQ",[2030,2034],{"title":2031,"path":2032,"stem":2033,"children":-1},"Automating Reporting Workflows with Python and Excel","\u002Fautomating-reporting-workflows","automating-reporting-workflows\u002Findex",{"title":1024,"path":2035,"stem":2036,"children":-1},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-summary-sheet-to-excel-report-python","automating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-summary-sheet-to-excel-report-python\u002Findex",1781773160643]