TransWikia.com

How can I work out which functions work with SetOptions?

Mathematica Asked by Mike Honeychurch on July 29, 2021

Not all functions seem to work with SetOptions. e.g.

SetOptions[Grid, BaseStyle -> Directive[Red]];
Grid[{{"hello", "world"}}]

hello world

the font is not red.

SetOptions[Row, BaseStyle -> Directive[Red]];
Row[{"hello", "world"}]

hello world

…and the font is red.

enter image description here

SetOptions[InputField, FieldSize -> 5];
InputField[Dynamic[x]]

the input field size is much larger than 5. But on the other hand

InputField[Dynamic[x], Sequence @@ Options[InputField]]

yields an input field with field size 5.

enter image description here

…and so on.

What is the easiest way to work out (i.e. make a list of …) which functions can’t be used with SetOptions?

5 Answers

As noted in the question, when you set an option to a function which appears not to work with SetOptions the options do get set, e.g. from the question:

InputField[Dynamic[x], Sequence @@ Options[InputField]]

but for whatever reason the global setting does not get used locally by default.

Another interesting case is this:

SetOptions[Grid, Background -> RGBColor[1, 0, 0]]

which returns a list of Grid options with the new Background, yet when you do the same with GridBox

SetOptions[GridBox, Background -> RGBColor[1, 0, 0]]
SetOptions::nspt: "SetOptions of GridBox is not supported"

So testing the actual "box form" seems to highlight the problem. In this case GridBox is not supported for SetOptions even though Grid did not return an error. So the following solution is a variation of what was provided to me by tech support. Check can be used to return something in the case of a SetOptions error:

Check[SetOptions[GridBox, "a" -> "b"], err, SetOptions::nspt]

SetOptions::nspt: "SetOptions of GridBox is not supported."
err

First get a list of names:

names = Names["System`*"]

test the names for the SetOptions error

