[{"data":1,"prerenderedAt":2101},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Finsert-a-matplotlib-plot-into-a-live-workbook-with-xlwings":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Finsert-a-matplotlib-plot-into-a-live-workbook-with-xlwings":2071,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Finsert-a-matplotlib-plot-into-a-live-workbook-with-xlwings":2079,"children:\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Finsert-a-matplotlib-plot-into-a-live-workbook-with-xlwings":2084,"related:\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Finsert-a-matplotlib-plot-into-a-live-workbook-with-xlwings":2085},{"id":4,"title":5,"body":6,"dateModified":2037,"datePublished":2037,"description":2038,"extension":2039,"faq":2040,"keyword":2049,"kind":2050,"meta":2051,"navigation":113,"path":2060,"seo":2061,"slug":2064,"stem":2065,"topics":2066,"type":2069,"__hash__":2070},"docs\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Finsert-a-matplotlib-plot-into-a-live-workbook-with-xlwings\u002Findex.md","Insert a matplotlib Plot into a Live Workbook with xlwings",{"type":7,"value":8,"toc":2023},"minimark",[9,24,29,61,68,72,191,205,251,255,699,713,857,861,932,968,972,975,1116,1146,1275,1279,1282,1402,1412,1621,1624,1851,1855,1866,1870,1888,1892,1947,1951,1958,1962,1968,1974,1980,1986,1990,2019],[10,11,12,13,17,18,23],"p",{},"Native Excel charts are great until you need something they do not offer — a box plot with outliers, a small-multiples grid, a regression line with a confidence band, annotations placed exactly where you want them. matplotlib draws all of that, and xlwings can place the figure into a workbook that is open in Excel, as a picture beside the data. The useful trick is not inserting the picture once but ",[14,15,16],"em",{},"refreshing"," it: give it a name, and every run replaces the previous image at the same position, so a button or a scheduled refresh keeps the chart current. This page covers reading the data, building the figure, inserting and positioning it, keeping it sharp, and refreshing it in place. It belongs to ",[19,20,22],"a",{"href":21},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002F","Automating Excel With xlwings: The Basics",".",[25,26,28],"h2",{"id":27},"prerequisites","Prerequisites",[30,31,36],"pre",{"className":32,"code":33,"language":34,"meta":35,"style":35},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install xlwings matplotlib pandas\n","bash","",[37,38,39],"code",{"__ignoreMap":35},[40,41,44,48,52,55,58],"span",{"class":42,"line":43},"line",1,[40,45,47],{"class":46},"sMTad","pip",[40,49,51],{"class":50},"srMev"," install",[40,53,54],{"class":50}," xlwings",[40,56,57],{"class":50}," matplotlib",[40,59,60],{"class":50}," pandas\n",[10,62,63,64,67],{},"xlwings needs Excel on Windows or macOS. The xlwings calls on this page are marked as not run by our Linux build; the matplotlib code is run, using the same data read with pandas instead of from Excel. The workbook ",[37,65,66],{},"weekly_sales.xlsx"," has 26 weeks of sales for two regions in columns A to C of its first sheet.",[25,69,71],{"id":70},"step-1-read-the-data-from-the-open-workbook","Step 1 — Read the data from the open workbook",[30,73,77],{"className":74,"code":75,"language":76,"meta":35,"style":35},"language-python shiki shiki-themes github-light github-dark-high-contrast","import xlwings as xw\nimport pandas as pd\n\nwb = xw.Book(\"weekly_sales.xlsx\")\nsheet = wb.sheets[0]\ndf = sheet.range(\"A1\").expand().options(pd.DataFrame, index=False, header=True).value\n","python",[37,78,79,95,108,115,133,151],{"__ignoreMap":35},[40,80,81,85,89,92],{"class":42,"line":43},[40,82,84],{"class":83},"s-kum","import",[40,86,88],{"class":87},"skGVy"," xlwings ",[40,90,91],{"class":83},"as",[40,93,94],{"class":87}," xw\n",[40,96,98,100,103,105],{"class":42,"line":97},2,[40,99,84],{"class":83},[40,101,102],{"class":87}," pandas ",[40,104,91],{"class":83},[40,106,107],{"class":87}," pd\n",[40,109,111],{"class":42,"line":110},3,[40,112,114],{"emptyLinePlaceholder":113},true,"\n",[40,116,118,121,124,127,130],{"class":42,"line":117},4,[40,119,120],{"class":87},"wb ",[40,122,123],{"class":83},"=",[40,125,126],{"class":87}," xw.Book(",[40,128,129],{"class":50},"\"weekly_sales.xlsx\"",[40,131,132],{"class":87},")\n",[40,134,136,139,141,144,148],{"class":42,"line":135},5,[40,137,138],{"class":87},"sheet ",[40,140,123],{"class":83},[40,142,143],{"class":87}," wb.sheets[",[40,145,147],{"class":146},"sP0c6","0",[40,149,150],{"class":87},"]\n",[40,152,154,157,159,162,165,168,172,174,177,180,183,185,188],{"class":42,"line":153},6,[40,155,156],{"class":87},"df ",[40,158,123],{"class":83},[40,160,161],{"class":87}," sheet.range(",[40,163,164],{"class":50},"\"A1\"",[40,166,167],{"class":87},").expand().options(pd.DataFrame, ",[40,169,171],{"class":170},"sa561","index",[40,173,123],{"class":83},[40,175,176],{"class":146},"False",[40,178,179],{"class":87},", ",[40,181,182],{"class":170},"header",[40,184,123],{"class":83},[40,186,187],{"class":146},"True",[40,189,190],{"class":87},").value\n",[10,192,193,196,197,200,201,204],{},[37,194,195],{},"options(pd.DataFrame, ...)"," converts the expanded range into a DataFrame in one bulk read, which is much faster than reading cells one by one; with ",[37,198,199],{},"index=False"," the first column stays a column instead of becoming the index. If the data lives in an Excel table, ",[37,202,203],{},"sheet.tables[\"Sales\"].range"," gives its range regardless of where it sits. For the rest of this page the same frame is read with pandas, so the plotting code can run anywhere:",[30,206,208],{"className":74,"code":207,"language":76,"meta":35,"style":35},"import pandas as pd\n\ndf = pd.read_excel(\"weekly_sales.xlsx\")\nprint(df.tail(3))\n",[37,209,210,220,224,237],{"__ignoreMap":35},[40,211,212,214,216,218],{"class":42,"line":43},[40,213,84],{"class":83},[40,215,102],{"class":87},[40,217,91],{"class":83},[40,219,107],{"class":87},[40,221,222],{"class":42,"line":97},[40,223,114],{"emptyLinePlaceholder":113},[40,225,226,228,230,233,235],{"class":42,"line":110},[40,227,156],{"class":87},[40,229,123],{"class":83},[40,231,232],{"class":87}," pd.read_excel(",[40,234,129],{"class":50},[40,236,132],{"class":87},[40,238,239,242,245,248],{"class":42,"line":117},[40,240,241],{"class":146},"print",[40,243,244],{"class":87},"(df.tail(",[40,246,247],{"class":146},"3",[40,249,250],{"class":87},"))\n",[25,252,254],{"id":253},"step-2-build-the-figure","Step 2 — Build the figure",[30,256,258],{"className":74,"code":257,"language":76,"meta":35,"style":35},"import matplotlib\nmatplotlib.use(\"Agg\")\nimport matplotlib.pyplot as plt\n\nfig, ax = plt.subplots(figsize=(7, 3.2), dpi=100)\nax.plot(df[\"Week\"], df[\"North\"], color=\"#5B5CF0\", label=\"North\", linewidth=2)\nax.plot(df[\"Week\"], df[\"South\"], color=\"#0F9488\", label=\"South\", linewidth=2)\nax.fill_between(df[\"Week\"], df[\"North\"].rolling(4).min(), df[\"North\"].rolling(4).max(),\n                color=\"#5B5CF0\", alpha=0.12, label=\"North 4-week range\")\nbest = df.loc[df[\"North\"].idxmax()]\nax.annotate(f\"peak {best['North']:,.0f}\", (best[\"Week\"], best[\"North\"]),\n            xytext=(10, 10), textcoords=\"offset points\", fontsize=8)\nax.set_title(\"Weekly sales by region\", loc=\"left\", fontsize=11)\nax.spines[[\"top\", \"right\"]].set_visible(False)\nax.legend(frameon=False, fontsize=8, loc=\"lower right\")\nfig.tight_layout()\nfig.savefig(\"weekly_sales_chart.png\")\nprint(\"figure size in pixels:\", fig.get_size_inches() * fig.dpi)\n",[37,259,260,267,277,289,293,332,378,417,447,478,494,541,580,610,631,663,669,680],{"__ignoreMap":35},[40,261,262,264],{"class":42,"line":43},[40,263,84],{"class":83},[40,265,266],{"class":87}," matplotlib\n",[40,268,269,272,275],{"class":42,"line":97},[40,270,271],{"class":87},"matplotlib.use(",[40,273,274],{"class":50},"\"Agg\"",[40,276,132],{"class":87},[40,278,279,281,284,286],{"class":42,"line":110},[40,280,84],{"class":83},[40,282,283],{"class":87}," matplotlib.pyplot ",[40,285,91],{"class":83},[40,287,288],{"class":87}," plt\n",[40,290,291],{"class":42,"line":117},[40,292,114],{"emptyLinePlaceholder":113},[40,294,295,298,300,303,306,308,311,314,316,319,322,325,327,330],{"class":42,"line":135},[40,296,297],{"class":87},"fig, ax ",[40,299,123],{"class":83},[40,301,302],{"class":87}," plt.subplots(",[40,304,305],{"class":170},"figsize",[40,307,123],{"class":83},[40,309,310],{"class":87},"(",[40,312,313],{"class":146},"7",[40,315,179],{"class":87},[40,317,318],{"class":146},"3.2",[40,320,321],{"class":87},"), ",[40,323,324],{"class":170},"dpi",[40,326,123],{"class":83},[40,328,329],{"class":146},"100",[40,331,132],{"class":87},[40,333,334,337,340,343,346,349,352,354,357,359,362,364,366,368,371,373,376],{"class":42,"line":153},[40,335,336],{"class":87},"ax.plot(df[",[40,338,339],{"class":50},"\"Week\"",[40,341,342],{"class":87},"], df[",[40,344,345],{"class":50},"\"North\"",[40,347,348],{"class":87},"], ",[40,350,351],{"class":170},"color",[40,353,123],{"class":83},[40,355,356],{"class":50},"\"#5B5CF0\"",[40,358,179],{"class":87},[40,360,361],{"class":170},"label",[40,363,123],{"class":83},[40,365,345],{"class":50},[40,367,179],{"class":87},[40,369,370],{"class":170},"linewidth",[40,372,123],{"class":83},[40,374,375],{"class":146},"2",[40,377,132],{"class":87},[40,379,381,383,385,387,390,392,394,396,399,401,403,405,407,409,411,413,415],{"class":42,"line":380},7,[40,382,336],{"class":87},[40,384,339],{"class":50},[40,386,342],{"class":87},[40,388,389],{"class":50},"\"South\"",[40,391,348],{"class":87},[40,393,351],{"class":170},[40,395,123],{"class":83},[40,397,398],{"class":50},"\"#0F9488\"",[40,400,179],{"class":87},[40,402,361],{"class":170},[40,404,123],{"class":83},[40,406,389],{"class":50},[40,408,179],{"class":87},[40,410,370],{"class":170},[40,412,123],{"class":83},[40,414,375],{"class":146},[40,416,132],{"class":87},[40,418,420,423,425,427,429,432,435,438,440,442,444],{"class":42,"line":419},8,[40,421,422],{"class":87},"ax.fill_between(df[",[40,424,339],{"class":50},[40,426,342],{"class":87},[40,428,345],{"class":50},[40,430,431],{"class":87},"].rolling(",[40,433,434],{"class":146},"4",[40,436,437],{"class":87},").min(), df[",[40,439,345],{"class":50},[40,441,431],{"class":87},[40,443,434],{"class":146},[40,445,446],{"class":87},").max(),\n",[40,448,450,453,455,457,459,462,464,467,469,471,473,476],{"class":42,"line":449},9,[40,451,452],{"class":170},"                color",[40,454,123],{"class":83},[40,456,356],{"class":50},[40,458,179],{"class":87},[40,460,461],{"class":170},"alpha",[40,463,123],{"class":83},[40,465,466],{"class":146},"0.12",[40,468,179],{"class":87},[40,470,361],{"class":170},[40,472,123],{"class":83},[40,474,475],{"class":50},"\"North 4-week range\"",[40,477,132],{"class":87},[40,479,481,484,486,489,491],{"class":42,"line":480},10,[40,482,483],{"class":87},"best ",[40,485,123],{"class":83},[40,487,488],{"class":87}," df.loc[df[",[40,490,345],{"class":50},[40,492,493],{"class":87},"].idxmax()]\n",[40,495,497,500,503,506,510,513,516,519,522,525,528,531,533,536,538],{"class":42,"line":496},11,[40,498,499],{"class":87},"ax.annotate(",[40,501,502],{"class":83},"f",[40,504,505],{"class":50},"\"peak ",[40,507,509],{"class":508},"sSjpA","{",[40,511,512],{"class":87},"best[",[40,514,515],{"class":50},"'North'",[40,517,518],{"class":87},"]",[40,520,521],{"class":83},":,.0f",[40,523,524],{"class":508},"}",[40,526,527],{"class":50},"\"",[40,529,530],{"class":87},", (best[",[40,532,339],{"class":50},[40,534,535],{"class":87},"], best[",[40,537,345],{"class":50},[40,539,540],{"class":87},"]),\n",[40,542,544,547,549,551,554,556,558,560,563,565,568,570,573,575,578],{"class":42,"line":543},12,[40,545,546],{"class":170},"            xytext",[40,548,123],{"class":83},[40,550,310],{"class":87},[40,552,553],{"class":146},"10",[40,555,179],{"class":87},[40,557,553],{"class":146},[40,559,321],{"class":87},[40,561,562],{"class":170},"textcoords",[40,564,123],{"class":83},[40,566,567],{"class":50},"\"offset points\"",[40,569,179],{"class":87},[40,571,572],{"class":170},"fontsize",[40,574,123],{"class":83},[40,576,577],{"class":146},"8",[40,579,132],{"class":87},[40,581,583,586,589,591,594,596,599,601,603,605,608],{"class":42,"line":582},13,[40,584,585],{"class":87},"ax.set_title(",[40,587,588],{"class":50},"\"Weekly sales by region\"",[40,590,179],{"class":87},[40,592,593],{"class":170},"loc",[40,595,123],{"class":83},[40,597,598],{"class":50},"\"left\"",[40,600,179],{"class":87},[40,602,572],{"class":170},[40,604,123],{"class":83},[40,606,607],{"class":146},"11",[40,609,132],{"class":87},[40,611,613,616,619,621,624,627,629],{"class":42,"line":612},14,[40,614,615],{"class":87},"ax.spines[[",[40,617,618],{"class":50},"\"top\"",[40,620,179],{"class":87},[40,622,623],{"class":50},"\"right\"",[40,625,626],{"class":87},"]].set_visible(",[40,628,176],{"class":146},[40,630,132],{"class":87},[40,632,634,637,640,642,644,646,648,650,652,654,656,658,661],{"class":42,"line":633},15,[40,635,636],{"class":87},"ax.legend(",[40,638,639],{"class":170},"frameon",[40,641,123],{"class":83},[40,643,176],{"class":146},[40,645,179],{"class":87},[40,647,572],{"class":170},[40,649,123],{"class":83},[40,651,577],{"class":146},[40,653,179],{"class":87},[40,655,593],{"class":170},[40,657,123],{"class":83},[40,659,660],{"class":50},"\"lower right\"",[40,662,132],{"class":87},[40,664,666],{"class":42,"line":665},16,[40,667,668],{"class":87},"fig.tight_layout()\n",[40,670,672,675,678],{"class":42,"line":671},17,[40,673,674],{"class":87},"fig.savefig(",[40,676,677],{"class":50},"\"weekly_sales_chart.png\"",[40,679,132],{"class":87},[40,681,683,685,687,690,693,696],{"class":42,"line":682},18,[40,684,241],{"class":146},[40,686,310],{"class":87},[40,688,689],{"class":50},"\"figure size in pixels:\"",[40,691,692],{"class":87},", fig.get_size_inches() ",[40,694,695],{"class":83},"*",[40,697,698],{"class":87}," fig.dpi)\n",[10,700,701,702,704,705,708,709,712],{},"The rolling band and the peak annotation are exactly the kind of detail native Excel charts make hard. ",[37,703,305],{}," is in inches; at 100 dots per inch the figure is 700 × 320 pixels, and that ratio decides the picture's shape in Excel. ",[37,706,707],{},"tight_layout"," keeps labels from being cut off at the edges — a common reason inserted figures look sloppy. The figure object itself, ",[37,710,711],{},"fig",", is what xlwings needs; saving a PNG here is only so the build can check it renders.",[714,715,723,724,723,728,723,732,723,738,723,748,723,759,723,766,723,771,723,777,723,782,723,786,723,793,723,798,723,801,723,805,723,808,723,811,723,817,723,821,723,824,723,828,723,831,723,836,723,843,723,849,723,853],"svg",{"viewBox":716,"role":717,"ariaLabelledBy":718,"xmlns":721,"style":722},"4 0 752 142","img",[719,720],"mpl-flow-t","mpl-flow-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[725,726,727],"title",{"id":719},"From cells to a picture in the sheet",[729,730,731],"desc",{"id":720},"The data range is read into a DataFrame with xlwings options, a matplotlib figure is drawn, and sheet.pictures.add inserts it with a name, placing it at an anchor cell. On later runs the same name with update=True replaces the image in place.",[733,734],"rect",{"x":434,"y":147,"width":735,"height":736,"fill":737},"752","142","#ffffff",[733,739],{"x":740,"y":741,"width":742,"height":743,"rx":744,"fill":745,"stroke":746,"style":747},"20.0","16.0","153.0","110.5","12","#e7ebef","var(--line,#cdd5e6)","stroke-width:2px",[749,750,752],"g",{"transform":751},"translate(84.5,37.8) scale(1.000)",[753,754],"path",{"d":755,"fill":756,"stroke":757,"style":758},"M4 4h16v16H4z M4 9h16 M4 14h16 M10 4v16","none","var(--text,#172033)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[760,761,765],"text",{"x":762,"y":763,"style":764},"96.5","84.2","font-size:13.5px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","Data range",[760,767,770],{"x":762,"y":768,"style":769},"103.8","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","A1.expand()",[42,772],{"x1":773,"y1":774,"x2":775,"y2":774,"stroke":776,"style":747},"178.0","71.2","200.0","var(--brand,#5b5cf0)",[778,779],"polygon",{"points":780,"fill":781},"204.0,71.2 195.0,76.2 195.0,66.2","#5b5cf0",[733,783],{"x":784,"y":741,"width":742,"height":743,"rx":744,"fill":785,"stroke":776,"style":747},"209.0","#f0f4ff",[749,787,789],{"transform":788},"translate(273.5,37.8) scale(1.000)",[753,790],{"d":791,"fill":756,"stroke":792,"style":758},"M3 5h18v14H3z M3 10h18 M3 15h18 M9 5v14","var(--brand-strong,#4338ca)",[760,794,797],{"x":795,"y":763,"style":796},"285.5","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","DataFrame",[760,799,800],{"x":795,"y":768,"style":769},"options(pd.DataFrame)",[42,802],{"x1":803,"y1":774,"x2":804,"y2":774,"stroke":776,"style":747},"367.0","389.0",[778,806],{"points":807,"fill":781},"393.0,71.2 384.0,76.2 384.0,66.2",[733,809],{"x":810,"y":741,"width":742,"height":743,"rx":744,"fill":785,"stroke":776,"style":747},"398.0",[749,812,814],{"transform":813},"translate(462.5,37.8) scale(1.000)",[753,815],{"d":816,"fill":756,"stroke":792,"style":758},"M4 20h16 M4 20V4 M6 16l4-5 4 3 6-7",[760,818,820],{"x":819,"y":763,"style":796},"474.5","matplotlib figure",[760,822,823],{"x":819,"y":768,"style":769},"fig, ax = plt.subplots()",[42,825],{"x1":826,"y1":774,"x2":827,"y2":774,"stroke":776,"style":747},"556.0","578.0",[778,829],{"points":830,"fill":781},"582.0,71.2 573.0,76.2 573.0,66.2",[733,832],{"x":833,"y":741,"width":742,"height":743,"rx":744,"fill":834,"stroke":835,"style":747},"587.0","#d9f4f1","var(--teal,#0f9488)",[749,837,839],{"transform":838},"translate(651.5,29.0) scale(1.000)",[753,840],{"d":841,"fill":756,"stroke":842,"style":758},"M4 5h16v14H4z M4 16l5-5 4 4 3-3 4 4 M15 9h.01","var(--teal-ink,#0b6157)",[760,844,848],{"x":845,"y":846,"style":847},"663.5","75.5","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","pictures.add(fig,",[760,850,852],{"x":845,"y":851,"style":847},"93.0","…)",[760,854,856],{"x":845,"y":855,"style":769},"112.5","named, anchored",[25,858,860],{"id":859},"step-3-insert-it-next-to-the-data","Step 3 — Insert it next to the data",[30,862,864],{"className":74,"code":863,"language":76,"meta":35,"style":35},"pic = sheet.pictures.add(\n    fig,\n    name=\"WeeklySalesChart\",\n    update=True,\n    anchor=sheet.range(\"E2\"),\n)\nprint(pic.name, pic.left, pic.top, pic.width, pic.height)\n",[37,865,866,876,881,894,905,921,925],{"__ignoreMap":35},[40,867,868,871,873],{"class":42,"line":43},[40,869,870],{"class":87},"pic ",[40,872,123],{"class":83},[40,874,875],{"class":87}," sheet.pictures.add(\n",[40,877,878],{"class":42,"line":97},[40,879,880],{"class":87},"    fig,\n",[40,882,883,886,888,891],{"class":42,"line":110},[40,884,885],{"class":170},"    name",[40,887,123],{"class":83},[40,889,890],{"class":50},"\"WeeklySalesChart\"",[40,892,893],{"class":87},",\n",[40,895,896,899,901,903],{"class":42,"line":117},[40,897,898],{"class":170},"    update",[40,900,123],{"class":83},[40,902,187],{"class":146},[40,904,893],{"class":87},[40,906,907,910,912,915,918],{"class":42,"line":135},[40,908,909],{"class":170},"    anchor",[40,911,123],{"class":83},[40,913,914],{"class":87},"sheet.range(",[40,916,917],{"class":50},"\"E2\"",[40,919,920],{"class":87},"),\n",[40,922,923],{"class":42,"line":153},[40,924,132],{"class":87},[40,926,927,929],{"class":42,"line":380},[40,928,241],{"class":146},[40,930,931],{"class":87},"(pic.name, pic.left, pic.top, pic.width, pic.height)\n",[10,933,934,937,938,941,942,945,946,949,950,953,954,179,957,179,960,963,964,967],{},[37,935,936],{},"pictures.add"," accepts a matplotlib figure directly, as well as image file paths. ",[37,939,940],{},"anchor"," puts the picture's top-left corner on cell E2, two columns to the right of the data, so the position follows the layout rather than fixed points. ",[37,943,944],{},"name"," identifies the picture for later runs, and ",[37,947,948],{},"update=True"," says: if a picture with this name already exists, replace its image and keep its position and size. The returned ",[37,951,952],{},"Picture"," has ",[37,955,956],{},"left",[37,958,959],{},"top",[37,961,962],{},"width"," and ",[37,965,966],{},"height"," in points, which you can set to move or resize it later.",[25,969,971],{"id":970},"step-4-keep-it-sharp-resolution-svg-and-size","Step 4 — Keep it sharp: resolution, SVG and size",[10,973,974],{},"A figure inserted at screen resolution looks soft on high-density displays and in print. Two options:",[30,976,978],{"className":74,"code":977,"language":76,"meta":35,"style":35},"pic = sheet.pictures.add(fig, name=\"WeeklySalesChart\", update=True, anchor=sheet.range(\"E2\"),\n                         export_options={\"bbox_inches\": \"tight\", \"dpi\": 200})\n# or, for crisp vector output in recent Excel versions:\npic_svg = sheet.pictures.add(fig, name=\"WeeklySalesChartSVG\", update=True,\n                             anchor=sheet.range(\"E20\"), format=\"svg\")\npic.width = 520                       # points; height follows when the aspect ratio is locked\n",[37,979,980,1016,1047,1053,1079,1103],{"__ignoreMap":35},[40,981,982,984,986,989,991,993,995,997,1000,1002,1004,1006,1008,1010,1012,1014],{"class":42,"line":43},[40,983,870],{"class":87},[40,985,123],{"class":83},[40,987,988],{"class":87}," sheet.pictures.add(fig, ",[40,990,944],{"class":170},[40,992,123],{"class":83},[40,994,890],{"class":50},[40,996,179],{"class":87},[40,998,999],{"class":170},"update",[40,1001,123],{"class":83},[40,1003,187],{"class":146},[40,1005,179],{"class":87},[40,1007,940],{"class":170},[40,1009,123],{"class":83},[40,1011,914],{"class":87},[40,1013,917],{"class":50},[40,1015,920],{"class":87},[40,1017,1018,1021,1023,1025,1028,1031,1034,1036,1039,1041,1044],{"class":42,"line":97},[40,1019,1020],{"class":170},"                         export_options",[40,1022,123],{"class":83},[40,1024,509],{"class":87},[40,1026,1027],{"class":50},"\"bbox_inches\"",[40,1029,1030],{"class":87},": ",[40,1032,1033],{"class":50},"\"tight\"",[40,1035,179],{"class":87},[40,1037,1038],{"class":50},"\"dpi\"",[40,1040,1030],{"class":87},[40,1042,1043],{"class":146},"200",[40,1045,1046],{"class":87},"})\n",[40,1048,1049],{"class":42,"line":110},[40,1050,1052],{"class":1051},"s-wDw","# or, for crisp vector output in recent Excel versions:\n",[40,1054,1055,1058,1060,1062,1064,1066,1069,1071,1073,1075,1077],{"class":42,"line":117},[40,1056,1057],{"class":87},"pic_svg ",[40,1059,123],{"class":83},[40,1061,988],{"class":87},[40,1063,944],{"class":170},[40,1065,123],{"class":83},[40,1067,1068],{"class":50},"\"WeeklySalesChartSVG\"",[40,1070,179],{"class":87},[40,1072,999],{"class":170},[40,1074,123],{"class":83},[40,1076,187],{"class":146},[40,1078,893],{"class":87},[40,1080,1081,1084,1086,1088,1091,1093,1096,1098,1101],{"class":42,"line":135},[40,1082,1083],{"class":170},"                             anchor",[40,1085,123],{"class":83},[40,1087,914],{"class":87},[40,1089,1090],{"class":50},"\"E20\"",[40,1092,321],{"class":87},[40,1094,1095],{"class":170},"format",[40,1097,123],{"class":83},[40,1099,1100],{"class":50},"\"svg\"",[40,1102,132],{"class":87},[40,1104,1105,1108,1110,1113],{"class":42,"line":153},[40,1106,1107],{"class":87},"pic.width ",[40,1109,123],{"class":83},[40,1111,1112],{"class":146}," 520",[40,1114,1115],{"class":1051},"                       # points; height follows when the aspect ratio is locked\n",[10,1117,1118,1121,1122,1125,1126,1129,1130,1133,1134,1137,1138,1140,1141,1145],{},[37,1119,1120],{},"export_options"," is passed to matplotlib's ",[37,1123,1124],{},"savefig",", so ",[37,1127,1128],{},"dpi=200"," doubles the pixel density and ",[37,1131,1132],{},"bbox_inches=\"tight\""," trims white margins. ",[37,1135,1136],{},"format=\"svg\""," inserts a vector image, which stays sharp at any zoom in Excel versions that support SVG pictures; for older versions, stay with PNG at a higher dpi. Setting ",[37,1139,962],{}," (in points) resizes the picture, and the height follows when its aspect ratio is locked, as it is by default. The relationship between figure size and on-sheet size is the same as in ",[19,1142,1144],{"href":1143},"\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Fembed-a-matplotlib-chart-in-an-excel-report\u002F","Embed a matplotlib Chart in an Excel Report",", which does it for files with openpyxl.",[714,1147,723,1152,723,1155,723,1158,723,1161,723,1166,723,1172,723,1178,723,1184,723,1190,723,1197,723,1201,723,1206,723,1210,723,1217,723,1221,723,1226,723,1228,723,1234,723,1238,723,1242,723,1246,723,1251,723,1254,723,1259,723,1262,723,1267,723,1270],{"viewBox":1148,"role":717,"ariaLabelledBy":1149,"xmlns":721,"style":722},"4 0 752 233",[1150,1151],"mpl-formats-t","mpl-formats-d",[725,1153,1154],{"id":1150},"PNG at higher dpi or SVG",[729,1156,1157],{"id":1151},"A PNG at 200 dpi works in every Excel version and looks sharp on most screens but gets soft when zoomed far in. An SVG picture stays sharp at any zoom and in print but needs a recent Excel version.",[733,1159],{"x":434,"y":147,"width":735,"height":1160,"fill":737},"233",[733,1162],{"x":740,"y":741,"width":1163,"height":1164,"rx":1165,"fill":785,"stroke":776,"style":747},"351.0","201.0","14",[749,1167,1169],{"transform":1168},"translate(182.5,29.0) scale(1.083)",[753,1170],{"d":841,"fill":756,"stroke":792,"style":1171},"stroke-width:1.66px;stroke-linecap:round;stroke-linejoin:round",[760,1173,1177],{"x":1174,"y":1175,"style":1176},"195.5","78.0","font-size:14px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","PNG, dpi=200",[42,1179],{"x1":1180,"y1":1181,"x2":1182,"y2":1181,"stroke":776,"style":1183},"36.0","91.0","355.0","stroke-width:1px",[760,1185,1189],{"x":1186,"y":1187,"style":1188},"60.0","111.0","font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:start","works in every Excel version",[749,1191,1193],{"transform":1192},"translate(38.5,99.5) scale(0.625)",[753,1194],{"d":1195,"fill":756,"stroke":835,"style":1196},"M5 12.5l4.5 4.5L19 7","stroke-width:2.88px;stroke-linecap:round;stroke-linejoin:round",[760,1198,1200],{"x":1186,"y":1199,"style":1188},"137.0","sharp on most screens",[749,1202,1204],{"transform":1203},"translate(38.5,125.5) scale(0.625)",[753,1205],{"d":1195,"fill":756,"stroke":835,"style":1196},[760,1207,1209],{"x":1186,"y":1208,"style":1188},"163.0","soft when zoomed far in",[749,1211,1213],{"transform":1212},"translate(38.5,151.5) scale(0.625)",[753,1214],{"d":1215,"fill":756,"stroke":1216,"style":1196},"M12 3.5l9.5 17h-19z M12 10v5 M12 17.8v.4","var(--gold,#b4740a)",[760,1218,1220],{"x":1186,"y":1219,"style":1188},"189.0","larger file for big figures",[749,1222,1224],{"transform":1223},"translate(38.5,177.5) scale(0.625)",[753,1225],{"d":1215,"fill":756,"stroke":1216,"style":1196},[733,1227],{"x":804,"y":741,"width":1163,"height":1164,"rx":1165,"fill":834,"stroke":835,"style":747},[749,1229,1231],{"transform":1230},"translate(551.5,29.0) scale(1.083)",[753,1232],{"d":1233,"fill":756,"stroke":842,"style":1171},"M9 8l-4 4 4 4 M15 8l4 4-4 4",[760,1235,1136],{"x":1236,"y":1175,"style":1237},"564.5","font-size:14px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle",[42,1239],{"x1":1240,"y1":1181,"x2":1241,"y2":1181,"stroke":835,"style":1183},"405.0","724.0",[760,1243,1245],{"x":1244,"y":1187,"style":1188},"429.0","sharp at any zoom and in print",[749,1247,1249],{"transform":1248},"translate(407.5,99.5) scale(0.625)",[753,1250],{"d":1195,"fill":756,"stroke":835,"style":1196},[760,1252,1253],{"x":1244,"y":1199,"style":1188},"small for simple charts",[749,1255,1257],{"transform":1256},"translate(407.5,125.5) scale(0.625)",[753,1258],{"d":1195,"fill":756,"stroke":835,"style":1196},[760,1260,1261],{"x":1244,"y":1208,"style":1188},"needs a recent Excel",[749,1263,1265],{"transform":1264},"translate(407.5,151.5) scale(0.625)",[753,1266],{"d":1215,"fill":756,"stroke":1216,"style":1196},[760,1268,1269],{"x":1244,"y":1219,"style":1188},"some effects rasterised",[749,1271,1273],{"transform":1272},"translate(407.5,177.5) scale(0.625)",[753,1274],{"d":1215,"fill":756,"stroke":1216,"style":1196},[25,1276,1278],{"id":1277},"step-5-refresh-the-chart-on-every-run","Step 5 — Refresh the chart on every run",[10,1280,1281],{},"Because the picture has a name, a refresh is the same call again. A small function makes that the only thing a button or scheduled job has to do:",[30,1283,1285],{"className":74,"code":1284,"language":76,"meta":35,"style":35},"def refresh_chart(book_path=\"weekly_sales.xlsx\"):\n    wb = xw.Book(book_path)\n    sheet = wb.sheets[0]\n    df = sheet.range(\"A1\").expand().options(pd.DataFrame, index=False).value\n    fig = build_figure(df)                          # the plotting code from Step 2, as a function\n    sheet.pictures.add(fig, name=\"WeeklySalesChart\", update=True, anchor=sheet.range(\"E2\"))\n    plt.close(fig)                                  # free the figure's memory\n",[37,1286,1287,1306,1316,1329,1350,1363,1394],{"__ignoreMap":35},[40,1288,1289,1292,1296,1299,1301,1303],{"class":42,"line":43},[40,1290,1291],{"class":83},"def",[40,1293,1295],{"class":1294},"s_Opv"," refresh_chart",[40,1297,1298],{"class":87},"(book_path",[40,1300,123],{"class":83},[40,1302,129],{"class":50},[40,1304,1305],{"class":87},"):\n",[40,1307,1308,1311,1313],{"class":42,"line":97},[40,1309,1310],{"class":87},"    wb ",[40,1312,123],{"class":83},[40,1314,1315],{"class":87}," xw.Book(book_path)\n",[40,1317,1318,1321,1323,1325,1327],{"class":42,"line":110},[40,1319,1320],{"class":87},"    sheet ",[40,1322,123],{"class":83},[40,1324,143],{"class":87},[40,1326,147],{"class":146},[40,1328,150],{"class":87},[40,1330,1331,1334,1336,1338,1340,1342,1344,1346,1348],{"class":42,"line":117},[40,1332,1333],{"class":87},"    df ",[40,1335,123],{"class":83},[40,1337,161],{"class":87},[40,1339,164],{"class":50},[40,1341,167],{"class":87},[40,1343,171],{"class":170},[40,1345,123],{"class":83},[40,1347,176],{"class":146},[40,1349,190],{"class":87},[40,1351,1352,1355,1357,1360],{"class":42,"line":135},[40,1353,1354],{"class":87},"    fig ",[40,1356,123],{"class":83},[40,1358,1359],{"class":87}," build_figure(df)                          ",[40,1361,1362],{"class":1051},"# the plotting code from Step 2, as a function\n",[40,1364,1365,1368,1370,1372,1374,1376,1378,1380,1382,1384,1386,1388,1390,1392],{"class":42,"line":153},[40,1366,1367],{"class":87},"    sheet.pictures.add(fig, ",[40,1369,944],{"class":170},[40,1371,123],{"class":83},[40,1373,890],{"class":50},[40,1375,179],{"class":87},[40,1377,999],{"class":170},[40,1379,123],{"class":83},[40,1381,187],{"class":146},[40,1383,179],{"class":87},[40,1385,940],{"class":170},[40,1387,123],{"class":83},[40,1389,914],{"class":87},[40,1391,917],{"class":50},[40,1393,250],{"class":87},[40,1395,1396,1399],{"class":42,"line":380},[40,1397,1398],{"class":87},"    plt.close(fig)                                  ",[40,1400,1401],{"class":1051},"# free the figure's memory\n",[10,1403,1404,1407,1408,23],{},[37,1405,1406],{},"plt.close(fig)"," matters in anything that runs repeatedly: matplotlib keeps every figure alive until it is closed, and a workbook button pressed fifty times would otherwise hold fifty figures in memory. Wiring the function to a button is covered in ",[19,1409,1411],{"href":1410},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Frun-python-from-an-excel-button-with-xlwings\u002F","Run Python from an Excel Button with xlwings",[30,1413,1415],{"className":74,"code":1414,"language":76,"meta":35,"style":35},"def build_figure(df):\n    fig, ax = plt.subplots(figsize=(7, 3.2), dpi=100)\n    for col, colour in ((\"North\", \"#5B5CF0\"), (\"South\", \"#0F9488\")):\n        ax.plot(df[\"Week\"], df[col], color=colour, label=col, linewidth=2)\n    ax.legend(frameon=False, fontsize=8)\n    fig.tight_layout()\n    return fig\n\nfigs_before = len(plt.get_fignums())\nfor _ in range(5):\n    plt.close(build_figure(df))\nprint(\"open figures after five refreshes:\", len(plt.get_fignums()) - figs_before)\n",[37,1416,1417,1427,1458,1490,1522,1543,1548,1556,1560,1573,1593,1598],{"__ignoreMap":35},[40,1418,1419,1421,1424],{"class":42,"line":43},[40,1420,1291],{"class":83},[40,1422,1423],{"class":1294}," build_figure",[40,1425,1426],{"class":87},"(df):\n",[40,1428,1429,1432,1434,1436,1438,1440,1442,1444,1446,1448,1450,1452,1454,1456],{"class":42,"line":97},[40,1430,1431],{"class":87},"    fig, ax ",[40,1433,123],{"class":83},[40,1435,302],{"class":87},[40,1437,305],{"class":170},[40,1439,123],{"class":83},[40,1441,310],{"class":87},[40,1443,313],{"class":146},[40,1445,179],{"class":87},[40,1447,318],{"class":146},[40,1449,321],{"class":87},[40,1451,324],{"class":170},[40,1453,123],{"class":83},[40,1455,329],{"class":146},[40,1457,132],{"class":87},[40,1459,1460,1463,1466,1469,1472,1474,1476,1478,1481,1483,1485,1487],{"class":42,"line":110},[40,1461,1462],{"class":83},"    for",[40,1464,1465],{"class":87}," col, colour ",[40,1467,1468],{"class":83},"in",[40,1470,1471],{"class":87}," ((",[40,1473,345],{"class":50},[40,1475,179],{"class":87},[40,1477,356],{"class":50},[40,1479,1480],{"class":87},"), (",[40,1482,389],{"class":50},[40,1484,179],{"class":87},[40,1486,398],{"class":50},[40,1488,1489],{"class":87},")):\n",[40,1491,1492,1495,1497,1500,1502,1504,1507,1509,1511,1514,1516,1518,1520],{"class":42,"line":117},[40,1493,1494],{"class":87},"        ax.plot(df[",[40,1496,339],{"class":50},[40,1498,1499],{"class":87},"], df[col], ",[40,1501,351],{"class":170},[40,1503,123],{"class":83},[40,1505,1506],{"class":87},"colour, ",[40,1508,361],{"class":170},[40,1510,123],{"class":83},[40,1512,1513],{"class":87},"col, ",[40,1515,370],{"class":170},[40,1517,123],{"class":83},[40,1519,375],{"class":146},[40,1521,132],{"class":87},[40,1523,1524,1527,1529,1531,1533,1535,1537,1539,1541],{"class":42,"line":135},[40,1525,1526],{"class":87},"    ax.legend(",[40,1528,639],{"class":170},[40,1530,123],{"class":83},[40,1532,176],{"class":146},[40,1534,179],{"class":87},[40,1536,572],{"class":170},[40,1538,123],{"class":83},[40,1540,577],{"class":146},[40,1542,132],{"class":87},[40,1544,1545],{"class":42,"line":153},[40,1546,1547],{"class":87},"    fig.tight_layout()\n",[40,1549,1550,1553],{"class":42,"line":380},[40,1551,1552],{"class":83},"    return",[40,1554,1555],{"class":87}," fig\n",[40,1557,1558],{"class":42,"line":419},[40,1559,114],{"emptyLinePlaceholder":113},[40,1561,1562,1565,1567,1570],{"class":42,"line":449},[40,1563,1564],{"class":87},"figs_before ",[40,1566,123],{"class":83},[40,1568,1569],{"class":146}," len",[40,1571,1572],{"class":87},"(plt.get_fignums())\n",[40,1574,1575,1578,1581,1583,1586,1588,1591],{"class":42,"line":480},[40,1576,1577],{"class":83},"for",[40,1579,1580],{"class":87}," _ ",[40,1582,1468],{"class":83},[40,1584,1585],{"class":146}," range",[40,1587,310],{"class":87},[40,1589,1590],{"class":146},"5",[40,1592,1305],{"class":87},[40,1594,1595],{"class":42,"line":496},[40,1596,1597],{"class":87},"    plt.close(build_figure(df))\n",[40,1599,1600,1602,1604,1607,1609,1612,1615,1618],{"class":42,"line":543},[40,1601,241],{"class":146},[40,1603,310],{"class":87},[40,1605,1606],{"class":50},"\"open figures after five refreshes:\"",[40,1608,179],{"class":87},[40,1610,1611],{"class":146},"len",[40,1613,1614],{"class":87},"(plt.get_fignums()) ",[40,1616,1617],{"class":83},"-",[40,1619,1620],{"class":87}," figs_before)\n",[10,1622,1623],{},"The local check confirms that building and closing the figure five times leaves no extra figures open.",[714,1625,723,1631,723,1634,723,1637,723,1642,723,1647,723,1651,723,1657,723,1661,723,1665,723,1669,723,1673,723,1677,723,1681,723,1685,723,1689,723,1692,723,1696,723,1700,723,1705,723,1708,723,1712,723,1714,723,1717,723,1719,723,1722,723,1724,723,1726,723,1728,723,1731,723,1734,723,1736,723,1740,723,1742,723,1745,723,1747,723,1750,723,1752,723,1754,723,1758,723,1760,723,1763,723,1766,723,1768,723,1771,723,1773,723,1776,723,1778,723,1781,723,1783,723,1785,723,1788,723,1790,723,1793,723,1796,723,1798,723,1801,723,1803,723,1806,723,1808,723,1811,723,1813,723,1815,723,1818,723,1820,723,1823,723,1826,723,1828,723,1831,723,1833,723,1835,723,1837,723,1839,723,1841,723,1843,723,1845],{"viewBox":1626,"role":717,"ariaLabelledBy":1627,"xmlns":721,"style":1630},"140 0 479 222",[1628,1629],"mpl-layout-t","mpl-layout-d","width:100%;max-width:479px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[725,1632,1633],{"id":1628},"Data and picture side by side",[729,1635,1636],{"id":1629},"The worksheet with Week, North and South in columns A to C and the WeeklySalesChart picture anchored at cell E2, occupying roughly columns E to L and rows 2 to 18.",[733,1638],{"x":1639,"y":147,"width":1640,"height":1641,"fill":737},"140","479","222",[733,1643],{"x":1644,"y":741,"width":1645,"height":1646,"rx":147,"fill":745,"stroke":746,"style":1183},"156.9","32.0","24.0",[733,1648],{"x":1649,"y":741,"width":1650,"height":1646,"rx":147,"fill":745,"stroke":746,"style":1183},"188.9","92.0",[760,1652,1656],{"x":1653,"y":1654,"style":1655},"234.9","33.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[733,1658],{"x":1659,"y":741,"width":1660,"height":1646,"rx":147,"fill":745,"stroke":746,"style":1183},"280.9","54.6",[760,1662,1664],{"x":1663,"y":1654,"style":1655},"308.1","B",[733,1666],{"x":1667,"y":741,"width":1668,"height":1646,"rx":147,"fill":745,"stroke":746,"style":1183},"335.4","55.8",[760,1670,1672],{"x":1671,"y":1654,"style":1655},"363.3","C",[733,1674],{"x":1675,"y":741,"width":1676,"height":1646,"rx":147,"fill":745,"stroke":746,"style":1183},"391.2","44.0",[760,1678,1680],{"x":1679,"y":1654,"style":1655},"413.2","D",[733,1682],{"x":1683,"y":741,"width":1684,"height":1646,"rx":147,"fill":745,"stroke":746,"style":1183},"435.2","123.9",[760,1686,1688],{"x":1687,"y":1654,"style":1655},"497.1","E",[733,1690],{"x":1691,"y":741,"width":1676,"height":1646,"rx":147,"fill":745,"stroke":746,"style":1183},"559.1",[760,1693,1695],{"x":1694,"y":1654,"style":1655},"581.1","F",[733,1697],{"x":1644,"y":1698,"width":1645,"height":1699,"rx":147,"fill":745,"stroke":746,"style":1183},"40.0","28.0",[760,1701,1704],{"x":1702,"y":1703,"style":1655},"172.9","59.0","1",[733,1706],{"x":1649,"y":1698,"width":1650,"height":1699,"rx":147,"fill":785,"stroke":776,"style":1707},"stroke-width:1.5px",[760,1709,1711],{"x":1653,"y":1703,"style":1710},"font-size:10.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","Week",[733,1713],{"x":1659,"y":1698,"width":1660,"height":1699,"rx":147,"fill":785,"stroke":776,"style":1707},[760,1715,1716],{"x":1663,"y":1703,"style":1710},"North",[733,1718],{"x":1667,"y":1698,"width":1668,"height":1699,"rx":147,"fill":785,"stroke":776,"style":1707},[760,1720,1721],{"x":1671,"y":1703,"style":1710},"South",[733,1723],{"x":1675,"y":1698,"width":1676,"height":1699,"rx":147,"fill":785,"stroke":776,"style":1707},[733,1725],{"x":1683,"y":1698,"width":1684,"height":1699,"rx":147,"fill":785,"stroke":776,"style":1707},[733,1727],{"x":1691,"y":1698,"width":1676,"height":1699,"rx":147,"fill":785,"stroke":776,"style":1707},[733,1729],{"x":1644,"y":1730,"width":1645,"height":1699,"rx":147,"fill":745,"stroke":746,"style":1183},"68.0",[760,1732,375],{"x":1702,"y":1733,"style":1655},"87.0",[733,1735],{"x":1649,"y":1730,"width":1650,"height":1699,"rx":147,"fill":737,"stroke":746,"style":1183},[760,1737,1739],{"x":1653,"y":1733,"style":1738},"font-size:10.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","06 Apr 2026",[733,1741],{"x":1659,"y":1730,"width":1660,"height":1699,"rx":147,"fill":737,"stroke":746,"style":1183},[760,1743,1744],{"x":1663,"y":1733,"style":1738},"1,176",[733,1746],{"x":1667,"y":1730,"width":1668,"height":1699,"rx":147,"fill":737,"stroke":746,"style":1183},[760,1748,1749],{"x":1671,"y":1733,"style":1738},"958",[733,1751],{"x":1675,"y":1730,"width":1676,"height":1699,"rx":147,"fill":737,"stroke":746,"style":1183},[733,1753],{"x":1683,"y":1730,"width":1684,"height":1699,"rx":147,"fill":834,"stroke":835,"style":1707},[760,1755,1757],{"x":1687,"y":1733,"style":1756},"font-size:10.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","WeeklySalesChart",[733,1759],{"x":1691,"y":1730,"width":1676,"height":1699,"rx":147,"fill":834,"stroke":835,"style":1707},[733,1761],{"x":1644,"y":1762,"width":1645,"height":1699,"rx":147,"fill":745,"stroke":746,"style":1183},"96.0",[760,1764,247],{"x":1702,"y":1765,"style":1655},"115.0",[733,1767],{"x":1649,"y":1762,"width":1650,"height":1699,"rx":147,"fill":737,"stroke":746,"style":1183},[760,1769,1770],{"x":1653,"y":1765,"style":1738},"13 Apr 2026",[733,1772],{"x":1659,"y":1762,"width":1660,"height":1699,"rx":147,"fill":737,"stroke":746,"style":1183},[760,1774,1775],{"x":1663,"y":1765,"style":1738},"1,180",[733,1777],{"x":1667,"y":1762,"width":1668,"height":1699,"rx":147,"fill":737,"stroke":746,"style":1183},[760,1779,1780],{"x":1671,"y":1765,"style":1738},"921",[733,1782],{"x":1675,"y":1762,"width":1676,"height":1699,"rx":147,"fill":737,"stroke":746,"style":1183},[733,1784],{"x":1683,"y":1762,"width":1684,"height":1699,"rx":147,"fill":834,"stroke":835,"style":1707},[760,1786,1787],{"x":1687,"y":1765,"style":1756},"(picture anchored",[733,1789],{"x":1691,"y":1762,"width":1676,"height":1699,"rx":147,"fill":834,"stroke":835,"style":1707},[733,1791],{"x":1644,"y":1792,"width":1645,"height":1699,"rx":147,"fill":745,"stroke":746,"style":1183},"124.0",[760,1794,434],{"x":1702,"y":1795,"style":1655},"143.0",[733,1797],{"x":1649,"y":1792,"width":1650,"height":1699,"rx":147,"fill":737,"stroke":746,"style":1183},[760,1799,1800],{"x":1653,"y":1795,"style":1738},"20 Apr 2026",[733,1802],{"x":1659,"y":1792,"width":1660,"height":1699,"rx":147,"fill":737,"stroke":746,"style":1183},[760,1804,1805],{"x":1663,"y":1795,"style":1738},"1,295",[733,1807],{"x":1667,"y":1792,"width":1668,"height":1699,"rx":147,"fill":737,"stroke":746,"style":1183},[760,1809,1810],{"x":1671,"y":1795,"style":1738},"968",[733,1812],{"x":1675,"y":1792,"width":1676,"height":1699,"rx":147,"fill":737,"stroke":746,"style":1183},[733,1814],{"x":1683,"y":1792,"width":1684,"height":1699,"rx":147,"fill":834,"stroke":835,"style":1707},[760,1816,1817],{"x":1687,"y":1795,"style":1756},"at E2)",[733,1819],{"x":1691,"y":1792,"width":1676,"height":1699,"rx":147,"fill":834,"stroke":835,"style":1707},[733,1821],{"x":1644,"y":1822,"width":1645,"height":1699,"rx":147,"fill":745,"stroke":746,"style":1183},"152.0",[760,1824,1590],{"x":1702,"y":1825,"style":1655},"171.0",[733,1827],{"x":1649,"y":1822,"width":1650,"height":1699,"rx":147,"fill":737,"stroke":746,"style":1183},[760,1829,1830],{"x":1653,"y":1825,"style":1738},"…",[733,1832],{"x":1659,"y":1822,"width":1660,"height":1699,"rx":147,"fill":737,"stroke":746,"style":1183},[760,1834,1830],{"x":1663,"y":1825,"style":1738},[733,1836],{"x":1667,"y":1822,"width":1668,"height":1699,"rx":147,"fill":737,"stroke":746,"style":1183},[760,1838,1830],{"x":1671,"y":1825,"style":1738},[733,1840],{"x":1675,"y":1822,"width":1676,"height":1699,"rx":147,"fill":737,"stroke":746,"style":1183},[733,1842],{"x":1683,"y":1822,"width":1684,"height":1699,"rx":147,"fill":737,"stroke":746,"style":1183},[733,1844],{"x":1691,"y":1822,"width":1676,"height":1699,"rx":147,"fill":737,"stroke":746,"style":1183},[760,1846,1850],{"x":1847,"y":1848,"style":1849},"380.0","202.0","font-size:12.5px;font-weight:400;fill:var(--muted,#5b6780);text-anchor:middle","The picture keeps its name and position across refreshes.",[25,1852,1854],{"id":1853},"step-6-picture-or-native-chart","Step 6 — Picture or native chart?",[10,1856,1857,1858,1861,1862,23],{},"A picture is a snapshot: it does not change when someone edits a cell until the script runs again, and readers cannot hover over points to see values. A native Excel chart, created with openpyxl or XlsxWriter in the file or through ",[37,1859,1860],{},".api"," in a live workbook, updates instantly and stays interactive. Use matplotlib pictures for the analyses Excel cannot draw — distributions, annotations, statistical bands, small multiples — and native charts for standard lines and bars that people will explore. Native charts from Python are covered in ",[19,1863,1865],{"href":1864},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002F","Creating Charts in Excel with openpyxl",[25,1867,1869],{"id":1868},"performance-notes","Performance notes",[10,1871,1872,1873,1875,1876,1879,1880,1883,1884,23],{},"Inserting a picture is quick — the figure is rendered to a temporary file and Excel loads it — so the time goes into reading the data and drawing the figure. Bulk-reading the range with ",[37,1874,800],{}," is one round trip; reading cell by cell would be hundreds. For dashboards with several charts, turn off ",[37,1877,1878],{},"app.screen_updating"," while all pictures are replaced and switch it back on in a ",[37,1881,1882],{},"finally"," block, so Excel redraws once instead of after every picture. Keep figure sizes modest: a simple 7 × 3.2 inch line chart at 200 dpi came to about 40 kB in testing, but photographic content, dense scatter plots and dozens of large high-resolution pictures make the workbook slow to open and to share, the problem described in ",[19,1885,1887],{"href":1886},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Freduce-the-file-size-of-an-excel-workbook-with-python\u002F","Reduce the File Size of an Excel Workbook with Python",[25,1889,1891],{"id":1890},"common-pitfalls","Common pitfalls",[1893,1894,1895,1907,1921,1933,1941],"ul",{},[1896,1897,1898,1902,1903,963,1905,23],"li",{},[1899,1900,1901],"strong",{},"Inserting without a name."," Every run adds another picture on top of the last; always pass ",[37,1904,944],{},[37,1906,948],{},[1896,1908,1909,1912,1913,1915,1916,1918,1919,23],{},[1899,1910,1911],{},"Blurry images."," Raise ",[37,1914,324],{}," through ",[37,1917,1120],{}," or use ",[37,1920,1136],{},[1896,1922,1923,1926,1927,1930,1931,23],{},[1899,1924,1925],{},"Labels cut off."," Call ",[37,1928,1929],{},"fig.tight_layout()"," or pass ",[37,1932,1132],{},[1896,1934,1935,1938,1939,23],{},[1899,1936,1937],{},"Memory growth in repeated runs."," Close figures with ",[37,1940,1406],{},[1896,1942,1943,1946],{},[1899,1944,1945],{},"Expecting interactivity."," A picture does not update with the cells; rerun the script or use a native chart.",[25,1948,1950],{"id":1949},"conclusion","Conclusion",[10,1952,1953,1954,1957],{},"Read the data with xlwings into a DataFrame, draw the figure with matplotlib, and add it with ",[37,1955,1956],{},"sheet.pictures.add(fig, name=..., update=True, anchor=...)"," so it sits next to the data and is replaced in place on every run. Raise the dpi or insert SVG for sharpness, size the picture in points, close figures after use, and keep native Excel charts for standard visuals that readers explore interactively.",[25,1959,1961],{"id":1960},"frequently-asked-questions","Frequently asked questions",[10,1963,1964,1967],{},[1899,1965,1966],{},"How do I insert a matplotlib figure into Excel with xlwings?","\nPass the figure to sheet.pictures.add(fig, name=\"MyChart\", update=True, anchor=sheet.range(\"E2\")). The picture is placed at the anchor cell and replaced in place the next time you call it with the same name.",[10,1969,1970,1973],{},[1899,1971,1972],{},"How do I update an existing chart picture instead of adding a new one?","\nGive the picture a name and call pictures.add with the same name and update=True. xlwings replaces the image and keeps the picture's position and size.",[10,1975,1976,1979],{},[1899,1977,1978],{},"Why does my inserted matplotlib chart look blurry in Excel?","\nIt was rendered at a low resolution. Pass export_options={\"dpi\": 200} to pictures.add, or insert it as SVG with format=\"svg\" in Excel versions that support SVG pictures.",[10,1981,1982,1985],{},[1899,1983,1984],{},"Should I use a matplotlib picture or a native Excel chart?","\nUse matplotlib for visuals Excel cannot draw, such as statistical bands, annotations and small multiples. Use native charts when readers need to interact with the chart or when it must update immediately as cells change.",[25,1987,1989],{"id":1988},"related","Related",[1893,1991,1992,1998,2005,2012],{},[1896,1993,1994,1995,1997],{},"Up one level: ",[19,1996,22],{"href":21}," — controlling Excel from Python.",[1896,1999,2000,2004],{},[19,2001,2003],{"href":2002},"\u002Fgetting-started-with-python-excel-automation\u002Fusing-python-in-excel\u002Fplot-charts-with-matplotlib-in-python-in-excel\u002F","Plot Charts with matplotlib in Python in Excel"," — matplotlib inside =PY() cells.",[1896,2006,2007,2011],{},[19,2008,2010],{"href":2009},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fformat-cells-in-a-live-workbook-with-xlwings\u002F","Format Cells in a Live Workbook with xlwings"," — styling the data next to the chart.",[1896,2013,2014,2018],{},[19,2015,2017],{"href":2016},"\u002Fformatting-and-charting-excel-reports-with-python\u002Finserting-images-and-logos-into-excel\u002Fexport-an-excel-chart-as-an-image\u002F","Export an Excel Chart as an Image"," — the reverse direction.",[2020,2021,2022],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}",{"title":35,"searchDepth":97,"depth":97,"links":2024},[2025,2026,2027,2028,2029,2030,2031,2032,2033,2034,2035,2036],{"id":27,"depth":97,"text":28},{"id":70,"depth":97,"text":71},{"id":253,"depth":97,"text":254},{"id":859,"depth":97,"text":860},{"id":970,"depth":97,"text":971},{"id":1277,"depth":97,"text":1278},{"id":1853,"depth":97,"text":1854},{"id":1868,"depth":97,"text":1869},{"id":1890,"depth":97,"text":1891},{"id":1949,"depth":97,"text":1950},{"id":1960,"depth":97,"text":1961},{"id":1988,"depth":97,"text":1989},"2026-10-02","Put a matplotlib chart into a workbook that is open in Excel and keep it up to date: read the data with xlwings, build the figure, add it with sheet.pictures.add, place it next to a cell with anchor, replace it in place on every run with name and update=True, choose PNG resolution or SVG, and size it so it looks right on screen and in print.","md",[2041,2043,2045,2047],{"q":1966,"a":2042},"Pass the figure to sheet.pictures.add(fig, name=\"MyChart\", update=True, anchor=sheet.range(\"E2\")). The picture is placed at the anchor cell and replaced in place the next time you call it with the same name.",{"q":1972,"a":2044},"Give the picture a name and call pictures.add with the same name and update=True. xlwings replaces the image and keeps the picture's position and size.",{"q":1978,"a":2046},"It was rendered at a low resolution. Pass export_options={\"dpi\": 200} to pictures.add, or insert it as SVG with format=\"svg\" in Excel versions that support SVG pictures.",{"q":1984,"a":2048},"Use matplotlib for visuals Excel cannot draw, such as statistical bands, annotations and small multiples. Use native charts when readers need to interact with the chart or when it must update immediately as cells change.","insert a matplotlib plot into a live workbook with xlwings",null,{"breadcrumb":2052},[2053,2056,2059],{"name":2054,"item":2055},"Home","\u002F",{"name":2057,"item":2058},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":22,"item":21},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Finsert-a-matplotlib-plot-into-a-live-workbook-with-xlwings",{"title":2062,"description":2063},"Insert a matplotlib Chart into Excel with xlwings","Add a matplotlib figure to an open Excel workbook with xlwings: pictures.add(fig, name, update=True, anchor), positioning and sizing, PNG dpi vs SVG, refreshing the same picture each run, and when to use a native Excel chart instead.","insert-a-matplotlib-plot-into-a-live-workbook-with-xlwings","getting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Finsert-a-matplotlib-plot-into-a-live-workbook-with-xlwings\u002Findex",[2067,2068],"xlwings","charts-and-images","how-to","w_osxMucIevNdaBbtvyG2Q3mkhIMyQF5DCxQl6RDoaQ",[2072,2075],{"title":2010,"path":2073,"stem":2074,"children":-1},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fformat-cells-in-a-live-workbook-with-xlwings","getting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fformat-cells-in-a-live-workbook-with-xlwings\u002Findex",{"title":2076,"path":2077,"stem":2078,"children":-1},"Read and Write a Live Excel Workbook with xlwings","\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fread-and-write-a-live-excel-workbook-with-xlwings","getting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fread-and-write-a-live-excel-workbook-with-xlwings\u002Findex",[2080,2082],{"path":2081,"title":22},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics",{"path":2083,"title":2057},"\u002Fgetting-started-with-python-excel-automation",[],[2086,2089,2092,2096,2099,2100],{"path":2087,"title":2088,"kind":2069},"\u002Fautomating-reporting-workflows\u002Ftesting-and-packaging-excel-automation-scripts\u002Ftest-win32com-and-xlwings-code-without-excel","Test win32com and xlwings Code Without Excel",{"path":2090,"title":2091,"kind":2069},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Fautomate-excel-on-macos-with-python","Automate Excel on macOS with Python",{"path":2093,"title":2094,"kind":2095},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Fxlwings-vs-pywin32-for-excel-automation","xlwings vs pywin32 for Excel Automation","compare",{"path":2097,"title":2098,"kind":2069},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-xlwings-basics\u002Fcontrol-how-xlwings-converts-dataframes-and-ranges","Control How xlwings Converts DataFrames and Ranges",{"path":2073,"title":2010,"kind":2069},{"path":2077,"title":2076,"kind":2069},1790972056978]