视图

Wolfram 语言支持各种能用来在输出中组织和显示信息的对象. 它被称视图,这些对象的范围包括从简单的 OpenerView 到复杂和多功能的 TabView.

所有的都有一个共同点,那就是它们的第一个参数包含一列在视图中显示为单独窗格(Pane)的表达式,可选的的第二参数来决定目前哪一个应该被显示. 所有的都提供一个用户界面使得你能改变哪一个窗格被显示:它们的意图是作为交互式的数据察看器.

首先描述单个的视图,然后是所有的或绝大多数都通用的选项和技巧.

OpenerView

OpenerView 使得你能打开或关闭一个含有一任意表达式的窗格. OpenerView 总是被给于一个两个元素的列表:第一个元素是标题(总是可见的),第二个是能通过点击三角形来显示的内容. 在这个例子中,点击三角形来显示Contents这个词.

In[1]:=
Click for copyable input
Out[1]=

这个控件能用来创建模拟在其它应用中揭露三角形使用的方式的对象, 例如,在 Finder (Macintosh) 或 Explorer (Windows) 中. 一般地第二个元素比第一个大,就像在这个例子中.

In[2]:=
Click for copyable input
Out[2]=

多个 OpenerView 对象的一列或一格使你以一个紧凑的形式浏览大量的数据.

In[1]:=
Click for copyable input
Out[1]=

标题不局限于一个简单的字符串:任何的排版表达式或图像都能被使用. 这里,例如,以拥有国家名字的国家的轮廓来作为标题行.

In[2]:=
Click for copyable input
Out[2]=

像这样的一列比一个 TabView 优越的一点,比如说,是你同时能打开两个或更多的窗格,而其它的视图一般是每次只看到一个窗格.

像其它的的视图一样,OpenerView 能任意嵌套多层次. 这个例子把任何表达式变成一个启动开关嵌套的树,其中,关闭的状态是表达式的头,开放的状态是每一自变量的一列启动开关.

In[5]:=
Click for copyable input

这里是一个简单的应用,显示所有的启动开关都在开放的状态.

In[7]:=
Click for copyable input
Out[7]=

这里是一个更深入嵌套的应用,只有一些是打开的.

In[8]:=
Click for copyable input
Out[8]=

关于更多的信息和一个详细的选项列单,请参看 OpenerView.

TabView

TabView 是一个丰富的对象它能创建相当有趣的用户界面. 给它一列表达式,它返回一个带有一行选项卡的窗格使你能一次一个地看那些表达式.

默认时,选项卡是按顺序排号的. 在下面的输出中,点击选项卡来在窗格之间进行翻阅.

In[9]:=
Click for copyable input
Out[9]=

更具有描述性的选项卡标记能够使用 (标记 -> 内容)的形式来添加.

In[10]:=
Click for copyable input
Out[10]=

内容当然能够用程序来产生. 这里使用一个 Table 命令产生十个不同的图形.

In[11]:=
Click for copyable input
Out[11]=

选项卡标记不局限于简单的字符串. 这里排版的数学表达式被用作选项卡标记.

In[10]:=
Click for copyable input
Out[10]=

这个例子使用国家的形状来作为选项卡标记,在它的窗格中是一个每一国家的 GDP (国家生产总产值)图形.

In[3]:=
Click for copyable input
Out[3]=

当选项卡标记放置在上方时,需要被保持成合理地短. ControlPlacement (控件放置)选项可以被用来把选项卡移到窗格的任何一端.

In[4]:=
Click for copyable input
Out[4]=

选项卡标记绝对可以是任何事物,包括排版的表达式、图像和动态输出,它使得 TabView 比你最初认为的更颇具灵活性. 例如,这里是一个 TabView,其中每一个窗格包含一个滑块使得你能调节那一个窗格的选项卡的标记.(在 "动态简介" 里讲解 DynamicDynamicModule.)

In[15]:=
Click for copyable input
Out[15]=

"控制当前显示的窗格" 这一节包含动态选项卡标记的进一步的例子.

TabView 对象能以任意的深度嵌套,使得大容量的内容能以紧缩的形式展现. 例如,Wolfram 语言 偏好设置对话框,它是作为一组嵌套的 TabView 对象来实现的. 一个如此复杂的对话框能被拷贝和粘贴到一个文件中而不失去任何功能这一事实是 Wolfram 语言 的符号动态界面技术的强大能力的一个例子.

