控件对象简介
按钮 | 检验栏(Checkbox)和其它切换控件 |
其它控件 | 单选按钮(RadioButton)和其它设置控件 |
滑块 | PopupMenu 和 ActionMenu |
Animator、Trigger 和 ProgressIndicator | InputField |
Slider2D 和 ColorSlider |
函数 Manipulate 是一个这种类型的接口,它为用户提供了一种简单而又强大的环境,使用它可以深入探索 Wolfram 语言表达式. 事实上,Manipulate 利用控制对象和其它布局结构,完全使用 Wolfram 编程语言编写. 对于许多应用程序,仅使用 Manipulate 是完全足够的,您无需考虑底层的控制对象或者利用本教程的内容. 但是,如果您期望生成的应用程序在已有的框架下无法满足时,直接使用控制对象是最好的解决方法.
注意,本教程不讨论在 "动态简介" 中已经涵盖的内容,我们建议您在阅读本教程之前,先熟悉一下 Dynamic 教程中的内容.
我们可以认为最简单的控件是按钮控件,它把一次鼠标点击链接到一个简单的动作. Button 的第一个变量是在按钮表面上显示出来的标签,它可以是任何 Wolfram 语言表达式,包括图形或者排版对象. 第二个变量是当您点击按钮时将要计算的函数. 计算在内核中进行,每次计算的输出通常是不可见的,除非它产生一些负面效果.
Wolfram 语言可以实现的任何计算都可以用一个按钮完成. 这个计算是在按钮被点击时产生的,而不是在按钮创建时产生的. (参见属性 HoldRest 的相关文档,以获取如何实现这个目的的技术细节信息.) 但是,请注意,计算结果的显示方式与使用 Shift+Enter 计算产生的结果不同. 因此,除非该按钮函数含有一个明显的负面效果,您可能会认为按钮什么都没有做,而事实上,它只是没有为您显示出它所做的工作而已. 下面是两个执行相同操作的按钮,不同之处在于,一个按钮在一个新的面板窗口中显示结果,而另一个按钮“无声地”执行所要求的计算.
当构建一个界面时,您可能需要调整按钮外观的一些细节部分. 这是由 Button 的 Appearance 选项控制的. Wolfram 语言提供了按钮的某些内置外观,包括面向平台-标准面板按钮的 "Palette" 和 "FramedPalette",以及默认的面向对话框框符按钮的 "DialogBox".
需要指出的是按钮和其他控件的默认 Wolfram 语言外观是直接从基本的操作系统获取. 因此,在 Windows 和 Macintosh 下控件的外观会有所不同. 例如,Macintosh 中的对话框按钮的设计是从不会小于某种最小尺寸.
下面是从带有 Appearance->None 的按钮创建的一个很简单的界面. 点击其中的一个方形以改变圆圈的颜色. 注意,使用没有框架的按钮可能会引起的一个问题是用户可能根本不知道它们是按钮. 在这种情况下,您可以决定如何创建一个明显可点击的外观.
Enabled 经常与 Dynamic 一起使用,所以只要符合条件,按钮就接受鼠标点击,最后停止. 例如上面提到的累加按钮,我们可以对它附加一个最大值的限制条件. 一旦得到最大值,Enabled 的设置可自动变成 False,然后按钮就变暗. 为了完整起见,我们也添加一个递减按钮,它将在得到最小值后停止,然后我们可以使用 DynamicModule 对整个表达式内容进行封装,以实现变量的局域化处理.
如果您通常使用的内核多于一个,或者想要创建直接使用前端的按钮,您将需要了解 Evaluator 选项的使用. 它可以设为任何内核的名称或者 Automatic (默认值),它意味着使用当前的默认内核. 设置 Evaluator->None 将在可能的情况下使行为完全在前端进行. 参见 FrontEndExecute 获取更多详细信息.
当在 Grid 或者 Column 中使用 Button 时,您应该了解的另一个选项是 ImageSize. 默认情况下,按钮将在各个方向上伸展以填充网格上的该元素. 这是由于 ImageSize 的默认设置—— Full. 下面对上面的例子做了一个细微的修改,当按钮所在的 Column 变宽时,按钮将自动重新调整大小.
我们所要讨论的 Button 的最后一个选项是 Method. Method 决定按钮与内核交流的方式. 在 "Preemptive" 的默认设置下,点击按钮将使计算通过抢占链接(preemptive link)发给内核. 参见 "高级动态功能" 获取抢占链接的更多详细内容. 这与动态计算使用相同的链接. 这样的按钮将马上对用户的点击作出响应,即使用户也在主链接上运行一些很长的 Shift+Enter 计算.
在正常的环境下,跳出 While 循环的唯一方式是终止计算. 但是,由于上面的按钮在一个不同的链接上与内核通讯,它没有被 While 循环阻塞. 点击这个按钮将启动一个抢占式计算,它将改变变量值,并且使 While 循环中止.
使用 Method->"Preemptive" 的一个重要的后果是按钮计算受与其它动态计算相同的超时限制,这使用前端 DynamicEvaluationTimeout 选项指定. 因此,如果您预期您的计算时间将超过几秒,您应该使用 Method->"Queued". 在计算函数时,点击一个这样的按钮将使用主链接,并且该计算将与使用 Shift+Enter 的计算一起排队.
比如,在非常大的输出抑制面板中的按钮使用 Method -> "Queued",因为处理很大的输出会需要很长的时间.
Button 控件是相当灵活和强大的,但是也存在一些常见的变种,而且肯定有简单的函数可以生成这些变种.
PasteButton 创建一个执行粘贴操作的按钮. Hyperlink 创建一个按钮,该按钮将跳到一个给定的 URI. FileNameSetter 创建一个按钮,该按钮将产生一个系统文件选择对话框,并且记录了到选中文件的路径. ColorSetter 创建了一个按钮,该按钮将产生一个系统颜色选择对话框.
"PopupMenu 和 ActionMenu" 中讨论的 ActionMenu 也执行一个与 Button 类似的函数. 它将多个操作合成为一组菜单项,每个菜单项有一个单独的函数,这些单独的函数的执行方式与 Button 的函数完全相同.
绝大多数控件都在某种程度上有一些不同的目的,并且把它们与一个变量联系在一起. 下面我们要讨论的滑块的例子在"动态简介"中有更详细的描述. 本教程中详细描述的所有其它控件和滑块都有这个功能. 它们的主要目的是显示一个变量的当前值,并且当使用该控件时,设置该变量的值.
control[Dynamic[var],domainSpec,opts]
所有的控件都在抢占链接上与内核交互作用,并且受与使用 Method->"Preemptive" 的按钮相同的超时时间(在 "控制对象简介" 中有相关描述)和其它动态显示限制,这更加说明了控件只应该用于控制迅速发生的事件. 使用滑块启动运行时间需要几分钟或者几小时的计算是毫无意义的. 与使用按钮相同,这里提供了一些方法可以避免出现这种超时,我们将在后面讨论这个问题.
在 Wolfram 语言中有几个滑块可供使用. 最简单的是一个水平滑块,使用它用户可以选择任何位于0和1之间的数字. 这里,我们把滑块附加到变量 x 上,并且添加该数值的一个动态显示,这样用户可以马上看到使用这个滑块的效果.
Slider 的基本用法在 "动态简介" 教程中有非常完整的描述,但是有一些用法不是很常见. 例如,用户可以通过按住修改键减缓滑块的拖动. 这个功能的细节在 "Manipulate 简介" 教程中描述.
除了连续的数值,用户也可以使用滑块在一组离散数值中选择. 这里有4个滑块,使用每个滑块都可以选择 0 和 10 之间的某些项. 第一个滑块是一个选择一个实数的连续滑块;第二个滑块是一个整数滑块;第三个滑块使用一个有理数作为步长来访问 1/3 的所有乘数;第四个滑块体现了 Slider 涵盖指定对象的一个离散集合的能力,这些指定对象可以是整数、实数或者任何其它表达式,以任意顺序排列.
与 Button 类似,Slider 有很多选项,可以改变外观和用法. Appearance 选项确定滑块“拇指”的尺寸和形状. 下面给出某些内置的变种,其中一些变种要求滑块改变它的方向. (注意,滑块由操作系统绘制,而每个操作系统支持一个略微不同的外观集合. 如果您的操作系统不支持一个滑块的外观,将使用默认的外观.)
关于 Slider 或者所有其它控件,我们将不再讨论 Enabled 和 ImageSize,它们的工作原理是完全相同的. 唯一的区别是 ImageSize->Full 仅仅被 Button 支持,而其它控件对其并不支持. 但是,Slider 和其它控件支持符号和固定的数值图像尺寸.
注意,对于不同的 ImageSize 设置,外观自动改变. 这是因为 Appearance 的默认设置是 Automatic. 如果由于某种原因,用户想要将 ImageSize 与一个不同的 Appearance 合用,这也是可以实现的. 下面,我们将使用中型尺寸绘制组件,而把整体尺寸限制在微小的尺寸.
下面介绍 Slider 的一个有趣的变种. Manipulator 控件含有与 Slider 完全相同的语法,但是还对显示添加了一个输入栏和一些动画控件. 默认情况下,这些都是可以折叠的. 点击 图标可以将其展开. (我们将看到如何很快创建与 Manipulator 不同的输入栏和动画控件.)
通过指定 Appearance->"Open",用户也可以自动显示动画控件.
Method->"Push" 允许使用一个滑块拇指推动另一个滑块. 例如,可试着拖动右手边的滑块拇指一直向左.
动画是 Wolfram 语言的另一个重要功能,而 Animator 是用来管理它们的控件名称. 一个 Animator 控件的任务是自动把变量值推到给定的域之外. 下面的 Animator 显示 x 从 0 到 1 的值. 当它到达拉杆的尾端,它就会从头再开始.
正如您可以看到的,在显示中 Animator 控件有一些不同的元素,称为 AppearanceElements. 这里的滑块给出变量的当前值(称为 "ProgressSlider");"-" 和 "+" 按钮,将值向左端或者右端微移("StepLeftButton" 和 "StepRightButton");用于切换 Animator 是否播放或停止的按钮("PlayPauseButton");以及用于控制动画的速度("FasterSlowerButtons")和方向("DirectionButton")的按钮. 用户可以基于自己的需要,改变 AppearanceElements 选项以包含上述的部分控件. 下面给出相同的动画效果,区别是 Animator 仅显示一个播放按钮.
除了给出动画在这些方面上的控件,Animator 提供设置控件初始状态的选项. AnimationRunning 使用户可以启动一个处于暂停状态的动画. AnimationDirection 使得用户可以设置一个动画的初始方向. DefaultDuration 和 AnimationRate 使得用户可以确定动画的速度.
Animator 支持所有与 Slider 在它的第二个变量中支持的域,即 {min,max}、{min,max,step} 和 {{a1,a2,a3,…}}. 另外,Animator 也支持 max 为 Infinity 以及 min 为 -Infinity.
关于动画器(animator)需要记住的一点是,与动态(dynamics)不同的是,只有当它们在屏幕上实际可见时,它们才是活跃的. 如果一个 Animator 在一个闭合的单元组中,在屏幕外滚动,或者在一些其它的隐藏位置,那么它将不会自动改变内在的变量. 如果您使用的是本文档的在线版,打开下面的单元组将使得 y 的值开始改变.
现在回到持续时间上. 这里有一些方法可以控制一个动画器运行的速度. 默认情况下,一个动画的运行速度足够快,使得它可以在5秒内从一个域的一头移到另一头. 这个时间由 DefaultDuration 选项控制. 下面有两个使用这个选项的动画器,一个的速度是另一个的两倍.
无需指定整个持续时间,用户可以通过 Animator 的可选第三个变量以秒为单位指定动画的运行速度,它是设置 AnimationRate 选项的一个便捷的变量. 这对于无穷动画特别有用,因为设置一个无穷动画的整体持续时间是没有意义的.
注意,DefaultDuration 和 AnimationRate 是最终指定动画运行速度的两种相同的方式. 默认情况下,AnimationRunning 设置为 Automatic,它表明将采用 DefaultDuration 选项值. 如果把 AnimationRate 设为其它值(例如,如果您把一个值放到 Animator 的第三个变量中),那么DefaultDuration 将被忽略.
与 DefaultDuration 和 AnimationRate 独立,您可以指定的另一个选项是 RefreshRate,它指定 Animator 应该每秒改变变量值多少次. 默认情况下,Animator 运行的速度尽量快,这样一直都需要 CPU 时间. 通过设置一个较低的 RefreshRate,内核将为其它计算提供更多的时间. 下面是使用三个不同的刷新速率的动画器. 每个动画器仍然需要5秒钟来从最小值到达最大值,但是具有较低刷新速率的动画器将在动画显示的过程中显示出较少的值.
如果需要的话,DisplayAllSteps 选项也重载 DefaultDuration 和 AnimationRate. 当它设置为 True 时,动画访问离散域的每个元素,无论它是否能在允许的时间内完成.
当使用 Animator 在一列元素中进行动画显示时,这里提供了一个语法用来指定每个框架的相对帧持续时间. 无需提供帧列表 {{a1,a2,a3,…}},把每个元素 ai 用 对替换,其中 是相应的元素的相对持续时间.
最后,AnimationRepetitions 是表明在自动停止前运行多少次动画的一个选项. 默认值是无限循环运行动画,相应设置为 AnimationRepetitions->Infinity,但是我们也可以把它设置为任何有限值. 下面的 Animator 运行两次之后停止.
事实上,Trigger 是 Animator 的一个变种,它用于使一个变量一次性穿过它的整个值域,然后停下来. Trigger 主要用于每次应该从开头开始的动画. 因此,Trigger 有一个暂停键,但是没有取消暂停的键. 这里还有一个重新设置的按钮,而且还有主触发按钮.
另一个与 Slider 和 Animator 很相似的控件是 ProgressIndicator. 正如它的名称所示,ProgressIndicator 将表现为一个平台标准的进度指示器,在大多数平台上以温度计的方式绘制. 它的语法与 Animator 和 Slider 相同,并且它所显示的值由动态变量进行传递,正如其它控件一样. 这里,我们模拟一次含有 Pause 的长时间计算,并且伴随进度显示.
对于其中有一个不正确的温度计的计算,用户可以对 ProgressIndicator 指定无穷极限,并且它将显示目前发生的变化,而不表明该计算将在何时结束.
这里还有一个较高层的函数 Monitor,该函数也能用于这种情况. Monitor 可以将任何东西显示为监控表达式,但是ProgressIndicator 特别有用. 注意,使用 Monitor 时,当第一个变量的计算结束时,监控表达式自动删除,这是非常好的功能.
这些一维的类似滑块的控件是很有用的,但是如果您想控制的是一个二维数据块,比如一个点的坐标,该怎么办呢?为了实现这个目的,我们使用 Slider2D,它帮助您将一个滑块头部移到一个给定区域的任何位置.
另一个可以在二维空间中移动的滑块是 ColorSlider,它使得用户可以通过点击或者拖动右边的颜色梯度来选择颜色. 用户也可以通过点击 ColorSlider 左边的有色方框来打开系统颜色选择对话框.
如果您想要利用单个控件使一个变量的值在二个或者更多个值中循环,您很可能需要使用 Checkbox 或者 Toggler. 下面这个简单的例子展示了如何使用 Checkbox[Dynamic[var]] 使变量 var 的值在 True 和 False 之间切换.
在它的第二个可选变量下,您不仅可以通过 True 和 False 使检验栏循环,还可以通过任何值列表. 使用 Shift+ Click 将回到前面的值. 下面我们使用 Checkbox 在制图轴的其它可能设置下循环. 我们也添加一个 DynamicModule 区分这个例子与前面的例子.
最后,一次性构建一组检验栏或者切换器有很多便利之处. TogglerBar 以一栏实际的按钮的方式排放控件,而 CheckboxBar 提供了一组带标签的检验栏. 在这些情况下,变量值以点击顺序采用目前选中的或者切换的项列表.
当然,一旦用户开始使用一组标签指定一组控件,就产生了很多关于这些控件如何准确排放的问题. 对于 CheckboxBar 和 TogglerBar,用户可以把 Appearance 设置为 "Horizontal"、"Vertical" 或者 "Row",以得到不同的布局和换行行为.
Opener 控件是 Checkbox 的另一个变种,它显示的是一个指向右方或者下方的三角符号,而不是显示一个带有或者不带有检验栏的框符. 赋给这个变量的值是一个布尔表达式,就像 Checkbox 一样.
这个学术性的例子无法在视觉上帮助您更深入理解,因为 Opener 控件的显示方式使您认为在这个控件下面应该出现一些内容. Opener 的主要用途实际上在 OpenerView 中,它通过在内部使用 Opener 以及 PaneSelector 和Grid自动提供了这个垂直布局.
另一个有用的组件是单选按钮(Radio Button),它使得用户可以从一组元素中选择一个元素. RadioButton 是Wolfram 语言创建一个单选按钮的函数. 下面的单选按钮,在选中时,将把 x 设置为 1.
现在没有使用标签,一组单选按钮的功能并不良好. 在您基于相同的变量和所有的标签指定所有单选按钮时,有大量冗余信息. Wolfram 语言提供了一个函数 RadioButtonBar,它将创建一组定义在这个相同变量上的单选按钮. 用户可以对每个单选按钮指定标签,默认的标签是它们自身的值.
对于更复杂的界面,有时候使一个单选按钮具有多选一的行为功能是有用的,同时也有提供一个完全自定义的显示的灵活性. 为了实现这个目的,我们使用 Setter 结构体. 默认情况下,看起来类似按钮(与单选按钮相同,可能按下,也可能没有按下)的一个设置器表现为或者选中或者没有选中.
总结一下,创建一组全部定义在相同变量上的设置器 SetterBar 有很多便利之处. SetterBar 对 Appearance 选项使用与 Setter 不同的一个默认设置,因此它的外观在不同平台上可能是不同的. 在它的给定外观下,用户可以把 SetterBar 当作一个无形的 TabView 控件.
注意,RadioButtonBar 和 SetterBar 接受 "Horizontal"、"Vertical" 和 "Row" 的相同的 Appearance 设置,正如 CheckboxBar 和 TogglerBar 一样.
我们已经看到如何创建并使用 radio 按钮. 但是,如果有更多的 radio 按钮,这样使用看起来就会有些不自然. 另一个让用户可以从一个集合中选择一个元素的控件是弹出菜单(popup menu). PopupMenu 函数与 RadioButtonBar 和 SetterBar 具有完全相同的语法,并且提供了相同的功能,除了具有一个不同的界面之外.
最后,第四个变量可以指定当没有人点击 PopupMenu 时所显示的任意内容. 使用第四个变量将阻止菜单绘制任何系统专用的提示信息,如控件框架或者颜色,所以开发者还可以决定用户是否可以点击自定义的显示信息.
另一类菜单的目的不是提供用于选择的 radio 按钮,而是执行与所选的项相关联的行为. 在 Wolfram 语言中,这种结构称为 ActionMenu. 您可以认为 ActionMenu 是一类“多-按钮”,它依赖于您所选择的菜单项计算其中一个函数. 它也有一些我们在 Button 中讨论的同样的警告,比如除了副作用外,它的行为实际上是不可见的.
注意,ActionMenu 的语法使用 RuleDelayed(:>)以避免在用户选中之前执行计算行为. 这说明,与 PopupMenu 不同,指定标签对 ActionMenu 来说不是可选项——它们是语法中不可或缺的部分.
最后,注意 ActionMenu 采用相同的 Method 选项 Button,它用来确定如何计算它的行为. 在默认的选项设置 Method->"Preemptive" 下,操作将立即计算,但是如果计算需要的时间多于由 DynamicEvaluationTimeout 选项指定的时间,那么将退出计算. 一个带有长时间行为的 ActionMenu 应该使用选项 Method->"Queued" 来指定计算应该放入规则计算队列,并且运行运行直至结束.
InputField 控件是独特的,因为它允许用户输入任意表达式. 在最简单的形式下,它将给定的笔记本与指定的动态变量联系在一起.
但是,InputField 也含有一个与其相关联的唯一的问题集. 例如,取决于相关的应用,用户可能想得到一个仅允许数字,仅允许字符串,或者任意排版或表达式的输入栏. 这里有大量方法可以实现这个目的. 最简单的方式是在第二个变量中定义 InputField 的一种预定义类型. 类型为 Number 的输入栏不允许键入任何除了数字以外的字符.
注意,任意表达式可以占用任意多的屏幕控件,而默认情况下,InputField 可以延伸以容纳这些空间. FieldSize 选项允许用户指定 InputField 的水平和垂直维数的最小值和最大值. 默认情况下,InputField 限制在恰好 20 ems 宽,但是却可以任意高.
另一个比大多数其它控件与 InputField 更相关的选项是 ContinuousAction. 默认情况下,InputField 没有记下新值,直至用户按下 Enter 或者 Tab 键. 但是,对于一个类型为 Number、String 和 Boxes 的 InputField ,用户可以通过设置 ContinuousAction->True 使得在每次按键的时候,都记下新数值.
设置 ContinuousAction->True, 用户也可以使用 "动态简介" 中描述的 Dynamic 的第二个变量来指定一个过滤器,它用于规范用户可以或者不可以输入的字符. 例如,InputField 使用 ToUpperCase 把输入变为大写字母.