From c010cb657b932d7d23f236804cd2a4e434f218a0 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 22 Aug 2026 14:44:39 +0200 Subject: [PATCH 1/2] plotting config --- README.md | 69 ++++++++----- README.qmd | 24 +++++ .../figure-commonmark/cell-14-output-1.png | Bin 19238 -> 58705 bytes src/maxplotlib/canvas/canvas.py | 95 ++++++++++++++++++ src/maxplotlib/tests/test_canvas.py | 64 +++++++++--- src/maxplotlib/tests/test_plot.py | 8 +- src/maxplotlib/tests/test_plotext.py | 14 +-- src/maxplotlib/tests/test_plotly_backend.py | 40 ++++---- 8 files changed, 248 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index 5e2ec5f..cbf1bcc 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,28 @@ to explicitly render an already-built canvas, use `canvas.render(...)`. The older `canvas.plot(backend=...)` spelling is still supported for compatibility, but emits a `FutureWarning`. +Add several lines at once with shared styling: + +``` python +canvas.plot_many( + [(x, np.sin(x)), (x, np.cos(x))], + labels=["sin(x)", "cos(x)"], + linewidth=2, +) +``` + +Common figure and axis settings can be grouped with `configure()`: + +``` python +canvas.configure( + title="Trigonometry", + xlabel="Angle", + ylabel="Value", + grid=True, + facecolor="whitesmoke", +) +``` + For Matplotlib-specific customization, pass method calls declaratively. Figure methods run once and axes methods run for every subplot, providing access to any Matplotlib API without requiring a maxplotlib @@ -165,26 +187,27 @@ terminal_fig = canvas.render(backend="plotext") print(terminal_fig.build(keep_colors=False)) ``` + Trigonometry Runtime - ┌─────────────────────────────────────────────────────────────────────────┐ - 1.00┤ ▗▄▞▀▀▀▀▀▙▄▖ │ - │ ▗▄▀▘ ▝▀▄ │ - │ ▗▞▘ ▀▄ │ - 0.67┤ ▟▀ ▀▄ │ - │ ▄▛ ▚▖ │ - 0.33┤ ▗▞ ▝▄ │ - │ ▄▀ ▚▖ │ - │▗▞▘ ▀▄ │ - 0.00┤▀ ▝▚▖ ▞│ - │ ▀▄ ▗▞▘│ - │ ▝▚ ▄▀ │ - -0.33┤ ▀▖ ▞▘ │ - │ ▝▚ ▟▀ │ - -0.67┤ ▀▄ ▄▛ │ - │ ▀▄ ▗▞▘ │ - │ ▀▄▖ ▗▄▀▘ │ - -1.00┤ ▝▀▜▄▄▄▄▄▞▀▘ │ - └┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬┘ + ┌┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬┐ + 1.00┼ ▞▞ sin(x) ──▗▄▞▀▀▀▀▀▙▄▖────────────┼─────────────────┼─────────────▄▄▀▀▀┤ + │ ▞▞ cos(x) ▄▀▘ │ ▝▀▄ │ │ ▄▞▀ ││ + ││ ▜▄▘ │ ▀▄ │ │ ▄▛ ││ + 0.67┼┼──────▟▀─▀▄──────┼─────────▀▄──────┼─────────────────┼──────▄▀─────────┼┤ + ││ ▄▛ ▝▚▖ │ ▚▖ │ │ ▗▞▘ ││ + 0.33┼┼──▗▞────────▀▖───┼────────────▝▄───┼─────────────────┼───▗▀────────────┼┤ + ││ ▄▀ ▝▚ │ ▚▖ │ │ ▞▘ ││ + │▗▞▘ ▀▖│ ▀▄│ │▗▀ ││ + 0.00┼▞────────────────▝▙────────────────▝▚▖────────────────▟▘────────────────▄┤ + ││ │▜▖ │▀▄ ▗▛│ ▗▞▘│ + ││ │ ▝▙ │ ▝▚ ▟▘ │ ▄▀ ││ + -0.33┼┼─────────────────┼───▚▖────────────┼───▀▖───────▗▞───┼─────────────▞▘──┼┤ + ││ │ ▝▄▖ │ ▝▚ ▗▄▘ │ ▟▀ ││ + -0.67┼┼─────────────────┼──────▀▖─────────┼──────▀▄─▗▀──────┼─────────▄▛──────┼┤ + ││ │ ▝▜▄ │ ▄▛▘ │ ▗▞▘ ││ + ││ │ ▀▄▖ │ ▗▄▀ ▀▄▖ │ ▗▄▀▘ ││ + -1.00┼┼─────────────────┼────────────▝▀▚▄▄▄▄▄▞▀▘───────▝▀▜▄▄▄▄▄▞▀▘────────────┼┤ + └┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼┘ 0.0 1.6 3.1 4.7 6.3 Duration Time @@ -194,7 +217,7 @@ Or plot with the TikZ backend: canvas.show(backend="tikzfigure") ``` -![](README_files/figure-commonmark/cell-12-output-1.png) +![](README_files/figure-commonmark/cell-14-output-1.png) ### Horizontal Subplots with TikZ Backend @@ -275,7 +298,7 @@ canvas.show(backend="plotext") 1.0 1.8 3.2 5.6 10.0 y x - + ### Layers @@ -311,7 +334,7 @@ Show layer 0 only, then layers 0 and 1, then everything: canvas.show(layers=[0]) ``` -![](README_files/figure-commonmark/cell-16-output-1.png) +![](README_files/figure-commonmark/cell-18-output-1.png) (
, array([[]], dtype=object)) @@ -322,7 +345,7 @@ Show all layers: canvas.show() ``` -![](README_files/figure-commonmark/cell-17-output-1.png) +![](README_files/figure-commonmark/cell-19-output-1.png) (
, array([[]], dtype=object)) diff --git a/README.qmd b/README.qmd index 7d4ab21..a27e50c 100644 --- a/README.qmd +++ b/README.qmd @@ -47,6 +47,30 @@ explicitly render an already-built canvas, use `canvas.render(...)`. The older `canvas.plot(backend=...)` spelling is still supported for compatibility, but emits a `FutureWarning`. +Add several lines at once with shared styling: + +```{python} +#| output: false +canvas.plot_many( + [(x, np.sin(x)), (x, np.cos(x))], + labels=["sin(x)", "cos(x)"], + linewidth=2, +) +``` + +Common figure and axis settings can be grouped with `configure()`: + +```{python} +#| output: false +canvas.configure( + title="Trigonometry", + xlabel="Angle", + ylabel="Value", + grid=True, + facecolor="whitesmoke", +) +``` + For Matplotlib-specific customization, pass method calls declaratively. Figure methods run once and axes methods run for every subplot, providing access to any Matplotlib API without requiring a maxplotlib wrapper: diff --git a/README_files/figure-commonmark/cell-14-output-1.png b/README_files/figure-commonmark/cell-14-output-1.png index ed52daeb3507c219c75131059ac09dace25c90cc..fee6ca25e2dd525a1a70a55de154ecf5b42dd961 100644 GIT binary patch literal 58705 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 B+iL&- literal 19238 zcmbrmbx_r7*af;p5ou{@K{}Q04(aZc?(S9&ARyh{B`Mtq(jXnu(z)r*`)Pmv|M>)y zy^}e!8D(73Bf0B^Ah2BvTgSZ8pl$t z;-!rii>l%aeDZ!$qDBXLI=V0V4aGY+80RdiLVv&DiCwt%U`uj<)9vQ&U9u_rAvT?05KU<-g^f7pGBlEIN&3i{RQYP z8-qO?3(r#>Qi0$s~x?fx*Ykj~(Qym-{Dj)%49B^=a_pU=>nQ^%6O{kd! zB@>hUUJmR%(wdr@ZS5qi|Bv`g@XO-k)BlQrds^czkjE@J#0BDRwro+mjOibeg6M;|n$s906)ePxG)j$QR5RKUf>z3?h)a^4O* zE6Vbl|Lf)M?*3uj>0(b!yV=FE?Gd)ZEIg{`n-^Q$?eqBLZEP4Qsi{?J%zo3ne_z!L zjfsgd+A{qWjC#Hn&P=fR^a7%YnIKImpUnqrIaBfdu3?X+k&#g?D|_9^*%@mi=})L? ziE6x#rnF~}X}p25yeW>Fs#*cLj0|#h!w+&vUh7kb`aG)0dXu^Sj&4XqL3MR(b#-+& zrk9ph%G9rCh$k=K;Njt&o}W|D)0e({dKzZY9FIS{I6f}EaMMRJN_QCwZA?t;)k2k# zmTtCOCBzPAB`(-Zz|Kk){Xshy@57$Or2o6oc9yI<+D+nN#Z7=9QsEGRDiNO&{dJ2Eozw;lW^ zf`cQvXye;{tsq&Tyl$h!NlB9{lC0G)gCwVNq4Zo#mfB~s0rpsXI6HC=qP`_TA#pL1 z=wrv%5uwj<{8UaaFL4QRzFi2T%gU&!CA?B*VPWaE{$fC=udk1K>z`^`)y1y%=XW8k zjEs!+;_RFIhldY=Mj{n@%sERxk}S7NhFl)4mfV8pv~*%?0#WA{G~){3nb9#4;x!cg zet22#m_0*55c1J(=KT!kllnE=R51j1V{^}N;|MAHlZBgtQ}){49^Hk5kO)z}mHu-} zOUt#o&xV^!yl-m`4i2>KM+XKlw(mFA;yzb9h!`Fe5NQ)$;7=U*%(uCp)EPkj;n&d=9c;Qr(rQoy72X9L1}0?Ync}VRzxF(n;&<+% zqbu#koQ%nro%g;C>qJ3|3awnnLJ&8;y;d^$s*ql;dyJHoFKETmZvKqXgUXU*g(|QweJ=AnQ?jIEzuv%^A6L1)cfGWkAf7LBWISe-Uz}T z636#~K?R%VLo4RHqwnxaVp$C@#9nw$Ucd53vLv&O{F+bzFJ6CzQ&eNwRJ)cXHt(&(rhAm8ESDF@V2_9bH*H^$b_6QEd6@f>bLbkEal4@GG4cX*9B(?C~t)J zXUa=Y>U8n`-W_!mJ>72$x*RTY|2Qi$=n!C90-a37gfWTv9`HEucdunv_TP6eqgm?Dv zhu5x3D_0-q;OPsII>TzihUj*-iBD=zEW$c3mhI5kY(IL(N$GAH^zHnIf>EnGpfrm- z@n*c7ou9q2tvC^REcta=z*HyFzTbo$04`OJ{m>1Hq$|69<9ro4{jT8A@a>#zkBd2$j?8oNdgDfv|jnKZvUtHAXJc% zg#2(f@G7R?O8WC)gsZ-XgvBxz#@gL@W4hi)X68uT8?Iz*`1g$WpYm-N>Lq-K(FN}n zhb@kS<8B+BHj%iwxW-^-xYK`z@Uk4Cm+N(X7OR&YwwR2Zuhai92RuHg=FbiD9jlu? z`gtVwi&T;yUZolD#<}*DRORcgVZa5bf}>_yM(!!b^;W=q9`yd5KCO=6`<@l3W*q^@84zg@aTf=SrOf zm*0cUV=g4TrD^v!S28QQx%Y=(T|*)Sv*AO#&=s_a`ed2dNK~N`BV%K6M&^oiJ9~SD z->K|awg&p1n>aDZPNnGy$aqX&_Yu&DKKAtVoL?^3tE6$*_K%Fns~SArpN(4oR?w+2 zL*3lm9M6jq@S1d#OJ$SdyObpLRb8)txVs8>BGJt{7*1iG-=~&O{|KI|b=g&p1wp>` zUKBMysbFQZU#E3Uag3?uUT+CEC0)MBqqu)Ktsc|K#>bbu?)wS zn~@s82Xr5ieggoL-Z`<3E`d7Z9(Y*4eu`!j_P>v=hztsRfI;Ttb>l7wd( zG5#*1eyHW;kn_$6sUh~|$_Xh$Ze@RYXt}X|_>r_sK%^;XFS4;TH;$&V@tZ!uvXY}P z(Z`VI8(qpv--=2cs%t9T9}14z?;@q8r7aE@n{s2D1u}m)xRx)h&}Q!bg)3SNXpoAd zT5EnM8Ts|+cA9-VhH!XIO$B4W;M2Y6%*;%KF5DVr5p|YNtl#~KK*EVcF)V z$2(eT>X4ZVJsID-OZIDmOk%6~T3TWrmybRMd@2+OuhfO=2kGc`*PlqR3bxtj*_;cb zNT5>s6=<=Ma!gzb+bz`OI}a;S7I&n!+*9Mqt>tV*#veN$ zdOrGJO^jz-zw<073&)c`7}LFR2WpZi za0w$&zTLDEUyS0#p9{a04*r|459{X*+A8BA&y<8%a4j{vVr>KHR@CwIm_|o;XcruM zrpooPQAtrTnrn8{chxYcsb$4aMHswyHwaJv^^j1yr1SA#VT_;09Ght;$H(tqb#FjA zk@~BHk=&)X1Oy3u1KV~Du)LyG_yyrp=pS9K2f8zJPs@A3jp=26Bm7|q4uWdj`wicy zH_)7%YxKY8uSWP+eQ+N5Viqjts!hvhtqBej6BDf}C;9C3(oWiM_ezbnw&v@sb2~`3 zs~5w}+^9Kcs}xe=t59B4D=R9?A2A4uAzC&Y#D4zt+pc!os+v{k}z)7XL zk@(`}%jHYQ!~B(4YgZCkzjm+PE4Lf5%C^hY;;b&N(M>nD&gir>!U6b4ug_(v80`%c z?-iRGoMpT`#kgX{_rE%;nh7i>5Iylo=_urf?Hj37?>?wWiYc=soglpDkXJ6ZKMX1H ze*$QCEnll})pm$pqaxpZ#WzX4Qr~v!X>@2vCf(fJeAaSn`)b9%X!TdfBmnU}i6%^s zZl#d0ID35i&O~G%Rd_9$@)4y_g=u&?bT@m^O1=Q0P5G0vp3#LW`DV+_WMr-L)xCG* zmiQ0?Tm-DvGYOTz0y2`7zP=D9y=IEAUrQ*=;=UKt8dEjq3Q9>q?%0m^kc?;UhIbUlT$7MhLT`lmwu+t<8Qy|0t-mRV!xMt&mEm1E)orGE_3z^^mX(I@tPG>1)D; z`s!j@zi;$x@#4e$nioXz$uP(|?!Ta*(BLmvRI;yI?6?&Fa^8WV>kxii!FVU})p;bQ zX?(fO+wyc{AXk4dkx{31h?|e(@LjhjLo1&e;qP@z5(6f1FPmKBZLOpkS+3ygiFWnB zCOvr>j*qimRb7jHp?>W-ea*`h;WtkbCEWm#IIi@62+#CbLp)gxOT%`GWlZ|*zgJS! zeIPC-CIgSTHGCxem1N*Y3m3UC{V4`+89#g6@dt~Ybn*DaFR3Yn)vf==9EhN0;-zo~?vhYf zgD20}NUDJ%X-dfATtzlMtJzqy>;BFZ?b6CrODB#K9-}rzVyIPVK0Qdv$BHp4H$pQ6 zQ(q{!Q}f<>7%5n-Se=IA?Y>pFGohQLHo`ebn@#Fr7%Zhs#*-ZGT6FY=!q6#Q8byeHd_bawxJ&9(J zXkyT}zv`0I;}w6{peJAL%Ef7Yqn8^6{9c;gH1y?9_+L@W@K8Yl2+1@*zpMkQvX;#}i*8r*i5-E}MH=M#jcX4qn4SZiK4fP^s8U1KO`w5GNSu zjuYp!j9X&ZtxEcU zhx}7bahV7a@4WV8`pMD{ajimKVq440MmrPh^2a!bwnkkx{*7)6h~K;FI<==mkbX<^ z6GbEbh9y&|#ATYCJ9&ZRj$gJo2^GeBhT)*Di$_J}N#XhHUv$o{91AogT=JP``o6Ar zpYP|oV?7;I*&r}6G3654OGpHBxj8wB%=yBP7GvY$S{WeU&s$i$QvbQbLDc>>b#s^6 zQKEj<^%4GtfM_`ubK=zEVHJ7J<8D9|$?8!0op)`5m&Mx;3JMA*FV&QlMJz3wSL_uz zyuH0oV9kVg7OydxPIf_0=GWzI`FBseH#C~@?GATyps1L3cy_#U<`?xDfh>-AD>LOf z;ha>zV_(xGm6w-$FdoCQWK>jEdPBCRpn~(0dE?ywc&+!06j&wQ$>?Qq(y-+N{M=>n zQXeM;2AOCI!srsNn?|x`qSB$Z`6MJCzplhLlA>mLG~uy(#t(FL<&Gx>W0VCbIaFpf z5gO6Zcc>ES=xC3%7`KG!vl)8vr9&;Ze(CL$8=!w(65eg<$=zKo`=J-p0T9f=7 zJLWe%^UnudxRf-~)=S8sw6z5-`Nm&v5$)Hd;t*a~M&*sywFe#EBns@Z zZwqX-R8^obNex7dc{Vj&S#o(@L~I`20SAdd>qMH85ticitv+mIQYZ-JZ+m=O-<*nU z$8|GPJhGwGmad_%MR}@$Br{k*LI9z8^#!NcfImS{L0Q7?$7MtWmX*@=@76}{x$%X( zmS|Jg`+*z!vxaUdeg=r*>lgG6v-O#cVs|6?)=^LPhS8mg8t+M4;eD0;^&SUDRFW)b zONL@25h3x0HW8fx~RQubmmp?qUbW1_#phti)|vSOM5rA@TrX(_aP9Ht@Kv& zGq1Xqnz%ukpfKgMuW{pL@3)wY9+ub1%oRyzTOeqQq6!a1Rc$f?wbT7)kZdxrk$CIr z=t9?2TzN(GNye2ZX1?53Kab%Fi?E- znSp^pDxW7yhM@mrB#HS*I`@38cwI)Je8#-tN+9>TZD|LTzlfh?@dx|`y7&qyWmAzc za&wWWGxS3TiN_reB!Ham4=x_wWHg!R&c*;h)h9*-AB86E>8d!Lptszww{`hp=wJD^AiqS$l>B1)}EDeWTsO7hYzX z#Z#isA*>W>rrEi<O&thBd)BkK7kqpM3~J6|hR{ef<7wY&6uJ0^U7{fF;; zv2j|kJu=#9&%zYx;gY=feP8F9%brnUzA+r+j3Uj@bhbiIRa2A3OgTLx!+MbfAD?!F z)8^0H&YgIumr{y_QPJx5lGv2$vt%BcWNl8hr}k46WX>7}rqXCHO^BhH30B$hDVp`H z{FRhk7Wk+0Vedy3IN#)46eCfNqFc_^BD5qo*E^^jZ_rRD zVFwrOLEL(xNDZxbt+2bBjPxi8frpq$aCAz0H8*2^50Ql8C@w86p&n}Bg`g4V!jhY_ zFJIt59@9ZXO)bel>w7*fX<{QKB?V3^_5A@=aq9fZ6-UeY^}Zv-!g_rsu&g&I8=DB_ z#^_&N&yHjBq~`DcM1>ftbJKq_7qT)2NY0^SVHH2&R|xu~b!8is+vD-N)8A^xbqS2? z;Z*kU+}zx6*Tl(L`#q-TM{+;rjVttdnl+%T2H7@PcL-sd!V#66nb>kn&mhBenvO0m z*xRP2rgh8u+S*p2VMHXPTr^^ro>DelJ-wa^@_Lg2OcpBn7h4p6=j976R_iC{H%WD@ zWxSxB4yq+V+9#)f3E%O?xVkp{vYwUM>9cKiLxDidFv_m4ufLxCwV7dkq6<<*m#+S1T}^m8I|e7;XmgQD6#GRDQ7RsFpe<^ekRE3*n4RTe%C zyVy8J+^-?LgG3jBlvfpdgt$uH0prFWKUgwYu zug@wh^I1A1)qni)J#!hMQLp9~r$li0Eb98~_$cq_M{qQ~cdSNK=fc~Br7umiLy3%~ zA4;o@`_O)rnq42S&hIN^@Q7nzV4RQe9=%!Y0PHZ}FsvBg_6&8JfTL85JPahrty1oT_ zAjxiRT$Wg}Q4LVQBA7)O5_H(^y|^fH^U_)D0OXbQ5IrR^79^Po zu<&STXt}Xa7|jnj5p11rU@6m?sYa3t>gR>77Axg-`(EpI2*?0}VW&Ul-T8zJbEsA- z0HEQNRQe*Pr>D7qy$k>O3gy9k8-K7>pPcEeB68N#UGl<3;Ah4d1F4ld`eb!DO0(;8 z$RZ+gf^>mh4)R4V?Y-Ieh4^)F;abaFSw~S{p|ky&d*;w^qhO6*9Jl4UaObWdp_pA+ zI`s<FhNttm{K}u@}u@oc2wIe9Mt(DRi;lPMlv~xy&pW(qbRN0+JBQ7aaI?7fPnb-XWpk z$(~3OQf!8cs-GxwgqYGKBYThfKu2IZs*GE^K!7OXy;8@v(UGOgXf3w3fB(>qwqaub zZa4WSUlZ4#v9qh)=BTrM`Hqa|ke}smHI<4NyOgzS^=1i66rwrB|0}*@ea7~mAJn&J zUv|BS_{62H__DVE0=a!%MuY9HxL{;T9BUr=_oyQVe|#ao`0n z)>ih{ut~kVKJgYQ*1|%N>U%NxcXLlD?qcuChTAbzzF=zEk{BZNyfdsT9wh!9ocM04 ze%U&Num@S1HFQqT>~i_HX7i{92qD=Vph2_gb}{KuM%<*PIF{L;LXxHy2P4DGs7G(D zZmYsm;A;xAl#x=xo>qBjDGtqrFHG-Ls0});n)%{8J8?Q8fNC#L2{zxB_9Jj zhopM=06i*(t|PqwjTLeG?>t~!3DW2EE_53vd|6eoKO zRVd0En62#_MMI`U(6NTax@BW8I*NiiO+qR^obP+G3j+%qh@D|@*a_-i%LfUT?$eYg ztiA`JpzMw5#;?z6Rxw5*$xQ12jgJisCFIJeDbawO$vLoV_bNCh2tL+I*z2Ch=j0J> zzBfhdrhwcZ9wJ2g1&7sr)k@&O@%YQ7hmV23)#@(AZZhQjWZzNbzmCgfgUb76c*vMg zl~FI3*2zkmamjZc%kjS7pGn5ORUpyfj=m2)`*CV&)Pe8jaA7Y_{$PiVmuB>*c&ws% ziaQ|vc>lD?8KY}g~aiI)^E;RxeKO3bs9z>qa2EDJAB$sDl{MsRR* z6G0%IVJX^|?q4jhi*BX{brPO!M4D@`4b;!U7m)wYU*q$*Q$}%+G!^q_PB90xE)ju^ zFQj+f%uBGL_Hgt%*7g}_W3Hr3dE7lsK8A|@M?w)6|Kften$okWC66`}9Y20pz#!ff zkob+y)$&14kiFO$AhPUpZVHPyUpbX9t02okJ1Wn*nac4KXgnM}w)GoL{-jj`28 z+O-e0m&tLM-tbs0RY401DxRhZjR(6%l2~`}ANeT)UY=QWKu&nf@0({09$!Gtz(Nos zmK)3mq5$+nC2%1wu)Gc|1&fdOw_CGKFFPKisLZ87;1)!$)MXXLO>r3i5ESvFS#Qb* z@~|a=H${9l*v?^Gs_PT9<7f0Eh5T#^*@L~-YbAcW!;1kAL=DF-0mMM%4vVjs!e=&##Sg~pwV0@Lk~T%_a0HU z`&0sA3J4A|?CEBhOAr#DW~Tz#jm2KJVM)k~v7KwzBa@<$aXN&~bro-ejF zw+jPJw03{W6t-+ppC7qVWmn~=;~w$k@sqsXlK}}dECM2z3u|eN&Srg0!6QCV@q!(~ z7lw8K_QO>55K_O7ot>Q~fRqOk1)Jxo2^h0`VnKB^H5}ioCHJWk6>29Jm;6GxG#HZJ z?GD8Ne#CXv*qq#e3X<34VSy)dfzPmh5=-Lz#H5j}XI=PH9KS52z%3 z$?f;2GPgTunb#3jZ3JwHKG>ejsBBXw@aA03AxdZseV4Z1OYy&E_w)5if@X)Atb8ouz zpEkjI=<9TU&kj9d{UtImGcOHRV1G~oBR7N<&pP1e|l`uXi`Mm2Agzd8GnUk&!N=>~52ka{BSIm7J} zWD)bp4+vdDdj@e+2O_sCIlKzP2#U=Sl1vkmf`-y<+V(cAQ|OUehKh(U($I#taJD-4obZrz~p*>UFM~QZq4Xo>%=eZU#P&UA(o;s*+gJCXr%qlmyOfUEniG!?zPnSKroNH?|vs1dZaN%B7403+1O&87+un3(#}LR~UMIYjKZ6R^A3Q zYhO0-D1M4`n_(*!MUH(wH(Yi+LaMG_SV8TSH!-0cc!$6KWAE0(wI}qaJ4>Jw>-|8q zAQ{siOv!i6b!61M7o_@-#x~%iU~^S`?c2dEuk*{+;6Kgdf02V&)ZC{1E3P?(uu z5_N%2;cmVZ;!QhXX4cc!Cz@GdRf7|s&wVo)%NEYn zTo{O>?!nz61R`JPSRMJL*43WjU8r>uPo))_KsJ&>83_-z3 zQX3oP1zM;sRbHAuYb@5>3;J~4(=KG@uRgJ+Ly6PLh;u)TLtcplqmx#&Wey!niiwT! z9<{2hpO_6NlZ^nT)5O8o&W@>EzfGIrnOp`>U1vKfBzn1)3@R?j*!xiDcekBBB?=dv zk#L7eOK*@dG*CmU8=z_ja;d7h8grE_0pA^H!wPP}X_Ln5(OMwwOT1tO;u|$RJ$+#8 zt#0Piq=H}pV9x{l)&}bUq4D1L>^J9{AD_-Io$w|v#-zEYi&=w#9`S}kRLEAyorNQ< zU&KWSlcIEC>w*o0t#D{AsP-^`$<#=fRPfZ5bGa7__#}vkh{Py$>;$-A{4cuc&RC9- z#OC$MdTw)hyHcJ+=-u&61iKoSW1_iY_)%EdNw}inw2T7J%J2 zKz224v}nE`QeI@LvV$aK6<)tf$IoeNq!1-`9Y>^9e5_-QooeLJ`==QN(u?Cul_8+% ztm@5YZKDxpu+0FWgx2AbqLW69ejDY0pF51`@Dw(d62{zM5SV&|!F2nLHy#4dbf@?vKVi#|6Vr zDtnw+js!!1=?nYwopr=vfwf&T4^QZX^X9DG^Xwec&7`r@w&n~+UD}M&!XbQM2UGk8 zJQ@9Y%|0Gl;t3y_UMQwgwqO=F%C@2Gtr?0walZY!0Wv`d&C9DfFDHkumt1sYfsy}i z_JCfPX=p*Akbv!R!%y4k-&;+A;{$tMd3Gu~4u$@EgB@IQ&G)@WK?1*JIWba3l>k^| zL2<8|?4zj?-fwC@D;M+;2AGbm75bsXJy%W1x$aId^ zQzQp>D}geg{9s8P^uw&m?w{T`S$J44@=F5049qP{=rYljX7}*Iu5zJ3t@bOTO`L?# zAvX^7mrXi_K-q)P#FpkU%HbC;z;{Z86sb6adq_yMF*(J)YKohz%i#Ps3nlQ=3H<<9 zxg50B1yzpwCi)KHSu#iw#gXeXXWA>86lVSi_?cJ%zg>WlmX`L!`P~%1%%3KD&)SP_ zN^ssA%8n!AyZqm-g)muESd47)EK~N&^)Bvk06_o4bTqV;`J@*}q)z^20>aBCVX!9G z>rN} z@{G|&CNMgC?*NPb*dnOq%~#lvwG(Cf9p(yr9ZD7Qo1du1bVMA6PCv@Oc+dWQPr7b% zIw2)(>GiiXa+s4)jC}Wa?o-bEdK_Mj<>)RwNLc>biDgKE%qCc>?y`QyWDwSv>OSyk z@jq{??Ss)3xf4jB#}3regLGCD(-Ohg(IqhFVRneQhE3U&KS9l@%$P?jPh&h z7?#Q;i#RKBQU)x!@6)gqBcyz3)2$@o4Y|>J8zPD{#iu#J~LAh&?{g#m>}_pUvl~oLVQ?&IFU|(O_l$+Yazn;(&fO5#C78haD))=J3zo5Ke#x!p%UteCPtnm^) zny2LQj?qz7D?@c3x+N4PmaOoqJQ8BwBhD{vxB2(%RHsUy zX#f9im-ild5DS~9InUtJ@9sq>HiPGPyvch1`+nEITPBhUXlUakPsB+Qednn?&)5=y z$HZbNR0hNkw@vP_g#xyf5b@G%0S>qXCrL(Eh-6H062S6_FKDd}YhS=F<-a_$6Up6#2bgWZqz*c8wmkcsF~zvGOlfS(V~c}91Z~1HI*gGg#X?;urwE$NiwhR-TGwnQanQnBteGy zkrbvAmvJTY>jTdN`}=;!#J_}%CO^vlRBYFRheg!3eQ4g7SSQfut(@Z8FYOQe@t0i1 zP4qBpAT0)LiF*Crz1EQUFD@eCcGS{MxP&GMP0>KcEi0$gbB~XPUIy6<`q@EzO0^`S ztbHGV^0DXWd_tL-nJ`x`a7^ThhvP0y6TE+V)-=7_6@-j>$`1)Rtvzg`8Ecc6KKR-WgA|l=WfnEu2opwm}YQ+A!q-|Q0MO4S_8AU?88rUZ9Mx|8ZNuPUXO~;%2J4u5B zQ34Qd?op_(@@M2S>iRU%zBGR?F3YWanyRxqc6HAOxHg|8k>MjrnX0n;4k{|#RMW$m-@8xSkZEL3XpIJ$V6bllbH; z@9krdq(0T8ry|uyK`nteClN&=cXD##e$L);e~Nr@Fc%jV7FIhuH$Ok)Wx&hJ>saBx zvM|@^_zt*)#O~@^o`<0u}LB9P?8R5CPxeX{(Z(RVe z`fH1`8ndhCc8;CgkAj}TLFR$q@XN06?u=i;Lr@X}doNFlrpNbei=TKsGRZ}JijDW@ zlUjv6Jv<$tet_v;K3r~MO!PNNsxdM(6$jR=v5~RG;@sRXFhdJdNJt3b=2O}C@0i=t z``U<*2>a^g!?&3m+q;il%I9csu18r-`wAA}9d+j}xzvYm5=iY#9_MMT3ZtV*0)kO^XOi1R`lEeH=5S^{<7CT zqf+iuZ$T2^`sbTnOF3x9uYjo`{0yMycLwkf0$XkiF86|Lcz-l#Fh{~wn+8EOz15vD&yn|P~OKj%MJL2e3 z-g3PtvQkV0s#NZEngoy_Qj2F_VVMw=Redn-{n|{^dR7F=5Xi^fdctsO4Ljj5`lk|U zhIsw1(>v5x8dm)1ZxJ2Ip?`iDi)Y{-Vsw8VU9j(9#QdVuV7K&jg&0DUkx)R{cB5$S zRHdeHh8G_{3A6K96=b6fL0a=By`hJzMA+oE;StnW06XJcGm=X%;17oM@Qr-Zsj5(vs&)Q=Y?c8OQd296lRBEVzA8;TZcF{!( z!7Qf^%z8yPs3FYCQz^XGGf0K27RprR{MqzgJh?j7d!-H7uNwYAJMyR%ELnj3Yx@jr zzvi5$H*?y+-wX{h=P=LgYfm_^uNQ^n3QlE+0KoY6lk0+epUb$NITPUGqGpM)JQ)mc z=}N!vOuF{bHYhITCpA!y`ZOZ**ss9DZc|LF-CYJuJNA*n&2fLX%ouQ&w%QFY7Avmb z+;yRtO=KU!K+#7V;O1v*Ev-=8Bt;liVLR23ozHOw=J@#L1&$V=b~ks(&`b6G53=q5 zS@TPzBqJ&O(&I4rO>?>)z*>I~b$`=y^s88I?us!-?9kMg|5^g3U&!0%i?&xwAN1;O zP|`b2E7>JW^2_a*lieEGhU3z{;R!7v6!Ap(f59@ z>+SPsCE#0g62C+J{IpMyxqjcM#DZhHQBBw1XHjo9EMQ~coYTWOG%0jF;UFip#UDmHeF zm@meL|5YfZ0Dp^_;`>7pGXdY*KF9B6k1x((ib^Y#^5Svgm#LV;h;;5!}`y40mWzcQU!%sj>@|Hgz{1`y%Ss^+J&`}AiITbewn>9o6z@pJ$;|6cHz z$c|&#F45H3#`}D-ngS1b|4jGUMIho*k33edC%x|KHaL>k>5h|dlaM(yGuABW1__v-jA&{_iu6Hi)D5fv~|L= zLZVvorbJo2VE|RuCgiofWqoz+n4RTT0A)+I*j`QwyI&@^;y`PKU*^qR7C3FKRwnfJ za%kbe-h^OHzWX_p9Ws`XPM%tK`W`3oyWmNnBOr0UZeJ-U+P$>CefTsbIHfV+NQ?OI zc{YkxN`7`OG4J$bKRxUS(WI?l+S@_{8B?bxA+IWL9jU$owttaebMg@Z_;SewfSp+- zj|eCNij5r7jn_~Q+ByG4-ZVWNa>QNvsY28JErJF-mHH($ZRtKJEm zmhAopnPOJFx$fw*c$!0jx<3DvI_Z2B9J)1 zIWE=8QRaXdLx8-0uAZ2fJ7_ERHA?vB>GGiHA4*k8%FTq_F@0<}oVm^#bci8U+J&cFU!TF|VGdFS&Ftn&}3GRJv;@RZSuMxrW!tgWR8CI}l9QlnX1Q zB_)X|Df4(C?#le81L#6*GHBQb>U!mBNlAc3t{8*O6BW@ zY5ESj0`x4-MsSp9E}3` zL4d{Ouh4hGqdWl8ffGS3@!;KH>ay@^0r6Ax9D6}yHc4L+lpnp39u}MgnE}IM*9dh)e zT(^etQ{R7r^YrwL2@DMUdLZz0?@Y(cS;?*i3Ap{sc?{?~%g5S};Lwosez;h3y8X0o znObj%4(0b1uXok*&{Z;@*liN`-rW~eAb3HS&Ud#I7nAFU1cx(8nU<0OoIzedAF@1I z>rqw8k@?cBb9Z%AL?#-nxIL0#Ihx5oRVYWO4azq7&xToRowwy-KBE5K-d-AGum=%r zQ=7&#RBk7-B$>uy^$^KV5`m7G8wED(-z_^fvWYA&crO4o2GhHR2gf(8Yi-=W?D*%8 z!W;rz=djX@-SUQImNj<>#PIey!dG?F4IG9^FkOp-v_(v9nw1XGvgy2Y zFOPgX&>agEB-KPVgip5lAd`0M(FaNeQ&{=zEf)j+>zVXS0!)$LsXJDK0hV!cUlu<{jx3tYC%CkOhSSq9VdAw zE@T)3@0(8bK8pDCghKzoi1v$@M1PJ)a)S?wem$E^8EUHKy<~xYZDOE1S^nz&UJn0_ z7=TCwHbmLSFLdOxjZg?VY+i2z3lF>9*~JAV(4s(HNG{0Rr@Tl(J*+hV5X+=18EY!e z7SPWoF@(x$4;V@Ni8efmeHs#Nv(8dGko)B2FMjR3yN^eJ#v6Ivl0THp| zH_XbtU9kbwd0pw>blAS%tbRqY0)~oUyd1U8Zg27Lir~i~hQNgLa~H?M)m5c^C{Wj4 zUOk5dG@k&Eqj_-f#KxmA2%gCe5hgVw{dSRIXX@@Pg6@q*Y7-SYtc#6Atd{ozwrM^k zb>T;C)$1X-)^itykS!Bt;dYddzi0$^K{>ICkIy~vcEUNxN(n@ilw!CV>gvib+(23i zWFq9z8H<@syZ>}?A@8YSdJ;uz0oRH8>Vx@YkMi%`DWVTDPOL;p7H;^{@T1u<3D);Z zYl&nw9=l`l!x2w1XC4qxUZq7xJ4hNB9|#F&<>lr5LDExKSKo(%buoYU3?lT<2taq0 ze0g{1m5s>;uF7J8jS@~$l9NX;C4Gn3aQ>pzOuiPD_c#vE&)I}8-2)LmfUUL7wvj+o z2jv99PHUhlJ2W&jJ|jbK9}1Grc@rJuwQjxPY)C+?qd*t{7+k1m_Lk9UU99h0p^X5F z5}h5A5n9VQ{b(Sg_9mPX{hY=!1TV<+qOTKa^m>Yln_Da0xj+|Iyv)bj#T$J6!l7um z`tmpsU4Ui;bijJC7x0@pU9Re0SBH$9JK!1-`p}7_ksa~vTiK6u7QcHBC4GNT3E=`t zp#v9if&G2Nz#Gb*hx4EHtMp>;8WX*OPUoE9ZDG{WK!QvJVm`dBC%$BxDktbX`z@%2 zrlF$?gHBeh*@{34nr&NoQ%ns^*JfJUy(k9vLTz@INnS zN5`DgQzxZ2BX2ys*;!}SQ&{-$tUO4jj`@mLJ;yqo$)2`9_w_dA-rU@P;`mqU-!$19 zs;Z*38|)Y_6hT8Kl$4Y~>FMc*&7a@@Z_69tlKlTurT_n_yx*iSKmR|0`(sfgG&Cwe zNu6F?6adL^5~$r$C8K@+S1G=#a;jd7+jyLUfRwPX@Y=;+P>Y|Flf!1*ivpy4iUKaD zjaO};o(vlwUy4z?_VuVZ7+Q<&uNbw!^r`$kJ|5d@A|FvpQ*#XPa&ppL{^-|t?Q@pE zb(~Mp7e$J9gj255Km|6O)l3;a%n`8cv5q=8I!dh)Vkj;V79MVS{I~NQkP{Q28c+=I zE48LWxIC_V*si-1K|sp`?o1TOoc|3(4-=9I&Gg!%t?_u* ztOLlBzRzH2`!E~aVrd*HEMY0oGtE88ipY{%?irc!{fKG{mM z|9&lb3K$MT-K^x~Vo-sv41P7CgpQ6*Rl3Tk2PvVnwAAYH{^om6I9~WRU@vItC~r=O znAN~9AAr${CunH9n9*6|ZUS|Gs?vX<(DGXll$EakMnA?0-=Dd;5@9qiX^nO zw9rvdi18t`xF4z0B0lja^Ly7z?}7m-udb?6gw;E-+sS`F;Ppg7rcXoC4?@s?8%X4UaHkfUf+jC2=$D8e*_zKLM>+3O4lPL`x$lo0o zfT6PICX$#J^ec9FDV5#Y0t^Uy#kN9hS#7N<13sPQO-K@4$ITWYD9%sx;(D9Ix$Ggz z0N;Mxh0JcZzPS!8n_ml5{>^O@BJAfRVD1w)w+ctz%IO+ZLXK$6^TVaIR8D(&>B~RH zQR84ztlNGAf-~LuZISW`g7w)?oLg~_yj7ErmhX24?2O^ zdA!bPE5 zZj=~=bpon&8BW!}H1`v|)r<-Kzb>v+g^1L<&(#lLj*ZmCLqiKvIScwo{nI5P2q+UJ zJWEs&xpI<=9ss7&*+0#m0M)R9=VPMip>nSPup{5);owlGNMq8e|27O<0o&a-pq9h( z{`!Q?cAgDjA#zG8s<7<<1N!^>sgj0Eb!pXL*EM|33ZL)cF#0bh6#|uA4i$|ESbf zBBBx;I(OYG6PrUxamuo(Y$d&}sp*9{^y)C-kUTGg^qY0*6czBPr#r`RDN^5PLXt-haeo) z`9@D~2&IbhQ^DdMP&_c3OdJ>(SW6Gj|9pB(;PUkN&{L5NG=?;YZ*HNXp^xs|IW5=H)^@MRE$*<)Ja&-L z^%=l29<)g!iTm@}P5Vs0nmR<@7PbtKG|QRcA$4Vb;6oR0zg;HjbNUa>m&xDeI=h^0 z+`EVsIT6z!9dEcW=BeL%dz9pSAX`l z7l(kV<$ne*A0J8pF7{73SE0^4oqK)mwub1CjBUEg6T(|=bvNqq!GWxjA(Kl8uc=;M8UL$khkps7Om+SBS`|b83Emd~4SuI|C zX%>+@m0YI|ZdIxTow}(bENDla6MO7!dqarJI*J` zx=(G#D#LKSXY=jFz3%L<-@omW;d6t#Yx1UA&NR=Tr@?c)SGv;Mp8YLwhU3e}7(Oph)vU=&>dV`Rdm^HJE)BCgx3Xa!})htALXzE8F;FR{_s)S-A>W zd@of2&Li!A`8x4H1EW@PcxdQSpl8=-0SmrW7cK;RoV8)&#=xyvSG`PuIpZpD18b<) zq}j8zt4^~5b68!{o$y>>)>}1IJABodR@VQ@r3YAm)qH{R32?K5jKg?Hs^)=Kfsg-b zI4NyWpa86eCFbbG>`?GCkK<=p#ts~+nz0A8BO180Yb9{EbxLloFQ|_PEZ4e%y}Y<8 ztEw&mi&)@Lf~Rc=!w%pn9}EX{Dypk51JBz4%|w7k!It#cR$M)xw$yb1- z^U8J(hVqk6t^HCA32t)re+oe%$q@nEe3S`1L8s)$hr~@=w*t4eF%(P(4h%Fzo;-hk z`G*e$6W1pJ*C?{{%ZVIkNZ1A3^~{h4TxZu>_v0aZ;myN`ou8~_XvhRDAxKLHZnu2` zoD%?^xB@yo2e@62VFhp%C2$`KuyAzWUG{d-j2RMKyu3@}>wdQW`|+3`xI~-bYr`B- z1_S9mwKG2lhpmh8Y-Z&^-*Eu6{1-oD!M< Dl{qX> diff --git a/src/maxplotlib/canvas/canvas.py b/src/maxplotlib/canvas/canvas.py index f65ff7a..fbcda42 100644 --- a/src/maxplotlib/canvas/canvas.py +++ b/src/maxplotlib/canvas/canvas.py @@ -459,6 +459,47 @@ def add_line( **kwargs, ) + def plot_many( + self, + series, + labels=None, + layer=0, + row: int | None = None, + col: int | None = None, + **kwargs, + ): + """Add several lines to the canvas and return the canvas. + + ``series`` is an iterable of ``(x, y)`` pairs. Shared line keyword + arguments are passed through ``kwargs``; individual labels can be + supplied with ``labels``. + """ + if labels is not None: + labels = list(labels) + count = 0 + for index, values in enumerate(series): + count += 1 + try: + x, y = values + except (TypeError, ValueError) as exc: + raise ValueError("each series entry must be an (x, y) pair") from exc + line_kwargs = dict(kwargs) + if labels is not None: + if index >= len(labels): + raise ValueError("labels must contain one label per series") + line_kwargs["label"] = labels[index] + self.add_line( + x, + y, + layer=layer, + row=row, + col=col, + **line_kwargs, + ) + if labels is not None and len(labels) != count: + raise ValueError("labels must contain one label per series") + return self + def _get_or_create_subplot(self, row, col): """Return the subplot at (row, col), creating it if needed.""" if row is not None and col is not None: @@ -985,6 +1026,60 @@ def set_title( """Set the title and text properties for a subplot.""" self._get_or_create_subplot(row, col).set_title(title, **kwargs) + def configure( + self, + *, + title=None, + xlabel=None, + ylabel=None, + grid=None, + facecolor=None, + axisbelow=None, + margins=None, + xscale=None, + yscale=None, + xlim=None, + ylim=None, + tight_layout=False, + row: int | None = None, + col: int | None = None, + ): + """Apply common figure and axis settings, returning the canvas. + + ``title``, ``xlabel``, and ``ylabel`` are figure-level settings. Axis + settings are applied to the selected subplot, or the default subplot + when ``row`` and ``col`` are omitted. Use ``tight_layout=True`` for a + final layout pass before rendering. + """ + if title is not None: + self.suptitle(title) + if xlabel is not None: + self.supxlabel(xlabel) + if ylabel is not None: + self.supylabel(ylabel) + if grid is not None: + self.set_grid(grid, row=row, col=col) + if facecolor is not None: + self.set_facecolor(facecolor, row=row, col=col) + if axisbelow is not None: + self.set_axisbelow(axisbelow, row=row, col=col) + if margins is not None: + if isinstance(margins, dict): + self.margins(row=row, col=col, **margins) + else: + self.margins(margins, row=row, col=col) + if xscale is not None: + self.set_xscale(xscale, row=row, col=col) + if yscale is not None: + self.set_yscale(yscale, row=row, col=col) + if xlim is not None: + self.set_xlim(*xlim, row=row, col=col) + if ylim is not None: + self.set_ylim(*ylim, row=row, col=col) + if tight_layout: + self.tight_layout() + return self + def set_xlim( self, left=None, right=None, row: int | None = None, col: int | None = None ): diff --git a/src/maxplotlib/tests/test_canvas.py b/src/maxplotlib/tests/test_canvas.py index 38c020e..423c0c4 100644 --- a/src/maxplotlib/tests/test_canvas.py +++ b/src/maxplotlib/tests/test_canvas.py @@ -160,7 +160,7 @@ def test_canvas_matplotlib_gridspec_kw_affects_2x2_line_spacing(): for ax in row_axes: ax.plot(x, (idx + 1) * x) idx += 1 - tight_fig, tight_matplotlib_axes = tight_canvas.plot(backend="matplotlib") + tight_fig, tight_matplotlib_axes = tight_canvas.render(backend="matplotlib") tight_hgap = ( tight_matplotlib_axes[0, 1].get_position().x0 - tight_matplotlib_axes[0, 0].get_position().x1 @@ -183,7 +183,7 @@ def test_canvas_matplotlib_gridspec_kw_affects_2x2_line_spacing(): for ax in row_axes: ax.plot(x, (idx + 1) * x) idx += 1 - loose_fig, loose_matplotlib_axes = loose_canvas.plot(backend="matplotlib") + loose_fig, loose_matplotlib_axes = loose_canvas.render(backend="matplotlib") loose_hgap = ( loose_matplotlib_axes[0, 1].get_position().x0 - loose_matplotlib_axes[0, 0].get_position().x1 @@ -222,7 +222,7 @@ def test_canvas_matplotlib_gridspec_kw_affects_2x2_imshow_spacing(): ax.add_imshow(data + idx, cmap="viridis") ax.set_title(f"Heatmap {idx + 1}") idx += 1 - tight_fig, tight_matplotlib_axes = tight_canvas.plot(backend="matplotlib") + tight_fig, tight_matplotlib_axes = tight_canvas.render(backend="matplotlib") tight_hgap = ( tight_matplotlib_axes[0, 1].get_position().x0 - tight_matplotlib_axes[0, 0].get_position().x1 @@ -246,7 +246,7 @@ def test_canvas_matplotlib_gridspec_kw_affects_2x2_imshow_spacing(): ax.add_imshow(data + idx, cmap="viridis") ax.set_title(f"Heatmap {idx + 1}") idx += 1 - loose_fig, loose_matplotlib_axes = loose_canvas.plot(backend="matplotlib") + loose_fig, loose_matplotlib_axes = loose_canvas.render(backend="matplotlib") loose_hgap = ( loose_matplotlib_axes[0, 1].get_position().x0 - loose_matplotlib_axes[0, 0].get_position().x1 @@ -672,7 +672,7 @@ def test_contour_labels_and_rasterization_zorder_are_supported(): axis.clabel(inline=True, fontsize=8) axis.set_rasterization_zorder(2) - fig, axes = canvas.plot(backend="matplotlib") + fig, axes = canvas.render(backend="matplotlib") assert len(axes[0, 0].texts) > 0 assert axes[0, 0].get_rasterization_zorder() == 2 @@ -694,7 +694,7 @@ def test_axis_layout_and_log_shortcuts_are_supported(): axis.set_xticklabels(["one", "two", "four"], rotation=30) axis.set_yticklabels(["low", "high"], color="navy") - fig, axes = canvas.plot(backend="matplotlib") + fig, axes = canvas.render(backend="matplotlib") matplotlib_axis = axes[0, 0] assert matplotlib_axis.get_xscale() == "log" @@ -718,7 +718,7 @@ def test_secondary_axes_are_supported_by_matplotlib(): "right", functions=(lambda y: y + 1, lambda y: y - 1), label="offset" ) - fig, axes = canvas.plot(backend="matplotlib") + fig, axes = canvas.render(backend="matplotlib") assert len(axes[0, 0].child_axes) == 2 plt.close(fig) @@ -734,7 +734,7 @@ def test_twiny_is_supported_by_matplotlib(): axis.plot([0, 1], [0, 1]) secondary.plot([10, 20], [0, 1], color="red") - fig, axes = canvas.plot(backend="matplotlib") + fig, axes = canvas.render(backend="matplotlib") assert canvas.twiny_axes[(0, 0)] is not axes[0, 0] plt.close(fig) @@ -759,7 +759,7 @@ def test_axis_state_setter_aliases_are_supported(): axis.set_xbound(-1, 2) axis.set_ybound(-2, 3) - fig, axes = canvas.plot(backend="matplotlib") + fig, axes = canvas.render(backend="matplotlib") matplotlib_axis = axes[0, 0] assert matplotlib_axis.get_frame_on() is False @@ -805,7 +805,7 @@ def test_generic_axis_setters_and_metadata_getters_are_supported(): axis.update({"aspect": "equal"}) axis.invert_xaxis() - fig, axes = canvas.plot(backend="matplotlib") + fig, axes = canvas.render(backend="matplotlib") matplotlib_axis = axes[0, 0] assert canvas.get_suptitle() == "Figure title" @@ -835,7 +835,7 @@ def test_figure_layout_helpers_and_aliases_are_supported(): canvas.align_ylabels() canvas.autofmt_xdate(rotation=20) - fig, axes = canvas.plot(backend="matplotlib") + fig, axes = canvas.render(backend="matplotlib") assert axes[0, 0].get_legend() is not None assert len(axes[0, 0].tables) == 1 @@ -913,6 +913,46 @@ def test_legacy_plot_backend_form_warns(): canvas.plot(backend="plotly") +def test_plot_many_adds_labeled_lines_and_returns_canvas(): + from maxplotlib import Canvas + + canvas = Canvas() + result = canvas.plot_many( + [([0, 1], [0, 1]), ([0, 1], [1, 0])], + labels=["up", "down"], + ) + + assert result is canvas + assert [line["kwargs"]["label"] for line in canvas._subplot_matrix[0][0].layered_line_data[0]] == [ + "up", + "down", + ] + + +def test_configure_applies_common_canvas_settings(): + from maxplotlib import Canvas + + canvas = Canvas() + canvas.plot([0, 1], [0, 1]).configure( + title="Example", + xlabel="Time", + ylabel="Value", + grid=True, + facecolor="whitesmoke", + xlim=(0, 2), + ylim=(-1, 2), + ) + + subplot = canvas._subplot_matrix[0][0] + assert canvas._suptitle == "Example" + assert canvas._supxlabel == "Time" + assert canvas._supylabel == "Value" + assert subplot._grid is True + assert subplot._facecolor == "whitesmoke" + assert (subplot._xmin, subplot._xmax) == (0, 2) + assert (subplot._ymin, subplot._ymax) == (-1, 2) + + def test_matplotlib_postprocess_can_customize_figure_and_axes(): import matplotlib.pyplot as plt from matplotlib.colors import to_rgba @@ -995,7 +1035,7 @@ def test_matplotlib_postprocess_rejects_non_matplotlib_backends(): from maxplotlib import Canvas with pytest.raises(ValueError, match="only supported with the matplotlib backend"): - Canvas().plot(backend="plotly", matplotlib_postprocess=lambda fig, axes: None) + Canvas().render(backend="plotly", matplotlib_postprocess=lambda fig, axes: None) def test_show_canvas_script_invokes_canvas_show(monkeypatch): diff --git a/src/maxplotlib/tests/test_plot.py b/src/maxplotlib/tests/test_plot.py index 7667ed3..3e65d65 100644 --- a/src/maxplotlib/tests/test_plot.py +++ b/src/maxplotlib/tests/test_plot.py @@ -19,7 +19,7 @@ def test_python_example_nxm_line_subplots_spacing_changes(): for i, row in enumerate(tight_axes): for j, ax in enumerate(row): ax.plot(x, np.sin((i + 1) * (j + 1) * x)) - tight_fig, tight_m_axes = tight_canvas.plot(backend="matplotlib") + tight_fig, tight_m_axes = tight_canvas.render(backend="matplotlib") tight_hgap = ( tight_m_axes[0, 1].get_position().x0 - tight_m_axes[0, 0].get_position().x1 ) @@ -38,7 +38,7 @@ def test_python_example_nxm_line_subplots_spacing_changes(): for i, row in enumerate(loose_axes): for j, ax in enumerate(row): ax.plot(x, np.sin((i + 1) * (j + 1) * x)) - loose_fig, loose_m_axes = loose_canvas.plot(backend="matplotlib") + loose_fig, loose_m_axes = loose_canvas.render(backend="matplotlib") loose_hgap = ( loose_m_axes[0, 1].get_position().x0 - loose_m_axes[0, 0].get_position().x1 ) @@ -69,7 +69,7 @@ def test_python_example_nxm_color_subplots_spacing_changes(): for ax in row: ax.add_imshow(base + idx, cmap="viridis") idx += 1 - tight_fig, tight_m_axes = tight_canvas.plot(backend="matplotlib") + tight_fig, tight_m_axes = tight_canvas.render(backend="matplotlib") tight_hgap = ( tight_m_axes[0, 1].get_position().x0 - tight_m_axes[0, 0].get_position().x1 ) @@ -90,7 +90,7 @@ def test_python_example_nxm_color_subplots_spacing_changes(): for ax in row: ax.add_imshow(base + idx, cmap="viridis") idx += 1 - loose_fig, loose_m_axes = loose_canvas.plot(backend="matplotlib") + loose_fig, loose_m_axes = loose_canvas.render(backend="matplotlib") loose_hgap = ( loose_m_axes[0, 1].get_position().x0 - loose_m_axes[0, 0].get_position().x1 ) diff --git a/src/maxplotlib/tests/test_plotext.py b/src/maxplotlib/tests/test_plotext.py index ee34a63..a43b6fd 100644 --- a/src/maxplotlib/tests/test_plotext.py +++ b/src/maxplotlib/tests/test_plotext.py @@ -27,7 +27,7 @@ def test_canvas_plot_plotext_builds_terminal_output(): ax.set_legend(True) canvas.suptitle("Plotext demo") - figure = canvas.plot(backend="plotext") + figure = canvas.render(backend="plotext") output = strip_ansi(figure.build()) assert isinstance(figure, PlotextFigure) @@ -57,7 +57,7 @@ def test_canvas_plot_plotext_supports_scalar_errorbars(): ax.set_xscale("log") ax.set_title("Log errors") - output = strip_ansi(canvas.plot(backend="plotext").build()) + output = strip_ansi(canvas.render(backend="plotext").build()) assert "Log errors" in output @@ -72,7 +72,7 @@ def test_canvas_plot_plotext_supports_fill_between_curves_and_annotations(): ax.set_title("Filled band") ax.set_legend(True) - output = strip_ansi(canvas.plot(backend="plotext").build()) + output = strip_ansi(canvas.render(backend="plotext").build()) assert "Filled band" in output assert "band" in output @@ -88,7 +88,7 @@ def test_canvas_plot_plotext_supports_matrix_plots_and_patches(): ax.add_patch(mpatches.Circle((1.8, 1.8), 0.4, fill=False, edgecolor="cyan")) ax.set_title("Matrix plot") - output = strip_ansi(canvas.plot(backend="plotext").build()) + output = strip_ansi(canvas.render(backend="plotext").build()) assert "Matrix plot" in output @@ -98,7 +98,7 @@ def test_canvas_plot_plotext_supports_colorbar_notes_symlog_aspect_and_generic_p ax.add_imshow(np.eye(3)) ax.add_colorbar(label="scale") ax.set_title("Heatmap") - output = strip_ansi(canvas.plot(backend="plotext").build()) + output = strip_ansi(canvas.render(backend="plotext").build()) assert "Heatmap" in output assert "scale:" in output @@ -111,7 +111,7 @@ def test_canvas_plot_plotext_supports_colorbar_notes_symlog_aspect_and_generic_p ax.set_aspect("equal") ax.add_caption("caption text") ax.set_title("Symlog view") - output = strip_ansi(canvas.plot(backend="plotext").build()) + output = strip_ansi(canvas.render(backend="plotext").build()) assert "Symlog view" in output assert "caption text" in output @@ -129,7 +129,7 @@ def test_canvas_plot_plotext_supports_colorbar_notes_symlog_aspect_and_generic_p ) ax.set_title("Generic patch") ax.set_legend(True) - output = strip_ansi(canvas.plot(backend="plotext").build()) + output = strip_ansi(canvas.render(backend="plotext").build()) assert "Generic patch" in output assert "ellipse" in output diff --git a/src/maxplotlib/tests/test_plotly_backend.py b/src/maxplotlib/tests/test_plotly_backend.py index 9fde583..8b0f3a9 100644 --- a/src/maxplotlib/tests/test_plotly_backend.py +++ b/src/maxplotlib/tests/test_plotly_backend.py @@ -18,7 +18,7 @@ def test_plotly_backend_supports_common_primitives(): ax.set_grid(True) ax.set_legend(True) - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert fig is not None assert len(fig.data) >= 4 # line, scatter, errorbar, fill_between @@ -35,7 +35,7 @@ def test_plotly_backend_supports_tick_label_rotation(): axis.plot([0, 1], [0, 1]) axis.set_xticks([0, 1], labels=["zero", "one"], rotation=45) - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert fig.layout.xaxis.tickangle == 45 @@ -49,7 +49,7 @@ def test_plotly_backend_supports_twinx(): secondary.plot([0, 1], [10, 20], color="red") secondary.set_ylabel("Secondary") - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert len(fig.data) == 2 assert fig.layout.yaxis2.title.text == "Secondary" @@ -66,7 +66,7 @@ def test_plotly_backend_supports_common_added_primitives(): axis.axhspan(0.25, 0.75, alpha=0.1) axis.arrow(0, 0, 1, 1) - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert len(fig.data) >= 3 assert len(fig.layout.shapes) >= 2 @@ -82,7 +82,7 @@ def test_plotly_backend_supports_step_stairs_broken_barh_and_pie(): axis.broken_barh([(0, 1), (2, 0.5)], (0, 0.5)) axis.pie([2, 3, 4], labels=["A", "B", "C"]) - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert any(trace.type == "pie" for trace in fig.data) assert len(fig.data) >= 5 @@ -98,7 +98,7 @@ def test_plotly_backend_supports_statistical_and_event_plots(): axis.violinplot([[1, 2, 3], [2, 4, 5]]) axis.eventplot([[0.2, 0.5], [1.0, 1.5]]) - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert any(trace.type == "box" for trace in fig.data) assert any(trace.type == "violin" for trace in fig.data) @@ -122,7 +122,7 @@ def test_plotly_backend_supports_scientific_field_plots(): axis.hexbin(xx.ravel(), yy.ravel(), gridsize=5) axis.matshow(z) - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert any(trace.type == "contour" for trace in fig.data) assert any(trace.type == "heatmap" for trace in fig.data) @@ -138,7 +138,7 @@ def test_plotly_backend_supports_contour_labels(): axis.contour(x, x, xx**2 + yy**2) axis.clabel(fontsize=10, color="black") - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert fig.data[0].contours.showlabels is True assert fig.data[0].contours.labelfont.size == 10 @@ -152,7 +152,7 @@ def test_plotly_backend_supports_fill_log_scales_and_ticklabels(): axis.loglog([1, 2, 4], [1, 4, 16]) axis.set_xticklabels(["one", "two", "four"], color="navy") - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert any(trace.fill == "toself" for trace in fig.data) assert fig.layout.xaxis.type == "log" @@ -168,8 +168,8 @@ def test_plotly_backend_respects_layers(): ax.plot(x, x, color="black", label="L0", layer=0) ax.plot(x, x**2, color="red", label="L1", layer=1) - fig0 = canvas.plot(backend="plotly", layers=[0]) - fig1 = canvas.plot(backend="plotly", layers=[1]) + fig0 = canvas.render(backend="plotly", layers=[0]) + fig1 = canvas.render(backend="plotly", layers=[1]) assert len(fig0.data) == 1 assert len(fig1.data) == 1 @@ -203,7 +203,7 @@ def test_plotly_backend_supports_common_patches_and_symlog(): ax.set_title("patches") ax.set_legend(True) - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert fig is not None assert len(getattr(fig.layout, "shapes", []) or []) >= 4 # patch labels become dummy legend traces @@ -214,7 +214,7 @@ def test_plotly_backend_supports_common_patches_and_symlog(): ax2.plot(x, x**3, color="cyan", label="x^3") ax2.set_xscale("symlog") ax2.set_yscale("symlog") - fig2 = canvas2.plot(backend="plotly") + fig2 = canvas2.render(backend="plotly") assert fig2 is not None @@ -230,7 +230,7 @@ def test_plotly_backend_renders_mixed_vector_primitives(): np.ones((3, 3)), ) - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert len(fig.data) > 1 @@ -241,7 +241,7 @@ def test_plotly_backend_supports_bar_labels(): axis.bar([0, 1], [2, 3]) axis.bar_label(fmt="%d") - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") labels = [annotation.text for annotation in fig.layout.annotations] assert labels[-2:] == ["2", "3"] @@ -264,7 +264,7 @@ def test_plotly_backend_supports_pseudocolor_spy_table_and_triplot(): axis.table(cellText=[["A", "B"], ["1", "2"]]) axis.triplot(points_x, points_y, triangles=triangles) - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert sum(trace.type == "heatmap" for trace in fig.data) >= 3 assert any(trace.type == "table" for trace in fig.data) @@ -277,7 +277,7 @@ def test_plotly_backend_supports_quiver(): canvas, axis = Canvas.subplots() axis.quiver([0, 1], [0, 1], [1, -1], [1, 1], color="purple", alpha=0.5) - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") arrows = [ annotation for annotation in fig.layout.annotations if annotation.showarrow @@ -297,7 +297,7 @@ def test_plotly_backend_supports_tripcolor(): triangles=[[0, 1, 2], [1, 3, 2]], ) - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert sum(trace.fill == "toself" for trace in fig.data) == 2 @@ -313,7 +313,7 @@ def test_plotly_backend_supports_triangulated_contours(): axis.tricontour(x, y, values, triangles=triangles, levels=3) axis.tricontourf(x, y, values, triangles=triangles, levels=3) - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert any(trace.fill is None for trace in fig.data) assert any(trace.fill == "toself" for trace in fig.data) @@ -332,7 +332,7 @@ def test_plotly_backend_supports_streamplot(): color="darkgreen", ) - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert len(fig.data) > 0 assert all(trace.type == "scatter" for trace in fig.data) From 707555edf12d0a082c7bc70adfb23b44fd980f7a Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 22 Aug 2026 14:45:00 +0200 Subject: [PATCH 2/2] fmt --- src/maxplotlib/tests/test_canvas.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/maxplotlib/tests/test_canvas.py b/src/maxplotlib/tests/test_canvas.py index 423c0c4..8a6de64 100644 --- a/src/maxplotlib/tests/test_canvas.py +++ b/src/maxplotlib/tests/test_canvas.py @@ -923,7 +923,10 @@ def test_plot_many_adds_labeled_lines_and_returns_canvas(): ) assert result is canvas - assert [line["kwargs"]["label"] for line in canvas._subplot_matrix[0][0].layered_line_data[0]] == [ + assert [ + line["kwargs"]["label"] + for line in canvas._subplot_matrix[0][0].layered_line_data[0] + ] == [ "up", "down", ]