Opened 3 years ago
Last modified 3 years ago
#25165 new defect
Showing a plot overwrites matplotlib global options
Reported by:  mmezzarobba  Owned by:  

Priority:  major  Milestone:  sage8.2 
Component:  graphics  Keywords:  
Cc:  strogdon  Merged in:  
Authors:  Reviewers:  
Report Upstream:  N/A  Work issues:  
Branch:  Commit:  
Dependencies:  Stopgaps: 
Description
sage: from matplotlib import rcParams sage: rcParams['font.family'] = 'serif' sage: p = plot(sin, 0, 1) sage: rcParams['font.family'] [u'serif'] sage: p.show() # or save(), or matplotlib() Launched png viewer for Graphics object consisting of 1 graphics primitive sage: rcParams['font.family'] [u'sansserif']
I think this is because Graphics.matplotlib()
always loads a matplotlib stylesheet—usually 'classic'
—, but I'm not sure what the right thing to do would be.
Change History (8)
comment:1 Changed 3 years ago by
 Cc strogdon added
comment:2 Changed 3 years ago by
comment:3 Changed 3 years ago by
Yes, prior to commit d109b7593e user changes via rcParams[]
were not altered after calls to matplotlib
.
comment:4 Changed 3 years ago by
I had not tested that particular scenario I must say. I thought it would work, there may be an ordering issue. One of the issue that lead to the inclusion of that style in matplotlib calls is that the default style changed between 1.5.x and 2.1.x. I got comments that plots after the upgrade to 2.1.x were not looking very good.
Including the style restored the default of 1.5.x and restored the general quality of the plots.
In effect I have been torn between importing a style sheet before every graphics call and documenting it somewhere that you needed it for nice plots (how many graphics calls for the documentation? A few hundreds probably). Or just applying it blindly.
Anyway, I'll have to dig that code again to see what is the right thing to do to override style sheet options. I added the option to change the style sheet but you want something more fine grained than that.
comment:5 Changed 3 years ago by
I really like the option to specify a style sheet, it is a great way to give more control to the user on the appearance of the plots at a low development effort and without duplicating the functionality of matplotlib. And it could be enough to document prominently that this is the recommended way to set matplotlib options in sage (at the moment, there are several answers on ask.sage that suggest using matplotlib.rc
or similar).
comment:6 Changed 3 years ago by
We could keep track of user changes. Something like

src/sage/plot/graphics.py
diff git a/src/sage/plot/graphics.py b/src/sage/plot/graphics.py index 58ece044f6..8d68b0a681 100644
a b class Graphics(WithEqualityById, SageObject): 2527 2527 if not isinstance(ticks, (list, tuple)): 2528 2528 ticks = (ticks, None) 2529 2529 2530 from matplotlib import rcParams, style 2531 2532 # save rcParams changed by the user 2533 userdefaults = rcParams.items() 2534 style.use('default') 2535 l = [] 2536 for i in range(0, len(userdefaults)): 2537 if userdefaults[i] != rcParams.items()[i]: 2538 l.append(userdefaults[i]) 2539 2530 2540 import matplotlib.pyplot as plt 2531 2541 if stylesheet not in plt.style.available: 2532 2542 stylesheet = 'classic' 2533 2543 plt.style.use(stylesheet) 2544 # add user changed rcParams to current stylesheet 2545 for i in range(0, len(l)): 2546 rcParams[l[i][0]] = l[i][1] 2534 2547 2535 2548 from sage.symbolic.ring import SR 2536 2549 if not isinstance(tick_formatter, (list, tuple)): # make sure both formatters typeset or both don't … … class Graphics(WithEqualityById, SageObject): 3005 3018 for g in self._objects: 3006 3019 g.set_options(old_opts[g]) 3007 3020 3021 # switch to the 'default' stylesheet and add back user changed rcParams 3022 style.use('default') 3023 for i in range(0, len(l)): 3024 rcParams[l[i][0]] = l[i][1] 3025 3008 3026 return figure 3009 3027 3010 3028 def save_image(self, filename=None, *args, **kwds):
I'm not sure it is robust enough, particularly with how the user changes are added in. And it will increase overhead if there are a lot of user changes. Before the patch
sage: from matplotlib import rcParams sage: rcParams['font.size'] 10.0 sage: p = plot(sin, 0, 1) sage: p.matplotlib() <matplotlib.figure.Figure object at 0x7f76037d2490> sage: rcParams['font.size'] 12.0
and after the patch
sage: from matplotlib import rcParams sage: rcParams['font.size'] 10.0 sage: p = plot(sin, 0, 1) sage: p.matplotlib() <matplotlib.figure.Figure object at 0x7f1928dff410> sage: rcParams['font.size'] 10.0
comment:7 Changed 3 years ago by
This
rcParams[l[i][0]] = l[i][1]
is not correct in all cases, in particular the l[i][1]
. But at the least we should restore the defaults.
comment:8 Changed 3 years ago by
Well, may be better than I thought
sage: from matplotlib import rcParams, rc sage: rc('font', size=9.0, family='serif') sage: rcParams['font.family'] [u'serif'] sage: rcParams['font.size'] 9.0 sage: p = plot(sin, 0, 1) sage: p.matplotlib() <matplotlib.figure.Figure object at 0x7f7ff24a2350> sage: rcParams['font.family'] [u'serif'] sage: rcParams['font.size'] 9.0
Probably any of the defaults in
matplotlib/mpldata/stylelib/classic.mplstyle
if changed throughrcParams[]
will be overwritten ifmatplotlib
is called. I don't know what would have happened prior to thematplotlib
upgrade.