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 OpenerView to the complex and versatile 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 allows you to open and close a pane containing an arbitrary expression. 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".
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.
A column or grid of more than one OpenerView object lets you browse a large amount of data in a compact format.
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.
One advantage of a column like this over a 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, 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.
For more information and a detailed listing of options, see OpenerView.
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.
The contents can of course be programmatically generated. Here a Table command is used to generate 10 different plots.
The tab labels are not restricted to simple strings. Here, typeset mathematical expressions are used as tab labels.
This example uses the shapes of countries as tab labels, with a plot of each country's GDP in its pane.
When arranged across the top, tab labels need to be kept reasonably short. The ControlPlacement option can be used to move the tabs to any side of the panel.
The fact that tab labels can be absolutely anything, including typeset expressions, graphics, and dynamic output, makes TabView considerably more flexible than you might at first think. Here, for example, is a TabView where each pane includes a slider that allows you to adjust the label of the tab for that pane. (Dynamic and DynamicModule are explained in "Introduction to Dynamic".)
"Controlling the Currently Displayed Pane" contains further examples of dynamic tab labels.
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 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.
Note that this is a fully functional copy, so if you change anything here it will in fact immediately change your preference settings.
For more information and a detailed listing of options, see TabView.
MenuView is much like 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 MenuView for TabView.
MenuView supports the same label->value syntax as TabView, allowing you to specify more descriptive labels.
In the case of TabView, all the labels are displayed simultaneously, which means there is a fairly small practical limit to the number of panes you can have. 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 G7 countries. With MenuView the same thing can be done for as many as 237 countries.
For more information and a detailed listing of options, see MenuView.
SlideView is basically much like TabView or MenuView, except with a set of first/previous/next/last buttons for navigating the panes.
The number of panes can be arbitrarily large, but navigation is limited to stepping through them like a slide show.
For more information and a detailed listing of options, see SlideView.
PopupView might seem at first similar to MenuView, but they are actually quite different. MenuView and TabView both, in effect, have two items representing each pane: a label and the actual contents of the pane. PopupView, on the other hand, has only one item per pane: the main contents of the pane.
Given a list of expressions, PopupView displays them as a popup menu.
Readers familiar with the PopupMenu control may wonder how this is different, since both appear to do basically the same thing. The difference is largely one of intent: 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. 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, PopupView fully supports arbitrary typeset or graphical content.
For more information and a detailed listing of options, see PopupView.
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 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 ImageSize option for FlipView versus the other views, as explained in "Controlling whether a View Changes Size".
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 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.
OpenerView normally starts in the closed state.
Its two panes are referred to with True (open) and False (closed), so this example will start in the open state.
TabView panes are by default referred to by index number.
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.
Using a Dynamic variable in the second argument allows you to control the currently displayed pane from a separate control. (Dynamic and 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.
In the previous example, having an index number to refer to the panes is good, as it makes linkage to a numerical 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.
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 OpenerView and FlipView, are made large enough to never change size. (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 ImageSize option. ImageSize->All means make the view as large as the largest pane, while ImageSize->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 ImageSize, in which case the view will attempt to fit its contents into the specified overall size.)
Compare these two examples (ImageSize->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.
The 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, ImageSize->All is best. On the other hand, using ImageSize->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.
This section assumes that you are familiar with the Dynamic mechanism (see "Introduction to Dynamic").
All the view objects fully support Dynamic content in any positions where it makes sense. Consider this MenuView example.
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 Dynamic around the contents of each page, the input evaluates almost instantly and produces an output that occupies very little memory.
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 ImageSize->All (the default for all views except OpenerView and 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 ImageSize option to Automatic or to a fixed numerical size.
(The astute reader will notice a subtlety here. With the setting ImageSize->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 ImageSize->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 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.
An important property of views is that currently hidden panes are not updated. Consider this example.
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 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.
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 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, PopupView versus PopupMenu, the functions are essentially identical with the arguments reversed.
Why have both? In the case of PopupView and PopupMenu it is simply for consistency with the other view and control functions, though there is the convenience that the second argument of PopupView is optional (since very often you do not need to provide any external control of the currently displayed pane). In the case of 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.
There are three objects that appear (and in fact are) very similar but not identical: FlipView, PaneSelector, and 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.)
FlipView and 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 FlipView flips to the next pane, while clicking in a PaneSelector allows you to edit the contents of the currently displayed pane (and there is no user interface to flip to any other pane).
Toggler behaves exactly like 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). Toggler also uses ImageSize->All by default, while PaneSelector uses ImageSize->Automatic.