Views

The Wolfram Language supports a variety of objects that can be used to organize and display information in output. Known collectively as views, these objects range from the simple (RefLink[OpenerView, paclet:ref/OpenerView]) to the complex and versatile (RefLink[TabView, paclet:ref/TabView]).

All have in common that they take a first argument containing a list of expressions to be displayed as separate panes in the view, and an optional second argument to determine which one should be displayed at the moment. All provide a user interface allowing you to change which pane is displayed: they are intended as interactive data-viewers.

The individual views are described first, then options and techniques common to all or most of them.

OpenerView

(RefLink[OpenerView, paclet:ref/OpenerView]) allows you to open and close a pane containing an arbitrary expression. (RefLink[OpenerView, paclet:ref/OpenerView]) is always given a list of two elements: the first element becomes the title (always visible) and the second becomes the contents that are revealed by clicking the disclosure triangle. In this example, click the triangle to reveal the word "Contents".

OpenerView[{"Title", "Contents"}]

This control can be used to create objects that mimic the way disclosure triangles are used in other applications, for example, in the Finder (Macintosh) or Explorer (Windows). Typically the second element is bigger than the first, as in this example.

OpenerView[{"Plot", Plot[Sin[x],{x,0,2Pi}]}]

A column or grid of more than one (RefLink[OpenerView, paclet:ref/OpenerView]) object lets you browse a large amount of data in a compact format.

