[{"data":1,"prerenderedAt":2184},["ShallowReactive",2],{"doc:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fstyle-chart-colors-and-data-labels-with-openpyxl":3,"surround:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fstyle-chart-colors-and-data-labels-with-openpyxl":2150,"ancestors:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fstyle-chart-colors-and-data-labels-with-openpyxl":2159,"children:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fstyle-chart-colors-and-data-labels-with-openpyxl":2164,"related:\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fstyle-chart-colors-and-data-labels-with-openpyxl":2165},{"id":4,"title":5,"body":6,"dateModified":2117,"datePublished":2117,"description":2118,"extension":2119,"faq":2120,"keyword":2129,"kind":2130,"meta":2131,"navigation":314,"path":2140,"seo":2141,"slug":2143,"stem":2144,"topics":2145,"type":2148,"__hash__":2149},"docs\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fstyle-chart-colors-and-data-labels-with-openpyxl\u002Findex.md","Style Chart Colours and Data Labels with openpyxl",{"type":7,"value":8,"toc":2104},"minimark",[9,19,240,245,270,273,692,696,714,760,763,767,774,842,845,991,994,998,1018,1099,1110,1225,1229,1251,1465,1483,1487,1490,1551,1558,1562,1565,1783,1793,1955,1959,2010,2014,2039,2043,2049,2055,2061,2067,2071,2100],[10,11,12,13,18],"p",{},"Charts generated by openpyxl start with Excel's default look: the theme's first colours in order, heavy gridlines and no labels. That is fine for a quick check and wrong for a report, where colours should mean something — the company palette, red for a missed month, a dashed line for the target — and the numbers should be readable without hovering. openpyxl exposes nearly all of Excel's chart formatting through a few property objects. This guide, part of ",[14,15,17],"a",{"href":16},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002F","Creating Charts in Excel with openpyxl",", styles a revenue-versus-target chart and checks every setting by rendering the workbook.",[20,21,29,30,29,34,29,38,29,46,29,55,29,62,29,67,29,71,29,79,29,84,29,88,29,92,29,95,29,99,29,102,29,106,29,109,29,115,29,119,29,123,29,127,29,131,29,135,29,141,29,147,29,152,29,157,29,162,29,167,29,171,29,175,29,180,29,185,29,190,29,195,29,200,29,206,29,209,29,212,29,215,29,218,29,223,29,228,29,231,29,234,29,237],"svg",{"viewBox":22,"role":23,"ariaLabelledBy":24,"xmlns":27,"style":28},"4 0 748 280","img",[25,26],"cs-chart-t","cs-chart-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:748px;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},"Revenue against a dashed target line",[35,36,37],"desc",{"id":26},"Six monthly revenue bars in a light brand colour, with March, the one month well below target, highlighted in red. A dashed teal line shows the target of 90K rising to 95K from April. Each bar carries a bold label in thousands.",[39,40],"rect",{"x":41,"y":42,"width":43,"height":44,"fill":45},"4","0","748","280","#ffffff",[39,47],{"x":48,"y":49,"width":50,"height":50,"rx":51,"fill":52,"stroke":53,"style":54},"64.0","18.0","14.0","3","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:1.5px",[56,57,61],"text",{"x":58,"y":59,"style":60},"84.0","29.5","font-size:12px;font-weight:600;fill:var(--text,#172033);text-anchor:start","Revenue (K)",[39,63],{"x":64,"y":49,"width":50,"height":50,"rx":51,"fill":65,"stroke":66,"style":54},"187.6","#d9f4f1","var(--teal,#0f9488)",[56,68,70],{"x":69,"y":59,"style":60},"207.6","Target (K)",[72,73],"line",{"x1":74,"y1":75,"x2":76,"y2":75,"stroke":77,"style":78},"51.3","242.0","736.0","var(--line,#cdd5e6)","stroke-width:1px",[56,80,42],{"x":81,"y":82,"style":83},"29.6","246.0","font-size:11px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle",[72,85],{"x1":74,"y1":86,"x2":76,"y2":86,"stroke":77,"style":87},"178.7","stroke-width:1px;stroke-dasharray:5 4",[56,89,91],{"x":81,"y":90,"style":83},"182.7","50",[72,93],{"x1":74,"y1":94,"x2":76,"y2":94,"stroke":77,"style":87},"115.3",[56,96,98],{"x":81,"y":97,"style":83},"119.3","100",[72,100],{"x1":74,"y1":101,"x2":76,"y2":101,"stroke":77,"style":87},"52.0",[56,103,105],{"x":81,"y":104,"style":83},"56.0","150",[72,107],{"x1":74,"y1":75,"x2":76,"y2":75,"stroke":108,"style":54},"var(--muted,#5b6780)",[56,110,114],{"x":111,"y":112,"style":113},"108.3","260.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","Jan",[56,116,118],{"x":117,"y":112,"style":113},"222.5","Feb",[56,120,122],{"x":121,"y":112,"style":113},"336.6","Mar",[56,124,126],{"x":125,"y":112,"style":113},"450.7","Apr",[56,128,130],{"x":129,"y":112,"style":113},"564.8","May",[56,132,134],{"x":133,"y":112,"style":113},"678.9","Jun",[39,136],{"x":137,"y":138,"width":139,"height":140,"rx":51,"fill":52,"stroke":53,"style":54},"71.3","138.1","36.0","103.9",[56,142,146],{"x":143,"y":144,"style":145},"89.3","132.1","font-size:10.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","82",[39,148],{"x":149,"y":150,"width":139,"height":151,"rx":51,"fill":52,"stroke":53,"style":54},"185.5","126.1","115.9",[56,153,156],{"x":154,"y":155,"style":145},"203.5","120.1","91.5",[39,158],{"x":159,"y":160,"width":139,"height":161,"rx":51,"fill":52,"stroke":53,"style":54},"299.6","142.9","99.1",[56,163,166],{"x":164,"y":165,"style":145},"317.6","136.9","78.25",[39,168],{"x":169,"y":170,"width":139,"height":144,"rx":51,"fill":52,"stroke":53,"style":54},"413.7","109.9",[56,172,174],{"x":173,"y":140,"style":145},"431.7","104.3",[39,176],{"x":177,"y":178,"width":139,"height":179,"rx":51,"fill":52,"stroke":53,"style":54},"527.8","115.6","126.4",[56,181,184],{"x":182,"y":183,"style":145},"545.8","109.6","99.8",[39,186],{"x":187,"y":188,"width":139,"height":189,"rx":51,"fill":52,"stroke":53,"style":54},"641.9","99.6","142.4",[56,191,194],{"x":192,"y":193,"style":145},"659.9","93.6","112.4",[39,196],{"x":197,"y":198,"width":139,"height":199,"rx":51,"fill":65,"stroke":66,"style":54},"109.3","128.0","114.0",[56,201,205],{"x":202,"y":203,"style":204},"127.3","122.0","font-size:10.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","90",[39,207],{"x":208,"y":198,"width":139,"height":199,"rx":51,"fill":65,"stroke":66,"style":54},"223.5",[56,210,205],{"x":211,"y":203,"style":204},"241.5",[39,213],{"x":214,"y":198,"width":139,"height":199,"rx":51,"fill":65,"stroke":66,"style":54},"337.6",[56,216,205],{"x":217,"y":203,"style":204},"355.6",[39,219],{"x":220,"y":221,"width":139,"height":222,"rx":51,"fill":65,"stroke":66,"style":54},"451.7","121.7","120.3",[56,224,227],{"x":225,"y":226,"style":204},"469.7","115.7","95",[39,229],{"x":230,"y":221,"width":139,"height":222,"rx":51,"fill":65,"stroke":66,"style":54},"565.8",[56,232,227],{"x":233,"y":226,"style":204},"583.8",[39,235],{"x":236,"y":221,"width":139,"height":222,"rx":51,"fill":65,"stroke":66,"style":54},"679.9",[56,238,227],{"x":239,"y":226,"style":204},"697.9",[241,242,244],"h2",{"id":243},"prerequisites","Prerequisites",[246,247,252],"pre",{"className":248,"code":249,"language":250,"meta":251,"style":251},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install openpyxl\n","bash","",[253,254,255],"code",{"__ignoreMap":251},[256,257,259,263,267],"span",{"class":72,"line":258},1,[256,260,262],{"class":261},"sMTad","pip",[256,264,266],{"class":265},"srMev"," install",[256,268,269],{"class":265}," openpyxl\n",[10,271,272],{},"Monthly revenue and a target, with a column chart for revenue and a line for the target:",[246,274,278],{"className":275,"code":276,"language":277,"meta":251,"style":251},"language-python shiki shiki-themes github-light github-dark-high-contrast","from openpyxl import Workbook\nfrom openpyxl.chart import BarChart, LineChart, Reference\n\nwb = Workbook()\nws = wb.active\nws.title = \"Sales\"\nws.append([\"Month\", \"Revenue\", \"Target\"])\nfor row in [(\"Jan\", 82000, 90000), (\"Feb\", 91500, 90000), (\"Mar\", 78250, 90000),\n            (\"Apr\", 104300, 95000), (\"May\", 99800, 95000), (\"Jun\", 112400, 95000)]:\n    ws.append(row)\n\nchart = BarChart()\nchart.x_axis.delete = False  # Excel hides axes unless this is set\nchart.y_axis.delete = False\nchart.type = \"col\"\nchart.add_data(Reference(ws, min_col=2, min_row=1, max_row=7), titles_from_data=True)\nchart.set_categories(Reference(ws, min_col=1, min_row=2, max_row=7))\ntarget = LineChart()\ntarget.x_axis.delete = False\ntarget.y_axis.delete = False\ntarget.add_data(Reference(ws, min_col=3, min_row=1, max_row=7), titles_from_data=True)\n","python",[253,279,280,296,309,316,328,339,350,373,434,484,490,495,506,521,532,543,592,623,634,644,654],{"__ignoreMap":251},[256,281,282,286,290,293],{"class":72,"line":258},[256,283,285],{"class":284},"s-kum","from",[256,287,289],{"class":288},"skGVy"," openpyxl ",[256,291,292],{"class":284},"import",[256,294,295],{"class":288}," Workbook\n",[256,297,299,301,304,306],{"class":72,"line":298},2,[256,300,285],{"class":284},[256,302,303],{"class":288}," openpyxl.chart ",[256,305,292],{"class":284},[256,307,308],{"class":288}," BarChart, LineChart, Reference\n",[256,310,312],{"class":72,"line":311},3,[256,313,315],{"emptyLinePlaceholder":314},true,"\n",[256,317,319,322,325],{"class":72,"line":318},4,[256,320,321],{"class":288},"wb ",[256,323,324],{"class":284},"=",[256,326,327],{"class":288}," Workbook()\n",[256,329,331,334,336],{"class":72,"line":330},5,[256,332,333],{"class":288},"ws ",[256,335,324],{"class":284},[256,337,338],{"class":288}," wb.active\n",[256,340,342,345,347],{"class":72,"line":341},6,[256,343,344],{"class":288},"ws.title ",[256,346,324],{"class":284},[256,348,349],{"class":265}," \"Sales\"\n",[256,351,353,356,359,362,365,367,370],{"class":72,"line":352},7,[256,354,355],{"class":288},"ws.append([",[256,357,358],{"class":265},"\"Month\"",[256,360,361],{"class":288},", ",[256,363,364],{"class":265},"\"Revenue\"",[256,366,361],{"class":288},[256,368,369],{"class":265},"\"Target\"",[256,371,372],{"class":288},"])\n",[256,374,376,379,382,385,388,391,393,397,399,402,405,408,410,413,415,417,419,422,424,427,429,431],{"class":72,"line":375},8,[256,377,378],{"class":284},"for",[256,380,381],{"class":288}," row ",[256,383,384],{"class":284},"in",[256,386,387],{"class":288}," [(",[256,389,390],{"class":265},"\"Jan\"",[256,392,361],{"class":288},[256,394,396],{"class":395},"sP0c6","82000",[256,398,361],{"class":288},[256,400,401],{"class":395},"90000",[256,403,404],{"class":288},"), (",[256,406,407],{"class":265},"\"Feb\"",[256,409,361],{"class":288},[256,411,412],{"class":395},"91500",[256,414,361],{"class":288},[256,416,401],{"class":395},[256,418,404],{"class":288},[256,420,421],{"class":265},"\"Mar\"",[256,423,361],{"class":288},[256,425,426],{"class":395},"78250",[256,428,361],{"class":288},[256,430,401],{"class":395},[256,432,433],{"class":288},"),\n",[256,435,437,440,443,445,448,450,453,455,458,460,463,465,467,469,472,474,477,479,481],{"class":72,"line":436},9,[256,438,439],{"class":288},"            (",[256,441,442],{"class":265},"\"Apr\"",[256,444,361],{"class":288},[256,446,447],{"class":395},"104300",[256,449,361],{"class":288},[256,451,452],{"class":395},"95000",[256,454,404],{"class":288},[256,456,457],{"class":265},"\"May\"",[256,459,361],{"class":288},[256,461,462],{"class":395},"99800",[256,464,361],{"class":288},[256,466,452],{"class":395},[256,468,404],{"class":288},[256,470,471],{"class":265},"\"Jun\"",[256,473,361],{"class":288},[256,475,476],{"class":395},"112400",[256,478,361],{"class":288},[256,480,452],{"class":395},[256,482,483],{"class":288},")]:\n",[256,485,487],{"class":72,"line":486},10,[256,488,489],{"class":288},"    ws.append(row)\n",[256,491,493],{"class":72,"line":492},11,[256,494,315],{"emptyLinePlaceholder":314},[256,496,498,501,503],{"class":72,"line":497},12,[256,499,500],{"class":288},"chart ",[256,502,324],{"class":284},[256,504,505],{"class":288}," BarChart()\n",[256,507,509,512,514,517],{"class":72,"line":508},13,[256,510,511],{"class":288},"chart.x_axis.delete ",[256,513,324],{"class":284},[256,515,516],{"class":395}," False",[256,518,520],{"class":519},"s-wDw","  # Excel hides axes unless this is set\n",[256,522,524,527,529],{"class":72,"line":523},14,[256,525,526],{"class":288},"chart.y_axis.delete ",[256,528,324],{"class":284},[256,530,531],{"class":395}," False\n",[256,533,535,538,540],{"class":72,"line":534},15,[256,536,537],{"class":288},"chart.type ",[256,539,324],{"class":284},[256,541,542],{"class":265}," \"col\"\n",[256,544,546,549,553,555,558,560,563,565,568,570,573,575,578,581,584,586,589],{"class":72,"line":545},16,[256,547,548],{"class":288},"chart.add_data(Reference(ws, ",[256,550,552],{"class":551},"sa561","min_col",[256,554,324],{"class":284},[256,556,557],{"class":395},"2",[256,559,361],{"class":288},[256,561,562],{"class":551},"min_row",[256,564,324],{"class":284},[256,566,567],{"class":395},"1",[256,569,361],{"class":288},[256,571,572],{"class":551},"max_row",[256,574,324],{"class":284},[256,576,577],{"class":395},"7",[256,579,580],{"class":288},"), ",[256,582,583],{"class":551},"titles_from_data",[256,585,324],{"class":284},[256,587,588],{"class":395},"True",[256,590,591],{"class":288},")\n",[256,593,595,598,600,602,604,606,608,610,612,614,616,618,620],{"class":72,"line":594},17,[256,596,597],{"class":288},"chart.set_categories(Reference(ws, ",[256,599,552],{"class":551},[256,601,324],{"class":284},[256,603,567],{"class":395},[256,605,361],{"class":288},[256,607,562],{"class":551},[256,609,324],{"class":284},[256,611,557],{"class":395},[256,613,361],{"class":288},[256,615,572],{"class":551},[256,617,324],{"class":284},[256,619,577],{"class":395},[256,621,622],{"class":288},"))\n",[256,624,626,629,631],{"class":72,"line":625},18,[256,627,628],{"class":288},"target ",[256,630,324],{"class":284},[256,632,633],{"class":288}," LineChart()\n",[256,635,637,640,642],{"class":72,"line":636},19,[256,638,639],{"class":288},"target.x_axis.delete ",[256,641,324],{"class":284},[256,643,531],{"class":395},[256,645,647,650,652],{"class":72,"line":646},20,[256,648,649],{"class":288},"target.y_axis.delete ",[256,651,324],{"class":284},[256,653,531],{"class":395},[256,655,657,660,662,664,666,668,670,672,674,676,678,680,682,684,686,688,690],{"class":72,"line":656},21,[256,658,659],{"class":288},"target.add_data(Reference(ws, ",[256,661,552],{"class":551},[256,663,324],{"class":284},[256,665,51],{"class":395},[256,667,361],{"class":288},[256,669,562],{"class":551},[256,671,324],{"class":284},[256,673,567],{"class":395},[256,675,361],{"class":288},[256,677,572],{"class":551},[256,679,324],{"class":284},[256,681,577],{"class":395},[256,683,580],{"class":288},[256,685,583],{"class":551},[256,687,324],{"class":284},[256,689,588],{"class":395},[256,691,591],{"class":288},[241,693,695],{"id":694},"step-1-series-fill-and-outline","Step 1 — Series fill and outline",[10,697,698,699,702,703,706,707,709,710,713],{},"Every series has ",[253,700,701],{},"graphicalProperties",": ",[253,704,705],{},"solidFill"," for the area, ",[253,708,72],{}," for the outline. Colours are hex strings without ",[253,711,712],{},"#",":",[246,715,717],{"className":275,"code":716,"language":277,"meta":251,"style":251},"revenue = chart.series[0]\nrevenue.graphicalProperties.solidFill = \"C7D2FE\"          # light indigo fill\nrevenue.graphicalProperties.line.solidFill = \"4338CA\"     # darker outline\n",[253,718,719,734,747],{"__ignoreMap":251},[256,720,721,724,726,729,731],{"class":72,"line":258},[256,722,723],{"class":288},"revenue ",[256,725,324],{"class":284},[256,727,728],{"class":288}," chart.series[",[256,730,42],{"class":395},[256,732,733],{"class":288},"]\n",[256,735,736,739,741,744],{"class":72,"line":298},[256,737,738],{"class":288},"revenue.graphicalProperties.solidFill ",[256,740,324],{"class":284},[256,742,743],{"class":265}," \"C7D2FE\"",[256,745,746],{"class":519},"          # light indigo fill\n",[256,748,749,752,754,757],{"class":72,"line":311},[256,750,751],{"class":288},"revenue.graphicalProperties.line.solidFill ",[256,753,324],{"class":284},[256,755,756],{"class":265}," \"4338CA\"",[256,758,759],{"class":519},"     # darker outline\n",[10,761,762],{},"A light fill with a darker outline of the same hue keeps bars crisp without making them heavy, and leaves room for labels and highlights to stand out. Set both properties: without an explicit outline, some viewers draw the theme's default border around a custom fill.",[241,764,766],{"id":765},"step-2-highlight-one-bar","Step 2 — Highlight one bar",[10,768,769,770,773],{},"To draw attention to one category — the month that missed its target — override that point only. A ",[253,771,772],{},"DataPoint"," takes the zero-based index of the category:",[246,775,777],{"className":275,"code":776,"language":277,"meta":251,"style":251},"from openpyxl.chart.marker import DataPoint\n\nmissed = DataPoint(idx=2)                                 # March\nmissed.graphicalProperties.solidFill = \"B91C1C\"\nmissed.graphicalProperties.line.solidFill = \"B91C1C\"\nrevenue.dPt.append(missed)\n",[253,778,779,791,795,818,828,837],{"__ignoreMap":251},[256,780,781,783,786,788],{"class":72,"line":258},[256,782,285],{"class":284},[256,784,785],{"class":288}," openpyxl.chart.marker ",[256,787,292],{"class":284},[256,789,790],{"class":288}," DataPoint\n",[256,792,793],{"class":72,"line":298},[256,794,315],{"emptyLinePlaceholder":314},[256,796,797,800,802,805,808,810,812,815],{"class":72,"line":311},[256,798,799],{"class":288},"missed ",[256,801,324],{"class":284},[256,803,804],{"class":288}," DataPoint(",[256,806,807],{"class":551},"idx",[256,809,324],{"class":284},[256,811,557],{"class":395},[256,813,814],{"class":288},")                                 ",[256,816,817],{"class":519},"# March\n",[256,819,820,823,825],{"class":72,"line":318},[256,821,822],{"class":288},"missed.graphicalProperties.solidFill ",[256,824,324],{"class":284},[256,826,827],{"class":265}," \"B91C1C\"\n",[256,829,830,833,835],{"class":72,"line":330},[256,831,832],{"class":288},"missed.graphicalProperties.line.solidFill ",[256,834,324],{"class":284},[256,836,827],{"class":265},[256,838,839],{"class":72,"line":341},[256,840,841],{"class":288},"revenue.dPt.append(missed)\n",[10,843,844],{},"In a generated report, compute the indexes from the data instead of hard-coding them — here, every month where revenue fell more than 5% short of target:",[246,846,848],{"className":275,"code":847,"language":277,"meta":251,"style":251},"revenue.dPt.clear()\nrows = list(ws.iter_rows(min_row=2, max_row=7, values_only=True))\nfor idx, (_, actual, goal_value) in enumerate(rows):\n    if actual \u003C goal_value * 0.95:\n        point = DataPoint(idx=idx)\n        point.graphicalProperties.solidFill = \"B91C1C\"\n        point.graphicalProperties.line.solidFill = \"B91C1C\"\n        revenue.dPt.append(point)\nprint([p.idx for p in revenue.dPt])       # [0, 2]\n",[253,849,850,855,893,908,931,947,956,965,970],{"__ignoreMap":251},[256,851,852],{"class":72,"line":258},[256,853,854],{"class":288},"revenue.dPt.clear()\n",[256,856,857,860,862,865,868,870,872,874,876,878,880,882,884,887,889,891],{"class":72,"line":298},[256,858,859],{"class":288},"rows ",[256,861,324],{"class":284},[256,863,864],{"class":395}," list",[256,866,867],{"class":288},"(ws.iter_rows(",[256,869,562],{"class":551},[256,871,324],{"class":284},[256,873,557],{"class":395},[256,875,361],{"class":288},[256,877,572],{"class":551},[256,879,324],{"class":284},[256,881,577],{"class":395},[256,883,361],{"class":288},[256,885,886],{"class":551},"values_only",[256,888,324],{"class":284},[256,890,588],{"class":395},[256,892,622],{"class":288},[256,894,895,897,900,902,905],{"class":72,"line":311},[256,896,378],{"class":284},[256,898,899],{"class":288}," idx, (_, actual, goal_value) ",[256,901,384],{"class":284},[256,903,904],{"class":395}," enumerate",[256,906,907],{"class":288},"(rows):\n",[256,909,910,913,916,919,922,925,928],{"class":72,"line":318},[256,911,912],{"class":284},"    if",[256,914,915],{"class":288}," actual ",[256,917,918],{"class":284},"\u003C",[256,920,921],{"class":288}," goal_value ",[256,923,924],{"class":284},"*",[256,926,927],{"class":395}," 0.95",[256,929,930],{"class":288},":\n",[256,932,933,936,938,940,942,944],{"class":72,"line":330},[256,934,935],{"class":288},"        point ",[256,937,324],{"class":284},[256,939,804],{"class":288},[256,941,807],{"class":551},[256,943,324],{"class":284},[256,945,946],{"class":288},"idx)\n",[256,948,949,952,954],{"class":72,"line":341},[256,950,951],{"class":288},"        point.graphicalProperties.solidFill ",[256,953,324],{"class":284},[256,955,827],{"class":265},[256,957,958,961,963],{"class":72,"line":352},[256,959,960],{"class":288},"        point.graphicalProperties.line.solidFill ",[256,962,324],{"class":284},[256,964,827],{"class":265},[256,966,967],{"class":72,"line":375},[256,968,969],{"class":288},"        revenue.dPt.append(point)\n",[256,971,972,975,978,980,983,985,988],{"class":72,"line":436},[256,973,974],{"class":395},"print",[256,976,977],{"class":288},"([p.idx ",[256,979,378],{"class":284},[256,981,982],{"class":288}," p ",[256,984,384],{"class":284},[256,986,987],{"class":288}," revenue.dPt])       ",[256,989,990],{"class":519},"# [0, 2]\n",[10,992,993],{},"The rule lives in code next to the data, so next month's chart highlights whichever months qualify. With a 5% tolerance both January (82K against 90K, 8.9% short) and March (13% short) are marked; a 10% tolerance would mark only March. Choose the threshold with whoever reads the report, and state it in the chart title or a note. Keep highlighting rare; a chart where half the bars are red highlights nothing, and readers stop looking for the signal.",[241,995,997],{"id":996},"step-3-a-dashed-target-line","Step 3 — A dashed target line",[10,999,1000,1001,1003,1004,1007,1008,361,1011,1014,1015,713],{},"Line series use the same ",[253,1002,72],{}," object for the line itself. Width is in EMUs — 12,700 per point — and ",[253,1005,1006],{},"dashStyle"," takes Excel's names such as ",[253,1009,1010],{},"\"dash\"",[253,1012,1013],{},"\"sysDot\""," and ",[253,1016,1017],{},"\"lgDash\"",[246,1019,1021],{"className":275,"code":1020,"language":277,"meta":251,"style":251},"goal = target.series[0]\ngoal.graphicalProperties.line.solidFill = \"0B6157\"\ngoal.graphicalProperties.line.width = 28575                # 2.25 pt\ngoal.graphicalProperties.line.dashStyle = \"dash\"\ngoal.marker.symbol = \"none\"\ngoal.smooth = False\nchart += target\n",[253,1022,1023,1037,1047,1060,1070,1080,1089],{"__ignoreMap":251},[256,1024,1025,1028,1030,1033,1035],{"class":72,"line":258},[256,1026,1027],{"class":288},"goal ",[256,1029,324],{"class":284},[256,1031,1032],{"class":288}," target.series[",[256,1034,42],{"class":395},[256,1036,733],{"class":288},[256,1038,1039,1042,1044],{"class":72,"line":298},[256,1040,1041],{"class":288},"goal.graphicalProperties.line.solidFill ",[256,1043,324],{"class":284},[256,1045,1046],{"class":265}," \"0B6157\"\n",[256,1048,1049,1052,1054,1057],{"class":72,"line":311},[256,1050,1051],{"class":288},"goal.graphicalProperties.line.width ",[256,1053,324],{"class":284},[256,1055,1056],{"class":395}," 28575",[256,1058,1059],{"class":519},"                # 2.25 pt\n",[256,1061,1062,1065,1067],{"class":72,"line":318},[256,1063,1064],{"class":288},"goal.graphicalProperties.line.dashStyle ",[256,1066,324],{"class":284},[256,1068,1069],{"class":265}," \"dash\"\n",[256,1071,1072,1075,1077],{"class":72,"line":330},[256,1073,1074],{"class":288},"goal.marker.symbol ",[256,1076,324],{"class":284},[256,1078,1079],{"class":265}," \"none\"\n",[256,1081,1082,1085,1087],{"class":72,"line":341},[256,1083,1084],{"class":288},"goal.smooth ",[256,1086,324],{"class":284},[256,1088,531],{"class":395},[256,1090,1091,1093,1096],{"class":72,"line":352},[256,1092,500],{"class":288},[256,1094,1095],{"class":284},"+=",[256,1097,1098],{"class":288}," target\n",[10,1100,1101,1102,1104,1105,1109],{},"Turning off markers and smoothing keeps the target a plain reference line that reads as a threshold rather than as data. Combining the bar and line charts with ",[253,1103,1095],{}," puts them on the same axes; a second axis is only needed when the units differ, as described in ",[14,1106,1108],{"href":1107},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fadd-a-combo-chart-with-a-secondary-axis-in-openpyxl\u002F","Add a Combo Chart with a Secondary Axis in openpyxl",".",[20,1111,29,1117,29,1120,29,1123,29,1127,29,1133,29,1138,29,1144,29,1148,29,1154,29,1156,29,1160,29,1163,29,1167,29,1169,29,1173,29,1176,29,1180,29,1182,29,1186,29,1189,29,1193,29,1195,29,1199,29,1202,29,1206,29,1208,29,1212,29,1215,29,1219,29,1221],{"viewBox":1112,"role":23,"ariaLabelledBy":1113,"xmlns":27,"style":1116},"4 0 752 276",[1114,1115],"cs-props-t","cs-props-d","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[31,1118,1119],{"id":1114},"Where each visual property lives",[35,1121,1122],{"id":1115},"A series fill is graphicalProperties solidFill, its outline or line graphicalProperties line solidFill, line thickness line width in EMUs and dashes line dashStyle. A single bar is a DataPoint in series dPt. Label text style is the DataLabelList txPr, and gridline colour is the axis majorGridlines spPr.",[39,1124],{"x":41,"y":42,"width":1125,"height":1126,"fill":45},"752","276",[39,1128],{"x":1129,"y":1130,"width":1131,"height":1132,"rx":42,"fill":45,"stroke":77,"style":78},"20.0","16.0","170.9","40.0",[39,1134],{"x":1135,"y":1130,"width":1136,"height":1132,"rx":42,"fill":52,"stroke":53,"style":1137},"190.9","549.1","stroke-width:1.2px",[56,1139,1143],{"x":1140,"y":1141,"style":1142},"465.5","41.0","font-size:13px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","openpyxl attribute",[39,1145],{"x":1129,"y":104,"width":1131,"height":1146,"rx":42,"fill":1147,"stroke":77,"style":78},"34.0","#e7ebef",[56,1149,1153],{"x":1150,"y":1151,"style":1152},"32.0","78.0","font-size:12.5px;font-weight:700;fill:var(--text,#172033);text-anchor:start","Bar or area fill",[39,1155],{"x":1135,"y":104,"width":1136,"height":1146,"rx":42,"fill":45,"stroke":77,"style":78},[56,1157,1159],{"x":1140,"y":1158,"style":113},"77.0","series.graphicalProperties.solidFill",[39,1161],{"x":1129,"y":1162,"width":1131,"height":1146,"rx":42,"fill":1147,"stroke":77,"style":78},"90.0",[56,1164,1166],{"x":1150,"y":1165,"style":1152},"112.0","Outline or line colour",[39,1168],{"x":1135,"y":1162,"width":1136,"height":1146,"rx":42,"fill":45,"stroke":77,"style":78},[56,1170,1172],{"x":1140,"y":1171,"style":113},"111.0","….line.solidFill",[39,1174],{"x":1129,"y":1175,"width":1131,"height":1146,"rx":42,"fill":1147,"stroke":77,"style":78},"124.0",[56,1177,1179],{"x":1150,"y":1178,"style":1152},"146.0","Line width and dashes",[39,1181],{"x":1135,"y":1175,"width":1136,"height":1146,"rx":42,"fill":45,"stroke":77,"style":78},[56,1183,1185],{"x":1140,"y":1184,"style":113},"145.0","….line.width \u002F dashStyle",[39,1187],{"x":1129,"y":1188,"width":1131,"height":1146,"rx":42,"fill":1147,"stroke":77,"style":78},"158.0",[56,1190,1192],{"x":1150,"y":1191,"style":1152},"180.0","One highlighted bar",[39,1194],{"x":1135,"y":1188,"width":1136,"height":1146,"rx":42,"fill":45,"stroke":77,"style":78},[56,1196,1198],{"x":1140,"y":1197,"style":113},"179.0","DataPoint(idx) in series.dPt",[39,1200],{"x":1129,"y":1201,"width":1131,"height":1146,"rx":42,"fill":1147,"stroke":77,"style":78},"192.0",[56,1203,1205],{"x":1150,"y":1204,"style":1152},"214.0","Label font",[39,1207],{"x":1135,"y":1201,"width":1136,"height":1146,"rx":42,"fill":45,"stroke":77,"style":78},[56,1209,1211],{"x":1140,"y":1210,"style":113},"213.0","DataLabelList.txPr (RichText)",[39,1213],{"x":1129,"y":1214,"width":1131,"height":1146,"rx":42,"fill":1147,"stroke":77,"style":78},"226.0",[56,1216,1218],{"x":1150,"y":1217,"style":1152},"248.0","Gridline colour",[39,1220],{"x":1135,"y":1214,"width":1136,"height":1146,"rx":42,"fill":45,"stroke":77,"style":78},[56,1222,1224],{"x":1140,"y":1223,"style":113},"247.0","axis.majorGridlines.spPr",[241,1226,1228],{"id":1227},"step-4-data-labels-with-a-readable-font","Step 4 — Data labels with a readable font",[10,1230,1231,1232,1235,1236,1239,1240,1243,1244,1247,1248,713],{},"Labels are a ",[253,1233,1234],{},"DataLabelList"," on the series. Switch on the value, switch off everything else, choose a position — ",[253,1237,1238],{},"\"outEnd\""," above a column, ",[253,1241,1242],{},"\"ctr\""," inside, ",[253,1245,1246],{},"\"t\""," above a line point — and style the text through ",[253,1249,1250],{},"txPr",[246,1252,1254],{"className":275,"code":1253,"language":277,"meta":251,"style":251},"from openpyxl.chart.label import DataLabelList\nfrom openpyxl.chart.text import RichText\nfrom openpyxl.drawing.text import CharacterProperties, Paragraph, ParagraphProperties\n\nlabels = DataLabelList()\nlabels.showVal = True\nlabels.position = \"outEnd\"\nfor part in (\"showSerName\", \"showCatName\", \"showLegendKey\", \"showPercent\"):\n    setattr(labels, part, False)\nbold_dark = CharacterProperties(sz=900, b=True, solidFill=\"1E293B\")       # 9 pt\nlabels.txPr = RichText(p=[Paragraph(pPr=ParagraphProperties(defRPr=bold_dark), endParaRPr=bold_dark)])\nrevenue.dLbls = labels\n",[253,1255,1256,1268,1280,1292,1296,1306,1316,1326,1359,1372,1414,1455],{"__ignoreMap":251},[256,1257,1258,1260,1263,1265],{"class":72,"line":258},[256,1259,285],{"class":284},[256,1261,1262],{"class":288}," openpyxl.chart.label ",[256,1264,292],{"class":284},[256,1266,1267],{"class":288}," DataLabelList\n",[256,1269,1270,1272,1275,1277],{"class":72,"line":298},[256,1271,285],{"class":284},[256,1273,1274],{"class":288}," openpyxl.chart.text ",[256,1276,292],{"class":284},[256,1278,1279],{"class":288}," RichText\n",[256,1281,1282,1284,1287,1289],{"class":72,"line":311},[256,1283,285],{"class":284},[256,1285,1286],{"class":288}," openpyxl.drawing.text ",[256,1288,292],{"class":284},[256,1290,1291],{"class":288}," CharacterProperties, Paragraph, ParagraphProperties\n",[256,1293,1294],{"class":72,"line":318},[256,1295,315],{"emptyLinePlaceholder":314},[256,1297,1298,1301,1303],{"class":72,"line":330},[256,1299,1300],{"class":288},"labels ",[256,1302,324],{"class":284},[256,1304,1305],{"class":288}," DataLabelList()\n",[256,1307,1308,1311,1313],{"class":72,"line":341},[256,1309,1310],{"class":288},"labels.showVal ",[256,1312,324],{"class":284},[256,1314,1315],{"class":395}," True\n",[256,1317,1318,1321,1323],{"class":72,"line":352},[256,1319,1320],{"class":288},"labels.position ",[256,1322,324],{"class":284},[256,1324,1325],{"class":265}," \"outEnd\"\n",[256,1327,1328,1330,1333,1335,1338,1341,1343,1346,1348,1351,1353,1356],{"class":72,"line":375},[256,1329,378],{"class":284},[256,1331,1332],{"class":288}," part ",[256,1334,384],{"class":284},[256,1336,1337],{"class":288}," (",[256,1339,1340],{"class":265},"\"showSerName\"",[256,1342,361],{"class":288},[256,1344,1345],{"class":265},"\"showCatName\"",[256,1347,361],{"class":288},[256,1349,1350],{"class":265},"\"showLegendKey\"",[256,1352,361],{"class":288},[256,1354,1355],{"class":265},"\"showPercent\"",[256,1357,1358],{"class":288},"):\n",[256,1360,1361,1364,1367,1370],{"class":72,"line":436},[256,1362,1363],{"class":395},"    setattr",[256,1365,1366],{"class":288},"(labels, part, ",[256,1368,1369],{"class":395},"False",[256,1371,591],{"class":288},[256,1373,1374,1377,1379,1382,1385,1387,1390,1392,1395,1397,1399,1401,1403,1405,1408,1411],{"class":72,"line":486},[256,1375,1376],{"class":288},"bold_dark ",[256,1378,324],{"class":284},[256,1380,1381],{"class":288}," CharacterProperties(",[256,1383,1384],{"class":551},"sz",[256,1386,324],{"class":284},[256,1388,1389],{"class":395},"900",[256,1391,361],{"class":288},[256,1393,1394],{"class":551},"b",[256,1396,324],{"class":284},[256,1398,588],{"class":395},[256,1400,361],{"class":288},[256,1402,705],{"class":551},[256,1404,324],{"class":284},[256,1406,1407],{"class":265},"\"1E293B\"",[256,1409,1410],{"class":288},")       ",[256,1412,1413],{"class":519},"# 9 pt\n",[256,1415,1416,1419,1421,1424,1426,1428,1431,1434,1436,1439,1442,1444,1447,1450,1452],{"class":72,"line":492},[256,1417,1418],{"class":288},"labels.txPr ",[256,1420,324],{"class":284},[256,1422,1423],{"class":288}," RichText(",[256,1425,10],{"class":551},[256,1427,324],{"class":284},[256,1429,1430],{"class":288},"[Paragraph(",[256,1432,1433],{"class":551},"pPr",[256,1435,324],{"class":284},[256,1437,1438],{"class":288},"ParagraphProperties(",[256,1440,1441],{"class":551},"defRPr",[256,1443,324],{"class":284},[256,1445,1446],{"class":288},"bold_dark), ",[256,1448,1449],{"class":551},"endParaRPr",[256,1451,324],{"class":284},[256,1453,1454],{"class":288},"bold_dark)])\n",[256,1456,1457,1460,1462],{"class":72,"line":497},[256,1458,1459],{"class":288},"revenue.dLbls ",[256,1461,324],{"class":284},[256,1463,1464],{"class":288}," labels\n",[10,1466,1467,1468,1470,1471,1474,1475,1478,1479,1482],{},"Font size ",[253,1469,1384],{}," is in hundredths of a point, so 900 is 9 pt. The same ",[253,1472,1473],{},"RichText"," pattern styles axis labels (",[253,1476,1477],{},"chart.x_axis.txPr",") and the title. Rendering the chart shows bold labels above each bar; the January label sits close to the target line, which is worth checking on real data — move labels inside the bars (",[253,1480,1481],{},"\"inEnd\"",") when a reference line runs through the label area.",[241,1484,1486],{"id":1485},"step-5-label-number-formats-that-work-everywhere","Step 5 — Label number formats that work everywhere",[10,1488,1489],{},"Revenue of 112,400 reads better as 112K. openpyxl lets you set a number format on the label list, but it writes it without the flag that tells a viewer to prefer it over the source format, and LibreOffice ignored it in our test. The reliable route is simpler: labels show values in the source cells' own number format, so format the cells:",[246,1491,1493],{"className":275,"code":1492,"language":277,"meta":251,"style":251},"for (cell,) in ws.iter_rows(min_row=2, min_col=2, max_col=2):\n    cell.number_format = '#,##0,\"K\"'\nchart.y_axis.number_format = '#,##0,\"K\"'\n",[253,1494,1495,1532,1542],{"__ignoreMap":251},[256,1496,1497,1499,1502,1504,1507,1509,1511,1513,1515,1517,1519,1521,1523,1526,1528,1530],{"class":72,"line":258},[256,1498,378],{"class":284},[256,1500,1501],{"class":288}," (cell,) ",[256,1503,384],{"class":284},[256,1505,1506],{"class":288}," ws.iter_rows(",[256,1508,562],{"class":551},[256,1510,324],{"class":284},[256,1512,557],{"class":395},[256,1514,361],{"class":288},[256,1516,552],{"class":551},[256,1518,324],{"class":284},[256,1520,557],{"class":395},[256,1522,361],{"class":288},[256,1524,1525],{"class":551},"max_col",[256,1527,324],{"class":284},[256,1529,557],{"class":395},[256,1531,1358],{"class":288},[256,1533,1534,1537,1539],{"class":72,"line":298},[256,1535,1536],{"class":288},"    cell.number_format ",[256,1538,324],{"class":284},[256,1540,1541],{"class":265}," '#,##0,\"K\"'\n",[256,1543,1544,1547,1549],{"class":72,"line":311},[256,1545,1546],{"class":288},"chart.y_axis.number_format ",[256,1548,324],{"class":284},[256,1550,1541],{"class":265},[10,1552,1553,1554,1109],{},"Rendered, the labels read 82K, 92K, 78K and so on, and the axis matches. The side effect — the table shows thousands too — is usually what a report wants; if it is not, point the chart at a hidden helper column with the labelled format. Scaling formats themselves are covered in ",[14,1555,1557],{"href":1556},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fapplying-number-and-date-formats-in-excel\u002Fformat-numbers-in-thousands-and-millions-in-excel\u002F","Format Numbers in Thousands and Millions in Excel with Python",[241,1559,1561],{"id":1560},"step-6-quieter-gridlines-and-one-palette-for-every-chart","Step 6 — Quieter gridlines and one palette for every chart",[10,1563,1564],{},"Default gridlines compete with the data. Give them a light colour, then collect every colour decision into one function so all charts in the report match:",[246,1566,1568],{"className":275,"code":1567,"language":277,"meta":251,"style":251},"from openpyxl.chart.shapes import GraphicalProperties\nfrom openpyxl.drawing.line import LineProperties\n\nPALETTE = {\"primary\": (\"C7D2FE\", \"4338CA\"), \"alert\": \"B91C1C\", \"reference\": \"0B6157\", \"grid\": \"E7EBEF\"}\n\ndef apply_house_style(ch):\n    ch.y_axis.majorGridlines.spPr = GraphicalProperties(ln=LineProperties(solidFill=PALETTE[\"grid\"]))\n    ch.legend.position = \"b\"\n    ch.height, ch.width = 8, 15\n\napply_house_style(chart)\nchart.title = \"Revenue vs target\"\nws.add_chart(chart, \"E2\")\nwb.save(\"styled.xlsx\")\nprint(chart.series[0].graphicalProperties.solidFill.srgbClr)          # C7D2FE\n",[253,1569,1570,1582,1594,1598,1656,1660,1672,1704,1714,1729,1733,1738,1748,1758,1768],{"__ignoreMap":251},[256,1571,1572,1574,1577,1579],{"class":72,"line":258},[256,1573,285],{"class":284},[256,1575,1576],{"class":288}," openpyxl.chart.shapes ",[256,1578,292],{"class":284},[256,1580,1581],{"class":288}," GraphicalProperties\n",[256,1583,1584,1586,1589,1591],{"class":72,"line":298},[256,1585,285],{"class":284},[256,1587,1588],{"class":288}," openpyxl.drawing.line ",[256,1590,292],{"class":284},[256,1592,1593],{"class":288}," LineProperties\n",[256,1595,1596],{"class":72,"line":311},[256,1597,315],{"emptyLinePlaceholder":314},[256,1599,1600,1603,1606,1609,1612,1615,1618,1620,1623,1625,1628,1630,1633,1635,1638,1640,1643,1645,1648,1650,1653],{"class":72,"line":318},[256,1601,1602],{"class":395},"PALETTE",[256,1604,1605],{"class":284}," =",[256,1607,1608],{"class":288}," {",[256,1610,1611],{"class":265},"\"primary\"",[256,1613,1614],{"class":288},": (",[256,1616,1617],{"class":265},"\"C7D2FE\"",[256,1619,361],{"class":288},[256,1621,1622],{"class":265},"\"4338CA\"",[256,1624,580],{"class":288},[256,1626,1627],{"class":265},"\"alert\"",[256,1629,702],{"class":288},[256,1631,1632],{"class":265},"\"B91C1C\"",[256,1634,361],{"class":288},[256,1636,1637],{"class":265},"\"reference\"",[256,1639,702],{"class":288},[256,1641,1642],{"class":265},"\"0B6157\"",[256,1644,361],{"class":288},[256,1646,1647],{"class":265},"\"grid\"",[256,1649,702],{"class":288},[256,1651,1652],{"class":265},"\"E7EBEF\"",[256,1654,1655],{"class":288},"}\n",[256,1657,1658],{"class":72,"line":330},[256,1659,315],{"emptyLinePlaceholder":314},[256,1661,1662,1665,1669],{"class":72,"line":341},[256,1663,1664],{"class":284},"def",[256,1666,1668],{"class":1667},"s_Opv"," apply_house_style",[256,1670,1671],{"class":288},"(ch):\n",[256,1673,1674,1677,1679,1682,1685,1687,1690,1692,1694,1696,1699,1701],{"class":72,"line":352},[256,1675,1676],{"class":288},"    ch.y_axis.majorGridlines.spPr ",[256,1678,324],{"class":284},[256,1680,1681],{"class":288}," GraphicalProperties(",[256,1683,1684],{"class":551},"ln",[256,1686,324],{"class":284},[256,1688,1689],{"class":288},"LineProperties(",[256,1691,705],{"class":551},[256,1693,324],{"class":284},[256,1695,1602],{"class":395},[256,1697,1698],{"class":288},"[",[256,1700,1647],{"class":265},[256,1702,1703],{"class":288},"]))\n",[256,1705,1706,1709,1711],{"class":72,"line":375},[256,1707,1708],{"class":288},"    ch.legend.position ",[256,1710,324],{"class":284},[256,1712,1713],{"class":265}," \"b\"\n",[256,1715,1716,1719,1721,1724,1726],{"class":72,"line":436},[256,1717,1718],{"class":288},"    ch.height, ch.width ",[256,1720,324],{"class":284},[256,1722,1723],{"class":395}," 8",[256,1725,361],{"class":288},[256,1727,1728],{"class":395},"15\n",[256,1730,1731],{"class":72,"line":486},[256,1732,315],{"emptyLinePlaceholder":314},[256,1734,1735],{"class":72,"line":492},[256,1736,1737],{"class":288},"apply_house_style(chart)\n",[256,1739,1740,1743,1745],{"class":72,"line":497},[256,1741,1742],{"class":288},"chart.title ",[256,1744,324],{"class":284},[256,1746,1747],{"class":265}," \"Revenue vs target\"\n",[256,1749,1750,1753,1756],{"class":72,"line":508},[256,1751,1752],{"class":288},"ws.add_chart(chart, ",[256,1754,1755],{"class":265},"\"E2\"",[256,1757,591],{"class":288},[256,1759,1760,1763,1766],{"class":72,"line":523},[256,1761,1762],{"class":288},"wb.save(",[256,1764,1765],{"class":265},"\"styled.xlsx\"",[256,1767,591],{"class":288},[256,1769,1770,1772,1775,1777,1780],{"class":72,"line":534},[256,1771,974],{"class":395},[256,1773,1774],{"class":288},"(chart.series[",[256,1776,42],{"class":395},[256,1778,1779],{"class":288},"].graphicalProperties.solidFill.srgbClr)          ",[256,1781,1782],{"class":519},"# C7D2FE\n",[10,1784,1785,1786,1788,1789,1109],{},"Once the palette lives in one place, a brand refresh is a one-line change and every chart in every report follows it. Explicit hex colours also make the chart independent of the workbook's theme: unstyled series take the theme's accent colours, which change if someone applies a different theme in Excel, while a series with its own ",[253,1787,705],{}," keeps its colour whatever theme is active. The same idea for number formats and cell styles is covered in ",[14,1790,1792],{"href":1791},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-a-formatted-excel-report-with-xlsxwriter\u002F","Write a Formatted Excel Report with xlsxwriter",[20,1794,29,1799,29,1802,29,1805,29,1808,29,1814,29,1825,29,1831,29,1835,29,1839,29,1844,29,1849,29,1854,29,1861,29,1866,29,1869,29,1871,29,1875,29,1878,29,1881,29,1888,29,1893,29,1896,29,1899,29,1903,29,1906,29,1909,29,1915,29,1919,29,1923,29,1926,29,1930,29,1933,29,1938,29,1945,29,1951],{"viewBox":1795,"role":23,"ariaLabelledBy":1796,"xmlns":27,"style":1116},"4 0 752 142",[1797,1798],"cs-flow-t","cs-flow-d",[31,1800,1801],{"id":1797},"Styling a generated chart in order",[35,1803,1804],{"id":1798},"Set series fills and outlines, override single data points that need attention, style reference lines, add data labels with a readable font, format the source cells for label numbers, and finish with gridlines and layout from one shared house-style function.",[39,1806],{"x":41,"y":42,"width":1125,"height":1807,"fill":45},"142",[39,1809],{"x":1129,"y":1130,"width":1810,"height":1811,"rx":1812,"fill":52,"stroke":53,"style":1813},"115.2","110.5","12","stroke-width:2px",[1815,1816,1818],"g",{"transform":1817},"translate(65.6,29.0) scale(1.000)",[1819,1820],"path",{"d":1821,"fill":1822,"stroke":1823,"style":1824},"M12 3l9 5-9 5-9-5z M3 13l9 5 9-5 M3 17l9 5 9-5","none","var(--brand-strong,#4338ca)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[56,1826,1830],{"x":1827,"y":1828,"style":1829},"77.6","75.5","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Series",[56,1832,1834],{"x":1827,"y":1833,"style":1829},"93.0","colours",[56,1836,1838],{"x":1827,"y":1837,"style":113},"112.5","fill + outline",[72,1840],{"x1":1841,"y1":1842,"x2":1843,"y2":1842,"stroke":53,"style":1813},"140.2","71.2","162.2",[1845,1846],"polygon",{"points":1847,"fill":1848},"166.2,71.2 157.2,76.2 157.2,66.2","#5b5cf0",[39,1850],{"x":1851,"y":1130,"width":1810,"height":1811,"rx":1812,"fill":1852,"stroke":1853,"style":1813},"171.2","#fee8f2","var(--accent,#f43f8f)",[1815,1855,1857],{"transform":1856},"translate(216.8,29.0) scale(1.000)",[1819,1858],{"d":1859,"fill":1822,"stroke":1860,"style":1824},"M12 3.5l9.5 17h-19z M12 10v5 M12 17.8v.4","var(--accent-ink,#be185d)",[56,1862,1865],{"x":1863,"y":1828,"style":1864},"228.8","font-size:13.5px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","Highlight",[56,1867,1868],{"x":1863,"y":1833,"style":1864},"points",[56,1870,772],{"x":1863,"y":1837,"style":113},[72,1872],{"x1":1873,"y1":1842,"x2":1874,"y2":1842,"stroke":53,"style":1813},"291.4","313.4",[1845,1876],{"points":1877,"fill":1848},"317.4,71.2 308.4,76.2 308.4,66.2",[39,1879],{"x":1880,"y":1130,"width":1810,"height":1811,"rx":1812,"fill":65,"stroke":66,"style":1813},"322.4",[1815,1882,1884],{"transform":1883},"translate(368.0,29.0) scale(1.000)",[1819,1885],{"d":1886,"fill":1822,"stroke":1887,"style":1824},"M4 20h16 M4 20V4 M6 16l4-5 4 3 6-7","var(--teal-ink,#0b6157)",[56,1889,1892],{"x":1890,"y":1828,"style":1891},"380.0","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Reference",[56,1894,1895],{"x":1890,"y":1833,"style":1891},"lines",[56,1897,1898],{"x":1890,"y":1837,"style":113},"dash, width",[72,1900],{"x1":1901,"y1":1842,"x2":1902,"y2":1842,"stroke":53,"style":1813},"442.6","464.6",[1845,1904],{"points":1905,"fill":1848},"468.6,71.2 459.6,76.2 459.6,66.2",[39,1907],{"x":1908,"y":1130,"width":1810,"height":1811,"rx":1812,"fill":52,"stroke":53,"style":1813},"473.6",[1815,1910,1912],{"transform":1911},"translate(519.2,29.0) scale(1.000)",[1819,1913],{"d":1914,"fill":1822,"stroke":1823,"style":1824},"M5 9h14 M4 15h14 M10 4l-2 16 M16 4l-2 16",[56,1916,1918],{"x":1917,"y":1828,"style":1829},"531.2","Labels",[56,1920,1922],{"x":1917,"y":1921,"style":113},"95.0","txPr font,",[56,1924,1925],{"x":1917,"y":1837,"style":113},"position",[72,1927],{"x1":1928,"y1":1842,"x2":1929,"y2":1842,"stroke":53,"style":1813},"593.8","615.8",[1845,1931],{"points":1932,"fill":1848},"619.8,71.2 610.8,76.2 610.8,66.2",[39,1934],{"x":1935,"y":1130,"width":1810,"height":1811,"rx":1812,"fill":1936,"stroke":1937,"style":1813},"624.8","#fdefd8","var(--gold,#b4740a)",[1815,1939,1941],{"transform":1940},"translate(670.4,37.8) scale(1.000)",[1819,1942],{"d":1943,"fill":1822,"stroke":1944,"style":1824},"M5 12.5l4.5 4.5L19 7","var(--gold-ink,#7a4e06)",[56,1946,1950],{"x":1947,"y":1948,"style":1949},"682.4","84.2","font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","House style",[56,1952,1954],{"x":1947,"y":1953,"style":113},"103.8","one function",[241,1956,1958],{"id":1957},"common-pitfalls","Common pitfalls",[1960,1961,1962,1977,1991,2004],"ul",{},[1963,1964,1965,1969,1970,1972,1973,1976],"li",{},[1966,1967,1968],"strong",{},"Colours with a #."," openpyxl expects ",[253,1971,1622],{},". It accepts ",[253,1974,1975],{},"\"#4338CA\""," without complaint and writes it into the file, where it is not a valid colour — LibreOffice drew such a bar in black in our test. Strip the hash before assigning.",[1963,1978,1979,1982,1983,1986,1987,1990],{},[1966,1980,1981],{},"Labels showing everything."," Leaving ",[253,1984,1985],{},"showSerName"," or ",[253,1988,1989],{},"showCatName"," unset can make Excel print \"Revenue; Jan; 82000\". Switch the unwanted parts off explicitly.",[1963,1992,1993,1996,1997,2000,2001,1109],{},[1966,1994,1995],{},"Labels on every point of a long series."," Twelve or more labels on a line chart collide. Label only the last point, or the highest and lowest, by giving those points their own label through ",[253,1998,1999],{},"DataLabel(idx=…)"," in ",[253,2002,2003],{},"dLbls.dLbl",[1963,2005,2006,2009],{},[1966,2007,2008],{},"Too many colours."," One neutral colour plus one highlight reads faster than six hues. Reserve colour for meaning.",[241,2011,2013],{"id":2012},"conclusion","Conclusion",[10,2015,2016,2017,2019,2020,2000,2022,2025,2026,2028,2029,2031,2032,2034,2035,2038],{},"openpyxl chart styling comes down to a few objects: ",[253,2018,701],{}," for fills, outlines, widths and dashes; ",[253,2021,772],{},[253,2023,2024],{},"series.dPt"," for single highlighted bars; ",[253,2027,1234],{}," with a ",[253,2030,1473],{}," ",[253,2033,1250],{}," for readable labels; and axis ",[253,2036,2037],{},"spPr"," for gridlines. Format label numbers through the source cells so every viewer shows them, and keep all colour decisions in one palette so every chart in the report looks like it belongs.",[241,2040,2042],{"id":2041},"frequently-asked-questions","Frequently asked questions",[10,2044,2045,2048],{},[1966,2046,2047],{},"How do I change the colour of a series in an openpyxl chart?","\nSet series.graphicalProperties.solidFill to a hex colour such as \"4338CA\" for the fill, and series.graphicalProperties.line.solidFill for the outline or, on a line chart, the line itself.",[10,2050,2051,2054],{},[1966,2052,2053],{},"How do I colour just one bar differently?","\nCreate a DataPoint with the bar's zero-based index, give its graphicalProperties a solidFill, and append it to series.dPt. The other bars keep the series colour.",[10,2056,2057,2060],{},[1966,2058,2059],{},"How do I format data label numbers, for example as thousands?","\nThe most reliable way is to format the source cells — labels show values in the cells' number format by default. openpyxl can also set a label number format, but it is written without the flag that separates it from the source format, and LibreOffice ignored it in our test.",[10,2062,2063,2066],{},[1966,2064,2065],{},"How do I make data labels bold or coloured?","\nAssign a RichText to the DataLabelList's txPr, containing a paragraph whose default run properties (CharacterProperties) set b=True, sz in hundredths of a point and solidFill for the colour.",[241,2068,2070],{"id":2069},"related","Related",[1960,2072,2073,2079,2086,2093],{},[1963,2074,2075,2076,2078],{},"Up one level: ",[14,2077,17],{"href":16}," — charts written from Python.",[1963,2080,2081,2085],{},[14,2082,2084],{"href":2083},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fcreate-a-stacked-column-chart-with-openpyxl\u002F","Create a Stacked Column Chart with openpyxl"," — labels and colours on stacks.",[1963,2087,2088,2092],{},[14,2089,2091],{"href":2090},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fadd-line-chart-to-excel-report-with-python\u002F","Add a Line Chart to an Excel Report with Python"," — line series in detail.",[1963,2094,2095,2099],{},[14,2096,2098],{"href":2097},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fadd-a-trendline-to-an-excel-chart-with-python\u002F","Add a Trendline to an Excel Chart with Python"," — another reference line, calculated by Excel.",[2101,2102,2103],"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 .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}",{"title":251,"searchDepth":298,"depth":298,"links":2105},[2106,2107,2108,2109,2110,2111,2112,2113,2114,2115,2116],{"id":243,"depth":298,"text":244},{"id":694,"depth":298,"text":695},{"id":765,"depth":298,"text":766},{"id":996,"depth":298,"text":997},{"id":1227,"depth":298,"text":1228},{"id":1485,"depth":298,"text":1486},{"id":1560,"depth":298,"text":1561},{"id":1957,"depth":298,"text":1958},{"id":2012,"depth":298,"text":2013},{"id":2041,"depth":298,"text":2042},{"id":2069,"depth":298,"text":2070},"2026-09-23","Replace Excel's default chart styling from Python — series fill and outline colours, one highlighted bar, dashed target lines, subtle gridlines, bold coloured data labels, label number formats that survive every viewer, and a single palette function so every chart in a report matches.","md",[2121,2123,2125,2127],{"q":2047,"a":2122},"Set series.graphicalProperties.solidFill to a hex colour such as \"4338CA\" for the fill, and series.graphicalProperties.line.solidFill for the outline or, on a line chart, the line itself.",{"q":2053,"a":2124},"Create a DataPoint with the bar's zero-based index, give its graphicalProperties a solidFill, and append it to series.dPt. The other bars keep the series colour.",{"q":2059,"a":2126},"The most reliable way is to format the source cells — labels show values in the cells' number format by default. openpyxl can also set a label number format, but it is written without the flag that separates it from the source format, and LibreOffice ignored it in our test.",{"q":2065,"a":2128},"Assign a RichText to the DataLabelList's txPr, containing a paragraph whose default run properties (CharacterProperties) set b=True, sz in hundredths of a point and solidFill for the colour.","style chart colours and data labels with openpyxl",null,{"breadcrumb":2132},[2133,2136,2139],{"name":2134,"item":2135},"Home","\u002F",{"name":2137,"item":2138},"Formatting and Charting Excel Reports with Python","\u002Fformatting-and-charting-excel-reports-with-python\u002F",{"name":17,"item":16},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fstyle-chart-colors-and-data-labels-with-openpyxl",{"title":5,"description":2142},"openpyxl chart styling — graphicalProperties fills and lines, DataPoint to highlight one bar, dashed and thicker lines, gridline colours, DataLabelList positions, label fonts with RichText and CharacterProperties, number formats via the source cells and a reusable palette.","style-chart-colors-and-data-labels-with-openpyxl","formatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fstyle-chart-colors-and-data-labels-with-openpyxl\u002Findex",[2146,2147],"charts-and-images","openpyxl","how-to","M6wmpibJtsq9dOzhN5tQI5B3DcOUFFfPuN6WR98XYZ0",[2151,2155],{"title":2152,"path":2153,"stem":2154,"children":-1},"Show Negative Bars in a Different Colour","\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fshow-negative-bars-in-a-different-colour","formatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fshow-negative-bars-in-a-different-colour\u002Findex",{"title":2156,"path":2157,"stem":2158,"children":-1},"Use a Date Axis in an Excel Chart with openpyxl","\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fuse-a-date-axis-in-an-excel-chart-with-openpyxl","formatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Fuse-a-date-axis-in-an-excel-chart-with-openpyxl\u002Findex",[2160,2162],{"path":2161,"title":17},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl",{"path":2163,"title":2137},"\u002Fformatting-and-charting-excel-reports-with-python",[],[2166,2169,2172,2175,2178,2181],{"path":2167,"title":2168,"kind":2148},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fbuild-a-dashboard-sheet-with-charts-from-multiple-tabs","Build a Dashboard Sheet with Charts from Multiple Tabs",{"path":2170,"title":2171,"kind":2148},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fbuild-a-rolling-12-month-chart-on-an-excel-dashboard","Build a Rolling 12-Month Chart on an Excel Dashboard",{"path":2173,"title":2174,"kind":2148},"\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Fkeep-charts-and-images-when-filling-an-excel-template","Keep Charts and Images When Filling an Excel Template",{"path":2176,"title":2177,"kind":2148},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fadd-a-chart-to-an-excel-file-with-xlsxwriter","Add a Chart to an Excel File with xlsxwriter",{"path":2179,"title":2180,"kind":2148},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fadd-sparklines-to-an-excel-report-with-xlsxwriter","Add Sparklines to an Excel Report with xlsxwriter",{"path":2182,"title":2183,"kind":2148},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fbuild-a-waterfall-chart-in-excel-with-python","Build a Waterfall Chart in Excel with Python",1790972114700]