@@ -1176,10 +1176,86 @@ def add_annotation(self, text: str, xy: Tuple[float, float],
11761176 self ._refresh ()
11771177
11781178 def apply_theme (self , theme_name : str ):
1179- """Apply a theme (StatsPAI, matplotlib, or seaborn) and record it."""
1179+ """Apply a theme (StatsPAI, matplotlib, or seaborn) to the live figure.
1180+
1181+ Unlike ``set_theme()`` alone (which only sets global rcParams for
1182+ *future* plots), this method walks through existing figure artists
1183+ and retroactively applies the new theme settings so the current
1184+ figure visually updates immediately.
1185+ """
1186+ import matplotlib as mpl
11801187 from .themes import set_theme
1181- set_theme (theme_name ) # handles all three sources
1182- self .fig .canvas .draw_idle ()
1188+
1189+ set_theme (theme_name ) # update global rcParams
1190+ rc = mpl .rcParams
1191+
1192+ # --- Apply rcParams to existing figure artists ---
1193+ fig = self .fig
1194+
1195+ # Figure-level
1196+ fig .set_facecolor (rc ['figure.facecolor' ])
1197+
1198+ for ax in fig .get_axes ():
1199+ # Axes background
1200+ ax .set_facecolor (rc ['axes.facecolor' ])
1201+
1202+ # Spines
1203+ for spine_name , spine in ax .spines .items ():
1204+ visible_key = f'axes.spines.{ spine_name } '
1205+ if visible_key in rc :
1206+ spine .set_visible (rc [visible_key ])
1207+ spine .set_linewidth (rc .get ('axes.linewidth' , 0.8 ))
1208+ if 'axes.edgecolor' in rc :
1209+ spine .set_edgecolor (rc ['axes.edgecolor' ])
1210+
1211+ # Grid
1212+ ax .grid (rc .get ('axes.grid' , False ),
1213+ color = rc .get ('grid.color' , '#b0b0b0' ),
1214+ linewidth = rc .get ('grid.linewidth' , 0.8 ),
1215+ alpha = rc .get ('grid.alpha' , 1.0 ))
1216+
1217+ # Title and labels — update font properties
1218+ ax .title .set_fontsize (rc .get ('axes.titlesize' , 12 ))
1219+ ax .xaxis .label .set_fontsize (rc .get ('axes.labelsize' , 11 ))
1220+ ax .yaxis .label .set_fontsize (rc .get ('axes.labelsize' , 11 ))
1221+
1222+ for lbl in ax .get_xticklabels ():
1223+ lbl .set_fontsize (rc .get ('xtick.labelsize' , 10 ))
1224+ for lbl in ax .get_yticklabels ():
1225+ lbl .set_fontsize (rc .get ('ytick.labelsize' , 10 ))
1226+
1227+ # Tick direction
1228+ ax .tick_params (axis = 'x' ,
1229+ direction = rc .get ('xtick.direction' , 'out' ))
1230+ ax .tick_params (axis = 'y' ,
1231+ direction = rc .get ('ytick.direction' , 'out' ))
1232+
1233+ # Legend
1234+ leg = ax .get_legend ()
1235+ if leg :
1236+ leg .set_frame_on (rc .get ('legend.frameon' , True ))
1237+ for txt in leg .get_texts ():
1238+ txt .set_fontsize (rc .get ('legend.fontsize' , 9 ))
1239+
1240+ # Lines — update width
1241+ for line in ax .get_lines ():
1242+ line .set_linewidth (rc .get ('lines.linewidth' , 1.5 ))
1243+
1244+ # Color cycle for existing line/collection colors
1245+ prop_cycle = rc .get ('axes.prop_cycle' )
1246+ if prop_cycle is not None :
1247+ colors = [p .get ('color' , None ) for p in prop_cycle ]
1248+ colors = [c for c in colors if c is not None ]
1249+ if colors :
1250+ for i , line in enumerate (ax .get_lines ()):
1251+ line .set_color (colors [i % len (colors )])
1252+
1253+ # Font family
1254+ font_family = rc .get ('font.family' , 'sans-serif' )
1255+ for txt_obj in [ax .title , ax .xaxis .label , ax .yaxis .label ]:
1256+ txt_obj .set_fontfamily (font_family )
1257+
1258+ self ._refresh ()
11831259 self .edits .append (EditRecord (
11841260 'theme' , 'name' , None , theme_name ,
11851261 f"sp.set_theme({ theme_name !r} )" ))
0 commit comments