注意这是一个完全起作用的复本,所以如果你在这里做任何改变事实上会立即改变你的偏好设置.
In[45]:=
Click for copyable input
Out[1]=

关于更多的信息和一个详细的选项列表,请参看 TabView.

MenuView

MenuView 很像 TabView,除了它使用一个弹出菜单而不是一行选项卡来选择哪一个窗格被显示这一点之外. 这两个例子和在 "TabView" 一节中的前两个例子是相同的;我们只是把 TabView 替换成 MenuView 这个词.

In[16]:=
Click for copyable input
Out[16]=

MenuView 支持和 TabView 同样的 语法,使得你能指定更具描述性的标记.

In[17]:=
Click for copyable input
Out[17]=

TabView 的情况下,所有的标记都同时显示,这意味着在实际中嵌入的窗格数是有限制的. MenuView 一次显示一个窗格,使得你能用的数目要多得多. 例如,在 "TabView" 这一节中有一个很好的例子,对 G8 国家用的是图形选项卡. 用 MenuView,可以容纳多达237个国家.

In[5]:=
Click for copyable input
Out[5]=

关于更多的信息和一个详细的选项列表,请参看 MenuView.

SlideView

SlideView 基本上是和 TabViewMenuView 一样的,除了用一组 第一个/上一个/下一个/最后一个 按钮来导航窗格之外.

In[19]:=
Click for copyable input
Out[19]=

窗格的数目是可以任意大的,但是导航只能是像幻灯片显示一样在它们中一步步地观看.

In[6]:=
Click for copyable input
Out[6]=

关于更多的信息和一个详细的选项列表,请参看 SlideView.

PopupView

PopupView 可能初看是和 MenuView 相似的,但事实上它们是很不一样的. MenuViewTabView 实际上都有两个条目来代表每一个窗格:一个标记和窗格的实际内容. 另一方面,PopupView 对每一个窗格只有一个条目:窗格的主要内容.

给它一列表达式,PopupView 把它们作为一个弹出菜单来显示.

In[28]:=
Click for copyable input
Out[28]=

熟悉 PopupMenu 控件的读者也许会想这里的区别是什么,因为两者看上去好像基本上是一样的. 主要的区别在于它们的目的:PopupMenu 的目的是作为一个控件,在某个条目被选择时会对某些事物产生影响;它有一个必需的第一自变量来保留一个跟踪当前被选条目的变量. 另一方面,PopupView 的目的只是作为显示信息的一种方式,选择不同的窗格时不一定会有任何影响.

就像在 Wolfram 语言 里的其它控件和视图一样,PopupView 完全支持任意的排版或图像的内容.

In[29]:=
Click for copyable input
Out[29]=

关于更多的信息和一个详细的选项列表,看 PopupView.

FlipView

FlipView 是不太寻常的,这在于它在它的窗格内容的周围不提供可见的用户界面. 然而,它确实提供一个改变正在被显示的窗格的机制. 在当前窗格内任何地方点击将翻阅显示下一个.

In[30]:=
Click for copyable input
Out[30]=

FlipView 还有不寻常的特征是,它没有一个固定的总体尺寸来包含最大的窗格,而是和当前显示的窗格的大小总是完全一样. 这相对于其它的视图,事实上只是在对 FlipViewImageSize 选项的默认值上的区别,这在 "控制是否一个图改变尺寸" 一节中讲解.

控制当前显示的窗格

所有视图的对象支持一个选择性的第二自变量,它指定哪一个窗格当前是可见的. 给它一个字面值,这个自变量决定在对象被创建时最初显示的窗格. 给它一个 Dynamic 变量,它能被用来外部地影响或跟踪当前被显示的窗格.

在第二自变量里对应于显示窗格的一组数值取决于视图.

OpenerView 一般以关闭的状态开始.

In[31]:=
Click for copyable input
Out[31]=

它的两个窗格为 True (开放)和 False (关闭),所以这个例子会以开放状态开始.

In[32]:=
Click for copyable input
Out[32]=

TabView 的窗格在默认时用索引数字来设置.

In[33]:=
Click for copyable input
Out[33]=
In[7]:=
Click for copyable input
Out[7]=