list = Quiet[Map[Check[SetOptions[ToExpression[#], "a" -> "b"]; Null, #, 
      SetOptions::nspt] &, names]];

delete the Null elements

DeleteCases[list, Null]

{ActionMenuBox,AnimatorBox,CheckboxBox,ColorSetterBox,CounterBox,DynamicBox,
DynamicModuleBox,DynamicWrapperBox,GridBox,InputFieldBox,ItemBox,Line3DBox,LineBox,
LocatorBox,LocatorPaneBox,OpenerBox,OptionValueBox,OverlayBox,PaneBox,PanelBox,
PaneSelectorBox,Point3DBox,PointBox,PopupMenuBox,ProgressIndicatorBox,RadioButtonBox,
RectangleBox,RotationBox,SetterBox,Slider2DBox,SliderBox,TabViewBox,TemplateBox,
TogglerBox,TooltipBox,ValueBox}

So we have a list of 36 types of boxes that cannot be used with SetOptions.

Correct answer by Mike Honeychurch on July 29, 2021

I don't know the direct answer to the specific question on SetOptions, but if we look at the purpose of (re)setting options globally, I have some alternative suggestion. A need to set options globally means that you need some persistent configuration of options which you'd like to be applied many times, without extra effort on your side. This can be achieved by creating such option(s) configuration and then always passing options locally (explicitly). It is possible to create helper functions / macros, which would automate this process for you and make it look and feel (almost) as if you have set your options globally.

I have implemented a simplistic options configuration manager, and a lexically scoped construct withOptionConfiguration, which can be wrapped around your code containing a function call of interest. One can also implement dynamically-scoped environments, for which the option-passing will happen also for all code called from the code within a construct. To my mind, this will save a lot of hassle even if / when you get the exhaustive answer to your direct question, since with the approach I suggest, you don't have at all to remember which functions work with SetOptions and which don't.

Answered by Leonid Shifrin on July 29, 2021

I think the reason for this is that many functions pass their options to other functions that they call during evaluation. See for example this answer to a related question on StackOverflow. So you can't set the option for the function you use, only the function that the option is passed to.

So for example, in one of my packages (you can guess which one, I have of course changed the initials of my employer to "XYZ") I have a function that starts with the definition:

XYZLineGraph[data:{{__?NumericQ} ..}, 
dates_List, opts:OptionsPattern[{XYZLineGraph, 
 DateListPlot, XYZDateTicks, XYZTickGrid, GraphNotesGrid}]] := (* more here *)

I cannot set the defaults for XYZLineGraph that are actually defaults of DateListPlot using SetOptions[XYZLineGraph].

An example:

test = FinancialData["AAPL", {2008}];
SetOptions[XYZLineGraph, BaseStyle -> Directive[Red]];
XYZLineGraph[test]  (* not red *)

enter image description here

SetOptions[DateListPlot, BaseStyle -> Directive[Red]];
XYZLineGraph[test]  

(* still not red, because I set the relevant defaults separately *)

DateListPlot[test]  (* is red *)

enter image description here

As for identifying which functions can have SetOptions work this way, I suspect the only way you could do this for built-in functions would be a laborious use of Trace.

Answered by Verbeia on July 29, 2021

I believe this is another problem caused by the significant change of graphics functionality between versions 5 and 6. (As to other problems, check post about AbsoluteOptions and post about FullGraphics.)

A key evidence is Text. As mentioned by Kuba, this is a function that doesn't call those functions mentioned in Mike's answer, yet SetOptions still fails on it, for example:

$Version

SetOptions[Text, Background -> RGBColor[1, 0, 0]];
Text["A", {0, 0}] // Graphics

enter image description here

But it's not the case in v5:

enter image description here

BTW, you may think you can reproduce the old behavior with

<<Version5`Graphics`

but unfortunately it's buggy in this case:

enter image description here

So, functions related to graphics added or modified in or after v6 are likely to be influenced, but I can't think out a systematic way to test my guess at the moment.

Answered by xzczd on July 29, 2021

I expanded upon Mike Honeychurch's excellent solution by generalizing his solution into a general-purpose "Option Dictionary" creator. It includes his use of Check to indicate whether you are allowed to use a SetOptions on a given symbol, but it also includes a formatted list of all the available options, not cluttered with the current default values.

The net output is formatted in a Grid-friendly manner, but need not be used with a Grid. For example, you can use the raw List output with no arguments to generate an option dictionary for all of Mathematica, then do things like use Cases or Position to search for some particular "child option" that exists in any symbol in Mathematica.

Here is the code:

(* HoldComplete needed -- TemplateSequence options "DefaultValue" and "Delimiters" contain Sequence[] *)

FreeRulesQ = 
  Function[expr, FreeQ[Unevaluated@expr, _Rule | _RuleDelayed], 
   HoldAllComplete];

RulesToOptionList[rules_List] := Replace[
  Replace[
   ReplaceAll[
    rules,
    {Rule[opt_, val_?(FreeRulesQ)] :> opt, 
     RuleDelayed[opt_, val_?(FreeRulesQ)] :> opt}
    ],
   {Rule[opt_, val_] | RuleDelayed[opt_, val_] :> {opt, val}},
   {1, Infinity}
   ],
  opt : Except[_List] :> {opt, {}},
  {1}
  ]

SetOptionsQ = 
  Function[symbol, 
   Quiet@Check[SetOptions[ReleaseHold@symbol, {}]; True, False], 
   Listable];

With[{symbolListDefault = MakeExpression@Names["System`*"]},
 OptionsDictionary[HoldPattern[optionList_ : Sequence[]], 
   symbolList_List : symbolListDefault] :=
  
  With[{optionsForSymbols = 
     Quiet@ReleaseHold@
       Thread@Hold[Options][symbolList, Hold@optionList]},
   Transpose@
      {symbolList[[#]] /. HoldComplete -> HoldForm, 
       RulesToOptionList@optionsForSymbols[[#]], 
       SetOptionsQ@symbolList[[#]]} &@
    Flatten@Position[optionsForSymbols, {__}, {1}]
   ]
 ]

Usage:

OptionsDictionary[{ContentPadding, ImageSize}] // Grid
OptionsDictionary[GeneratedCellStyles] // Grid

Output:

enter image description here

enter image description here

As a bonus (off-topic a bit), here are a couple helper utilities that I found useful in visualizing the data structure while I was developing this:

LevelFormatted = Function[{expr, level, patternTest}, {
    level,
    Row@Map[
       Framed[patternTest@First@#, 
         Background -> If[level == 0, None, Last@#]] &]@
      MapIndexed[{#1, GrayLevel[.3, 0.2 - 0.1*Mod[First@#2, 2]]} &]@
       Level[expr, {level}]
    },
   Listable
   ];

LevelGrid = Function[{expr, patternTest},
   Grid[#, Frame -> All, Background -> {
        {1 -> LightYellow},
        {{{LightBlue, None}}, Depth[expr] + 3 -> LightYellow}
        }] &@
    Prepend[{Style[ToString@Unevaluated@expr, Bold], SpanFromLeft}]@
     LevelFormatted[Unevaluated@expr, 
      Range[-Depth[expr] - 1, Depth[expr]], patternTest],
   HoldAllComplete
   ];

RuleLevels = 
  Function[expr, 
   LevelGrid[expr, Row@{Framed@#, Framed@FreeQ[_Rule]@#} &], HoldAll];

Answered by Sean on July 29, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP