[{"data":1,"prerenderedAt":2083},["ShallowReactive",2],{"doc:\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fdefine-names-and-protect-sheets-with-xlsxwriter":3,"surround:\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fdefine-names-and-protect-sheets-with-xlsxwriter":2052,"ancestors:\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fdefine-names-and-protect-sheets-with-xlsxwriter":2061,"children:\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fdefine-names-and-protect-sheets-with-xlsxwriter":2066,"related:\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fdefine-names-and-protect-sheets-with-xlsxwriter":2067},{"id":4,"title":5,"body":6,"dateModified":2018,"datePublished":2018,"description":2019,"extension":2020,"faq":2021,"keyword":2030,"kind":2031,"meta":2032,"navigation":98,"path":2042,"seo":2043,"slug":2045,"stem":2046,"topics":2047,"type":2050,"__hash__":2051},"docs\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fdefine-names-and-protect-sheets-with-xlsxwriter\u002Findex.md","Define Names and Protect Sheets with XlsxWriter",{"type":7,"value":8,"toc":2007},"minimark",[9,36,41,69,72,76,311,352,355,515,519,1201,1228,1232,1305,1320,1343,1419,1532,1535,1539,1749,1756,1845,1851,1855,1901,1905,1921,1925,1939,1949,1961,1970,1974,2003],[10,11,12,13,17,18,21,22,25,26,29,30,35],"p",{},"A workbook that other people fill in needs two things from the code that builds it. Its formulas should be readable — ",[14,15,16],"code",{},"=Amount*VAT_Rate"," says what it does, ",[14,19,20],{},"=C5*Lists!$D$2"," does not — and its structure should be safe from accidents, so that someone typing figures cannot overwrite a formula or delete the list behind a dropdown. XlsxWriter handles both: ",[14,23,24],{},"define_name"," creates named ranges and named constants, and ",[14,27,28],{},"protect"," locks a sheet so only the cells you have explicitly unlocked can be edited. This page builds a small expense claim template that uses both, then reads the file back to check that the names, locks and hidden formulas are what was intended. It belongs to ",[31,32,34],"a",{"href":33},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002F","Building Excel Reports with XlsxWriter",".",[37,38,40],"h2",{"id":39},"prerequisites","Prerequisites",[42,43,48],"pre",{"className":44,"code":45,"language":46,"meta":47,"style":47},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install xlsxwriter openpyxl\n","bash","",[14,49,50],{"__ignoreMap":47},[51,52,55,59,63,66],"span",{"class":53,"line":54},"line",1,[51,56,58],{"class":57},"sMTad","pip",[51,60,62],{"class":61},"srMev"," install",[51,64,65],{"class":61}," xlsxwriter",[51,67,68],{"class":61}," openpyxl\n",[10,70,71],{},"openpyxl is used only to inspect the finished file. XlsxWriter writes new workbooks; it cannot open and modify an existing one.",[37,73,75],{"id":74},"step-1-names-for-a-list-a-constant-and-a-sheet-only-range","Step 1 — Names for a list, a constant and a sheet-only range",[42,77,81],{"className":78,"code":79,"language":80,"meta":47,"style":47},"language-python shiki shiki-themes github-light github-dark-high-contrast","import xlsxwriter\n\nwb = xlsxwriter.Workbook(\"expense_claim.xlsx\")\ninp = wb.add_worksheet(\"Claim\")\nlists = wb.add_worksheet(\"Lists\")\n\ncategories = [\"Travel\", \"Accommodation\", \"Meals\", \"Equipment\", \"Training\"]\nlists.write_column(\"A1\", [\"Category\"] + categories)\nlists.hide()\n\nwb.define_name(\"Categories\", f\"=Lists!$A$2:$A${len(categories) + 1}\")    # a workbook-wide range\nwb.define_name(\"VAT_Rate\", \"=0.2\")                                      # a named constant\nwb.define_name(\"Claim!Amounts\", \"=Claim!$C$5:$C$24\")                     # scoped to the Claim sheet only\n","python",[14,82,83,93,100,118,134,149,154,192,216,222,227,273,292],{"__ignoreMap":47},[51,84,85,89],{"class":53,"line":54},[51,86,88],{"class":87},"s-kum","import",[51,90,92],{"class":91},"skGVy"," xlsxwriter\n",[51,94,96],{"class":53,"line":95},2,[51,97,99],{"emptyLinePlaceholder":98},true,"\n",[51,101,103,106,109,112,115],{"class":53,"line":102},3,[51,104,105],{"class":91},"wb ",[51,107,108],{"class":87},"=",[51,110,111],{"class":91}," xlsxwriter.Workbook(",[51,113,114],{"class":61},"\"expense_claim.xlsx\"",[51,116,117],{"class":91},")\n",[51,119,121,124,126,129,132],{"class":53,"line":120},4,[51,122,123],{"class":91},"inp ",[51,125,108],{"class":87},[51,127,128],{"class":91}," wb.add_worksheet(",[51,130,131],{"class":61},"\"Claim\"",[51,133,117],{"class":91},[51,135,137,140,142,144,147],{"class":53,"line":136},5,[51,138,139],{"class":91},"lists ",[51,141,108],{"class":87},[51,143,128],{"class":91},[51,145,146],{"class":61},"\"Lists\"",[51,148,117],{"class":91},[51,150,152],{"class":53,"line":151},6,[51,153,99],{"emptyLinePlaceholder":98},[51,155,157,160,162,165,168,171,174,176,179,181,184,186,189],{"class":53,"line":156},7,[51,158,159],{"class":91},"categories ",[51,161,108],{"class":87},[51,163,164],{"class":91}," [",[51,166,167],{"class":61},"\"Travel\"",[51,169,170],{"class":91},", ",[51,172,173],{"class":61},"\"Accommodation\"",[51,175,170],{"class":91},[51,177,178],{"class":61},"\"Meals\"",[51,180,170],{"class":91},[51,182,183],{"class":61},"\"Equipment\"",[51,185,170],{"class":91},[51,187,188],{"class":61},"\"Training\"",[51,190,191],{"class":91},"]\n",[51,193,195,198,201,204,207,210,213],{"class":53,"line":194},8,[51,196,197],{"class":91},"lists.write_column(",[51,199,200],{"class":61},"\"A1\"",[51,202,203],{"class":91},", [",[51,205,206],{"class":61},"\"Category\"",[51,208,209],{"class":91},"] ",[51,211,212],{"class":87},"+",[51,214,215],{"class":91}," categories)\n",[51,217,219],{"class":53,"line":218},9,[51,220,221],{"class":91},"lists.hide()\n",[51,223,225],{"class":53,"line":224},10,[51,226,99],{"emptyLinePlaceholder":98},[51,228,230,233,236,238,241,244,248,252,255,257,260,263,266,269],{"class":53,"line":229},11,[51,231,232],{"class":91},"wb.define_name(",[51,234,235],{"class":61},"\"Categories\"",[51,237,170],{"class":91},[51,239,240],{"class":87},"f",[51,242,243],{"class":61},"\"=Lists!$A$2:$A$",[51,245,247],{"class":246},"sSjpA","{",[51,249,251],{"class":250},"sP0c6","len",[51,253,254],{"class":91},"(categories) ",[51,256,212],{"class":87},[51,258,259],{"class":250}," 1",[51,261,262],{"class":246},"}",[51,264,265],{"class":61},"\"",[51,267,268],{"class":91},")    ",[51,270,272],{"class":271},"s-wDw","# a workbook-wide range\n",[51,274,276,278,281,283,286,289],{"class":53,"line":275},12,[51,277,232],{"class":91},[51,279,280],{"class":61},"\"VAT_Rate\"",[51,282,170],{"class":91},[51,284,285],{"class":61},"\"=0.2\"",[51,287,288],{"class":91},")                                      ",[51,290,291],{"class":271},"# a named constant\n",[51,293,295,297,300,302,305,308],{"class":53,"line":294},13,[51,296,232],{"class":91},[51,298,299],{"class":61},"\"Claim!Amounts\"",[51,301,170],{"class":91},[51,303,304],{"class":61},"\"=Claim!$C$5:$C$24\"",[51,306,307],{"class":91},")                     ",[51,309,310],{"class":271},"# scoped to the Claim sheet only\n",[10,312,313,315,316,320,321,324,325,320,328,331,332,335,336,339,340,343,344,347,348,351],{},[14,314,24],{}," takes a name and what it refers to, written as a formula. Three kinds of name are useful in templates. A ",[317,318,319],"strong",{},"range name"," such as ",[14,322,323],{},"Categories"," points at cells, here the list of expense categories on a separate sheet. A ",[317,326,327],{},"constant",[14,329,330],{},"VAT_Rate"," points at a value rather than a cell, so it does not live anywhere a user can edit it; changing the rate means changing the code that builds the template, which is usually what you want for a rate set by policy. A ",[317,333,334],{},"sheet-scoped name"," is written with the sheet name in front — ",[14,337,338],{},"Claim!Amounts"," — and is visible only on that sheet, which avoids clashes when several sheets each need their own ",[14,341,342],{},"Amounts",". Names must start with a letter or underscore, contain no spaces, and must not look like a cell address: ",[14,345,346],{},"VAT2026"," is fine, ",[14,349,350],{},"VAT1"," is not, because it is the address of a cell in column VAT.",[10,353,354],{},"The list sheet is hidden. Hiding is not security — anyone can unhide a sheet unless the workbook structure is protected — but it keeps the template tidy and stops casual edits.",[356,357,365,366,365,370,365,374,365,382,365,391,365,395,365,402,365,406,365,410,365,414,365,418,365,422,365,427,365,432,365,436,365,438,365,441,365,443,365,446,365,449,365,453,365,455,365,458,365,460,365,463,365,465,365,468,365,471,365,475,365,477,365,479,365,483,365,487,365,489,365,491,365,494,365,498,365,500,365,502,365,504,365,507,365,511],"svg",{"viewBox":358,"role":359,"ariaLabelledBy":360,"xmlns":363,"style":364},"163 0 434 168","img",[361,362],"xw-names-t","xw-names-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:434px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[367,368,369],"title",{"id":361},"The names in the template",[371,372,373],"desc",{"id":362},"Categories refers to five cells on the hidden Lists sheet and is visible everywhere. VAT_Rate is a constant that lives in no cell. Amounts is scoped to the Claim sheet and refers to the amount column.",[375,376],"rect",{"x":377,"y":378,"width":379,"height":380,"fill":381},"163","0","434","168","#ffffff",[375,383],{"x":384,"y":385,"width":386,"height":387,"rx":378,"fill":388,"stroke":389,"style":390},"179.2","16.0","32.0","24.0","#e7ebef","var(--line,#cdd5e6)","stroke-width:1px",[375,392],{"x":393,"y":385,"width":394,"height":387,"rx":378,"fill":388,"stroke":389,"style":390},"211.2","91.6",[396,397,401],"text",{"x":398,"y":399,"style":400},"257.0","33.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[375,403],{"x":404,"y":385,"width":405,"height":387,"rx":378,"fill":388,"stroke":389,"style":390},"302.8","152.0",[396,407,409],{"x":408,"y":399,"style":400},"378.8","B",[375,411],{"x":412,"y":385,"width":413,"height":387,"rx":378,"fill":388,"stroke":389,"style":390},"454.7","126.1",[396,415,417],{"x":416,"y":399,"style":400},"517.8","C",[375,419],{"x":384,"y":420,"width":386,"height":421,"rx":378,"fill":388,"stroke":389,"style":390},"40.0","28.0",[396,423,426],{"x":424,"y":425,"style":400},"195.2","59.0","1",[375,428],{"x":393,"y":420,"width":394,"height":421,"rx":378,"fill":429,"stroke":430,"style":431},"#f0f4ff","var(--brand,#5b5cf0)","stroke-width:1.5px",[396,433,435],{"x":398,"y":425,"style":434},"font-size:12px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Name",[375,437],{"x":404,"y":420,"width":405,"height":421,"rx":378,"fill":429,"stroke":430,"style":431},[396,439,440],{"x":408,"y":425,"style":434},"Refers to",[375,442],{"x":412,"y":420,"width":413,"height":421,"rx":378,"fill":429,"stroke":430,"style":431},[396,444,445],{"x":416,"y":425,"style":434},"Scope",[375,447],{"x":384,"y":448,"width":386,"height":421,"rx":378,"fill":388,"stroke":389,"style":390},"68.0",[396,450,452],{"x":424,"y":451,"style":400},"87.0","2",[375,454],{"x":393,"y":448,"width":394,"height":421,"rx":378,"fill":381,"stroke":389,"style":390},[396,456,323],{"x":398,"y":451,"style":457},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle",[375,459],{"x":404,"y":448,"width":405,"height":421,"rx":378,"fill":381,"stroke":389,"style":390},[396,461,462],{"x":408,"y":451,"style":457},"=Lists!$A$2:$A$6",[375,464],{"x":412,"y":448,"width":413,"height":421,"rx":378,"fill":381,"stroke":389,"style":390},[396,466,467],{"x":416,"y":451,"style":457},"workbook",[375,469],{"x":384,"y":470,"width":386,"height":421,"rx":378,"fill":388,"stroke":389,"style":390},"96.0",[396,472,474],{"x":424,"y":473,"style":400},"115.0","3",[375,476],{"x":393,"y":470,"width":394,"height":421,"rx":378,"fill":381,"stroke":389,"style":390},[396,478,330],{"x":398,"y":473,"style":457},[375,480],{"x":404,"y":470,"width":405,"height":421,"rx":378,"fill":481,"stroke":482,"style":431},"#fdefd8","var(--gold,#b4740a)",[396,484,486],{"x":408,"y":473,"style":485},"font-size:12px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","=0.2",[375,488],{"x":412,"y":470,"width":413,"height":421,"rx":378,"fill":381,"stroke":389,"style":390},[396,490,467],{"x":416,"y":473,"style":457},[375,492],{"x":384,"y":493,"width":386,"height":421,"rx":378,"fill":388,"stroke":389,"style":390},"124.0",[396,495,497],{"x":424,"y":496,"style":400},"143.0","4",[375,499],{"x":393,"y":493,"width":394,"height":421,"rx":378,"fill":381,"stroke":389,"style":390},[396,501,342],{"x":398,"y":496,"style":457},[375,503],{"x":404,"y":493,"width":405,"height":421,"rx":378,"fill":381,"stroke":389,"style":390},[396,505,506],{"x":408,"y":496,"style":457},"=Claim!$C$5:$C$24",[375,508],{"x":412,"y":493,"width":413,"height":421,"rx":378,"fill":509,"stroke":510,"style":431},"#d9f4f1","var(--teal,#0f9488)",[396,512,514],{"x":416,"y":496,"style":513},"font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Claim sheet only",[37,516,518],{"id":517},"step-2-input-cells-dropdowns-and-formulas-that-use-the-names","Step 2 — Input cells, dropdowns and formulas that use the names",[42,520,522],{"className":78,"code":521,"language":80,"meta":47,"style":47},"title = wb.add_format({\"bold\": True, \"font_size\": 14})\nhead = wb.add_format({\"bold\": True, \"bottom\": 1, \"bg_color\": \"#EEF3FA\"})\nunlocked = wb.add_format({\"locked\": False, \"bg_color\": \"#FFF9DB\", \"border\": 1, \"border_color\": \"#D9D9D9\"})\nunlocked_money = wb.add_format({\"locked\": False, \"bg_color\": \"#FFF9DB\", \"num_format\": \"#,##0.00\", \"border\": 1, \"border_color\": \"#D9D9D9\"})\ncalc_money = wb.add_format({\"num_format\": \"#,##0.00\", \"hidden\": True})\ntotal_fmt = wb.add_format({\"bold\": True, \"num_format\": \"#,##0.00\", \"top\": 1, \"hidden\": True})\n\ninp.write(\"A1\", \"Expense claim\", title)\ninp.write(\"A2\", \"Name\")\ninp.write(\"B2\", None, unlocked)\ninp.write_row(\"A4\", [\"Date\", \"Category\", \"Amount (gross)\", \"VAT\", \"Net\"], head)\nfor r in range(4, 24):                       # rows 5–24 in Excel terms\n    inp.write_blank(r, 0, None, unlocked)\n    inp.write_blank(r, 1, None, unlocked)\n    inp.write_blank(r, 2, None, unlocked_money)\n    inp.write_formula(r, 3, f\"=IF(C{r + 1}=\\\"\\\",\\\"\\\",ROUND(C{r + 1}*VAT_Rate\u002F(1+VAT_Rate),2))\", calc_money)\n    inp.write_formula(r, 4, f\"=IF(C{r + 1}=\\\"\\\",\\\"\\\",C{r + 1}-D{r + 1})\", calc_money)\ninp.data_validation(\"B5:B24\", {\"validate\": \"list\", \"source\": \"=Categories\"})\ninp.data_validation(\"C5:C24\", {\"validate\": \"decimal\", \"criteria\": \">=\", \"value\": 0,\n                               \"error_message\": \"Enter the amount as a positive number.\"})\ninp.write(\"B26\", \"Total\")\ninp.write_formula(\"C26\", \"=SUM(Amounts)\", total_fmt)\ninp.set_column(\"A:A\", 12, wb.add_format({\"num_format\": \"dd\u002Fmm\u002Fyyyy\"}))\ninp.set_column(\"B:B\", 16)\ninp.set_column(\"C:E\", 15)\n",[14,523,524,556,592,639,690,716,758,762,777,791,806,841,871,884,897,911,966,1028,1060,1099,1112,1127,1144,1171,1186],{"__ignoreMap":47},[51,525,526,529,531,534,537,540,543,545,548,550,553],{"class":53,"line":54},[51,527,528],{"class":91},"title ",[51,530,108],{"class":87},[51,532,533],{"class":91}," wb.add_format({",[51,535,536],{"class":61},"\"bold\"",[51,538,539],{"class":91},": ",[51,541,542],{"class":250},"True",[51,544,170],{"class":91},[51,546,547],{"class":61},"\"font_size\"",[51,549,539],{"class":91},[51,551,552],{"class":250},"14",[51,554,555],{"class":91},"})\n",[51,557,558,561,563,565,567,569,571,573,576,578,580,582,585,587,590],{"class":53,"line":95},[51,559,560],{"class":91},"head ",[51,562,108],{"class":87},[51,564,533],{"class":91},[51,566,536],{"class":61},[51,568,539],{"class":91},[51,570,542],{"class":250},[51,572,170],{"class":91},[51,574,575],{"class":61},"\"bottom\"",[51,577,539],{"class":91},[51,579,426],{"class":250},[51,581,170],{"class":91},[51,583,584],{"class":61},"\"bg_color\"",[51,586,539],{"class":91},[51,588,589],{"class":61},"\"#EEF3FA\"",[51,591,555],{"class":91},[51,593,594,597,599,601,604,606,609,611,613,615,618,620,623,625,627,629,632,634,637],{"class":53,"line":102},[51,595,596],{"class":91},"unlocked ",[51,598,108],{"class":87},[51,600,533],{"class":91},[51,602,603],{"class":61},"\"locked\"",[51,605,539],{"class":91},[51,607,608],{"class":250},"False",[51,610,170],{"class":91},[51,612,584],{"class":61},[51,614,539],{"class":91},[51,616,617],{"class":61},"\"#FFF9DB\"",[51,619,170],{"class":91},[51,621,622],{"class":61},"\"border\"",[51,624,539],{"class":91},[51,626,426],{"class":250},[51,628,170],{"class":91},[51,630,631],{"class":61},"\"border_color\"",[51,633,539],{"class":91},[51,635,636],{"class":61},"\"#D9D9D9\"",[51,638,555],{"class":91},[51,640,641,644,646,648,650,652,654,656,658,660,662,664,667,669,672,674,676,678,680,682,684,686,688],{"class":53,"line":120},[51,642,643],{"class":91},"unlocked_money ",[51,645,108],{"class":87},[51,647,533],{"class":91},[51,649,603],{"class":61},[51,651,539],{"class":91},[51,653,608],{"class":250},[51,655,170],{"class":91},[51,657,584],{"class":61},[51,659,539],{"class":91},[51,661,617],{"class":61},[51,663,170],{"class":91},[51,665,666],{"class":61},"\"num_format\"",[51,668,539],{"class":91},[51,670,671],{"class":61},"\"#,##0.00\"",[51,673,170],{"class":91},[51,675,622],{"class":61},[51,677,539],{"class":91},[51,679,426],{"class":250},[51,681,170],{"class":91},[51,683,631],{"class":61},[51,685,539],{"class":91},[51,687,636],{"class":61},[51,689,555],{"class":91},[51,691,692,695,697,699,701,703,705,707,710,712,714],{"class":53,"line":136},[51,693,694],{"class":91},"calc_money ",[51,696,108],{"class":87},[51,698,533],{"class":91},[51,700,666],{"class":61},[51,702,539],{"class":91},[51,704,671],{"class":61},[51,706,170],{"class":91},[51,708,709],{"class":61},"\"hidden\"",[51,711,539],{"class":91},[51,713,542],{"class":250},[51,715,555],{"class":91},[51,717,718,721,723,725,727,729,731,733,735,737,739,741,744,746,748,750,752,754,756],{"class":53,"line":151},[51,719,720],{"class":91},"total_fmt ",[51,722,108],{"class":87},[51,724,533],{"class":91},[51,726,536],{"class":61},[51,728,539],{"class":91},[51,730,542],{"class":250},[51,732,170],{"class":91},[51,734,666],{"class":61},[51,736,539],{"class":91},[51,738,671],{"class":61},[51,740,170],{"class":91},[51,742,743],{"class":61},"\"top\"",[51,745,539],{"class":91},[51,747,426],{"class":250},[51,749,170],{"class":91},[51,751,709],{"class":61},[51,753,539],{"class":91},[51,755,542],{"class":250},[51,757,555],{"class":91},[51,759,760],{"class":53,"line":156},[51,761,99],{"emptyLinePlaceholder":98},[51,763,764,767,769,771,774],{"class":53,"line":194},[51,765,766],{"class":91},"inp.write(",[51,768,200],{"class":61},[51,770,170],{"class":91},[51,772,773],{"class":61},"\"Expense claim\"",[51,775,776],{"class":91},", title)\n",[51,778,779,781,784,786,789],{"class":53,"line":218},[51,780,766],{"class":91},[51,782,783],{"class":61},"\"A2\"",[51,785,170],{"class":91},[51,787,788],{"class":61},"\"Name\"",[51,790,117],{"class":91},[51,792,793,795,798,800,803],{"class":53,"line":224},[51,794,766],{"class":91},[51,796,797],{"class":61},"\"B2\"",[51,799,170],{"class":91},[51,801,802],{"class":250},"None",[51,804,805],{"class":91},", unlocked)\n",[51,807,808,811,814,816,819,821,823,825,828,830,833,835,838],{"class":53,"line":229},[51,809,810],{"class":91},"inp.write_row(",[51,812,813],{"class":61},"\"A4\"",[51,815,203],{"class":91},[51,817,818],{"class":61},"\"Date\"",[51,820,170],{"class":91},[51,822,206],{"class":61},[51,824,170],{"class":91},[51,826,827],{"class":61},"\"Amount (gross)\"",[51,829,170],{"class":91},[51,831,832],{"class":61},"\"VAT\"",[51,834,170],{"class":91},[51,836,837],{"class":61},"\"Net\"",[51,839,840],{"class":91},"], head)\n",[51,842,843,846,849,852,855,858,860,862,865,868],{"class":53,"line":275},[51,844,845],{"class":87},"for",[51,847,848],{"class":91}," r ",[51,850,851],{"class":87},"in",[51,853,854],{"class":250}," range",[51,856,857],{"class":91},"(",[51,859,497],{"class":250},[51,861,170],{"class":91},[51,863,864],{"class":250},"24",[51,866,867],{"class":91},"):                       ",[51,869,870],{"class":271},"# rows 5–24 in Excel terms\n",[51,872,873,876,878,880,882],{"class":53,"line":294},[51,874,875],{"class":91},"    inp.write_blank(r, ",[51,877,378],{"class":250},[51,879,170],{"class":91},[51,881,802],{"class":250},[51,883,805],{"class":91},[51,885,887,889,891,893,895],{"class":53,"line":886},14,[51,888,875],{"class":91},[51,890,426],{"class":250},[51,892,170],{"class":91},[51,894,802],{"class":250},[51,896,805],{"class":91},[51,898,900,902,904,906,908],{"class":53,"line":899},15,[51,901,875],{"class":91},[51,903,452],{"class":250},[51,905,170],{"class":91},[51,907,802],{"class":250},[51,909,910],{"class":91},", unlocked_money)\n",[51,912,914,917,919,921,923,926,928,931,933,935,937,939,942,945,947,950,952,954,956,958,960,963],{"class":53,"line":913},16,[51,915,916],{"class":91},"    inp.write_formula(r, ",[51,918,474],{"class":250},[51,920,170],{"class":91},[51,922,240],{"class":87},[51,924,925],{"class":61},"\"=IF(C",[51,927,247],{"class":246},[51,929,930],{"class":91},"r ",[51,932,212],{"class":87},[51,934,259],{"class":250},[51,936,262],{"class":246},[51,938,108],{"class":61},[51,940,941],{"class":246},"\\\"\\\"",[51,943,944],{"class":61},",",[51,946,941],{"class":246},[51,948,949],{"class":61},",ROUND(C",[51,951,247],{"class":246},[51,953,930],{"class":91},[51,955,212],{"class":87},[51,957,259],{"class":250},[51,959,262],{"class":246},[51,961,962],{"class":61},"*VAT_Rate\u002F(1+VAT_Rate),2))\"",[51,964,965],{"class":91},", calc_money)\n",[51,967,969,971,973,975,977,979,981,983,985,987,989,991,993,995,997,1000,1002,1004,1006,1008,1010,1013,1015,1017,1019,1021,1023,1026],{"class":53,"line":968},17,[51,970,916],{"class":91},[51,972,497],{"class":250},[51,974,170],{"class":91},[51,976,240],{"class":87},[51,978,925],{"class":61},[51,980,247],{"class":246},[51,982,930],{"class":91},[51,984,212],{"class":87},[51,986,259],{"class":250},[51,988,262],{"class":246},[51,990,108],{"class":61},[51,992,941],{"class":246},[51,994,944],{"class":61},[51,996,941],{"class":246},[51,998,999],{"class":61},",C",[51,1001,247],{"class":246},[51,1003,930],{"class":91},[51,1005,212],{"class":87},[51,1007,259],{"class":250},[51,1009,262],{"class":246},[51,1011,1012],{"class":61},"-D",[51,1014,247],{"class":246},[51,1016,930],{"class":91},[51,1018,212],{"class":87},[51,1020,259],{"class":250},[51,1022,262],{"class":246},[51,1024,1025],{"class":61},")\"",[51,1027,965],{"class":91},[51,1029,1031,1034,1037,1040,1043,1045,1048,1050,1053,1055,1058],{"class":53,"line":1030},18,[51,1032,1033],{"class":91},"inp.data_validation(",[51,1035,1036],{"class":61},"\"B5:B24\"",[51,1038,1039],{"class":91},", {",[51,1041,1042],{"class":61},"\"validate\"",[51,1044,539],{"class":91},[51,1046,1047],{"class":61},"\"list\"",[51,1049,170],{"class":91},[51,1051,1052],{"class":61},"\"source\"",[51,1054,539],{"class":91},[51,1056,1057],{"class":61},"\"=Categories\"",[51,1059,555],{"class":91},[51,1061,1063,1065,1068,1070,1072,1074,1077,1079,1082,1084,1087,1089,1092,1094,1096],{"class":53,"line":1062},19,[51,1064,1033],{"class":91},[51,1066,1067],{"class":61},"\"C5:C24\"",[51,1069,1039],{"class":91},[51,1071,1042],{"class":61},[51,1073,539],{"class":91},[51,1075,1076],{"class":61},"\"decimal\"",[51,1078,170],{"class":91},[51,1080,1081],{"class":61},"\"criteria\"",[51,1083,539],{"class":91},[51,1085,1086],{"class":61},"\">=\"",[51,1088,170],{"class":91},[51,1090,1091],{"class":61},"\"value\"",[51,1093,539],{"class":91},[51,1095,378],{"class":250},[51,1097,1098],{"class":91},",\n",[51,1100,1102,1105,1107,1110],{"class":53,"line":1101},20,[51,1103,1104],{"class":61},"                               \"error_message\"",[51,1106,539],{"class":91},[51,1108,1109],{"class":61},"\"Enter the amount as a positive number.\"",[51,1111,555],{"class":91},[51,1113,1115,1117,1120,1122,1125],{"class":53,"line":1114},21,[51,1116,766],{"class":91},[51,1118,1119],{"class":61},"\"B26\"",[51,1121,170],{"class":91},[51,1123,1124],{"class":61},"\"Total\"",[51,1126,117],{"class":91},[51,1128,1130,1133,1136,1138,1141],{"class":53,"line":1129},22,[51,1131,1132],{"class":91},"inp.write_formula(",[51,1134,1135],{"class":61},"\"C26\"",[51,1137,170],{"class":91},[51,1139,1140],{"class":61},"\"=SUM(Amounts)\"",[51,1142,1143],{"class":91},", total_fmt)\n",[51,1145,1147,1150,1153,1155,1158,1161,1163,1165,1168],{"class":53,"line":1146},23,[51,1148,1149],{"class":91},"inp.set_column(",[51,1151,1152],{"class":61},"\"A:A\"",[51,1154,170],{"class":91},[51,1156,1157],{"class":250},"12",[51,1159,1160],{"class":91},", wb.add_format({",[51,1162,666],{"class":61},[51,1164,539],{"class":91},[51,1166,1167],{"class":61},"\"dd\u002Fmm\u002Fyyyy\"",[51,1169,1170],{"class":91},"}))\n",[51,1172,1174,1176,1179,1181,1184],{"class":53,"line":1173},24,[51,1175,1149],{"class":91},[51,1177,1178],{"class":61},"\"B:B\"",[51,1180,170],{"class":91},[51,1182,1183],{"class":250},"16",[51,1185,117],{"class":91},[51,1187,1189,1191,1194,1196,1199],{"class":53,"line":1188},25,[51,1190,1149],{"class":91},[51,1192,1193],{"class":61},"\"C:E\"",[51,1195,170],{"class":91},[51,1197,1198],{"class":250},"15",[51,1200,117],{"class":91},[10,1202,1203,1204,1208,1209,1212,1213,1215,1216,1219,1220,1223,1224,1227],{},"Every cell in a new sheet is ",[1205,1206,1207],"em",{},"locked"," by default, but locking does nothing until the sheet is protected. So the template marks the cells people may edit — name, date, category, amount — with a format that has ",[14,1210,1211],{},"\"locked\": False",", and gives them a pale yellow fill so users can see where to type. The VAT and net columns are formulas that refer to ",[14,1214,330],{}," by name, and the total is ",[14,1217,1218],{},"=SUM(Amounts)",", which is easier to check than a range address. The category dropdown takes its options from ",[14,1221,1222],{},"=Categories",", so adding a category means extending the list and the name, not editing every validation rule. The formula cells use ",[14,1225,1226],{},"\"hidden\": True",", which hides the formula from the formula bar once the sheet is protected — users see the result, not the calculation. That is a convenience, not secrecy: the formulas are still in the file.",[37,1229,1231],{"id":1230},"step-3-protect-the-sheet-but-leave-useful-actions-allowed","Step 3 — Protect the sheet, but leave useful actions allowed",[42,1233,1235],{"className":78,"code":1234,"language":80,"meta":47,"style":47},"inp.protect(\"\", {\"sort\": False, \"autofilter\": False, \"format_columns\": True, \"select_locked_cells\": True})\ninp.activate()\nwb.close()\nprint(\"template written\")\n",[14,1236,1237,1283,1288,1293],{"__ignoreMap":47},[51,1238,1239,1242,1245,1247,1250,1252,1254,1256,1259,1261,1263,1265,1268,1270,1272,1274,1277,1279,1281],{"class":53,"line":54},[51,1240,1241],{"class":91},"inp.protect(",[51,1243,1244],{"class":61},"\"\"",[51,1246,1039],{"class":91},[51,1248,1249],{"class":61},"\"sort\"",[51,1251,539],{"class":91},[51,1253,608],{"class":250},[51,1255,170],{"class":91},[51,1257,1258],{"class":61},"\"autofilter\"",[51,1260,539],{"class":91},[51,1262,608],{"class":250},[51,1264,170],{"class":91},[51,1266,1267],{"class":61},"\"format_columns\"",[51,1269,539],{"class":91},[51,1271,542],{"class":250},[51,1273,170],{"class":91},[51,1275,1276],{"class":61},"\"select_locked_cells\"",[51,1278,539],{"class":91},[51,1280,542],{"class":250},[51,1282,555],{"class":91},[51,1284,1285],{"class":53,"line":95},[51,1286,1287],{"class":91},"inp.activate()\n",[51,1289,1290],{"class":53,"line":102},[51,1291,1292],{"class":91},"wb.close()\n",[51,1294,1295,1298,1300,1303],{"class":53,"line":120},[51,1296,1297],{"class":250},"print",[51,1299,857],{"class":91},[51,1301,1302],{"class":61},"\"template written\"",[51,1304,117],{"class":91},[10,1306,1307,1310,1311,1314,1315,1319],{},[14,1308,1309],{},"protect()"," switches the locks on. Its options decide what users may still do on a protected sheet: here they can resize columns so long category names fit, and select locked cells to copy values, but cannot insert or delete rows, change formats or edit locked cells. An empty password, as here, protects against accidents only — anyone can unprotect the sheet from the ",[1205,1312,1313],{},"Review"," tab — which is often the right choice for an internal template where the goal is to stop mistakes, not people. A password adds a prompt, but sheet passwords in the Excel format are stored as a weak hash and are easily removed, so they are not a security control either; see ",[31,1316,1318],{"href":1317},"\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"," for the same caveat.",[10,1321,1322,1323,1326,1327,1330,1331,1334,1335,1338,1339,1342],{},"There is one gap. XlsxWriter cannot protect the ",[1205,1324,1325],{},"workbook structure"," — the setting that stops users unhiding, renaming, adding or deleting sheets — so the hidden ",[14,1328,1329],{},"Lists"," sheet can be unhidden. If that matters, open the finished file with openpyxl and set ",[14,1332,1333],{},"wb.security.lockStructure = True",", or set the list sheet to ",[1205,1336,1337],{},"very hidden"," with ",[14,1340,1341],{},"lists.very_hidden()",", which removes it from Excel's Unhide dialog altogether.",[356,1344,365,1350,365,1353,365,1356,365,1360,365,1366,365,1372,365,1377,365,1381,365,1385,365,1389,365,1393,365,1398,365,1403,365,1407,365,1410,365,1413,365,1416],{"viewBox":1345,"role":359,"ariaLabelledBy":1346,"xmlns":363,"style":1349},"4 0 752 199",[1347,1348],"xw-locks-t","xw-locks-d","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[367,1351,1352],{"id":1347},"What protection does and does not do",[371,1354,1355],{"id":1348},"Sheet protection stops edits to locked cells, hides formulas marked hidden and limits actions such as inserting rows. It does not encrypt the file, does not stop the hidden list sheet being unhidden, and its password is easy to remove.",[375,1357],{"x":497,"y":378,"width":1358,"height":1359,"fill":381},"752","199",[375,1361],{"x":1362,"y":385,"width":1363,"height":1364,"rx":552,"fill":509,"stroke":510,"style":1365},"20.0","351.0","167.0","stroke-width:2px",[396,1367,1371],{"x":1368,"y":1369,"style":1370},"195.5","44.0","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Sheet protection does",[53,1373],{"x1":1374,"y1":1375,"x2":1376,"y2":1375,"stroke":510,"style":390},"36.0","57.0","355.0",[396,1378,1380],{"x":1368,"y":1379,"style":457},"77.0","block edits to locked cells",[396,1382,1384],{"x":1368,"y":1383,"style":457},"103.0","hide formulas marked hidden",[396,1386,1388],{"x":1368,"y":1387,"style":457},"129.0","limit inserting and deleting rows",[396,1390,1392],{"x":1368,"y":1391,"style":457},"155.0","allow chosen actions like resizing",[375,1394],{"x":1395,"y":385,"width":1363,"height":1364,"rx":552,"fill":1396,"stroke":1397,"style":1365},"389.0","#fce9e9","var(--danger,#dc2626)",[396,1399,1402],{"x":1400,"y":1369,"style":1401},"564.5","font-size:14px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","It does not",[53,1404],{"x1":1405,"y1":1375,"x2":1406,"y2":1375,"stroke":1397,"style":390},"405.0","724.0",[396,1408,1409],{"x":1400,"y":1379,"style":457},"encrypt or hide the data",[396,1411,1412],{"x":1400,"y":1383,"style":457},"stop sheets being unhidden",[396,1414,1415],{"x":1400,"y":1387,"style":457},"resist a determined user",[396,1417,1418],{"x":1400,"y":1391,"style":457},"protect workbook structure in XlsxWriter",[356,1420,365,1425,365,1428,365,1431,365,1434,365,1438,365,1443,365,1448,365,1452,365,1457,365,1462,365,1465,365,1471,365,1475,365,1479,365,1482,365,1485,365,1490,365,1493,365,1497,365,1500,365,1503,365,1508,365,1511,365,1513,365,1517,365,1520,365,1523,365,1526,365,1529],{"viewBox":1421,"role":359,"ariaLabelledBy":1422,"xmlns":363,"style":1349},"4 0 752 108",[1423,1424],"xw-build-t","xw-build-d",[367,1426,1427],{"id":1423},"Building the protected template",[371,1429,1430],{"id":1424},"The code first defines the names, then writes the input cells with an unlocked format and the formula cells with a hidden format, adds dropdowns that use the named list, and finally protects the sheet with the options users still need.",[375,1432],{"x":497,"y":378,"width":1358,"height":1433,"fill":381},"108",[375,1435],{"x":1362,"y":385,"width":1436,"height":1437,"rx":1157,"fill":429,"stroke":430,"style":1365},"115.2","76.5",[396,1439,24],{"x":1440,"y":1441,"style":1442},"77.6","41.5","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle",[396,1444,1447],{"x":1440,"y":1445,"style":1446},"61.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","list, constant,",[396,1449,1451],{"x":1440,"y":1450,"style":1446},"78.5","scoped range",[53,1453],{"x1":1454,"y1":1455,"x2":1456,"y2":1455,"stroke":430,"style":1365},"140.2","54.2","162.2",[1458,1459],"polygon",{"points":1460,"fill":1461},"166.2,54.2 157.2,59.2 157.2,49.2","#5b5cf0",[375,1463],{"x":1464,"y":385,"width":1436,"height":1437,"rx":1157,"fill":481,"stroke":482,"style":1365},"171.2",[396,1466,1470],{"x":1467,"y":1468,"style":1469},"228.8","50.2","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","Input cells",[396,1472,1474],{"x":1467,"y":1473,"style":1446},"69.8","locked: False",[53,1476],{"x1":1477,"y1":1455,"x2":1478,"y2":1455,"stroke":430,"style":1365},"291.4","313.4",[1458,1480],{"points":1481,"fill":1461},"317.4,54.2 308.4,59.2 308.4,49.2",[375,1483],{"x":1484,"y":385,"width":1436,"height":1437,"rx":1157,"fill":388,"stroke":389,"style":1365},"322.4",[396,1486,1489],{"x":1487,"y":1468,"style":1488},"380.0","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Formula cells",[396,1491,1492],{"x":1487,"y":1473,"style":1446},"hidden: True",[53,1494],{"x1":1495,"y1":1455,"x2":1496,"y2":1455,"stroke":430,"style":1365},"442.6","464.6",[1458,1498],{"points":1499,"fill":1461},"468.6,54.2 459.6,59.2 459.6,49.2",[375,1501],{"x":1502,"y":385,"width":1436,"height":1437,"rx":1157,"fill":509,"stroke":510,"style":1365},"473.6",[396,1504,1507],{"x":1505,"y":1441,"style":1506},"531.2","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Dropdown",[396,1509,1510],{"x":1505,"y":1445,"style":1446},"source",[396,1512,1222],{"x":1505,"y":1450,"style":1446},[53,1514],{"x1":1515,"y1":1455,"x2":1516,"y2":1455,"stroke":430,"style":1365},"593.8","615.8",[1458,1518],{"points":1519,"fill":1461},"619.8,54.2 610.8,59.2 610.8,49.2",[375,1521],{"x":1522,"y":385,"width":1436,"height":1437,"rx":1157,"fill":429,"stroke":430,"style":1365},"624.8",[396,1524,1309],{"x":1525,"y":1441,"style":1442},"682.4",[396,1527,1528],{"x":1525,"y":1445,"style":1446},"with allowed",[396,1530,1531],{"x":1525,"y":1450,"style":1446},"actions",[10,1533,1534],{},"The order matters in one respect only: formats are fixed when a cell is written, so the unlocked and hidden formats must be applied as the cells are written, not afterwards. Protection itself can be switched on at any point before the workbook is closed.",[37,1536,1538],{"id":1537},"step-4-check-the-finished-file","Step 4 — Check the finished file",[42,1540,1542],{"className":78,"code":1541,"language":80,"meta":47,"style":47},"from openpyxl import load_workbook\n\nwb2 = load_workbook(\"expense_claim.xlsx\")\nprint(\"workbook names:\", {n: d.attr_text for n, d in wb2.defined_names.items()})\nws = wb2[\"Claim\"]\nprint(\"sheet names on Claim:\", {n: d.attr_text for n, d in ws.defined_names.items()})\nprint(\"Claim protected:\", ws.protection.sheet, \"| Lists state:\", wb2[\"Lists\"].sheet_state)\nfor ref in [\"B2\", \"C5\", \"D5\", \"C26\"]:\n    c = ws[ref]\n    print(f\"{ref}: locked={c.protection.locked} hidden={c.protection.hidden} value={c.value!r}\")\n",[14,1543,1544,1557,1561,1575,1597,1611,1631,1654,1684,1694],{"__ignoreMap":47},[51,1545,1546,1549,1552,1554],{"class":53,"line":54},[51,1547,1548],{"class":87},"from",[51,1550,1551],{"class":91}," openpyxl ",[51,1553,88],{"class":87},[51,1555,1556],{"class":91}," load_workbook\n",[51,1558,1559],{"class":53,"line":95},[51,1560,99],{"emptyLinePlaceholder":98},[51,1562,1563,1566,1568,1571,1573],{"class":53,"line":102},[51,1564,1565],{"class":91},"wb2 ",[51,1567,108],{"class":87},[51,1569,1570],{"class":91}," load_workbook(",[51,1572,114],{"class":61},[51,1574,117],{"class":91},[51,1576,1577,1579,1581,1584,1587,1589,1592,1594],{"class":53,"line":120},[51,1578,1297],{"class":250},[51,1580,857],{"class":91},[51,1582,1583],{"class":61},"\"workbook names:\"",[51,1585,1586],{"class":91},", {n: d.attr_text ",[51,1588,845],{"class":87},[51,1590,1591],{"class":91}," n, d ",[51,1593,851],{"class":87},[51,1595,1596],{"class":91}," wb2.defined_names.items()})\n",[51,1598,1599,1602,1604,1607,1609],{"class":53,"line":136},[51,1600,1601],{"class":91},"ws ",[51,1603,108],{"class":87},[51,1605,1606],{"class":91}," wb2[",[51,1608,131],{"class":61},[51,1610,191],{"class":91},[51,1612,1613,1615,1617,1620,1622,1624,1626,1628],{"class":53,"line":151},[51,1614,1297],{"class":250},[51,1616,857],{"class":91},[51,1618,1619],{"class":61},"\"sheet names on Claim:\"",[51,1621,1586],{"class":91},[51,1623,845],{"class":87},[51,1625,1591],{"class":91},[51,1627,851],{"class":87},[51,1629,1630],{"class":91}," ws.defined_names.items()})\n",[51,1632,1633,1635,1637,1640,1643,1646,1649,1651],{"class":53,"line":156},[51,1634,1297],{"class":250},[51,1636,857],{"class":91},[51,1638,1639],{"class":61},"\"Claim protected:\"",[51,1641,1642],{"class":91},", ws.protection.sheet, ",[51,1644,1645],{"class":61},"\"| Lists state:\"",[51,1647,1648],{"class":91},", wb2[",[51,1650,146],{"class":61},[51,1652,1653],{"class":91},"].sheet_state)\n",[51,1655,1656,1658,1661,1663,1665,1667,1669,1672,1674,1677,1679,1681],{"class":53,"line":194},[51,1657,845],{"class":87},[51,1659,1660],{"class":91}," ref ",[51,1662,851],{"class":87},[51,1664,164],{"class":91},[51,1666,797],{"class":61},[51,1668,170],{"class":91},[51,1670,1671],{"class":61},"\"C5\"",[51,1673,170],{"class":91},[51,1675,1676],{"class":61},"\"D5\"",[51,1678,170],{"class":91},[51,1680,1135],{"class":61},[51,1682,1683],{"class":91},"]:\n",[51,1685,1686,1689,1691],{"class":53,"line":218},[51,1687,1688],{"class":91},"    c ",[51,1690,108],{"class":87},[51,1692,1693],{"class":91}," ws[ref]\n",[51,1695,1696,1699,1701,1703,1705,1707,1710,1712,1715,1717,1720,1722,1725,1727,1730,1732,1735,1737,1740,1743,1745,1747],{"class":53,"line":224},[51,1697,1698],{"class":250},"    print",[51,1700,857],{"class":91},[51,1702,240],{"class":87},[51,1704,265],{"class":61},[51,1706,247],{"class":246},[51,1708,1709],{"class":91},"ref",[51,1711,262],{"class":246},[51,1713,1714],{"class":61},": locked=",[51,1716,247],{"class":246},[51,1718,1719],{"class":91},"c.protection.locked",[51,1721,262],{"class":246},[51,1723,1724],{"class":61}," hidden=",[51,1726,247],{"class":246},[51,1728,1729],{"class":91},"c.protection.hidden",[51,1731,262],{"class":246},[51,1733,1734],{"class":61}," value=",[51,1736,247],{"class":246},[51,1738,1739],{"class":91},"c.value",[51,1741,1742],{"class":87},"!r",[51,1744,262],{"class":246},[51,1746,265],{"class":61},[51,1748,117],{"class":91},[10,1750,1751,1752,1755],{},"Reading the file back confirms each decision: the three workbook names and the one scoped to ",[1205,1753,1754],{},"Claim",", the protection flag on the sheet, the hidden list sheet, and the lock state of an input cell (unlocked), a formula cell (locked and hidden) and the total. This kind of check is worth keeping as a test, because a template that silently lost its protection or a name would not show any error until someone overwrote a formula.",[42,1757,1759],{"className":44,"code":1758,"language":46,"meta":47,"style":47},"python - \u003C\u003C'EOF'\nfrom openpyxl import load_workbook\nwb = load_workbook(\"expense_claim.xlsx\"); ws = wb[\"Claim\"]\nws[\"B2\"] = \"A. Tester\"; ws[\"A5\"] = \"2026-09-14\"; ws[\"B5\"] = \"Travel\"; ws[\"C5\"] = 120\nws[\"B6\"] = \"Meals\"; ws[\"C6\"] = 36.6\nwb.save(\"claim_filled.xlsx\")\nEOF\nsoffice --headless --convert-to 'csv:Text - txt - csv (StarCalc):44,34,76,1,,0,false,true,true' claim_filled.xlsx > \u002Fdev\u002Fnull 2>&1\nsed -n '4,6p;26p' claim_filled.csv\n",[14,1760,1761,1775,1780,1785,1790,1795,1800,1805,1831],{"__ignoreMap":47},[51,1762,1763,1765,1768,1771],{"class":53,"line":54},[51,1764,80],{"class":57},[51,1766,1767],{"class":61}," -",[51,1769,1770],{"class":87}," \u003C\u003C",[51,1772,1774],{"class":1773},"sXKb-","'EOF'\n",[51,1776,1777],{"class":53,"line":95},[51,1778,1779],{"class":61},"from openpyxl import load_workbook\n",[51,1781,1782],{"class":53,"line":102},[51,1783,1784],{"class":61},"wb = load_workbook(\"expense_claim.xlsx\"); ws = wb[\"Claim\"]\n",[51,1786,1787],{"class":53,"line":120},[51,1788,1789],{"class":61},"ws[\"B2\"] = \"A. Tester\"; ws[\"A5\"] = \"2026-09-14\"; ws[\"B5\"] = \"Travel\"; ws[\"C5\"] = 120\n",[51,1791,1792],{"class":53,"line":136},[51,1793,1794],{"class":61},"ws[\"B6\"] = \"Meals\"; ws[\"C6\"] = 36.6\n",[51,1796,1797],{"class":53,"line":151},[51,1798,1799],{"class":61},"wb.save(\"claim_filled.xlsx\")\n",[51,1801,1802],{"class":53,"line":156},[51,1803,1804],{"class":1773},"EOF\n",[51,1806,1807,1810,1813,1816,1819,1822,1825,1828],{"class":53,"line":194},[51,1808,1809],{"class":57},"soffice",[51,1811,1812],{"class":250}," --headless",[51,1814,1815],{"class":250}," --convert-to",[51,1817,1818],{"class":61}," 'csv:Text - txt - csv (StarCalc):44,34,76,1,,0,false,true,true'",[51,1820,1821],{"class":61}," claim_filled.xlsx",[51,1823,1824],{"class":87}," >",[51,1826,1827],{"class":61}," \u002Fdev\u002Fnull",[51,1829,1830],{"class":87}," 2>&1\n",[51,1832,1833,1836,1839,1842],{"class":53,"line":218},[51,1834,1835],{"class":57},"sed",[51,1837,1838],{"class":250}," -n",[51,1840,1841],{"class":61}," '4,6p;26p'",[51,1843,1844],{"class":61}," claim_filled.csv\n",[10,1846,1847,1848,1850],{},"Filling in two lines and recalculating in LibreOffice shows the names working in formulas: VAT of 20.00 on a gross 120.00 and 6.10 on 36.60, net amounts of 100.00 and 30.50, and a total of 156.60 from ",[14,1849,1218],{},". The fill-in script uses openpyxl, which writes cells without asking about protection — sheet protection is enforced by the spreadsheet application, not by the file format, which is one more reason to see it as a guard against accidents.",[37,1852,1854],{"id":1853},"common-pitfalls","Common pitfalls",[1856,1857,1858,1868,1879,1889,1895],"ul",{},[1859,1860,1861,1864,1865,1867],"li",{},[317,1862,1863],{},"Unlocked formats forgotten."," Without ",[14,1866,1211],{}," on input cells, nobody can type anything once protected.",[1859,1869,1870,1873,1874,1876,1877,35],{},[317,1871,1872],{},"Names that look like cell addresses."," ",[14,1875,350],{}," is a cell; use ",[14,1878,330],{},[1859,1880,1881,1884,1885,1888],{},[317,1882,1883],{},"Hidden sheets treated as secure."," Users can unhide them; use ",[14,1886,1887],{},"very_hidden()"," or structure protection.",[1859,1890,1891,1894],{},[317,1892,1893],{},"Passwords as security."," Sheet protection prevents accidents, not determined users.",[1859,1896,1897,1900],{},[317,1898,1899],{},"Hard-coded rates in formulas."," Put them in named constants so they change in one place.",[37,1902,1904],{"id":1903},"conclusion","Conclusion",[10,1906,1907,1908,1910,1911,1914,1915,1917,1918,1920],{},"In XlsxWriter, ",[14,1909,24],{}," gives formulas readable names for ranges and constants — workbook-wide or scoped to one sheet with ",[14,1912,1913],{},"Sheet!Name"," — and a named list makes dropdowns easy to maintain. Mark input cells with an unlocked format, hide formulas where you want only results visible, and call ",[14,1916,1309],{}," with the options users still need. XlsxWriter cannot lock workbook structure, so use ",[14,1919,1887],{}," or openpyxl for that, and read the file back in a test to confirm the result.",[37,1922,1924],{"id":1923},"frequently-asked-questions","Frequently asked questions",[10,1926,1927,1930,1931,1934,1935,1938],{},[317,1928,1929],{},"How do I create a named range with XlsxWriter?","\nCall ",[14,1932,1933],{},"workbook.define_name(\"Name\", \"=Sheet1!$A$1:$A$10\")","; prefix the name with a sheet name, as in ",[14,1936,1937],{},"\"Sheet1!Name\"",", to scope it to that sheet.",[10,1940,1941,1944,1945,1948],{},[317,1942,1943],{},"Can a defined name hold a constant instead of a range?","\nYes: ",[14,1946,1947],{},"workbook.define_name(\"VAT_Rate\", \"=0.2\")"," creates a name that formulas can use like a cell.",[10,1950,1951,1954,1955,1957,1958,35],{},[317,1952,1953],{},"How do I let users edit only some cells in XlsxWriter?","\nGive those cells a format with ",[14,1956,1211],{},", then call ",[14,1959,1960],{},"worksheet.protect()",[10,1962,1963,1966,1967,1969],{},[317,1964,1965],{},"Can XlsxWriter protect the workbook structure?","\nNo; it protects sheets only. Use ",[14,1968,1887],{}," for sheets that must not be unhidden, or set workbook structure protection with openpyxl afterwards.",[37,1971,1973],{"id":1972},"related","Related",[1856,1975,1976,1982,1989,1996],{},[1859,1977,1978,1979,1981],{},"Up one level: ",[31,1980,34],{"href":33}," — reports with XlsxWriter.",[1859,1983,1984,1988],{},[31,1985,1987],{"href":1986},"\u002Fgetting-started-with-python-excel-automation\u002Fworking-with-excel-formulas-in-python\u002Fcreate-named-range-in-excel-with-openpyxl\u002F","Create a Named Range in Excel with openpyxl"," — the openpyxl way.",[1859,1990,1991,1995],{},[31,1992,1994],{"href":1993},"\u002Fadvanced-data-transformation-and-cleaning\u002Fvalidating-excel-data-with-python\u002Fcreate-dependent-dropdown-lists-in-excel-with-python\u002F","Create Dependent Dropdown Lists in Excel with Python"," — dropdowns driven by named lists.",[1859,1997,1998,2002],{},[31,1999,2001],{"href":2000},"\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fread-a-named-range-into-pandas\u002F","Read a Named Range into pandas"," — using the names later.",[2004,2005,2006],"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 .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html pre.shiki code .sXKb-, html code.shiki .sXKb-{--shiki-default:#032F62;--shiki-dark:#F0F3F6}",{"title":47,"searchDepth":95,"depth":95,"links":2008},[2009,2010,2011,2012,2013,2014,2015,2016,2017],{"id":39,"depth":95,"text":40},{"id":74,"depth":95,"text":75},{"id":517,"depth":95,"text":518},{"id":1230,"depth":95,"text":1231},{"id":1537,"depth":95,"text":1538},{"id":1853,"depth":95,"text":1854},{"id":1903,"depth":95,"text":1904},{"id":1923,"depth":95,"text":1924},{"id":1972,"depth":95,"text":1973},"2026-10-02","Build an input template with XlsxWriter in which formulas use defined names instead of cell addresses and only the input cells can be edited: workbook and sheet-scoped names for ranges and constants, a dropdown fed by a named list on a hidden sheet, unlocked input cells, hidden formulas, protect() options that still allow sorting and filtering, and a check of the result with openpyxl and LibreOffice.","md",[2022,2024,2026,2028],{"q":1929,"a":2023},"Call workbook.define_name(\"Name\", \"=Sheet1!$A$1:$A$10\"); prefix the name with a sheet name, as in \"Sheet1!Name\", to scope it to that sheet.",{"q":1943,"a":2025},"Yes: workbook.define_name(\"VAT_Rate\", \"=0.2\") creates a name that formulas can use like a cell.",{"q":1953,"a":2027},"Give those cells a format with \"locked\": False, then call worksheet.protect().",{"q":1965,"a":2029},"No; it protects sheets only. Use very_hidden() for sheets that must not be unhidden, or set workbook structure protection with openpyxl afterwards.","xlsxwriter define_name protect",null,{"breadcrumb":2033},[2034,2037,2040],{"name":2035,"item":2036},"Home","\u002F",{"name":2038,"item":2039},"Formatting and Charting Excel Reports with Python","\u002Fformatting-and-charting-excel-reports-with-python\u002F",{"name":2041,"item":33},"Building Excel Reports with xlsxwriter","\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fdefine-names-and-protect-sheets-with-xlsxwriter",{"title":5,"description":2044},"XlsxWriter define_name for ranges and constants, sheet-scoped names, named dropdown lists, unlocked input cells, hidden formulas, protect() options and what XlsxWriter cannot protect.","define-names-and-protect-sheets-with-xlsxwriter","formatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fdefine-names-and-protect-sheets-with-xlsxwriter\u002Findex",[2048,2049],"xlsxwriter","protection-and-sharing","how-to","8uDw0KjmhPuOitpMtnWboA8EKhTf3S-3KpOiIG2KKjg",[2053,2057],{"title":2054,"path":2055,"stem":2056,"children":-1},"Combine Chart Types and Chartsheets with XlsxWriter","\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fcombine-chart-types-and-chartsheets-with-xlsxwriter","formatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fcombine-chart-types-and-chartsheets-with-xlsxwriter\u002Findex",{"title":2058,"path":2059,"stem":2060,"children":-1},"Freeze Panes and Set Zoom with XlsxWriter","\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Ffreeze-panes-and-set-zoom-with-xlsxwriter","formatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Ffreeze-panes-and-set-zoom-with-xlsxwriter\u002Findex",[2062,2064],{"path":2063,"title":2041},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter",{"path":2065,"title":2038},"\u002Fformatting-and-charting-excel-reports-with-python",[],[2068,2072,2073,2074,2077,2080],{"path":2069,"title":2070,"kind":2071},"\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",{"path":2055,"title":2054,"kind":2050},{"path":2059,"title":2058,"kind":2050},{"path":2075,"title":2076,"kind":2050},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Finsert-checkboxes-with-xlsxwriter","Insert Checkboxes in Excel with xlsxwriter",{"path":2078,"title":2079,"kind":2050},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fset-print-options-headers-and-footers-with-xlsxwriter","Set Print Options, Headers and Footers with XlsxWriter",{"path":2081,"title":2082,"kind":2050},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-an-excel-table-with-formula-columns-using-xlsxwriter","Write an Excel Table with Formula Columns Using XlsxWriter",1790972108548]