有时用符号识别符来替代索引数字指定窗格,使得你能用名字来引用它们. 这可以用 这个形式来完成. 例如,这里在第二自变量中使用了Japan(日本)而不是6.

In[9]:=
Click for copyable input
Out[9]=

在第二自变量里使用一个 Dynamic 变量能使你从一个单独的控件来控制当前显示的窗格. (DynamicDynamicModule"动态简介" 中讲解.)例如,这里添加了一个滑块使得你能翻阅选项卡. 注意连接是自动地双向的:如果你点击选项卡中的一个,滑块会移动到相应的位置.

In[10]:=
Click for copyable input
Out[10]=

在上一个例子中,有一个索引数字来提及窗格是好的,因为这很容易连接一个数值 Slider. 另一方面,如果你希望有一个文字区,在其中你能输入国家的名字,有一个已命名的窗格会更方便. 这个例子提供了一个文字区,在其中你能直接地输入一个国家名.

In[2]:=
Click for copyable input
Out[2]=

控制一个视图是否改变尺寸

视图总是显示多个窗格中的一个. 在决定视图的总体大小时,有两个显然的供选择的方式:使得视图足够大能包含当前显示的窗格,或者使得它足够大以至于在转换窗格时永远也不需要改变尺寸(也就是说,在每一个方向上与最大的一样大).

默认时,所有的视图,除了 OpenerViewFlipView 之外,都被作得足够大使得永远不用改变大小.(尤其是OpenerView, 如果它在打开时不变得大一些是没有意义的.)

任何视图的行为可以使用 ImageSize 选项来改变. ImageSize->All 意味着使视图和最大的窗格一样大,而 ImageSize->Automatic 意味着使视图只是和当前显示的一幅一样大,在视图被转换成一个新的窗格时有可能改变大小.(你也能指定一个固定数字的 ImageSize,在这种情况下视图会将它的内容融入到一个指定的总体大小中.)

比较这两个例子(ImageSize->All 是默认的;只是为了表达清楚才包含它). 第一个总是大的,但是保持同样的尺寸,第二个只是和它所需要的一样大,因此改变了大小.

In[40]:=
Click for copyable input
Out[40]=
In[41]:=
Click for copyable input
Out[41]=

ImageSize 选项对所有视图的对象都是以这种方式工作.

哪一个行为更好由情况来决定. 一般的,选项卡视图和在除 Wolfram 语言 之外的应用程序中使用的类似的控件很少改变大小,所以如果你试图创建一个看上去和工作起来像一个传统的硬编码的应用程序,那么 ImageSize->All 是最佳选择. 另一方面,使用 ImageSize->Automatic 能使你利用这样一个事实,那就是在 Wolfram 语言 中,对话框和控件不是固定的对象. 正是因为这些对象能改变大小,大量的自由和灵活性才成为可能.

视图中的动态内容

这一节假设你对 Dynamic 机制是熟悉的(参看 "动态简介").

所有的视图对象完全支持任何有意义的位置里的 Dynamic 内容,考虑这个 MenuView 例子.

Click for copyable input

在这种形式里,这个例子提前计算所有237个 GDP 的图形,对数据缺损的国家产生错误,完成比必需的多得多的计算,因为不太可能有任何人会想看每一个单个的国家. 程序花费很长的时间计算,浪费了很多的内存.

通过仅仅用 Dynamic 来封装每一页的内容,几乎立即计算输入并产生一个占据很少内存的输出.

In[1]:=
Click for copyable input
Out[1]=

这里要权衡的是它是实时计算每一个被选择的新国家的 GDP 图. 幸运的是这一般发生得很快以至于不被察觉. 对一个特定国家的错误信息只有在那个国家被选时才显示.

注意在用 ImageSize->All 设置时(这是除了 OpenerViewFlipView 之外的所有视图的默认设置),每一个窗格,在当对象第一次产生时,都被格式化一次,以便决定视图对象的总体大小. 为了避免这个发生,你可以通过把 ImageSize 选项设置为 Automatic 或者设置为一个固定的数值大小.

