2

I'm trying to redefine an option of the PlotLegends package after having loaded it, but I get for example

Needs["PlotLegends`"]
SetOptions[ListPlot,LegendPosition->{0,0.5}]
=> SetOptions::optnf: LegendPosition is not a known option for ListPlot.

I expect such a thing as the options in the PlotLegends package aren't built-in to Plot and ListPlot.

Is there a way to redefine the default options of the PlotLegends package?

Sjoerd C. de Vries
  • 16,122
  • 3
  • 42
  • 94
faysou
  • 1,142
  • 11
  • 25
  • 2
    The only correct answer here is to **not** use the `PlotLegends` package. See [this](http://stackoverflow.com/questions/7221315/how-do-i-label-different-curves-in-mathematica) question for an alternative. – abcd Sep 23 '11 at 14:04

3 Answers3

2

The problem is not really in the defaults for PlotLegends`. To see it, you should inspect the ListPlot implementation:

In[28]:= Needs["PlotLegends`"]
In[50]:= DownValues[ListPlot]
Out[50]=    
{HoldPattern[ListPlot[PlotLegends`Private`a:PatternSequence[___,
    Except[_?OptionQ]]|PatternSequence[],PlotLegends`Private`opts__?OptionQ]]:>
  PlotLegends`Private`legendListPlot[ListPlot,PlotLegends`Private`a,
    PlotLegend/.Flatten[{PlotLegends`Private`opts}],PlotLegends`Private`opts] 
      /;!FreeQ[Flatten[{PlotLegends`Private`opts}],PlotLegend]}

What you see from here is that options must be passed explicitly for it to work, and moreover, PlotLegend option must be present.

One way to achieve what you want is to use my option configuration manager, which imitates global options by passing local ones. Here is a version where option-filtering is made optional:

ClearAll[setOptionConfiguration, getOptionConfiguration, withOptionConfiguration];
SetAttributes[withOptionConfiguration, HoldFirst];
Module[{optionConfiguration}, optionConfiguration[_][_] = {};
   setOptionConfiguration[f_, tag_, {opts___?OptionQ}, filterQ : (True | False) : True] :=
      optionConfiguration[f][tag] = 
         If[filterQ, FilterRules[{opts}, Options[f]], {opts}];
   getOptionConfiguration[f_, tag_] := optionConfiguration[f][tag];
   withOptionConfiguration[f_[args___], tag_] := 
        f[args, Sequence @@ optionConfiguration[f][tag]];
];

To use this, first define your configuration and a short-cut macro, as follows:

setOptionConfiguration[ListPlot,"myConfig", {LegendPosition -> {0.8, -0.8}}, False];
withMyConfig =   Function[code, withOptionConfiguration[code, "myConfig"], HoldAll];

Now, here you go:

withMyConfig[
   ListPlot[{#, Sin[#]} & /@ Range[0, 2 Pi, 0.1], PlotLegend -> {"sine"}]
]

enter image description here

Community
  • 1
  • 1
Leonid Shifrin
  • 22,449
  • 4
  • 68
  • 100
0

LegendsPosition works in ListPlot without problems (for me at least). You don't happen to have forgotten to load the package by using Needs["PlotLegends"]`?

Sjoerd C. de Vries
  • 16,122
  • 3
  • 42
  • 94
0

@Leonid, I added the possibility to setOptionConfiguration to set default options to f without having to use a short-cut macro.

I use the trick exposed by Alexey Popkov in What is in your Mathematica tool bag?

Example:

Needs["PlotLegends`"];
setOptionConfiguration[ListPlot, "myConfig", {LegendPosition -> {0.8, -0.8}},SetAsDefault -> True]
ListPlot[{#, Sin[#]} & /@ Range[0, 2 Pi, 0.1], PlotLegend -> {"sine"}]

Here is the implementation

Options[setOptionConfiguration] = {FilterQ -> False, SetAsDefault -> False};

setOptionConfiguration[f_, tag_, {opts___?OptionQ}, OptionsPattern[]] := 
    Module[{protectedFunction}, 

        optionConfiguration[f][tag] = 
            If[OptionValue@FilterQ, FilterRules[{opts}, 
                Options[f]]
                , 
                {opts}
            ];

        If[OptionValue@SetAsDefault, 
            If[(protectedFunction = MemberQ[Attributes[f], Protected]), 
                Unprotect[f];
            ];

            DownValues[f] = 
                Union[
                    {
                        (*I want this new rule to be the first in the DownValues of f*)
                        HoldPattern[f[args___]] :> 
                            Block[{$inF = True}, 
                                withOptionConfiguration[f[args], tag]
                            ] /; ! TrueQ[$inF]
                    }
                    , 
                    DownValues[f]
                ];

            If[protectedFunction, 
                Protect[f];
            ];
        ];
    ];
Community
  • 1
  • 1
faysou
  • 1,142
  • 11
  • 25
  • I see your point, and this is a nice code, but I would not do this in this fashion. The point is that the Gayley - Villegas trick exposed by @Alexey must be used very sparingly. Generally, it is an intrusive operation. If you make routine use of this, your code will be much harder to debug, since you may easily forget for which functions you did the modifications. One of the main points of my option configurator suggestion was that while it "imitates" global option resettings, it is cleaner since it only works within a *lexical* scope of `withOptionConfiguration` or its short-cuts. The ... – Leonid Shifrin Sep 26 '11 at 08:22
  • ... function itself is not modified in any way. And, no state is ever changed, since the options are passed locally. This is a sort of a functional imitation of state. Lexically scoped (better yet immutable) code is usually easier to debug because, by avoiding manipulations with the state, it remains compositional. And composition is the key, since it allows you to take some smaller part of the code and debug or modify it in complete isolation from other parts. The Gayley - Villegas trick is a powerful tool, but IMO should be used sparingly and only in cases when nothing else helps. – Leonid Shifrin Sep 26 '11 at 08:28
  • By the way, using @Leonid in the answer body does not get me notified - I discovered your newly added answer by chance. It is better to write a comment below the answer of the person you want to notify, or any other answer on which that person commented, then he/she will be notified for sure. – Leonid Shifrin Sep 26 '11 at 08:33