The display forms for objects like ClassifierFunction are nice clickable summary boxes

enter image description here

I like this, and now I'm trying to create my a custom version of this for my functions, so I dissected the code in the output cell and trimmed it down to this:

CellPrint@
     Cell[BoxData[
       InterpretationBox[
        RowBox[{DynamicModuleBox[{Typeset`open$$ = True}, 
           PanelBox[
            PaneSelectorBox[{False -> 
               GridBox[{{PaneBox[
                   ButtonBox[
                    DynamicBox[
                     FEPrivate`FrontEndResource["FEBitmaps", 
                      "SquarePlusIconMedium"], 
                     ImageSizeCache -> {12., {0., 12.}}], 
                    Appearance -> None, 
                    ButtonFunction :> (Typeset`open$$ = True), 
                    Evaluator -> Automatic, Method -> "Preemptive"], 
                   Alignment -> {Center, Center}, 
                   ImageSize -> 
                    Dynamic[{Automatic, 
                      3.5 (CurrentValue["FontCapHeight"]/
                        AbsoluteCurrentValue[Magnification])}]], 
                  GridBox[{{RowBox[{TagBox["\<\"Collapsed Form!\"\>", 
                        "SummaryItemAnnotation"]}]}}, 
                   BaseStyle -> {ShowStringCharacters -> False, 
                     NumberMarks -> False, PrintPrecision -> 3, 
                     ShowSyntaxStyles -> False}, 
                   GridBoxAlignment -> {"Columns" -> {{Left}}, 
                     "Rows" -> {{Automatic}}}, 
                   GridBoxItemSize -> {"Columns" -> {{Automatic}}, 
                     "Rows" -> {{Automatic}}}, 
                   GridBoxSpacings -> {"Columns" -> {{2}}, 
                     "Rows" -> {{Automatic}}}]}}, AutoDelete -> False, 
                BaselinePosition -> {1, 1}, 
                GridBoxAlignment -> {"Rows" -> {{Center}}}, 
                GridBoxItemSize -> {"Columns" -> {{Automatic}}, 
                  "Rows" -> {{Automatic}}}], 
              True -> GridBox[{{PaneBox[
                   ButtonBox[
                    DynamicBox[
                     FEPrivate`FrontEndResource["FEBitmaps", 
                      "SquareMinusIconMedium"], 
                     ImageSizeCache -> {12., {0., 12.}}], 
                    Appearance -> None, 
                    ButtonFunction :> (Typeset`open$$ = False), 
                    Evaluator -> Automatic, Method -> "Preemptive"], 
                   Alignment -> {Center, Center}, 
                   ImageSize -> 
                    Dynamic[{Automatic, 
                      3.5 (CurrentValue["FontCapHeight"]/
                        AbsoluteCurrentValue[Magnification])}]], 
                  GridBox[{{RowBox[{TagBox["\<\"Open Form\"\>", 
                        "SummaryItemAnnotation"]}]}, {RowBox[{TagBox[
                        "\<\"Open Form\"\>", 
                        "SummaryItemAnnotation"]}]}, {RowBox[{TagBox[
                        "\<\"Open Form\"\>", "SummaryItemAnnotation"]}]}},
                    BaseStyle -> {ShowStringCharacters -> False, 
                     NumberMarks -> False, PrintPrecision -> 3, 
                     ShowSyntaxStyles -> False}, 
                   GridBoxAlignment -> {"Columns" -> {{Left}}, 
                     "Rows" -> {{Automatic}}}, 
                   GridBoxItemSize -> {"Columns" -> {{Automatic}}, 
                     "Rows" -> {{Automatic}}}, 
                   GridBoxSpacings -> {"Columns" -> {{2}}, 
                     "Rows" -> {{Automatic}}}]}}, AutoDelete -> False, 
                BaselinePosition -> {1, 1}, 
                GridBoxAlignment -> {"Rows" -> {{Center}}}, 
                GridBoxItemSize -> {"Columns" -> {{Automatic}}, 
                  "Rows" -> {{Automatic}}}]}, Dynamic[Typeset`open$$], 
             ImageSize -> Automatic], BaselinePosition -> Baseline], 
           DynamicModuleValues :> {}]}], Missing[]]], "Output", 
      ImageSize -> {350, 47}, ImageMargins -> {{0, 0}, {0, 0}}, 
      ImageRegion -> {{0, 1}, {0, 1}}]

