3
$\begingroup$

The documentation of LineLegend, PointLegend etc. says:

The layout specification f is applied to the table of values {{col1, lbl1}, ...} to generate the legend.

This seems to be confirmed by the following:

With[{
  colors = {Red, Blue},
  labels = {x, y},
  layout = # &},
 LineLegend[colors, labels, LegendLayout -> layout]
]

enter image description here

This example with Lists as labels also works as expected:

With[{
  colors = {Red, Blue},
  labels = {{a, b}, {x, y}},
  layout = # &},
 LineLegend[colors, labels, LegendLayout -> layout]
]

enter image description here

And so does this one with a more complicated layout function:

With[{
  colors = {Red, Blue},
  labels = {{a, b}, {x, y}},
  layout = #[[All, 2]] &},
 LineLegend[colors, labels, LegendLayout -> layout]
]

enter image description here

However, the following example breaks:

With[{
  colors = {Red, Blue},
  labels = {{a, b}, {x, y}},
  layout = #[[All, 2, 1]] &},
 LineLegend[colors, labels, LegendLayout -> layout]
]

enter image description here

I expected {a, x} as the result. What is going wrong? What does the legend function do to the labels such that their parts cannot be extracted?

$\endgroup$
3
$\begingroup$

(All following observations were made in Mathematica 10.1 -- details may be version specific.)

A peek at the InputForm is informative. I'll shorten Graphics expressions for clarity.

With[{colors = {Red, Blue}, labels = {{a, b}, {x, y}}, 
  layout = Print[InputForm@# /. Graphics[___] :> "Graphics[<<>>]"] &}, 
 LineLegend[colors, labels, LegendLayout -> layout]]
{{"Graphics[<<>>]", RawBoxes[#1]}, {"Graphics[<<>>]", RawBoxes[#2]}}

So it seems that labels are not actually filled in at the time the LegendLayout function is evaluated; rather it is used to create another function that eventually receives the values.


A look at part of the Box structure that is produced when the legend is displayed shows in more detail what happens.

legend = With[{colors = {Red, Blue}, labels = {{a, b}, {x, y}}}, 
   LineLegend[colors, labels, LegendLayout -> foo]];

dfn = DisplayFunction /. Options[ToBoxes@legend, DisplayFunction];

dfn /. _GraphicsBox :> "GraphicsBox[<<>>]"
StyleBox[StyleBox[
   PaneBox[RowBox[{"foo", "[", 
      RowBox[{"{", 
        RowBox[{RowBox[{"{", RowBox[{"GraphicsBox[<<>>]", ",", #1}], "}"}], ",", 
          RowBox[{"{", RowBox[{"GraphicsBox[<<>>]", ",", #2}], "}"}]}], "}"}], 
      "]"}], Alignment -> Left, AppearanceElements -> None, 
    ImageMargins -> {{5, 5}, {5, 5}}, ImageSizeAction -> "ResizeToFit"], 
   LineIndent -> 0, StripOnInput -> False], {FontFamily -> "Arial"}, 
  Background -> Automatic, StripOnInput -> False] &

I used foo as a dummy function just to show where it ends up in this, and I abbreviated the GraphicsBox expressions to reduce verbosity.

This ends up being the real function used to produce the legend, as we see if we give it some dummy values:

dfn["one", "two"] // RawBoxes

enter image description here

By the time these are filled in foo has been turned into an inert structure, so it cannot change these values.

$\endgroup$
  • 3
    $\begingroup$ It generates TemplateBox and those Slots are filled from template's first argument. This way labels structure will never be accessible for LegendLayout function. Documentation is not precise, to say at least. $\endgroup$ – Kuba Mar 9 '17 at 12:32
  • $\begingroup$ @Kuba Yes, I was just tracking that down myself and figuring out how to write that is an informative way. $\endgroup$ – Mr.Wizard Mar 9 '17 at 12:33
2
$\begingroup$

This is less of an answer but more like a reason why it was designed in that way.

You may have noticed that all the legend functions(Swatch, Point, Line) except BarLegend allow interactive editing: you can toggle to the label by mouse or Tab key and then change it. Because of this design decision and also the implementation, label is atomic and thus LegendLayout has the limitation of looking into what labels are. With the current implementation, it seems logically impossible to achieve both.

Mr. Wizard's impressive observation provides the evidence of the implementation which is using TemplateBox.

enter image description here

$\endgroup$
  • 2
    $\begingroup$ Thank you! I am always interested in the reason behind the design. $\endgroup$ – Mr.Wizard Mar 9 '17 at 23:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.