(敏锐的读者会注意到这里的一个微妙的地方. 当用 ImageSize->All 这个设置和动态内容在当前隐藏的窗格里时,既然视图的大小作为一个总体应该取决于最大的那一个窗格的尺寸,即使它当前没有被显示,所以理论上是必须继续更新所有隐藏的动态的数值的. 但是,这里故意决定,做这样的隐藏窗格的更新. 结果是,一个带有 ImageSize->All 的视图能实际上在一个新的窗格被选时改变尺寸,如果那个窗格含有在上一次被显示之后改变了尺寸的动态内容. 另外的一个选择是当一个隐藏窗格里的活动引起那个隐藏窗格改变尺寸时使得视图神奇地改变大小. 这会有些奇怪以及没有什么可想到的用途.)

TabView 的情况下,动态选项卡标记能被用来执行各种特殊的行为. 在这个例子中,通过使标记动态地依赖那个跟踪当前被选的窗格的变量,当前被选的选项卡以自定义的方式突出显示.

In[43]:=
Click for copyable input
Out[43]=

视图的一个重要的特性是当前隐藏的窗格不被更新. 考虑这个例子:

In[44]:=
Click for copyable input
Out[44]=

当输出是在开放的状态时,鼠标指针的当前位置被显示并被持续更新,消耗一定量的 CPU 时间. 但是,当输出是在关闭的状态时,鼠标的位置不再被跟踪,没有任何 CPU 时间被使用.(如果内容是比简单地显示鼠标位置更加计算密集型的,这当然是更需要关注的.)

这个属性使你能构建大型的、复杂的 TabView,其中昂贵的计算在视图的每一个窗格中完成,没有使所有的窗格始终保持被更新的花费.

视图与控件

Wolfram 语言 中有两类函数来代表相当低级别的用户界面的对象:视图和控件. 这个教程描述了视图这一类的函数,但在某些情况下和控件有相当大的重复.

视图是为了展现多个数据的窗格和提供一个用户界面来在它们之间转换而设计的,所以逻辑上的第一个自变量是一列代表窗格内容的表达式.

控件主要是为了通过一个 Dynamic 连接来影响一个变量的数值而设计的,所以所有控件函数的第一个自变量是一个代表控件值的变量.

可能会让人疑惑的是视图也能允许你来控制一个变量的值,就像控件一样. 至少在一个情况下,PopupViewPopupMenu,函数基本上是相同的只是自变量的顺序是反的.

In[45]:=
Click for copyable input
Out[45]=
In[46]:=
Click for copyable input
Out[46]=

为什么要设计两个呢?在 PopupViewPopupMenu 的情况这只是为了和其它的视图和控件函数相一致,尽管有一个方便的地方,就是 PopupView 的第二个自变量是选择性的(因为,经常你不需要提供任何当前显示的外部控制). 在 PopupMenu 的情况,创建一个控件的唯一目的是使它来控制一个变量,所以第一个自变量当然不是选择性的.

除了在 "FlipView 与 PaneSelector 与 Toggler"中所描述的那一组,视图不太直接地和任何控件对象对应. 然而,记住这一点是有益的,那就是通过它们的第二个自变量,视图能够基本上作为控件对象来使用:它们能控制和被一个变量的值所控制,只是那不是它们的唯一意图.

FlipView 与 PaneSelector 与 Toggler

有三个对象它们看上去(实际上是)非常相似但不是完全相同的:FlipView (翻阅视图),PaneSelector (窗格选择),和 Toggler (触发器). 这些对象的每一个都取一列表达式并一次显示其中的一个. 它们在它们的自变量的顺序的细节上和点击的行为上有区别.(但是主要地它们区别于它们的使用意图,这比它们的实际行为上的区别更重要.)

FlipViewPaneSelector 取相同的自变量:一列表达式和一个指定哪一个窗格应该被显示的数字. 区别是在一个 FlipView 里点击任何地方会翻阅到下一个窗格,而在一个 PaneSelector 中点击能使你编辑当前被显示的窗格的内容(并且没有用户界面来翻阅到任何其它的窗格).

In[47]:=
Click for copyable input
Out[47]=
In[48]:=
Click for copyable input
Out[48]=

Toggler 的行为和 FlipView 完全一样,这在于当点击时它在窗格之间翻阅,但是自变量的顺序是相反的,首先是索引数字(对于为什么这事实上是有意义的,参看 "视图与控件" ). Toggler 也在默认时使用 ImageSize->All,而 PaneSelector 使用 ImageSize->Automatic.

In[49]:=
Click for copyable input
Out[49]=

关于更多的信息和一个详细的选项列表,请参看 FlipViewPaneSelectorToggler.