This code is a bit confusing to me and sadly many of the functions used have no documentation like DynamicBox, PanelBox, PaneSelectorBox... Perhaps there is a more convenient way of doing this than resorting to esoteric boxes?

up vote 44 down vote accepted
+100

Mathematica does it internally by using BoxForm`ArrangeSummaryBox, which is quite straightforward to figure out.

Example

ClearAll[MyObject];

MyObject /: MakeBoxes[obj : MyObject[asc_? myObjectAscQ], form : (StandardForm | TraditionalForm)] := 
    Module[{above, below},
        above = { (* example grid *)
          {BoxForm`SummaryItem[{"Name: ", asc["Name"]}], SpanFromLeft},
          {BoxForm`SummaryItem[{"Variables: ", asc["Variables"]}], BoxForm`SummaryItem[{"Length: ", asc["Length"]}]}
        };
        below = { (* example column *)
          BoxForm`SummaryItem[{"Date: ", asc["Date"]}],
          BoxForm`SummaryItem[{"Metadata: ", asc[MetaInformation]}]
        };
        BoxForm`ArrangeSummaryBox[
           MyObject, (* head *)
           obj,      (* interpretation *)
           $icon,    (* icon, use None if not needed *)
           (* above and below must be in a format suitable for Grid or Column *)
           above,    (* always shown content *)
           below,    (* expandable content *)
           form,
           "Interpretable" -> Automatic
        ]
    ];

It is useful to define a function to test whether MyObject is in the correct format (and whether a summary box can be generated with no errors).

myObjectAscQ[asc_?AssociationQ] := AllTrue[{"Name", "Variables", "Date", "Length", MetaInformation}, KeyExistsQ[asc, #]&]
myObjectAscQ[_] = False;

Summary boxes typically have icons of a certain size:

$icon = 
  Graphics[{Red,Disk[]},
     ImageSize -> Dynamic[{ (* this seems to be the standard icon size *)
         Automatic, 
         3.5 CurrentValue["FontCapHeight"]/AbsoluteCurrentValue[Magnification]
       }]
  ];

Let us test it:

MyObject[<|
  "Name" -> "My particular object",
  "Length" -> 10,
  "Variables" -> {a,b,c},
  "Date" -> Today,
  MetaInformation -> "more info..." |>]

Mathematica graphics

In its expanded form it looks like this:

Mathematica graphics

The "Interpretable" option

If "Interpretable" is set to True, the formatted object can be used directly as input, and will be interpreted as the second argument of ArrangeSummaryBox.

If "Interpretable" is set to Automatic, Mathematica 11.2 and later will decide whether to embed the data into the displayed form of the object based on $SummaryBoxDataSizeLimit. When this size is exceeded, there will be a button that can be used to embed the data.

Mathematica graphics

Usage

Let us define a property retrieval interface, so out MyObject actually does something:

MyObject[asc_?AssociationQ][prop_] := Lookup[asc, prop]

Let's copy-paste the formatted object from above as new input:

Mathematica graphics

  • Is this a v10 only feature? – QuantumDot Dec 26 '15 at 22:13
  • @Quantum I don't think so, as this type of object-representation is certainly older than v8. However, I cannot test it as I only have v10; perhaps someone cares to add this information. – István Zachar Jan 23 '16 at 16:57
  • 1
    I modernized the answer. Everything shown here should work in versions 10.0+ and is current as of 11.2. This is partly based on the implementation found in built-in packages, so this method of defining summary boxes should be safe. – Szabolcs Sep 21 '17 at 15:10
  • Thanks a lot @Szabolcs, this greatly enhanced the post! – István Zachar Sep 22 '17 at 9:28

The following shows a way to emulate the summary boxes using only documented constructs:

grid[g_] := 
 Column[Row /@ MapAt[Style[#, Gray] &, g, Table[{i, 1}, {i, Length[g]}]]]

foo /: MakeBoxes[c : foo[___], form : (StandardForm | TraditionalForm)] := 
 With[{boxes = 
    RowBox[{"foo", "[", 
      ToBoxes[Panel[
        OpenerView[
         {grid[{{"Something: ", 1}, {"Other: ", 2}}], 
          grid[{{"Extra: ", 3}}]}
        ], 
        BaselinePosition -> Baseline], 
       form], "]"}]
    }, 
    InterpretationBox[boxes, c]
 ]

foo[]

Mathematica graphics

This is a simplified version of something I used in practice.

It only makes sense to use this if you have some sort of requirement to only use documented functions.


If you do this, do not forget to also provide a nice OutputForm, which will be automatically used when running Mathematica in command line mode. It can be done like this:

Format[foo[___], OutputForm] := "foo[<>]"

Perhaps there is a more convenient way of doing this than resorting to esoteric boxes.

The following uses the code you posted to define a function:

summaryDisplay = DynamicModule[{open = True, 
  sqrplus = RawBoxes@FrontEndResource["FEBitmaps", "SquarePlusIconMedium"],
  sqrminus = RawBoxes@FrontEndResource["FEBitmaps", "SquareMinusIconMedium"],
  paneF = Pane[Button[Dynamic[#, ImageSizeCache -> {12., {0., 12.}}], 
      open = #2, Appearance -> None, Evaluator -> Automatic,  Method -> "Preemptive"], 
     Alignment -> {Center, Center}, 
     ImageSize -> Dynamic[{Automatic, 3.5 (CurrentValue["FontCapHeight"]/
           AbsoluteCurrentValue[Magnification])}]] &,
  gridF1 = Grid[#, BaseStyle -> {ShowStringCharacters -> False, 
       NumberMarks -> False, PrintPrecision -> 3, ShowSyntaxStyles -> False}, 
     Alignment -> {{{Left}}, {{Automatic}}}, 
     ItemSize -> {{{Automatic}}, {{Automatic}}}, 
     Spacings -> {{{2}}, {{Automatic}}}] &,
  gridF0 = Grid[{{##}}, AutoDelete -> False, BaselinePosition -> {1, 1}, 
     Alignment -> {{{Center}}}, ItemSize -> {{{Automatic}}, {{Automatic}}}] &}, 
 Panel[PaneSelector[{False -> 
   gridF0[paneF[sqrplus, True], gridF1[{Row[{#}]} & /@ #]], 
  True -> gridF0[paneF[sqrminus, False], 
    gridF1[{Row[{#}]} & /@ #2]]}, Dynamic[open], 
 ImageSize -> Automatic], BaselinePosition -> Baseline, 
ImageSize -> {200, Automatic}, ImageMargins -> {{0, 0}, {0, 0}}], 
DynamicModuleValues :> {}] &;

Example:

summaryDisplay[{"ClosedForm"}, {"OpenForm", "OpenForm", "OpenForm"}]

enter image description here

The following solution is slightly more general than @Istvàn's answer, and also fixes the icon-size to what seems to be the standard.

First, we define

ClearAll[summaryForm];
summaryForm[name_, icon_, infos_, maxInfos_, form_, expr_] := RawBoxes@Quiet@Check[
  Module[{shown, hidden},
    (* convert info-table into summary items *)
    {shown, hidden} = Map[BoxForm`MakeSummaryItem[#, form]&, TakeDrop[infos, maxInfos], {2}];
    (* arrange the summary and make sure the label is gray (therefore StyleBox) *)
    StyleBox[BoxForm`ArrangeSummaryBox[
      name, 
      expr,
      Deploy@Show[icon, 
        (* avoid gray background before mouse-over *)
        Background -> White, 
        (* set standard image size *)
        ImageSize -> Dynamic[{
          Automatic, 
          3.5 CurrentValue["FontCapHeight"]/AbsoluteCurrentValue[Magnification]
        }]
      ],
      shown,
      hidden,
      form,
      (* formatted object should be uasable as input *)
      "Interpretable" -> True
    ], "NonInterpretableSummary"]
  ],
  (* in case something goes wrong *)
  ToBoxes@Panel@Short[expr]
]

Now you can specify automatic formatting for your own function, using

Format[testfunction[x_], StandardForm] := summaryForm[
  "testfunction",
   Graphics[{Circle[], Disk[{0,0},1,{0,x}]}],
  {{"Even: ",EvenQ[x]},{"Value: ",x},{"Prime: ",PrimeQ[x]}},2,
  StandardForm,
  x
]

The last argument x is how the summary should be interpreted, so in the more general case you may want to replace it by something like testfunction[x].

Then testfunction is displayed as

testfunction[2]

testfunction summary panel

And of course you can use it as input, and the opener works as expected:

testfunction summary panel in equation

(* -15 *)

Using summaryForm it does not matter how your object is structured internally, and, once defined, you can use it to create many different kinds of formatted objects with very little additional code.

Since this Q&A is a bit hard to find if you don't know how this feature is called, I mention a few more functions which have these summary panels: InterpolatingFunction, TimeSeries, EventSeries, DataDistribution.

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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