28

Is there a way to SetOptions locally? For example, I have a Module and inside it I am doing a lot of Plots with similar options. If I do a SetOptions[Plot, ...] inside the Module, the change propagates outside. I don't like this because I have other Modules where I am doing other plots with different options.

Is there a way to do a SetOptions[Plot, ...] that only affects "local" plots (say inside a Module, but I am open to any scoping construct)? Here Plot is only an example. In general I want to set options locally for any symbol.

a06e
  • 11,327
  • 4
  • 48
  • 108

4 Answers4

26

Version 12.2

In version 12.2, use the new function WithCleanup[]:

With[{plotOptions = Options[Plot]}, 
     WithCleanup[SetOptions[Plot, PlotStyle -> Green], 
                 Plot[Sin[x], {x, -π, π}],
                 SetOptions[Plot, plotOptions]]]

which is a direct replacement of the older undocumented function described below.


Older versions

Usual caveats about using undocumented functions aside, here is how one might use Internal`WithLocalSettings[]:

With[{plotOptions = Options[Plot]}, 
     Internal`WithLocalSettings[SetOptions[Plot, PlotStyle -> Green], 
                                Plot[Sin[x], {x, -π, π}],
                                SetOptions[Plot, plotOptions]]]

but I do not think this to be any better than Nasser's proposal. As can be surmised from how it was used above, you can think of the three arguments of Internal`WithLocalSettings[] as three stages: setup, body, and clean-up. Carl notes in a comment below that the advantage of using this function is that any code in the first and third arguments are uninterruptible.

A more usual case for its use would be for localizing changes to system settings that are not easily accessible to SetOptions[]. Using the Wizard's code from here as an example:

With[{spopt = SystemOptions["SparseArrayOptions"]},
     Internal`WithLocalSettings[
              SetSystemOptions["SparseArrayOptions" -> {"TreatRepeatedEntries" -> 1}],
              ind = {{3, 1}, {3, 3}, {1, 3}, {2, 1}, {3, 2}, {3, 1},
                     {3, 2}, {3, 3}, {1, 3}, {3, 1}};
              val = {1, 1, 3, 0, 3, 4, 3, 1, 1, 1};
              SparseArray[ind -> val] // Normal,
              SetSystemOptions[spopt]]]
   {{0, 0, 4}, {0, 0, 0}, {6, 6, 2}}

(See also this SO thread and this example usage by Oleksandr.)

J. M.'s missing motivation
  • 124,525
  • 11
  • 401
  • 574
  • 9
    I would say using Internal\WithLocalSettingsis better than usingModule, because the first and third arguments ofInternal`WithLocalSettingsare not interruptible, and always happen. If you add aPause[10]to Nasser's example and abort the computation, you will see that thePlotStyleremainsGreen`. Also, it's convenient that the second argument is the output, so one doesn't need to store the result so that it can be returned as the output after the cleanup occurs. – Carl Woll Jan 23 '17 at 17:11
  • "Also, it's convenient that the second argument is the output, so one doesn't need to store the result so that it can be returned as the output after the cleanup occurs." - that was certainly a convenient feature for me, but I definitely didn't know about the non-interruptibility. Thanks @Carl! – J. M.'s missing motivation Jan 23 '17 at 17:22
  • @CarlWoll Maybe for you but if it eventually break your app you can't even complain because you used undocumented function. (the point here is to push it to System`​ since everyone is using it anyway). – Kuba Jan 24 '17 at 06:32
17

I have used several methods, including the ones in the other answers. I have found that the simplest method is Internal`InheritedBlock as it allows for temporary changes to a symbol to be made, including changes to Options. For example,

Internal`InheritedBlock[{Plot},
  SetOptions[Plot, PlotStyle -> Red, Frame -> True];
  Plot[Sin[x], {x, 0, 2 Pi}]
]
Plot[Sin[x], {x, 0, 2 Pi}]

enter image description here

Obviously, this has the most utility when you are making multiple plots, but this illustrates the point.

rcollyer
  • 33,976
  • 7
  • 92
  • 191
  • 2
    Why don't you (not you personally ofc) document them then? Just add where it would not be a good idea to use it, if there are any problems. It is not like System`​ is perfect anyway. – Kuba Jan 24 '17 at 06:30
  • 1
    @Kuba not unreasonable, but not my department. – rcollyer Jan 24 '17 at 12:41
  • 1
    I have a particular project in which I want to change the options of a function locally in a ScheduledTaskObject without the options change altering subsequent calls in the FrontEnd. This solution seems to do the trick for me. – bobthechemist Apr 13 '17 at 14:07
12

Just define your options as sequence held in a local variable.

Module[{opts = Sequence[PlotStyle -> Red, Frame -> True]},
  Plot[Sin[2 π x], {x, 0, 1}, Evaluate @ opts]]

plot

m_goldberg
  • 107,779
  • 16
  • 103
  • 257
10

This is a simple solution. The idea is to save the Plot options on entry, and restore it on leaving the module to whatever it was

f := Module[{savedOpt = Options[Plot]},
  SetOptions[Plot, PlotStyle -> Green];
  Print@Plot[Sin[x], {x, -Pi, Pi}];
  SetOptions[Plot, savedOpt]
  ]

Now global Plot options are not changed. Test:

Mathematica graphics

Nasser
  • 143,286
  • 11
  • 154
  • 359