Column[Map[OpenerView[{#,DateListPlot[CountryData[#,{{"GDP"},{1970,2005}}]]}]&,CountryData["GroupOf8"]]]

The title is not limited to being a plain string: any arbitrary typeset expression or graphic can be used. Here, for example, is an outline of the country with its name as the title line.

Column[Map[OpenerView[{Row[{Rasterize[Show[CountryData[#,"Shape"],ImageSize->{40,40}],Background->None],#}],DateListPlot[CountryData[#,{{"GDP"},{1970,2005}}]]}]&,CountryData["GroupOf8"]]]

One advantage of a column like this over a (RefLink[TabView, paclet:ref/TabView]), for example, is that you can have two or more panes open at once, while other views typically let you see only one pane at a time.

Like other views, (RefLink[OpenerView, paclet:ref/OpenerView]) can be nested arbitrarily deeply. This example turns any expression into a nested tree of openers in which the closed state is the head of the expression and the open state is a column of openers for each argument.

OpenerTree[expr_]:=OpenerView[{OpenerTree[Head[expr]],Column[Map[OpenerTree,Apply[List,expr]]]}];
OpenerTree[expr_]:=expr/;(Length[expr]===0)

Here is a simple application shown with all the openers in the open state.

OpenerTree[(a+b)(c+d)]

Here is a more deeply nested application with just a few opened.

OpenerTree[Integrate[1/(1-x^9),x]]

For more information and a detailed listing of options, see (RefLink[OpenerView, paclet:ref/OpenerView]).

TabView

(RefLink[TabView, paclet:ref/TabView]) is a rich object capable of creating surprisingly interesting user interfaces. Given a list of expressions, it returns a panel with a row of tabs that allow you to look at the expressions one at a time.

By default, the tabs are numbered sequentially. In the output below, click the tabs to flip between panes.

TabView[{40!, Plot[Sin[x],{x,0,2Pi}], Factor[x^14-1]}]

More descriptive tab labels can be added using the form label->contents.

TabView[{"Factorial"->40!, "Plot"->Plot[Sin[x],{x,0,2Pi}], "Factor"->Factor[x^14-1]}]

The contents can of course be programmatically generated. Here a (RefLink[Table, paclet:ref/Table]) command is used to generate 10 different plots.

TabView[Table[Plot[Sin[n x],{x,0,2Pi}],{n,10}]]

The tab labels are not restricted to simple strings. Here typeset mathematical expressions are used as tab labels.

TabView[(HoldForm[Integrate[1/(1-x^#),x]]->Integrate[1/(1-x^#),x])&/@Range[6]]

This example uses the shapes of countries as tab labels, with a plot of each country's GDP in its pane.

TabView[Rasterize[Show[CountryData[#,"Shape"],ImageSize->{40,40}],Background->None]->DateListPlot[CountryData[#,{{"GDP"},{1970,2005}}]]&/@CountryData["GroupOf8"]]

When arranged across the top, tab labels need to be kept reasonably short. The (RefLink[ControlPlacement, paclet:ref/ControlPlacement]) option can be used to move the tabs to any side of the panel.

TabView[Row[{Rasterize[Show[CountryData[#,"Shape"],ImageSize->{40,40}],Background->None],#}]->DateListPlot[CountryData[#,{{"GDP"},{1970,2005}}]]&/@CountryData["GroupOf8"], ControlPlacement->Left]

The fact that tab labels can be absolutely anything, including typeset expressions, graphics, and dynamic output, makes (RefLink[TabView, paclet:ref/TabView]) considerably more flexible than you might at first think. Here, for example, is a (RefLink[TabView, paclet:ref/TabView]) where each pane includes a slider that allows you to adjust the label of the tab for that pane. ((RefLink[Dynamic, paclet:ref/Dynamic]) and (RefLink[DynamicModule, paclet:ref/DynamicModule]) are explained in "Introduction to Dynamic".)

DynamicModule[{values=RandomInteger[{0,100},{10}]},TabView[Table[With[{i=i},Dynamic[values[[i]]]->Slider[Dynamic[values[[i]]],{1,100,1}]],{i,10}]]]

"Controlling the Currently Displayed Pane" contains further examples of dynamic tab labels.

(RefLink[TabView, paclet:ref/TabView]) objects can be nested to arbitrary depth, allowing very large amounts of content to be presented in compact form. Here, for example, is a copy of the Wolfram System Preferences dialog box, which is implemented as a set of nested (RefLink[TabView, paclet:ref/TabView]) objects. The fact that a complex dialog box like this can be copied and pasted into a document without loss of functionality is an example of the power of the Wolfram Language's symbolic dynamic interface technology.

DynamicModule[{nb},
 Dynamic[RawBoxes[nb[[1, 1]]]],
 Initialization :> (nb = 
    Get[ToFileName[
      FrontEnd`FileName[{$InstallationDirectory, "SystemFiles", 
        "FrontEnd", "SystemResources"}, "Preferences.nb"]]]),
 UnsavedVariables -> {nb}]

For more information and a detailed listing of options, see (RefLink[TabView, paclet:ref/TabView]).

MenuView

(RefLink[MenuView, paclet:ref/MenuView]) is much like (RefLink[TabView, paclet:ref/TabView]), except that it uses a popup menu rather than a row of tabs to select which pane is displayed. These two examples are identical to the first two examples given in "TabView"; we have simply substituted the word (RefLink[MenuView, paclet:ref/MenuView]) for (RefLink[TabView, paclet:ref/TabView]).

MenuView[{40!, Plot[Sin[x],{x,0,2Pi}], Factor[x^14-1]}]

(RefLink[MenuView, paclet:ref/MenuView]) supports the same label->value syntax as (RefLink[TabView, paclet:ref/TabView]), allowing you to specify more descriptive labels.

MenuView[{"Factorial"->40!, "Plot"->Plot[Sin[x],{x,0,2Pi}], "Factor"->Factor[x^14-1]}]

In the case of (RefLink[TabView, paclet:ref/TabView]), all the labels are displayed simultaneously, which means there is a fairly small practical limit to the number of panes you can have. (RefLink[MenuView, paclet:ref/MenuView]) displays only one label at a time, allowing you to use many more. For example, in "TabView" there is a nice example with graphical tabs for the G8 countries. With (RefLink[MenuView, paclet:ref/MenuView]) the same thing can be done for as many as 237 countries.

MenuView[Row[{Rasterize[Show[CountryData[#,"Shape"],ImageSize->{40,40}],Background->None],#}]->Dynamic[DateListPlot[CountryData[#,{{"GDP"},{1970,2005}}]]]&/@CountryData[],ImageSize->Automatic]

For more information and a detailed listing of options, see (RefLink[MenuView, paclet:ref/MenuView]).

SlideView

(RefLink[SlideView, paclet:ref/SlideView]) is basically much like (RefLink[TabView, paclet:ref/TabView]) or (RefLink[MenuView, paclet:ref/MenuView]), except with a set of first/previous/next/last buttons for navigating the panes.

SlideView[{40!, Plot[Sin[x],{x,0,2Pi}], Factor[x^14-1]}]

The number of panes can be arbitrarily large, but navigation is limited to stepping through them like a slide show.

SlideView[Dynamic[Column[{Rasterize[Show[CountryData[#,"Shape"],ImageSize->{40,40}],Background->None],#,DateListPlot[CountryData[#,{{"GDP"},{1970,2005}}]]}],SynchronousUpdating->False]&/@CountryData[],ImageSize->Automatic]

For more information and a detailed listing of options, see (RefLink[SlideView, paclet:ref/SlideView]).

PopupView

(RefLink[PopupView, paclet:ref/PopupView]) might seem at first similar to (RefLink[MenuView, paclet:ref/MenuView]), but they are actually quite different. (RefLink[MenuView, paclet:ref/MenuView]) and (RefLink[TabView, paclet:ref/TabView]) both, in effect, have two items representing each pane: a label and the actual contents of the pane. (RefLink[PopupView, paclet:ref/PopupView]), on the other hand, has only one item per pane: the main contents of the pane.

Given a list of expressions, (RefLink[PopupView, paclet:ref/PopupView]) displays them as a popup menu.

PopupView[Table[i!,{i,20}]]

Readers familiar with the (RefLink[PopupMenu, paclet:ref/PopupMenu]) control may wonder how this is different, since both appear to do basically the same thing. The difference is largely one of intent: (RefLink[PopupMenu, paclet:ref/PopupMenu]) is intended as a control that affects something when an item is selected; it has a required first argument that holds a variable that tracks the currently selected item. (RefLink[PopupView, paclet:ref/PopupView]), on the other hand, is intended simply as a way of displaying information, without necessarily having any effect when a different pane is selected.

As will other controls and views in the Wolfram Language, (RefLink[PopupView, paclet:ref/PopupView]) fully supports arbitrary typeset or graphical content.

PopupView[{24!, Plot[Sin[x],{x,0,2Pi}], Factor[x^6-1]}]

For more information and a detailed listing of options, see (RefLink[PopupView, paclet:ref/PopupView]).

FlipView

(RefLink[FlipView, paclet:ref/FlipView]) is unusual in that it provides no visible user interface around the contents of its panes. It does, however, provide a mechanism for changing the pane being displayed. Clicking anywhere in the contents of the current pane flips the display to the next one.

FlipView[{40!, Plot[Sin[x],{x,0,2Pi}], Factor[x^14-1]}]

(RefLink[FlipView, paclet:ref/FlipView]) is also unusual in that instead of having a fixed overall size large enough to hold the largest pane, it is always exactly as large as the currently displayed pane. This is, in fact, simply a difference in the default value of the (RefLink[ImageSize, paclet:ref/ImageSize]) option for (RefLink[FlipView, paclet:ref/FlipView]) versus the other views, as explained in "Controlling whether a View Changes Size".

Controlling the Currently Displayed Pane

All view objects support an optional second argument that specifies which pane is currently visible. Given a literal value, this argument determines the initially displayed pane when the object is first created. Given a (RefLink[Dynamic, paclet:ref/Dynamic]) variable, it can be used to externally influence, or to track, the currently displayed pane.

The set of values in the second argument that corresponds to the displayed panes depends on the view.

(RefLink[OpenerView, paclet:ref/OpenerView]) normally starts in the closed state.

OpenerView[{"Title","Contents"}]

Its two panes are referred to with (RefLink[True, paclet:ref/True]) (open) and (RefLink[False, paclet:ref/False]) (closed), so this example will start in the open state.

OpenerView[{"Title","Contents"},True]

(RefLink[TabView, paclet:ref/TabView]) panes are by default referred to by index number.

TabView[Table[i!,{i,10}],6]

TabView[Map[#->DateListPlot[CountryData[#,{{"GDP"},{1970,2005}}]]&,CountryData["GroupOf8"]],4]

Sometimes it is desirable to give symbolic identifiers to the panes in place of index numbers, allowing you to refer to them by name. This can be done using the form {id,label->contents}. For example, here "Japan" is used rather than "6" in the second argument.

TabView[Map[{#,#->DateListPlot[CountryData[#,{{"GDP"},{1970,2005}}]]}&,CountryData["GroupOf8"]],Entity["Country","Japan"]]

Using a (RefLink[Dynamic, paclet:ref/Dynamic]) variable in the second argument allows you to control the currently displayed pane from a separate control. ((RefLink[Dynamic, paclet:ref/Dynamic]) and (RefLink[DynamicModule, paclet:ref/DynamicModule]) are explained in "Introduction to Dynamic".) For example, here a slider is added that allows you to flip through the tabs. Note that the linkage is automatically bidirectional: if you click one of the tabs, the slider moves to the corresponding position.

DynamicModule[{index=1},
Column[{Slider[Dynamic[index],{1,Length[CountryData["GroupOf8"]],1}],TabView[Map[#->DateListPlot[CountryData[#,{{"GDP"},{1970,2005}}]]&,CountryData["GroupOf8"]],Dynamic[index]]}]]

In the previous example, having an index number to refer to the panes is good, as it makes linkage to a numerical (RefLink[Slider, paclet:ref/Slider]) easy. If, on the other hand, you want to have a text field where you can enter the country name, having named panes is more convenient. This example provides a text field where you can enter a country name directly.

DynamicModule[{country="Canada"},
Column[{InputField[Dynamic[country],String],MenuView[Map[{#,#->Dynamic[DateListPlot[CountryData[#,{{"GDP"},{1970,2005}}]]]}&,CountryData[]],Dynamic[Entity["Country",country],(country=CanonicalName[#])&],ImageSize->Automatic]}]]

Controlling whether a View Changes Size

Views always display one of several alternate panes. In determining the overall size of the view, there are two obvious alternatives: make the view big enough to hold the currently displayed pane, or make it big enough so that it never has to change size when switching between panes (i.e. as big as the biggest one in each dimension).

By default all views, other than (RefLink[OpenerView, paclet:ref/OpenerView]) and (RefLink[FlipView, paclet:ref/FlipView]), are made large enough to never change size. ((RefLink[OpenerView, paclet:ref/OpenerView]) in particular would make little sense if it did not get bigger when opened.)

The behavior of any view can be changed using the (RefLink[ImageSize, paclet:ref/ImageSize]) option. (RefLink[ImageSize, paclet:ref/ImageSize])->(RefLink[All, paclet:ref/All]) means make the view as large as the largest pane, while (RefLink[ImageSize, paclet:ref/ImageSize])->(RefLink[Automatic, paclet:ref/Automatic]) means make the view only as large as the currently displayed frame, potentially changing size any time the view is switched to a new pane. (You can also specify a fixed numerical (RefLink[ImageSize, paclet:ref/ImageSize]), in which case the view will attempt to fit its contents into the specified overall size.)

Compare these two examples ((RefLink[ImageSize, paclet:ref/ImageSize])->(RefLink[All, paclet:ref/All]) is the default; it is included only for clarity). The first one is always big, but stays the same size. The second one is only as big as it needs to be, and thus changes size.

TabView[Table[(50i)!,{i,10}],ImageSize->All]

TabView[Table[(50i)!,{i,10}],ImageSize->Automatic]

The (RefLink[ImageSize, paclet:ref/ImageSize]) option works this way for all view objects.

Which behavior is best depends on the situation. In general, tab views and similar controls used in applications other than the Wolfram System rarely change size, so if you are trying to make something that looks and acts like a traditional hard-coded application, (RefLink[ImageSize, paclet:ref/ImageSize])->(RefLink[All, paclet:ref/All]) is best. On the other hand, using (RefLink[ImageSize, paclet:ref/ImageSize])->(RefLink[Automatic, paclet:ref/Automatic]) allows you to take advantage of the fact that, in the Wolfram Language, dialog boxes and controls are not fixed objects. A great deal of freedom and flexibility is possible precisely because these objects can change size.

Dynamic Content in Views

This section assumes that you are familiar with the (RefLink[Dynamic, paclet:ref/Dynamic]) mechanism (see "Introduction to Dynamic").

All the view objects fully support (RefLink[Dynamic, paclet:ref/Dynamic]) content in any positions where it makes sense. Consider this (RefLink[MenuView, paclet:ref/MenuView]) example.

MenuView[Map[#->DateListPlot[CountryData[#,{{"GDP"},{1970,2005}}]]&,CountryData[]],ImageSize->Automatic]

In this form, the example computes in advance all 237 GDP plots, generating errors for some countries where data is missing, and doing far more computation than necessary, since it is unlikely anyone will try to look at every single country. The code takes a long time to evaluate and wastes a lot of memory.

By simply wrapping (RefLink[Dynamic, paclet:ref/Dynamic]) around the contents of each page, the input evaluates almost instantly and produces an output that occupies very little memory.

MenuView[Map[#->Dynamic[DateListPlot[CountryData[#,{{"GDP"},{1970,2005}}]]]&,CountryData[]],ImageSize->Automatic]

The trade-off is that each new country selected computes the GDP plot on the fly. Fortunately this generally happens so fast as to be unnoticeable. Errors for a particular country are displayed only if that country is selected.

Note that with the setting (RefLink[ImageSize, paclet:ref/ImageSize])->(RefLink[All, paclet:ref/All]) (the default for all views except (RefLink[OpenerView, paclet:ref/OpenerView]) and (RefLink[FlipView, paclet:ref/FlipView])), every pane is formatted once when the object is first created, in order to determine the overall size of the view object. You can avoid this by setting the (RefLink[ImageSize, paclet:ref/ImageSize]) option to (RefLink[Automatic, paclet:ref/Automatic]) or to a fixed numerical size.

(The astute reader will notice a subtlety here. With the setting (RefLink[ImageSize, paclet:ref/ImageSize])->(RefLink[All, paclet:ref/All]) and dynamic content in currently invisible panes, it would theoretically be necessary to continually update the values of all the hidden dynamics, since the size of the view as a whole should depend on the size of the largest pane, even if it is not currently being displayed. An intentional decision was made not to do such updating of hidden panes. As a result, a view with (RefLink[ImageSize, paclet:ref/ImageSize])->(RefLink[All, paclet:ref/All]) can in fact change size when a new pane is selected, if that pane contains dynamic content that has changed size since the last time it was displayed. The alternative would be for the view to change size mysteriously when activity in a hidden pane caused that hidden pane to change size. This would be peculiar and of little conceivable use.)

In the case of (RefLink[TabView, paclet:ref/TabView]), dynamic tab labels can be used to implement a variety of special behaviors. In this example, the currently selected tab is highlighted in a custom-defined way, by making the labels dynamically dependent on the variable that tracks the currently selected pane.

DynamicModule[{j},TabView[Table[With[{i=i},Graphics[Dynamic[
If[i===j,
{Disk[],White,Inset[i]},
{Circle[],Inset[i]}]],ImageSize->25,PlotRangePadding->.5]->Plot[Sin[i x],{x,0,2Pi}]],{i,10}],Dynamic[j]]]

An important property of views is that currently hidden panes are not updated. Consider this example.

OpenerView[{"Mouse Position", Column[{"Mouse Position",Dynamic[MousePosition[]]}]}]

When the output is in the open state, the current position of the mouse pointer is displayed and continuously updated, consuming a certain amount of CPU time. However, when the output is in the closed state, the mouse position is no longer tracked and no CPU time is used. (This is of course of more concern if the contents are something more compute-intensive than simply displaying the mouse position.)

This property allows you to do things like build large, complex instances of (RefLink[TabView, paclet:ref/TabView]) in which expensive computations are done in each pane of the view, without incurring the cost of keeping all the panes updated all the time.

Views versus Controls

There are two classes of functions in the Wolfram Language that represent relatively low-level user interface objects: views and controls. This tutorial describes the views class of functions, but there is considerable overlap with controls in some cases.

Views are designed to present multiple panes of data and provide a user interface for switching among them, so the logical first argument is the list of expressions representing the contents of the panes.

Controls are primarily designed to influence the value of a variable through a (RefLink[Dynamic, paclet:ref/Dynamic]) connection, so the first argument of all control functions is the variable representing the value of the control.

What is potentially confusing is that views also allow you to control the value of a variable, just like controls do. In at least one case, (RefLink[PopupView, paclet:ref/PopupView]) versus (RefLink[PopupMenu, paclet:ref/PopupMenu]), the functions are essentially identical with the arguments reversed.

PopupView[{1,2,3,4},Dynamic[popPosition]]

PopupMenu[Dynamic[popPosition],{1,2,3,4}]

Why have both? In the case of (RefLink[PopupView, paclet:ref/PopupView]) and (RefLink[PopupMenu, paclet:ref/PopupMenu]) it is simply for consistency with the other view and control functions, though there is the convenience that the second argument of (RefLink[PopupView, paclet:ref/PopupView]) is optional (since very often you do not need to provide any external control of the currently displayed pane). In the case of (RefLink[PopupMenu, paclet:ref/PopupMenu]), the only purpose in creating the control is for it to control a variable, so the first argument is of course not optional.

Other than the set described in "FlipView versus PaneSelector versus Toggler", views do not correspond quite so directly with any control objects. It is, however, useful to keep in mind that views can, through their second argument, be used essentially as control objects: they can control and be controlled by the value of a variable, that is simply not their only purpose.

FlipView versus PaneSelector versus Toggler

There are three objects that appear (and in fact are) very similar but not identical: (RefLink[FlipView, paclet:ref/FlipView]), (RefLink[PaneSelector, paclet:ref/PaneSelector]), and (RefLink[Toggler, paclet:ref/Toggler]). Each of these objects takes a list of expressions and displays one of them at a time. They differ in the details of their argument order and click behavior. (But mainly they differ in their intended use more so than in their actual behavior.)

(RefLink[FlipView, paclet:ref/FlipView]) and (RefLink[PaneSelector, paclet:ref/PaneSelector]) take identical arguments: a list of expressions and a number that specifies which pane should be displayed. The difference is that clicking anywhere in a (RefLink[FlipView, paclet:ref/FlipView]) flips to the next pane, while clicking in a (RefLink[PaneSelector, paclet:ref/PaneSelector]) allows you to edit the contents of the currently displayed pane (and there is no user interface to flip to any other pane).

FlipView[Table[{i,i!},{i,10}],6]

PaneSelector[Table[{i,i!},{i,10}],6]

(RefLink[Toggler, paclet:ref/Toggler]) behaves exactly like (RefLink[FlipView, paclet:ref/FlipView]) in that it flips between panes when clicked, but the arguments are in the opposite order, with the index number first (see "Views versus Controls" for why this actually makes sense). (RefLink[Toggler, paclet:ref/Toggler]) also uses (RefLink[ImageSize, paclet:ref/ImageSize])->(RefLink[All, paclet:ref/All]) by default, while (RefLink[PaneSelector, paclet:ref/PaneSelector]) uses (RefLink[ImageSize, paclet:ref/ImageSize])->(RefLink[Automatic, paclet:ref/Automatic]).

Toggler[6,Table[{i,i!},{i,10}]]

For more information and a detailed listing of options, see (RefLink[FlipView, paclet:ref/FlipView]), (RefLink[PaneSelector, paclet:ref/PaneSelector]), and (RefLink[Toggler, paclet:ref/Toggler]).