[{"data":1,"prerenderedAt":2398},["ShallowReactive",2],{"doc:\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-slicer-style-filters-to-an-excel-dashboard":3,"surround:\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-slicer-style-filters-to-an-excel-dashboard":2362,"ancestors:\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-slicer-style-filters-to-an-excel-dashboard":2371,"children:\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-slicer-style-filters-to-an-excel-dashboard":2376,"related:\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-slicer-style-filters-to-an-excel-dashboard":2377},{"id":4,"title":5,"body":6,"dateModified":2327,"datePublished":2327,"description":2328,"extension":2329,"faq":2330,"keyword":2339,"kind":2340,"meta":2341,"navigation":309,"path":2350,"seo":2351,"slug":2354,"stem":2355,"topics":2356,"type":2360,"__hash__":2361},"docs\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-slicer-style-filters-to-an-excel-dashboard\u002Findex.md","Add a Dropdown Filter That Drives an Excel Dashboard",{"type":7,"value":8,"toc":2314},"minimark",[9,19,196,201,226,229,585,589,592,749,756,760,771,893,909,913,923,1084,1102,1237,1241,1244,1424,1435,1439,1445,1570,1573,1701,1705,1708,2178,2181,2188,2192,2235,2239,2249,2253,2259,2265,2271,2277,2281,2310],[10,11,12,13,18],"p",{},"Readers of a dashboard want to ask their own questions — this region, that product — without editing formulas. In Excel you would add a slicer. Slicers cannot be created from Python: they live in parts of the workbook format that neither openpyxl nor XlsxWriter writes. A dropdown cell built with data validation, with formulas and charts that follow it, gives readers the same control, and it works in every Excel version and in LibreOffice. This guide, part of ",[14,15,17],"a",{"href":16},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002F","Building Multi-Sheet Excel Dashboards",", builds that filter step by step and checks each selection by recalculating the workbook.",[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,110,29,114,29,117,29,120,29,126,29,131,29,135,29,139,29,143,29,146,29,151,29,158,29,163,29,167,29,170,29,174,29,177,29,180,29,186,29,190,29,193],"svg",{"viewBox":22,"role":23,"ariaLabelledBy":24,"xmlns":27,"style":28},"4 0 752 142","img",[25,26],"sf-flow-t","sf-flow-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[31,32,33],"title",{"id":25},"How the dropdown drives the dashboard",[35,36,37],"desc",{"id":26},"The data sheet holds the rows. A hidden Lists sheet holds All plus the unique regions, named RegionList. The dropdown cell on the dashboard is restricted to that list. SUMIFS formulas read the dropdown, treating All as a wildcard, and a chart plots the formula cells, so a new selection recalculates the figures and redraws the chart.",[39,40],"rect",{"x":41,"y":42,"width":43,"height":44,"fill":45},"4","0","752","142","#ffffff",[39,47],{"x":48,"y":49,"width":50,"height":51,"rx":52,"fill":53,"stroke":54,"style":55},"20.0","16.0","115.2","110.5","12","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:2px",[57,58,60],"g",{"transform":59},"translate(65.6,37.8) scale(1.000)",[61,62],"path",{"d":63,"fill":64,"stroke":65,"style":66},"M3 5h18v14H3z M3 10h18 M3 15h18 M9 5v14","none","var(--brand-strong,#4338ca)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[68,69,73],"text",{"x":70,"y":71,"style":72},"77.6","84.2","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Data sheet",[68,75,78],{"x":70,"y":76,"style":77},"103.8","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","Sales table",[80,81],"line",{"x1":82,"y1":83,"x2":84,"y2":83,"stroke":54,"style":55},"140.2","71.2","162.2",[86,87],"polygon",{"points":88,"fill":89},"166.2,71.2 157.2,76.2 157.2,66.2","#5b5cf0",[39,91],{"x":92,"y":49,"width":50,"height":51,"rx":52,"fill":93,"stroke":94,"style":55},"171.2","#d9f4f1","var(--teal,#0f9488)",[57,96,98],{"transform":97},"translate(216.8,37.8) scale(1.000)",[61,99],{"d":100,"fill":64,"stroke":101,"style":66},"M12 3l9 5-9 5-9-5z M3 13l9 5 9-5 M3 17l9 5 9-5","var(--teal-ink,#0b6157)",[68,103,106],{"x":104,"y":71,"style":105},"228.8","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Hidden list",[68,108,109],{"x":104,"y":76,"style":77},"All + regions",[80,111],{"x1":112,"y1":83,"x2":113,"y2":83,"stroke":54,"style":55},"291.4","313.4",[86,115],{"points":116,"fill":89},"317.4,71.2 308.4,76.2 308.4,66.2",[39,118],{"x":119,"y":49,"width":50,"height":51,"rx":52,"fill":53,"stroke":54,"style":55},"322.4",[57,121,123],{"transform":122},"translate(368.0,29.0) scale(1.000)",[61,124],{"d":125,"fill":64,"stroke":65,"style":66},"M3 5h18l-7 8v6l-4 2v-8z",[68,127,130],{"x":128,"y":129,"style":72},"380.0","75.5","Dropdown",[68,132,134],{"x":128,"y":133,"style":72},"93.0","cell",[68,136,138],{"x":128,"y":137,"style":77},"112.5","data validation",[80,140],{"x1":141,"y1":83,"x2":142,"y2":83,"stroke":54,"style":55},"442.6","464.6",[86,144],{"points":145,"fill":89},"468.6,71.2 459.6,76.2 459.6,66.2",[39,147],{"x":148,"y":49,"width":50,"height":51,"rx":52,"fill":149,"stroke":150,"style":55},"473.6","#fdefd8","var(--gold,#b4740a)",[57,152,154],{"transform":153},"translate(519.2,29.0) scale(1.000)",[61,155],{"d":156,"fill":64,"stroke":157,"style":66},"M5 9h14 M4 15h14 M10 4l-2 16 M16 4l-2 16","var(--gold-ink,#7a4e06)",[68,159,162],{"x":160,"y":129,"style":161},"531.2","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","SUMIFS",[68,164,166],{"x":160,"y":165,"style":77},"95.0","IF(All, \"*\",",[68,168,169],{"x":160,"y":137,"style":77},"region)",[80,171],{"x1":172,"y1":83,"x2":173,"y2":83,"stroke":54,"style":55},"593.8","615.8",[86,175],{"points":176,"fill":89},"619.8,71.2 610.8,76.2 610.8,66.2",[39,178],{"x":179,"y":49,"width":50,"height":51,"rx":52,"fill":53,"stroke":54,"style":55},"624.8",[57,181,183],{"transform":182},"translate(670.4,29.0) scale(1.000)",[61,184],{"d":185,"fill":64,"stroke":65,"style":66},"M4 20h16 M7 17v-5 M12 17V7 M17 17v-8",[68,187,189],{"x":188,"y":129,"style":72},"682.4","Chart",[68,191,192],{"x":188,"y":165,"style":77},"plots formula",[68,194,195],{"x":188,"y":137,"style":77},"cells",[197,198,200],"h2",{"id":199},"prerequisites","Prerequisites",[202,203,208],"pre",{"className":204,"code":205,"language":206,"meta":207,"style":207},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl\n","bash","",[209,210,211],"code",{"__ignoreMap":207},[212,213,215,219,223],"span",{"class":80,"line":214},1,[212,216,218],{"class":217},"sMTad","pip",[212,220,222],{"class":221},"srMev"," install",[212,224,225],{"class":221}," openpyxl\n",[10,227,228],{},"Monthly revenue by region in a table on its own sheet:",[202,230,234],{"className":231,"code":232,"language":233,"meta":207,"style":207},"language-python shiki shiki-themes github-light github-dark-high-contrast","from openpyxl import Workbook\nfrom openpyxl.chart import BarChart, Reference\nfrom openpyxl.workbook.defined_name import DefinedName\nfrom openpyxl.worksheet.datavalidation import DataValidation\nfrom openpyxl.worksheet.table import Table\n\nROWS = [(\"Jul\", \"North\", 100), (\"Jul\", \"South\", 80), (\"Aug\", \"North\", 120), (\"Aug\", \"South\", 90),\n        (\"Aug\", \"East\", 40), (\"Sep\", \"North\", 140), (\"Sep\", \"South\", 70), (\"Sep\", \"East\", 55)]\nwb = Workbook()\ndash = wb.active\ndash.title = \"Dashboard\"\ndata = wb.create_sheet(\"Data\")\ndata.append([\"Month\", \"Region\", \"Revenue\"])\nfor row in ROWS:\n    data.append(row)\ndata.add_table(Table(displayName=\"Sales\", ref=f\"A1:C{data.max_row}\"))\n","python",[209,235,236,252,265,278,291,304,311,383,444,456,467,478,495,517,535,541],{"__ignoreMap":207},[212,237,238,242,246,249],{"class":80,"line":214},[212,239,241],{"class":240},"s-kum","from",[212,243,245],{"class":244},"skGVy"," openpyxl ",[212,247,248],{"class":240},"import",[212,250,251],{"class":244}," Workbook\n",[212,253,255,257,260,262],{"class":80,"line":254},2,[212,256,241],{"class":240},[212,258,259],{"class":244}," openpyxl.chart ",[212,261,248],{"class":240},[212,263,264],{"class":244}," BarChart, Reference\n",[212,266,268,270,273,275],{"class":80,"line":267},3,[212,269,241],{"class":240},[212,271,272],{"class":244}," openpyxl.workbook.defined_name ",[212,274,248],{"class":240},[212,276,277],{"class":244}," DefinedName\n",[212,279,281,283,286,288],{"class":80,"line":280},4,[212,282,241],{"class":240},[212,284,285],{"class":244}," openpyxl.worksheet.datavalidation ",[212,287,248],{"class":240},[212,289,290],{"class":244}," DataValidation\n",[212,292,294,296,299,301],{"class":80,"line":293},5,[212,295,241],{"class":240},[212,297,298],{"class":244}," openpyxl.worksheet.table ",[212,300,248],{"class":240},[212,302,303],{"class":244}," Table\n",[212,305,307],{"class":80,"line":306},6,[212,308,310],{"emptyLinePlaceholder":309},true,"\n",[212,312,314,318,321,324,327,330,333,335,338,341,343,345,348,350,353,355,358,360,362,364,367,369,371,373,375,377,380],{"class":80,"line":313},7,[212,315,317],{"class":316},"sP0c6","ROWS",[212,319,320],{"class":240}," =",[212,322,323],{"class":244}," [(",[212,325,326],{"class":221},"\"Jul\"",[212,328,329],{"class":244},", ",[212,331,332],{"class":221},"\"North\"",[212,334,329],{"class":244},[212,336,337],{"class":316},"100",[212,339,340],{"class":244},"), (",[212,342,326],{"class":221},[212,344,329],{"class":244},[212,346,347],{"class":221},"\"South\"",[212,349,329],{"class":244},[212,351,352],{"class":316},"80",[212,354,340],{"class":244},[212,356,357],{"class":221},"\"Aug\"",[212,359,329],{"class":244},[212,361,332],{"class":221},[212,363,329],{"class":244},[212,365,366],{"class":316},"120",[212,368,340],{"class":244},[212,370,357],{"class":221},[212,372,329],{"class":244},[212,374,347],{"class":221},[212,376,329],{"class":244},[212,378,379],{"class":316},"90",[212,381,382],{"class":244},"),\n",[212,384,386,389,391,393,396,398,401,403,406,408,410,412,415,417,419,421,423,425,428,430,432,434,436,438,441],{"class":80,"line":385},8,[212,387,388],{"class":244},"        (",[212,390,357],{"class":221},[212,392,329],{"class":244},[212,394,395],{"class":221},"\"East\"",[212,397,329],{"class":244},[212,399,400],{"class":316},"40",[212,402,340],{"class":244},[212,404,405],{"class":221},"\"Sep\"",[212,407,329],{"class":244},[212,409,332],{"class":221},[212,411,329],{"class":244},[212,413,414],{"class":316},"140",[212,416,340],{"class":244},[212,418,405],{"class":221},[212,420,329],{"class":244},[212,422,347],{"class":221},[212,424,329],{"class":244},[212,426,427],{"class":316},"70",[212,429,340],{"class":244},[212,431,405],{"class":221},[212,433,329],{"class":244},[212,435,395],{"class":221},[212,437,329],{"class":244},[212,439,440],{"class":316},"55",[212,442,443],{"class":244},")]\n",[212,445,447,450,453],{"class":80,"line":446},9,[212,448,449],{"class":244},"wb ",[212,451,452],{"class":240},"=",[212,454,455],{"class":244}," Workbook()\n",[212,457,459,462,464],{"class":80,"line":458},10,[212,460,461],{"class":244},"dash ",[212,463,452],{"class":240},[212,465,466],{"class":244}," wb.active\n",[212,468,470,473,475],{"class":80,"line":469},11,[212,471,472],{"class":244},"dash.title ",[212,474,452],{"class":240},[212,476,477],{"class":221}," \"Dashboard\"\n",[212,479,481,484,486,489,492],{"class":80,"line":480},12,[212,482,483],{"class":244},"data ",[212,485,452],{"class":240},[212,487,488],{"class":244}," wb.create_sheet(",[212,490,491],{"class":221},"\"Data\"",[212,493,494],{"class":244},")\n",[212,496,498,501,504,506,509,511,514],{"class":80,"line":497},13,[212,499,500],{"class":244},"data.append([",[212,502,503],{"class":221},"\"Month\"",[212,505,329],{"class":244},[212,507,508],{"class":221},"\"Region\"",[212,510,329],{"class":244},[212,512,513],{"class":221},"\"Revenue\"",[212,515,516],{"class":244},"])\n",[212,518,520,523,526,529,532],{"class":80,"line":519},14,[212,521,522],{"class":240},"for",[212,524,525],{"class":244}," row ",[212,527,528],{"class":240},"in",[212,530,531],{"class":316}," ROWS",[212,533,534],{"class":244},":\n",[212,536,538],{"class":80,"line":537},15,[212,539,540],{"class":244},"    data.append(row)\n",[212,542,544,547,551,553,556,558,561,563,566,569,573,576,579,582],{"class":80,"line":543},16,[212,545,546],{"class":244},"data.add_table(Table(",[212,548,550],{"class":549},"sa561","displayName",[212,552,452],{"class":240},[212,554,555],{"class":221},"\"Sales\"",[212,557,329],{"class":244},[212,559,560],{"class":549},"ref",[212,562,452],{"class":240},[212,564,565],{"class":240},"f",[212,567,568],{"class":221},"\"A1:C",[212,570,572],{"class":571},"sSjpA","{",[212,574,575],{"class":244},"data.max_row",[212,577,578],{"class":571},"}",[212,580,581],{"class":221},"\"",[212,583,584],{"class":244},"))\n",[197,586,588],{"id":587},"step-1-build-the-option-list-from-the-data","Step 1 — Build the option list from the data",[10,590,591],{},"Generate the dropdown's options from the data itself, with \"All\" first, on a hidden sheet, and name the range. The list then never falls out of step with the data:",[202,593,595],{"className":231,"code":594,"language":233,"meta":207,"style":207},"lists = wb.create_sheet(\"Lists\")\nregions = [\"All\"] + sorted({region for _, region, _ in ROWS})\nfor i, region in enumerate(regions, start=1):\n    lists.cell(i, 1, region)\nlists.sheet_state = \"hidden\"\nwb.defined_names[\"RegionList\"] = DefinedName(\"RegionList\", attr_text=f\"Lists!$A$1:$A${len(regions)}\")\nprint(regions)                                    # ['All', 'East', 'North', 'South']\n",[209,596,597,611,648,674,684,694,737],{"__ignoreMap":207},[212,598,599,602,604,606,609],{"class":80,"line":214},[212,600,601],{"class":244},"lists ",[212,603,452],{"class":240},[212,605,488],{"class":244},[212,607,608],{"class":221},"\"Lists\"",[212,610,494],{"class":244},[212,612,613,616,618,621,624,627,630,633,636,638,641,643,645],{"class":80,"line":254},[212,614,615],{"class":244},"regions ",[212,617,452],{"class":240},[212,619,620],{"class":244}," [",[212,622,623],{"class":221},"\"All\"",[212,625,626],{"class":244},"] ",[212,628,629],{"class":240},"+",[212,631,632],{"class":316}," sorted",[212,634,635],{"class":244},"({region ",[212,637,522],{"class":240},[212,639,640],{"class":244}," _, region, _ ",[212,642,528],{"class":240},[212,644,531],{"class":316},[212,646,647],{"class":244},"})\n",[212,649,650,652,655,657,660,663,666,668,671],{"class":80,"line":267},[212,651,522],{"class":240},[212,653,654],{"class":244}," i, region ",[212,656,528],{"class":240},[212,658,659],{"class":316}," enumerate",[212,661,662],{"class":244},"(regions, ",[212,664,665],{"class":549},"start",[212,667,452],{"class":240},[212,669,670],{"class":316},"1",[212,672,673],{"class":244},"):\n",[212,675,676,679,681],{"class":80,"line":280},[212,677,678],{"class":244},"    lists.cell(i, ",[212,680,670],{"class":316},[212,682,683],{"class":244},", region)\n",[212,685,686,689,691],{"class":80,"line":293},[212,687,688],{"class":244},"lists.sheet_state ",[212,690,452],{"class":240},[212,692,693],{"class":221}," \"hidden\"\n",[212,695,696,699,702,704,706,709,711,713,716,718,720,723,725,728,731,733,735],{"class":80,"line":306},[212,697,698],{"class":244},"wb.defined_names[",[212,700,701],{"class":221},"\"RegionList\"",[212,703,626],{"class":244},[212,705,452],{"class":240},[212,707,708],{"class":244}," DefinedName(",[212,710,701],{"class":221},[212,712,329],{"class":244},[212,714,715],{"class":549},"attr_text",[212,717,452],{"class":240},[212,719,565],{"class":240},[212,721,722],{"class":221},"\"Lists!$A$1:$A$",[212,724,572],{"class":571},[212,726,727],{"class":316},"len",[212,729,730],{"class":244},"(regions)",[212,732,578],{"class":571},[212,734,581],{"class":221},[212,736,494],{"class":244},[212,738,739,742,745],{"class":80,"line":313},[212,740,741],{"class":316},"print",[212,743,744],{"class":244},"(regions)                                    ",[212,746,748],{"class":747},"s-wDw","# ['All', 'East', 'North', 'South']\n",[10,750,751,752,755],{},"A list typed straight into the validation rule — ",[209,753,754],{},"formula1='\"All,East,North,South\"'"," — also works, but it is limited to 255 characters, cannot be sorted or checked with a formula, and has to be rebuilt inside the rule. A named range on a hidden sheet avoids all three.",[197,757,759],{"id":758},"step-2-the-dropdown-cell","Step 2 — The dropdown cell",[10,761,762,763,766,767,770],{},"A ",[209,764,765],{},"DataValidation"," of type ",[209,768,769],{},"list"," restricts the cell to the named range and shows the dropdown arrow. An error message explains what went wrong when someone types a value instead:",[202,772,774],{"className":231,"code":773,"language":233,"meta":207,"style":207},"dash[\"A1\"] = \"Region\"\ndash[\"B1\"] = \"All\"\npicker = DataValidation(type=\"list\", formula1=\"=RegionList\", allow_blank=False,\n                        showErrorMessage=True, errorTitle=\"Region\",\n                        error=\"Choose a region from the list.\")\ndash.add_data_validation(picker)\npicker.add(\"B1\")\n",[209,775,776,791,805,846,867,879,884],{"__ignoreMap":207},[212,777,778,781,784,786,788],{"class":80,"line":214},[212,779,780],{"class":244},"dash[",[212,782,783],{"class":221},"\"A1\"",[212,785,626],{"class":244},[212,787,452],{"class":240},[212,789,790],{"class":221}," \"Region\"\n",[212,792,793,795,798,800,802],{"class":80,"line":254},[212,794,780],{"class":244},[212,796,797],{"class":221},"\"B1\"",[212,799,626],{"class":244},[212,801,452],{"class":240},[212,803,804],{"class":221}," \"All\"\n",[212,806,807,810,812,815,818,820,823,825,828,830,833,835,838,840,843],{"class":80,"line":267},[212,808,809],{"class":244},"picker ",[212,811,452],{"class":240},[212,813,814],{"class":244}," DataValidation(",[212,816,817],{"class":549},"type",[212,819,452],{"class":240},[212,821,822],{"class":221},"\"list\"",[212,824,329],{"class":244},[212,826,827],{"class":549},"formula1",[212,829,452],{"class":240},[212,831,832],{"class":221},"\"=RegionList\"",[212,834,329],{"class":244},[212,836,837],{"class":549},"allow_blank",[212,839,452],{"class":240},[212,841,842],{"class":316},"False",[212,844,845],{"class":244},",\n",[212,847,848,851,853,856,858,861,863,865],{"class":80,"line":280},[212,849,850],{"class":549},"                        showErrorMessage",[212,852,452],{"class":240},[212,854,855],{"class":316},"True",[212,857,329],{"class":244},[212,859,860],{"class":549},"errorTitle",[212,862,452],{"class":240},[212,864,508],{"class":221},[212,866,845],{"class":244},[212,868,869,872,874,877],{"class":80,"line":293},[212,870,871],{"class":549},"                        error",[212,873,452],{"class":240},[212,875,876],{"class":221},"\"Choose a region from the list.\"",[212,878,494],{"class":244},[212,880,881],{"class":80,"line":306},[212,882,883],{"class":244},"dash.add_data_validation(picker)\n",[212,885,886,889,891],{"class":80,"line":313},[212,887,888],{"class":244},"picker.add(",[212,890,797],{"class":221},[212,892,494],{"class":244},[10,894,895,896,899,900,903,904,908],{},"Give the cell a distinct look — a light fill and a border — so readers recognise it as the control, and place it at the top left of the dashboard, where people look first. An input message (",[209,897,898],{},"promptTitle"," and ",[209,901,902],{},"prompt"," on the validation) can add a short hint that appears when the cell is selected. Dropdowns and their options are covered in more detail in ",[14,905,907],{"href":906},"\u002Fadvanced-data-transformation-and-cleaning\u002Fvalidating-excel-data-with-python\u002Fadd-dropdown-data-validation-to-excel-with-openpyxl\u002F","Add Dropdown Data Validation to Excel with openpyxl",".",[197,910,912],{"id":911},"step-3-formulas-that-follow-the-selection-including-all","Step 3 — Formulas that follow the selection, including All",[10,914,915,916,918,919,922],{},"Each figure is a ",[209,917,162],{}," whose region criterion comes from the dropdown. \"All\" is handled by swapping in the wildcard ",[209,920,921],{},"*",", which matches any text:",[202,924,926],{"className":231,"code":925,"language":233,"meta":207,"style":207},"dash[\"A3\"], dash[\"B3\"] = \"Month\", \"Revenue\"\nfor row, month in enumerate([\"Jul\", \"Aug\", \"Sep\"], start=4):\n    dash[f\"A{row}\"] = month\n    dash[f\"B{row}\"] = f'=SUMIFS(Sales[Revenue], Sales[Month], A{row}, Sales[Region], IF($B$1=\"All\",\"*\",$B$1))'\ndash[\"A7\"], dash[\"B7\"] = \"Total\", \"=SUM(B4:B6)\"\nwb.save(\"filtered_dashboard.xlsx\")\n",[209,927,928,953,988,1014,1050,1074],{"__ignoreMap":207},[212,929,930,932,935,938,941,943,945,948,950],{"class":80,"line":214},[212,931,780],{"class":244},[212,933,934],{"class":221},"\"A3\"",[212,936,937],{"class":244},"], dash[",[212,939,940],{"class":221},"\"B3\"",[212,942,626],{"class":244},[212,944,452],{"class":240},[212,946,947],{"class":221}," \"Month\"",[212,949,329],{"class":244},[212,951,952],{"class":221},"\"Revenue\"\n",[212,954,955,957,960,962,964,967,969,971,973,975,977,980,982,984,986],{"class":80,"line":254},[212,956,522],{"class":240},[212,958,959],{"class":244}," row, month ",[212,961,528],{"class":240},[212,963,659],{"class":316},[212,965,966],{"class":244},"([",[212,968,326],{"class":221},[212,970,329],{"class":244},[212,972,357],{"class":221},[212,974,329],{"class":244},[212,976,405],{"class":221},[212,978,979],{"class":244},"], ",[212,981,665],{"class":549},[212,983,452],{"class":240},[212,985,41],{"class":316},[212,987,673],{"class":244},[212,989,990,993,995,998,1000,1003,1005,1007,1009,1011],{"class":80,"line":267},[212,991,992],{"class":244},"    dash[",[212,994,565],{"class":240},[212,996,997],{"class":221},"\"A",[212,999,572],{"class":571},[212,1001,1002],{"class":244},"row",[212,1004,578],{"class":571},[212,1006,581],{"class":221},[212,1008,626],{"class":244},[212,1010,452],{"class":240},[212,1012,1013],{"class":244}," month\n",[212,1015,1016,1018,1020,1023,1025,1027,1029,1031,1033,1035,1038,1041,1043,1045,1047],{"class":80,"line":280},[212,1017,992],{"class":244},[212,1019,565],{"class":240},[212,1021,1022],{"class":221},"\"B",[212,1024,572],{"class":571},[212,1026,1002],{"class":244},[212,1028,578],{"class":571},[212,1030,581],{"class":221},[212,1032,626],{"class":244},[212,1034,452],{"class":240},[212,1036,1037],{"class":240}," f",[212,1039,1040],{"class":221},"'=SUMIFS(Sales[Revenue], Sales[Month], A",[212,1042,572],{"class":571},[212,1044,1002],{"class":244},[212,1046,578],{"class":571},[212,1048,1049],{"class":221},", Sales[Region], IF($B$1=\"All\",\"*\",$B$1))'\n",[212,1051,1052,1054,1057,1059,1062,1064,1066,1069,1071],{"class":80,"line":293},[212,1053,780],{"class":244},[212,1055,1056],{"class":221},"\"A7\"",[212,1058,937],{"class":244},[212,1060,1061],{"class":221},"\"B7\"",[212,1063,626],{"class":244},[212,1065,452],{"class":240},[212,1067,1068],{"class":221}," \"Total\"",[212,1070,329],{"class":244},[212,1072,1073],{"class":221},"\"=SUM(B4:B6)\"\n",[212,1075,1076,1079,1082],{"class":80,"line":306},[212,1077,1078],{"class":244},"wb.save(",[212,1080,1081],{"class":221},"\"filtered_dashboard.xlsx\"",[212,1083,494],{"class":244},[10,1085,1086,1087,1090,1091,1094,1095,1098,1099,908],{},"Recalculating the workbook in LibreOffice with each selection gives the expected figures: All shows 180, 250 and 265 for July to September with a total of 695; North shows 100, 120 and 140; East shows 0, 40 and 55, because East had no sales in July. The wildcard matches text only — if the filtered column holds numbers, such as a year, use two formulas selected with ",[209,1088,1089],{},"IF",", or ",[209,1092,1093],{},"SUMPRODUCT"," with an ",[209,1096,1097],{},"OR"," condition, instead of ",[209,1100,1101],{},"\"*\"",[20,1103,29,1108,29,1111,29,1114,29,1117,29,1123,29,1128,29,1134,29,1137,29,1141,29,1144,29,1148,29,1151,29,1155,29,1160,29,1166,29,1168,29,1172,29,1174,29,1177,29,1179,29,1182,29,1184,29,1187,29,1190,29,1194,29,1196,29,1199,29,1201,29,1203,29,1205,29,1207,29,1209,29,1212,29,1215,29,1219,29,1221,29,1224,29,1226,29,1228,29,1230,29,1232,29,1234],{"viewBox":1104,"role":23,"ariaLabelledBy":1105,"xmlns":27,"style":28},"4 0 752 174",[1106,1107],"sf-results-t","sf-results-d",[31,1109,1110],{"id":1106},"The dashboard for three selections",[35,1112,1113],{"id":1107},"With All selected, July to September revenue is 180, 250 and 265, total 695. With North selected it is 100, 120 and 140, total 360. With East selected it is 0, 40 and 55, total 95, because East had no July sales.",[39,1115],{"x":41,"y":42,"width":43,"height":1116,"fill":45},"174",[39,1118],{"x":48,"y":49,"width":1119,"height":1120,"rx":42,"fill":45,"stroke":1121,"style":1122},"64.4","40.0","var(--line,#cdd5e6)","stroke-width:1px",[39,1124],{"x":1125,"y":49,"width":1126,"height":1120,"rx":42,"fill":53,"stroke":54,"style":1127},"84.4","163.9","stroke-width:1.2px",[68,1129,1133],{"x":1130,"y":1131,"style":1132},"166.3","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Jul",[39,1135],{"x":1136,"y":49,"width":1126,"height":1120,"rx":42,"fill":53,"stroke":54,"style":1127},"248.3",[68,1138,1140],{"x":1139,"y":1131,"style":1132},"330.2","Aug",[39,1142],{"x":1143,"y":49,"width":1126,"height":1120,"rx":42,"fill":53,"stroke":54,"style":1127},"412.2",[68,1145,1147],{"x":1146,"y":1131,"style":1132},"494.1","Sep",[39,1149],{"x":1150,"y":49,"width":1126,"height":1120,"rx":42,"fill":53,"stroke":54,"style":1127},"576.1",[68,1152,1154],{"x":1153,"y":1131,"style":1132},"658.0","Total",[39,1156],{"x":48,"y":1157,"width":1119,"height":1158,"rx":42,"fill":1159,"stroke":1121,"style":1122},"56.0","34.0","#e7ebef",[68,1161,1165],{"x":1162,"y":1163,"style":1164},"32.0","78.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start","All",[39,1167],{"x":1125,"y":1157,"width":1126,"height":1158,"rx":42,"fill":45,"stroke":1121,"style":1122},[68,1169,1171],{"x":1130,"y":1170,"style":77},"77.0","180",[39,1173],{"x":1136,"y":1157,"width":1126,"height":1158,"rx":42,"fill":45,"stroke":1121,"style":1122},[68,1175,1176],{"x":1139,"y":1170,"style":77},"250",[39,1178],{"x":1143,"y":1157,"width":1126,"height":1158,"rx":42,"fill":45,"stroke":1121,"style":1122},[68,1180,1181],{"x":1146,"y":1170,"style":77},"265",[39,1183],{"x":1150,"y":1157,"width":1126,"height":1158,"rx":42,"fill":45,"stroke":1121,"style":1122},[68,1185,1186],{"x":1153,"y":1170,"style":77},"695",[39,1188],{"x":48,"y":1189,"width":1119,"height":1158,"rx":42,"fill":1159,"stroke":1121,"style":1122},"90.0",[68,1191,1193],{"x":1162,"y":1192,"style":1164},"112.0","North",[39,1195],{"x":1125,"y":1189,"width":1126,"height":1158,"rx":42,"fill":45,"stroke":1121,"style":1122},[68,1197,337],{"x":1130,"y":1198,"style":77},"111.0",[39,1200],{"x":1136,"y":1189,"width":1126,"height":1158,"rx":42,"fill":45,"stroke":1121,"style":1122},[68,1202,366],{"x":1139,"y":1198,"style":77},[39,1204],{"x":1143,"y":1189,"width":1126,"height":1158,"rx":42,"fill":45,"stroke":1121,"style":1122},[68,1206,414],{"x":1146,"y":1198,"style":77},[39,1208],{"x":1150,"y":1189,"width":1126,"height":1158,"rx":42,"fill":45,"stroke":1121,"style":1122},[68,1210,1211],{"x":1153,"y":1198,"style":77},"360",[39,1213],{"x":48,"y":1214,"width":1119,"height":1158,"rx":42,"fill":1159,"stroke":1121,"style":1122},"124.0",[68,1216,1218],{"x":1162,"y":1217,"style":1164},"146.0","East",[39,1220],{"x":1125,"y":1214,"width":1126,"height":1158,"rx":42,"fill":45,"stroke":1121,"style":1122},[68,1222,42],{"x":1130,"y":1223,"style":77},"145.0",[39,1225],{"x":1136,"y":1214,"width":1126,"height":1158,"rx":42,"fill":45,"stroke":1121,"style":1122},[68,1227,400],{"x":1139,"y":1223,"style":77},[39,1229],{"x":1143,"y":1214,"width":1126,"height":1158,"rx":42,"fill":45,"stroke":1121,"style":1122},[68,1231,440],{"x":1146,"y":1223,"style":77},[39,1233],{"x":1150,"y":1214,"width":1126,"height":1158,"rx":42,"fill":45,"stroke":1121,"style":1122},[68,1235,1236],{"x":1153,"y":1223,"style":77},"95",[197,1238,1240],{"id":1239},"step-4-a-chart-that-follows-the-dropdown","Step 4 — A chart that follows the dropdown",[10,1242,1243],{},"Point the chart at the formula cells, not at the data table. Every change of selection recalculates the formulas, and the chart redraws from them:",[202,1245,1247],{"className":231,"code":1246,"language":233,"meta":207,"style":207},"chart = BarChart()\nchart.x_axis.delete = False  # Excel hides axes unless this is set\nchart.y_axis.delete = False\nchart.title = \"Revenue by month\"\nchart.add_data(Reference(dash, min_col=2, min_row=3, max_row=6), titles_from_data=True)\nchart.set_categories(Reference(dash, min_col=1, min_row=4, max_row=6))\nchart.legend = None\nchart.height, chart.width = 7, 12\ndash.add_chart(chart, \"D1\")\nwb.save(\"filtered_dashboard.xlsx\")\nprint(len(dash._charts))                          # 1\n",[209,1248,1249,1259,1272,1282,1292,1337,1366,1376,1391,1401,1409],{"__ignoreMap":207},[212,1250,1251,1254,1256],{"class":80,"line":214},[212,1252,1253],{"class":244},"chart ",[212,1255,452],{"class":240},[212,1257,1258],{"class":244}," BarChart()\n",[212,1260,1261,1264,1266,1269],{"class":80,"line":254},[212,1262,1263],{"class":244},"chart.x_axis.delete ",[212,1265,452],{"class":240},[212,1267,1268],{"class":316}," False",[212,1270,1271],{"class":747},"  # Excel hides axes unless this is set\n",[212,1273,1274,1277,1279],{"class":80,"line":267},[212,1275,1276],{"class":244},"chart.y_axis.delete ",[212,1278,452],{"class":240},[212,1280,1281],{"class":316}," False\n",[212,1283,1284,1287,1289],{"class":80,"line":280},[212,1285,1286],{"class":244},"chart.title ",[212,1288,452],{"class":240},[212,1290,1291],{"class":221}," \"Revenue by month\"\n",[212,1293,1294,1297,1300,1302,1305,1307,1310,1312,1315,1317,1320,1322,1325,1328,1331,1333,1335],{"class":80,"line":293},[212,1295,1296],{"class":244},"chart.add_data(Reference(dash, ",[212,1298,1299],{"class":549},"min_col",[212,1301,452],{"class":240},[212,1303,1304],{"class":316},"2",[212,1306,329],{"class":244},[212,1308,1309],{"class":549},"min_row",[212,1311,452],{"class":240},[212,1313,1314],{"class":316},"3",[212,1316,329],{"class":244},[212,1318,1319],{"class":549},"max_row",[212,1321,452],{"class":240},[212,1323,1324],{"class":316},"6",[212,1326,1327],{"class":244},"), ",[212,1329,1330],{"class":549},"titles_from_data",[212,1332,452],{"class":240},[212,1334,855],{"class":316},[212,1336,494],{"class":244},[212,1338,1339,1342,1344,1346,1348,1350,1352,1354,1356,1358,1360,1362,1364],{"class":80,"line":306},[212,1340,1341],{"class":244},"chart.set_categories(Reference(dash, ",[212,1343,1299],{"class":549},[212,1345,452],{"class":240},[212,1347,670],{"class":316},[212,1349,329],{"class":244},[212,1351,1309],{"class":549},[212,1353,452],{"class":240},[212,1355,41],{"class":316},[212,1357,329],{"class":244},[212,1359,1319],{"class":549},[212,1361,452],{"class":240},[212,1363,1324],{"class":316},[212,1365,584],{"class":244},[212,1367,1368,1371,1373],{"class":80,"line":313},[212,1369,1370],{"class":244},"chart.legend ",[212,1372,452],{"class":240},[212,1374,1375],{"class":316}," None\n",[212,1377,1378,1381,1383,1386,1388],{"class":80,"line":385},[212,1379,1380],{"class":244},"chart.height, chart.width ",[212,1382,452],{"class":240},[212,1384,1385],{"class":316}," 7",[212,1387,329],{"class":244},[212,1389,1390],{"class":316},"12\n",[212,1392,1393,1396,1399],{"class":80,"line":446},[212,1394,1395],{"class":244},"dash.add_chart(chart, ",[212,1397,1398],{"class":221},"\"D1\"",[212,1400,494],{"class":244},[212,1402,1403,1405,1407],{"class":80,"line":458},[212,1404,1078],{"class":244},[212,1406,1081],{"class":221},[212,1408,494],{"class":244},[212,1410,1411,1413,1416,1418,1421],{"class":80,"line":469},[212,1412,741],{"class":316},[212,1414,1415],{"class":244},"(",[212,1417,727],{"class":316},[212,1419,1420],{"class":244},"(dash._charts))                          ",[212,1422,1423],{"class":747},"# 1\n",[10,1425,1426,1427,1430,1431,908],{},"A title that names the selection helps readers — put a formula such as ",[209,1428,1429],{},"=\"Revenue: \"&B1"," in the cell above the chart, since chart titles written by openpyxl are static text. Charts on dashboards are covered in ",[14,1432,1434],{"href":1433},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fbuild-a-dashboard-sheet-with-charts-from-multiple-tabs\u002F","Build a Dashboard Sheet with Charts from Multiple Tabs",[197,1436,1438],{"id":1437},"step-5-a-second-filter","Step 5 — A second filter",[10,1440,1441,1442,1444],{},"A second dropdown works the same way; each ",[209,1443,162],{}," simply gains another criterion pair. Suppose the data had a product column — the formula for a region and product filter, both with \"All\", becomes:",[202,1446,1448],{"className":231,"code":1447,"language":233,"meta":207,"style":207},"def filtered_sum(month_cell, region_cell=\"$B$1\", product_cell=\"$B$2\"):\n    return (f'=SUMIFS(Sales[Revenue], Sales[Month], {month_cell}, '\n            f'Sales[Region], IF({region_cell}=\"All\",\"*\",{region_cell}), '\n            f'Sales[Product], IF({product_cell}=\"All\",\"*\",{product_cell}))')\n\nprint(filtered_sum(\"A4\"))\n",[209,1449,1450,1477,1500,1527,1554,1558],{"__ignoreMap":207},[212,1451,1452,1455,1459,1462,1464,1467,1470,1472,1475],{"class":80,"line":214},[212,1453,1454],{"class":240},"def",[212,1456,1458],{"class":1457},"s_Opv"," filtered_sum",[212,1460,1461],{"class":244},"(month_cell, region_cell",[212,1463,452],{"class":240},[212,1465,1466],{"class":221},"\"$B$1\"",[212,1468,1469],{"class":244},", product_cell",[212,1471,452],{"class":240},[212,1473,1474],{"class":221},"\"$B$2\"",[212,1476,673],{"class":244},[212,1478,1479,1482,1485,1487,1490,1492,1495,1497],{"class":80,"line":254},[212,1480,1481],{"class":240},"    return",[212,1483,1484],{"class":244}," (",[212,1486,565],{"class":240},[212,1488,1489],{"class":221},"'=SUMIFS(Sales[Revenue], Sales[Month], ",[212,1491,572],{"class":571},[212,1493,1494],{"class":244},"month_cell",[212,1496,578],{"class":571},[212,1498,1499],{"class":221},", '\n",[212,1501,1502,1505,1508,1510,1513,1515,1518,1520,1522,1524],{"class":80,"line":267},[212,1503,1504],{"class":240},"            f",[212,1506,1507],{"class":221},"'Sales[Region], IF(",[212,1509,572],{"class":571},[212,1511,1512],{"class":244},"region_cell",[212,1514,578],{"class":571},[212,1516,1517],{"class":221},"=\"All\",\"*\",",[212,1519,572],{"class":571},[212,1521,1512],{"class":244},[212,1523,578],{"class":571},[212,1525,1526],{"class":221},"), '\n",[212,1528,1529,1531,1534,1536,1539,1541,1543,1545,1547,1549,1552],{"class":80,"line":280},[212,1530,1504],{"class":240},[212,1532,1533],{"class":221},"'Sales[Product], IF(",[212,1535,572],{"class":571},[212,1537,1538],{"class":244},"product_cell",[212,1540,578],{"class":571},[212,1542,1517],{"class":221},[212,1544,572],{"class":571},[212,1546,1538],{"class":244},[212,1548,578],{"class":571},[212,1550,1551],{"class":221},"))'",[212,1553,494],{"class":244},[212,1555,1556],{"class":80,"line":293},[212,1557,310],{"emptyLinePlaceholder":309},[212,1559,1560,1562,1565,1568],{"class":80,"line":306},[212,1561,741],{"class":316},[212,1563,1564],{"class":244},"(filtered_sum(",[212,1566,1567],{"class":221},"\"A4\"",[212,1569,584],{"class":244},[10,1571,1572],{},"Keeping formula construction in a function means the dashboard's cells all follow one pattern, and adding a third filter is a one-line change. Put all the filter cells together at the top of the dashboard, labelled, so readers see at a glance what the figures are filtered by.",[20,1574,29,1579,29,1582,29,1585,29,1588,29,1593,29,1599,29,1604,29,1609,29,1614,29,1621,29,1625,29,1630,29,1634,29,1641,29,1645,29,1651,29,1654,29,1659,29,1664,29,1668,29,1672,29,1677,29,1680,29,1685,29,1688,29,1693,29,1696],{"viewBox":1575,"role":23,"ariaLabelledBy":1576,"xmlns":27,"style":28},"4 0 752 233",[1577,1578],"sf-vs-t","sf-vs-d",[31,1580,1581],{"id":1577},"Native slicer or dropdown filter",[35,1583,1584],{"id":1578},"A native slicer offers buttons and multi-select and connects to pivot tables and tables, but it cannot be created from Python. A dropdown filter with formulas can be generated by openpyxl or XlsxWriter, works in every Excel version and LibreOffice, and drives any formula or chart, but selects one value at a time.",[39,1586],{"x":41,"y":42,"width":43,"height":1587,"fill":45},"233",[39,1589],{"x":48,"y":49,"width":1590,"height":1591,"rx":1592,"fill":149,"stroke":150,"style":55},"351.0","201.0","14",[57,1594,1596],{"transform":1595},"translate(182.5,29.0) scale(1.083)",[61,1597],{"d":125,"fill":64,"stroke":157,"style":1598},"stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[68,1600,1603],{"x":1601,"y":1163,"style":1602},"195.5","font-size:14px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Native slicer",[80,1605],{"x1":1606,"y1":1607,"x2":1608,"y2":1607,"stroke":150,"style":1122},"36.0","91.0","355.0",[68,1610,1613],{"x":1611,"y":1198,"style":1612},"60.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:start","buttons, multi-select",[57,1615,1617],{"transform":1616},"translate(38.5,99.5) scale(0.625)",[61,1618],{"d":1619,"fill":64,"stroke":94,"style":1620},"M5 12.5l4.5 4.5L19 7","stroke-width:2.88px;stroke-linecap:round;stroke-linejoin:round",[68,1622,1624],{"x":1611,"y":1623,"style":1612},"137.0","drives pivots and tables",[57,1626,1628],{"transform":1627},"translate(38.5,125.5) scale(0.625)",[61,1629],{"d":1619,"fill":64,"stroke":94,"style":1620},[68,1631,1633],{"x":1611,"y":1632,"style":1612},"163.0","cannot be written from Python",[57,1635,1637],{"transform":1636},"translate(38.5,151.5) scale(0.625)",[61,1638],{"d":1639,"fill":64,"stroke":1640,"style":1620},"M6 6l12 12 M18 6L6 18","var(--accent,#f43f8f)",[68,1642,1644],{"x":1611,"y":1643,"style":1612},"189.0","added by hand in Excel",[57,1646,1648],{"transform":1647},"translate(38.5,177.5) scale(0.625)",[61,1649],{"d":1650,"fill":64,"stroke":150,"style":1620},"M12 3.5l9.5 17h-19z M12 10v5 M12 17.8v.4",[39,1652],{"x":1653,"y":49,"width":1590,"height":1591,"rx":1592,"fill":93,"stroke":94,"style":55},"389.0",[57,1655,1657],{"transform":1656},"translate(551.5,29.0) scale(1.083)",[61,1658],{"d":1619,"fill":64,"stroke":101,"style":1598},[68,1660,1663],{"x":1661,"y":1163,"style":1662},"564.5","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Dropdown + formulas",[80,1665],{"x1":1666,"y1":1607,"x2":1667,"y2":1607,"stroke":94,"style":1122},"405.0","724.0",[68,1669,1671],{"x":1670,"y":1198,"style":1612},"429.0","generated from Python",[57,1673,1675],{"transform":1674},"translate(407.5,99.5) scale(0.625)",[61,1676],{"d":1619,"fill":64,"stroke":94,"style":1620},[68,1678,1679],{"x":1670,"y":1623,"style":1612},"works everywhere",[57,1681,1683],{"transform":1682},"translate(407.5,125.5) scale(0.625)",[61,1684],{"d":1619,"fill":64,"stroke":94,"style":1620},[68,1686,1687],{"x":1670,"y":1632,"style":1612},"drives any formula or chart",[57,1689,1691],{"transform":1690},"translate(407.5,151.5) scale(0.625)",[61,1692],{"d":1619,"fill":64,"stroke":94,"style":1620},[68,1694,1695],{"x":1670,"y":1643,"style":1612},"one value at a time",[57,1697,1699],{"transform":1698},"translate(407.5,177.5) scale(0.625)",[61,1700],{"d":1639,"fill":64,"stroke":1640,"style":1620},[197,1702,1704],{"id":1703},"step-6-test-every-selection","Step 6 — Test every selection",[10,1706,1707],{},"A filter has as many states as it has options, and each should be checked. Compute the expected figures for every option with pandas, then set the dropdown to each option in a copy of the workbook, recalculate it and compare:",[202,1709,1711],{"className":231,"code":1710,"language":233,"meta":207,"style":207},"import subprocess\n\nimport pandas as pd\nfrom openpyxl import load_workbook\n\nframe = pd.DataFrame(ROWS, columns=[\"Month\", \"Region\", \"Revenue\"])\nexpected = {}\nfor option in regions:\n    chosen = frame if option == \"All\" else frame[frame[\"Region\"] == option]\n    by_month = chosen.groupby(\"Month\")[\"Revenue\"].sum()\n    expected[option] = [int(by_month.get(m, 0)) for m in [\"Jul\", \"Aug\", \"Sep\"]]\nprint(expected[\"East\"])                            # [0, 40, 55]\n\ndef rendered_figures(option):\n    wb_copy = load_workbook(\"filtered_dashboard.xlsx\")\n    wb_copy[\"Dashboard\"][\"B1\"] = option\n    wb_copy.save(f\"check_{option}.xlsx\")\n    subprocess.run([\"soffice\", \"--headless\", \"--convert-to\",\n                    \"csv:Text - txt - csv (StarCalc):44,34,76,1,,0,false,true,true\", f\"check_{option}.xlsx\"],\n                   check=True, capture_output=True, timeout=120)\n    lines = open(f\"check_{option}.csv\").read().splitlines()\n    return [int(line.split(\",\")[1]) for line in lines[3:6]]\n\nfor option in regions:\n    assert rendered_figures(option) == expected[option], option\nprint(\"all selections match\")\n",[209,1712,1713,1720,1724,1737,1748,1752,1786,1796,1808,1844,1864,1906,1921,1925,1935,1949,1969,1992,2013,2036,2066,2095,2136,2141,2152,2166],{"__ignoreMap":207},[212,1714,1715,1717],{"class":80,"line":214},[212,1716,248],{"class":240},[212,1718,1719],{"class":244}," subprocess\n",[212,1721,1722],{"class":80,"line":254},[212,1723,310],{"emptyLinePlaceholder":309},[212,1725,1726,1728,1731,1734],{"class":80,"line":267},[212,1727,248],{"class":240},[212,1729,1730],{"class":244}," pandas ",[212,1732,1733],{"class":240},"as",[212,1735,1736],{"class":244}," pd\n",[212,1738,1739,1741,1743,1745],{"class":80,"line":280},[212,1740,241],{"class":240},[212,1742,245],{"class":244},[212,1744,248],{"class":240},[212,1746,1747],{"class":244}," load_workbook\n",[212,1749,1750],{"class":80,"line":293},[212,1751,310],{"emptyLinePlaceholder":309},[212,1753,1754,1757,1759,1762,1764,1766,1769,1771,1774,1776,1778,1780,1782,1784],{"class":80,"line":306},[212,1755,1756],{"class":244},"frame ",[212,1758,452],{"class":240},[212,1760,1761],{"class":244}," pd.DataFrame(",[212,1763,317],{"class":316},[212,1765,329],{"class":244},[212,1767,1768],{"class":549},"columns",[212,1770,452],{"class":240},[212,1772,1773],{"class":244},"[",[212,1775,503],{"class":221},[212,1777,329],{"class":244},[212,1779,508],{"class":221},[212,1781,329],{"class":244},[212,1783,513],{"class":221},[212,1785,516],{"class":244},[212,1787,1788,1791,1793],{"class":80,"line":313},[212,1789,1790],{"class":244},"expected ",[212,1792,452],{"class":240},[212,1794,1795],{"class":244}," {}\n",[212,1797,1798,1800,1803,1805],{"class":80,"line":385},[212,1799,522],{"class":240},[212,1801,1802],{"class":244}," option ",[212,1804,528],{"class":240},[212,1806,1807],{"class":244}," regions:\n",[212,1809,1810,1813,1815,1818,1821,1823,1826,1829,1832,1835,1837,1839,1841],{"class":80,"line":446},[212,1811,1812],{"class":244},"    chosen ",[212,1814,452],{"class":240},[212,1816,1817],{"class":244}," frame ",[212,1819,1820],{"class":240},"if",[212,1822,1802],{"class":244},[212,1824,1825],{"class":240},"==",[212,1827,1828],{"class":221}," \"All\"",[212,1830,1831],{"class":240}," else",[212,1833,1834],{"class":244}," frame[frame[",[212,1836,508],{"class":221},[212,1838,626],{"class":244},[212,1840,1825],{"class":240},[212,1842,1843],{"class":244}," option]\n",[212,1845,1846,1849,1851,1854,1856,1859,1861],{"class":80,"line":458},[212,1847,1848],{"class":244},"    by_month ",[212,1850,452],{"class":240},[212,1852,1853],{"class":244}," chosen.groupby(",[212,1855,503],{"class":221},[212,1857,1858],{"class":244},")[",[212,1860,513],{"class":221},[212,1862,1863],{"class":244},"].sum()\n",[212,1865,1866,1869,1871,1873,1876,1879,1881,1884,1886,1889,1891,1893,1895,1897,1899,1901,1903],{"class":80,"line":469},[212,1867,1868],{"class":244},"    expected[option] ",[212,1870,452],{"class":240},[212,1872,620],{"class":244},[212,1874,1875],{"class":316},"int",[212,1877,1878],{"class":244},"(by_month.get(m, ",[212,1880,42],{"class":316},[212,1882,1883],{"class":244},")) ",[212,1885,522],{"class":240},[212,1887,1888],{"class":244}," m ",[212,1890,528],{"class":240},[212,1892,620],{"class":244},[212,1894,326],{"class":221},[212,1896,329],{"class":244},[212,1898,357],{"class":221},[212,1900,329],{"class":244},[212,1902,405],{"class":221},[212,1904,1905],{"class":244},"]]\n",[212,1907,1908,1910,1913,1915,1918],{"class":80,"line":480},[212,1909,741],{"class":316},[212,1911,1912],{"class":244},"(expected[",[212,1914,395],{"class":221},[212,1916,1917],{"class":244},"])                            ",[212,1919,1920],{"class":747},"# [0, 40, 55]\n",[212,1922,1923],{"class":80,"line":497},[212,1924,310],{"emptyLinePlaceholder":309},[212,1926,1927,1929,1932],{"class":80,"line":519},[212,1928,1454],{"class":240},[212,1930,1931],{"class":1457}," rendered_figures",[212,1933,1934],{"class":244},"(option):\n",[212,1936,1937,1940,1942,1945,1947],{"class":80,"line":537},[212,1938,1939],{"class":244},"    wb_copy ",[212,1941,452],{"class":240},[212,1943,1944],{"class":244}," load_workbook(",[212,1946,1081],{"class":221},[212,1948,494],{"class":244},[212,1950,1951,1954,1957,1960,1962,1964,1966],{"class":80,"line":543},[212,1952,1953],{"class":244},"    wb_copy[",[212,1955,1956],{"class":221},"\"Dashboard\"",[212,1958,1959],{"class":244},"][",[212,1961,797],{"class":221},[212,1963,626],{"class":244},[212,1965,452],{"class":240},[212,1967,1968],{"class":244}," option\n",[212,1970,1972,1975,1977,1980,1982,1985,1987,1990],{"class":80,"line":1971},17,[212,1973,1974],{"class":244},"    wb_copy.save(",[212,1976,565],{"class":240},[212,1978,1979],{"class":221},"\"check_",[212,1981,572],{"class":571},[212,1983,1984],{"class":244},"option",[212,1986,578],{"class":571},[212,1988,1989],{"class":221},".xlsx\"",[212,1991,494],{"class":244},[212,1993,1995,1998,2001,2003,2006,2008,2011],{"class":80,"line":1994},18,[212,1996,1997],{"class":244},"    subprocess.run([",[212,1999,2000],{"class":221},"\"soffice\"",[212,2002,329],{"class":244},[212,2004,2005],{"class":221},"\"--headless\"",[212,2007,329],{"class":244},[212,2009,2010],{"class":221},"\"--convert-to\"",[212,2012,845],{"class":244},[212,2014,2016,2019,2021,2023,2025,2027,2029,2031,2033],{"class":80,"line":2015},19,[212,2017,2018],{"class":221},"                    \"csv:Text - txt - csv (StarCalc):44,34,76,1,,0,false,true,true\"",[212,2020,329],{"class":244},[212,2022,565],{"class":240},[212,2024,1979],{"class":221},[212,2026,572],{"class":571},[212,2028,1984],{"class":244},[212,2030,578],{"class":571},[212,2032,1989],{"class":221},[212,2034,2035],{"class":244},"],\n",[212,2037,2039,2042,2044,2046,2048,2051,2053,2055,2057,2060,2062,2064],{"class":80,"line":2038},20,[212,2040,2041],{"class":549},"                   check",[212,2043,452],{"class":240},[212,2045,855],{"class":316},[212,2047,329],{"class":244},[212,2049,2050],{"class":549},"capture_output",[212,2052,452],{"class":240},[212,2054,855],{"class":316},[212,2056,329],{"class":244},[212,2058,2059],{"class":549},"timeout",[212,2061,452],{"class":240},[212,2063,366],{"class":316},[212,2065,494],{"class":244},[212,2067,2069,2072,2074,2077,2079,2081,2083,2085,2087,2089,2092],{"class":80,"line":2068},21,[212,2070,2071],{"class":244},"    lines ",[212,2073,452],{"class":240},[212,2075,2076],{"class":316}," open",[212,2078,1415],{"class":244},[212,2080,565],{"class":240},[212,2082,1979],{"class":221},[212,2084,572],{"class":571},[212,2086,1984],{"class":244},[212,2088,578],{"class":571},[212,2090,2091],{"class":221},".csv\"",[212,2093,2094],{"class":244},").read().splitlines()\n",[212,2096,2098,2100,2102,2104,2107,2110,2112,2114,2117,2119,2122,2124,2127,2129,2132,2134],{"class":80,"line":2097},22,[212,2099,1481],{"class":240},[212,2101,620],{"class":244},[212,2103,1875],{"class":316},[212,2105,2106],{"class":244},"(line.split(",[212,2108,2109],{"class":221},"\",\"",[212,2111,1858],{"class":244},[212,2113,670],{"class":316},[212,2115,2116],{"class":244},"]) ",[212,2118,522],{"class":240},[212,2120,2121],{"class":244}," line ",[212,2123,528],{"class":240},[212,2125,2126],{"class":244}," lines[",[212,2128,1314],{"class":316},[212,2130,2131],{"class":244},":",[212,2133,1324],{"class":316},[212,2135,1905],{"class":244},[212,2137,2139],{"class":80,"line":2138},23,[212,2140,310],{"emptyLinePlaceholder":309},[212,2142,2144,2146,2148,2150],{"class":80,"line":2143},24,[212,2145,522],{"class":240},[212,2147,1802],{"class":244},[212,2149,528],{"class":240},[212,2151,1807],{"class":244},[212,2153,2155,2158,2161,2163],{"class":80,"line":2154},25,[212,2156,2157],{"class":240},"    assert",[212,2159,2160],{"class":244}," rendered_figures(option) ",[212,2162,1825],{"class":240},[212,2164,2165],{"class":244}," expected[option], option\n",[212,2167,2169,2171,2173,2176],{"class":80,"line":2168},26,[212,2170,741],{"class":316},[212,2172,1415],{"class":244},[212,2174,2175],{"class":221},"\"all selections match\"",[212,2177,494],{"class":244},[10,2179,2180],{},"The pandas side is an independent calculation of the same question, so agreement for every option is strong evidence that the criteria, the wildcard and the cell references are right. Run it whenever the dashboard's layout or formulas change; four recalculations take a few seconds.",[10,2182,2183,2184,2187],{},"With XlsxWriter instead of openpyxl, the dropdown is one call — ",[209,2185,2186],{},"worksheet.data_validation(\"B1\", {\"validate\": \"list\", \"source\": \"=RegionList\"})"," — and the formulas and chart are written the same way; remember to store the formula results for readers that do not recalculate.",[197,2189,2191],{"id":2190},"common-pitfalls","Common pitfalls",[2193,2194,2195,2207,2213,2223,2229],"ul",{},[2196,2197,2198,2202,2203,2206],"li",{},[2199,2200,2201],"strong",{},"Typing in the dropdown cell."," Without ",[209,2204,2205],{},"showErrorMessage=True",", a typo like \"Nroth\" is accepted and every figure shows 0. Keep the error message on.",[2196,2208,2209,2212],{},[2199,2210,2211],{},"Lists that go stale."," Regenerate the hidden list on every run from the data; a hard-coded list misses new regions.",[2196,2214,2215,2218,2219,908],{},[2199,2216,2217],{},"Protecting the dashboard but not the dropdown."," If the dashboard sheet is protected, unlock the dropdown cell first, or readers cannot change the selection at all. Locking cells while leaving inputs editable is covered in ",[14,2220,2222],{"href":2221},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fprotecting-and-sharing-excel-workbooks\u002Flock-cells-and-protect-a-sheet-with-openpyxl\u002F","Lock Cells and Protect a Sheet with openpyxl",[2196,2224,2225,2228],{},[2199,2226,2227],{},"A default that hides data."," Opening on a specific region makes the dashboard look incomplete to readers who do not notice the filter. Default to \"All\".",[2196,2230,2231,2234],{},[2199,2232,2233],{},"Wildcards in the data."," A region literally called \"North*\" would match more than itself. Such values are rare, but clean them if they occur.",[197,2236,2238],{"id":2237},"conclusion","Conclusion",[10,2240,2241,2242,2244,2245,2248],{},"Native slicers are out of reach from Python, but a data validation dropdown gives readers the same control. Build the options from the data on a hidden sheet with \"All\" first, name the range, restrict the dropdown cell to it with an error message, write ",[209,2243,162],{}," criteria as ",[209,2246,2247],{},"IF($B$1=\"All\",\"*\",$B$1)",", and point charts at the formula cells. Add further filters as extra criterion pairs, and check each selection by recalculating the workbook.",[197,2250,2252],{"id":2251},"frequently-asked-questions","Frequently asked questions",[10,2254,2255,2258],{},[2199,2256,2257],{},"Can openpyxl or XlsxWriter create a real Excel slicer?","\nNo. Slicers are stored in parts of the file format that neither library writes. A data validation dropdown that drives formulas gives readers the same kind of control and works in every Excel version and in LibreOffice.",[10,2260,2261,2264],{},[2199,2262,2263],{},"How do I add an All option to the filter?","\nPut \"All\" at the top of the list and write the criterion as IF($B$1=\"All\",\"*\",$B$1). The asterisk is a wildcard that matches any text, so SUMIFS includes every region when All is selected.",[10,2266,2267,2270],{},[2199,2268,2269],{},"Why does the dropdown list come from a separate sheet?","\nA list typed directly into the validation rule is limited to 255 characters and must be edited in the rule. A range on a hidden sheet, referenced through a name, can be long, is easy to regenerate from the data and is easy to check.",[10,2272,2273,2276],{},[2199,2274,2275],{},"Will the chart update when the selection changes?","\nYes, if the chart's series refer to the formula cells that follow the dropdown rather than to the raw data. Changing the selection recalculates the formulas, and the chart redraws from them.",[197,2278,2280],{"id":2279},"related","Related",[2193,2282,2283,2289,2296,2303],{},[2196,2284,2285,2286,2288],{},"Up one level: ",[14,2287,17],{"href":16}," — dashboards across sheets.",[2196,2290,2291,2295],{},[14,2292,2294],{"href":2293},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Flink-dashboard-cells-to-data-sheets-with-formulas\u002F","Link Dashboard Cells to Data Sheets with Formulas"," — the formulas behind the figures.",[2196,2297,2298,2302],{},[14,2299,2301],{"href":2300},"\u002Fadvanced-data-transformation-and-cleaning\u002Fexcel-formula-equivalents-in-pandas\u002Faverageifs-maxifs-minifs-in-pandas\u002F","AVERAGEIFS, MAXIFS and MINIFS Equivalents in pandas"," — the conditional aggregates in pandas.",[2196,2304,2305,2309],{},[14,2306,2308],{"href":2307},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcreating-pivot-tables-from-excel-data\u002Fadd-a-native-excel-pivot-table-with-python\u002F","Add a Native Excel Pivot Table with Python"," — when readers need Excel's own pivot tools.",[2311,2312,2313],"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 .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}",{"title":207,"searchDepth":254,"depth":254,"links":2315},[2316,2317,2318,2319,2320,2321,2322,2323,2324,2325,2326],{"id":199,"depth":254,"text":200},{"id":587,"depth":254,"text":588},{"id":758,"depth":254,"text":759},{"id":911,"depth":254,"text":912},{"id":1239,"depth":254,"text":1240},{"id":1437,"depth":254,"text":1438},{"id":1703,"depth":254,"text":1704},{"id":2190,"depth":254,"text":2191},{"id":2237,"depth":254,"text":2238},{"id":2251,"depth":254,"text":2252},{"id":2279,"depth":254,"text":2280},"2026-09-23","Give an Excel dashboard a slicer-like control from Python — a data validation dropdown fed by a hidden list, an \"All\" option, SUMIFS formulas and a chart that follow the selection, and a second filter combined with the first — because native slicers cannot be written by openpyxl or XlsxWriter.","md",[2331,2333,2335,2337],{"q":2257,"a":2332},"No. Slicers are stored in parts of the file format that neither library writes. A data validation dropdown that drives formulas gives readers the same kind of control and works in every Excel version and in LibreOffice.",{"q":2263,"a":2334},"Put \"All\" at the top of the list and write the criterion as IF($B$1=\"All\",\"*\",$B$1). The asterisk is a wildcard that matches any text, so SUMIFS includes every region when All is selected.",{"q":2269,"a":2336},"A list typed directly into the validation rule is limited to 255 characters and must be edited in the rule. A range on a hidden sheet, referenced through a name, can be long, is easy to regenerate from the data and is easy to check.",{"q":2275,"a":2338},"Yes, if the chart's series refer to the formula cells that follow the dropdown rather than to the raw data. Changing the selection recalculates the formulas, and the chart redraws from them.","add a dropdown filter that drives an excel dashboard",null,{"breadcrumb":2342},[2343,2346,2349],{"name":2344,"item":2345},"Home","\u002F",{"name":2347,"item":2348},"Automating Reporting Workflows with Python and Excel","\u002Fautomating-reporting-workflows\u002F",{"name":17,"item":16},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-slicer-style-filters-to-an-excel-dashboard",{"title":2352,"description":2353},"Add a Dropdown Filter That Drives an Excel Dashboard (Python)","Slicer-style filtering for generated Excel dashboards — DataValidation list from a named range, an All option with a wildcard criterion, SUMIFS that follow the dropdown, charts on the result cells, two combined filters and checking selections with LibreOffice.","add-slicer-style-filters-to-an-excel-dashboard","automating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-slicer-style-filters-to-an-excel-dashboard\u002Findex",[2357,2358,2359],"report-layouts","data-validation","excel-formulas","how-to","nKMuzecL5w2qGaPeDPX9WC17x44KhlquUUuduk6SA28",[2363,2367],{"title":2364,"path":2365,"stem":2366,"children":-1},"Add a Table of Contents Sheet with Hyperlinks in Excel","\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-a-table-of-contents-sheet-with-hyperlinks-in-excel","automating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fadd-a-table-of-contents-sheet-with-hyperlinks-in-excel\u002Findex",{"title":2368,"path":2369,"stem":2370,"children":-1},"Add a Summary Sheet to an Excel Report with Python","\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",[2372,2374],{"path":2373,"title":17},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards",{"path":2375,"title":2347},"\u002Fautomating-reporting-workflows",[],[2378,2382,2385,2388,2391,2394],{"path":2379,"title":2380,"kind":2381},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports","Building Finance and Operations Reports in Excel with Python","guide",{"path":2383,"title":2384,"kind":2360},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fallocate-shared-costs-across-departments","Allocate Shared Costs Across Departments with Python",{"path":2386,"title":2387,"kind":2360},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-bank-reconciliation-report","Build a Bank Reconciliation Report with Python and Excel",{"path":2389,"title":2390,"kind":2360},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-bill-of-materials-cost-rollup","Build a Bill of Materials Cost Rollup",{"path":2392,"title":2393,"kind":2360},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-break-even-analysis-in-excel-with-python","Build a Break-Even Analysis in Excel with Python",{"path":2395,"title":2396,"kind":2397},"\u002Fautomating-reporting-workflows\u002Fbuilding-finance-and-operations-reports\u002Fbuild-a-budget-vs-actual-variance-report","Build a Budget vs Actual Variance Report in Excel with Python","compare",1790972128242]