界面设计实例
虽然上面已经介绍了很多界面设计的原理和规范,但实际界面设计是非常灵活的,同一个功能可以有很多种设计方式。
为了对界面设计中的各种方法有进一步的了解,下面用编写一个黑白棋游戏的界面为例,来介绍一些具体的技巧。利用这些技巧可以让界面编写更快捷、美观。之所以选择黑白棋,是因为这个游戏比较有趣味性,而它的界面又不是特别复杂,适合作为教学范例。黑白棋的棋盘由 8×8 个正方格组成,棋子为黑白两色,放置在方格中。关于这个游戏的游戏规则,在互联网上可以方便地查找到。
编写这样一个界面可以使用多种不同的思路和技巧,以下将按照从简到繁的顺序,分几步来介绍几个不同的方法。
利用 LabVIEW 自带控件
界面设计的时候,首先要考虑尽可能使用或借助已有的控件。借用已有控件可以大大缩短开发时间。这个游戏界面上的按钮、文本框等均可以使用 LabVIEW 自带的控件。黑白棋的棋盘棋子,也可以上网找一找,看有没有别人已经做好的可供使用的控件。
假如没有现成的控件可以借用,那就得自己动手做了。有时,虽然作为整体,没有现成的东西可用,但在把它分解成更小的基础部分后,完全有可能利用一些已有控件的。
比如说棋子:这个游戏的棋子为圆形,只有黑白两色,个数最多 64 个。这个特点很适合用 LabVIEW 中的圆形指示灯控件来表示,圆形指示灯的外观与 LED 灯泡相似:

为了使它看起来更像真的棋子模样,还要对它进行一些加工:把它的尺寸调大;用工具选板上的颜色画笔工具把它在“真”“假”状态下的颜色分别设置成黑色和白色;给它起一个有意义的标签名:"chess 0",并在前面板上把标签隐藏起来,标签名称是为了以后编程用的。改进后的棋子,外观如下图所示:

黑白棋总共需要 64 个这样的棋子,并排成 8 行 8 列。其它棋子可以以第一个棋子为模板,拷贝复制,就生成了第二个;再把两个棋子都选中,复制生成四个;重复这一过程,依次生成了 8、16、32、64 个棋子。如下图所示:

接下来需要把这些棋子排列整齐。利用工具条上的 "对齐对象","分布对象","调整对象大小" 等几个工具,可快速地把 64 个棋子排列整齐。首先利用对齐工具把首行和首列棋子对齐、再利用间距调整按钮使它们间距均匀。然后利用对齐工具让其它棋子都与首行首列对齐即可。调整后的界面如下图所示:

到此为止,棋子的界面部分就完全设计好了。我们需要进一步考虑编写棋子在游戏过程中不断变化时的相关程序代码。
实现运行时改变界面的代码
64 颗棋子并非总是显示在屏幕上的。游戏一开始,屏幕上只有四颗棋子,以后每走一步多一颗棋子。LabVIEW 的每个控件都有一个 "可见(Visible)" 属性,用来输入控件是否需要在前面板上显示出来。棋盘的某个位置还没有放棋子时,可将该位置的棋子控件隐藏起来。这只需要一个非常简 单的控件属性设置操作:
设计界面时,经常会遇到这种情况:有些控件在某些特定情况下出现在界面上,在另一些情况下必须隐藏起来。解决这一类问题有几种最常见的解决方案。
一是上面提到的,可以在不需要看见某个控件时设置它的可见属性为 "假",将其隐藏;在需要显示时,再将它的可见属性设置为真。假如控件数量较少,则这种方法编写代码比较简单,但是不易于界面编辑调试。有时候程序停止时,控件却恰好隐藏了,还需要先找到它、把它变为可见才能对其编辑修改。此外,在有多种不同的控件需要在界面的同一位置轮流出现的情况下,编辑调试程序更是麻烦。多个控件需要在同一位置上重叠摆放,必然有些控件被其他控件遮挡,很不利于对其进行编辑调整。
第二种方法是通过控件的 "位置(Position)" 属性,设置它在界面上的位置。需要显示控件时,把它设定到应该出现的位置;需要隐藏它的时候,把它移出 VI 前面板的可视范围。比如说,VI 前面板显示区域的大小是 500×400,把控件挪到相对左上角 100×600 的位置,用户就不会在程序运行时的界面上看到这个控件了。而需要编辑这个控件的时候,可以拖动 VI 前面板上的滚动条,找到控件,再对其编辑。
使用这种方法,优点是在任何情况下都可以在 VI 前面板上找到控件,进行编辑修改。但是需要在程序中设定控件的位置,需要精心设计控件的位置坐标,编程相对繁琐。
如果有一组控件需要同时出现或隐藏,还可以考虑利用选项卡控件。把这一组控件添加在选项卡控件的某个页面上,调整选项卡控件的显示页面,就控制了控件是否出现。如果把选项卡设置为透明,则可以更加灵活地输入控件的显示或隐藏。向导类型的界面是一种比较常见的界面类型,用户完成一个步骤的操作,点击界面上的 "下一步" 或者 "上一步" 按钮,就会出现一个包含了新内容的新界面。这类界面非常适合使用选项卡控件。选项卡控件的每一页对应向导中的每一个步骤。点击 "下一步" 或者 "上一步",便可以切换到选项卡的另一页。
重新回到黑白棋游戏程序上来。打开程序的框图,可以看到整齐排列着的 64 个控件端子:

假如要分别对它们一一进行操作,光这 64 个控件端子的连线就已经使得程序代码变得非常杂乱难懂了。为了让程序简明清晰,可以把这 64 个控件按照其在棋盘上的位置,组织成一个 8×8 的二维数组。之后,程序要对哪个位置的棋子进行操作就一目了然了。
直接把它们组成数组的方法是为每个控件建立一个引用,然后使用创建数组函数把它们组织起来。但是对 64 个控件进行一一操作还是够麻烦的。这其实完全可以通过编程解决。由于这 64 粒棋子的名字是有规律的,因此可通过如下图所示的程序,按照名字一一得到这些控件的引用,再将得到的引用转换成 8×8 的数组。

在这段程序中,有一个名为 "Get Control.vi" 的子 VI。它的功能是按照控件的名字得到一个控件的引用。要得到一个控件的引用有很多种方法,因为这个 VI 已经存在于 LabVIEW 中([LabVIEW]\resource\importtools\Common\VI Scripting\VI\Front Panel\Method\Get Control.vi), 因此可以直接使用。
(注意: 使用 Get Control.vi 是为了方便演示,但这是 NI 的内部保留代码,随时可能在未来的版本中被删除或修改,导致程序无法运行。对于正式项目,建议避免使用这种内部 VI,而是应该使用前面板的 Controls[] 属性节点获取所有控件的引用数组,然后在 For 循环中通过读取“标签 -> 文本”属性来比对查找。)
这段代码输出的 "chess array" 是一个 8×8 数组,它包含了所有 64 个控件。往后,程序需对棋子进行操作,可以先从中得到相应位置的棋子的引用,再对其进行操作。
实际编程时,可能会需要编写更为复杂的界面。而且,需要在子 VI 中对界面上的多个控件的属性进行设置。这需要把这些控件的引用全部传递到子 VI 中去。有一个简便直观的做法,即在程序开始时,为主 VI 界面上控件建立引用,把这些引用捆绑成一个簇,传递到子 VI 中去。因为控件的个数较多,使用簇可以减少程序框图上数据线的数量。
这种方法的缺点也很明显,一旦界面发生变化,比如增减或改变了某个控件。这个包含所有控件引用的簇自然而然也要被修改,而所有使用到这个簇的 VI 也都可能需要被修改,相当不便。所以,这样的程序也可以使用上段文字介绍的、使用 "Get Control.vi" 子 VI 方案,只把主 VI 的引用传递给子 VI,在使用到某个主 VI 控件的时候,按照名字得到它的引用再对其进行操作。
装饰和背景图片
棋子都已经摆放到位了,下面考虑如何把棋盘加上去。由于棋盘是静态不动的,设计比较简 单。LabVIEW 自带了包括线条、方块等各种形状的装饰组件(在控件选板 "新式 -> 修饰" 中):

利用这些装饰图案,很容易搭建出一个棋盘来。下图就是由几根黑色线条组成的部分棋盘。

在编写程序界面时,装饰最常用的用法,是用来将一组相关联的控件包围起来,或把不相关的控件相互隔离开。
利用 LabVIEW 自带的简单图形拼画出来的棋盘也许不够漂亮。想得到更漂亮的棋盘,可以先使用专业画图工具(或者画图板)先画一个漂亮的棋盘,保存成图片文件。然后,把图片贴到 VI 的前面板,当作背景图片。这样,就可以得到一个漂亮的棋盘了。
若需把图片放置到 VI 前面板上,可以用 Ctrl+C、Ctrl+V 复制图片;也可以直接在文件浏览器中,用鼠标把图片文件拖拽到 VI 前面板。拖拽到 VI 上的图片,位于界面最上层,覆盖住了棋子。利用 VI 前面板工具条上 "重新排序" 工具中的 "移至后面",可以把它挪到最下层。调整好棋子和棋盘的位置,如下图所示,一个棋子棋盘界面就做好了。

棋盘棋子的相对位置应该是固定的。用鼠标把棋盘和全部棋子选中,再在 "重新排序" 工具中选择 "组合" ,就可以把它们设定为一组。之后,这一组控件之间的相对位置就固定下来了,只能同时在界面上同步移动。而如果选择了 "锁定",则被选中的控件将被完全锁定在界面上,不能移动。
上图中粘贴上的图片是矩形的,可是有时候也许需要不规则形状的背景图片。这需要使用支持透明色的图片格式,比如 gif 格式,把不规则图片空白部分设为透明即可。还有一种常用的文件格式 png 格式,支持像素点透明度的设置,利用不同的透明度设置还可以给背景图片做出阴影等效果。例如下图中的 VI,它的界面上两个粉色的带阴影效果的解说框,使用的就是 png 文件格式的图片。

编写程序过程中,界面的尺寸随时可能更改,而通过粘贴得到的图片,其大小尺寸是固定的,故不适合做背景图片。而通过设置 VI 前面板窗格的属性,可以让整个前面板都被某一背景图片铺满。鼠标右键点击前面板窗口的滚动条,选择 "属性",在 "背景" 设置中选择一个背景,或添加一幅所选的图片,就可以在 VI 前面板上铺设所选的背景图片了。下图是选择 "Clouds" 背景的效果。

用户自定义控件
至此,棋盘已经很漂亮了,而棋子看上去仍然像个灯泡。可以通过制作自定义控件, 为棋子制作一个外观如同真实棋子那样的专用控件。
对棋子外观影响最大的是棋子周围一圈的光影效果,可以设法把这个光圈移走。右键点击一颗棋子,按照用户自定义控件一节介绍的制作用户自定义控件的方法,选中棋子最外面那个白色的框,即边框,删除即可。编辑完成后保存,新的棋子就不再有边框了。其实针对这个 "棋子" 而言,去除边框圈有一个最简单的办法:在该控件的右键菜单的 "显示项" 栏中,放弃选择 "始终"(英文是 decal,正确中文翻译应为 "印花花边")项即可:

如果需要进一步制作更加美观的自定义控件,那就需要利用专业制图工具做出精美的图片,再应用于控件上。下图是通过专业图像处理软件做成的一个带有阴影效果的棋子按钮。

所有的控件都可以选择严格类型定义。以后,如若在严格类型定义 ctl 文件中改动了控件,则界面上所有用到的该自定义控件都会有相应的一模一样的改动。
改进界面的实现方法
到目前为止,棋盘棋子的界面已经基本成型。下面编写一小段代码,来验证这个界面设计方案,看看是否可行,如何改进。
以棋盘的初始化为例。在游戏开始时,只有两黑、两白,共四颗棋子,摆在棋盘的最中央。实现这个操作的代码如下:

它的运行结果如下:

示例中的子 VI,Get All Chess.vi,负责得到所有棋子的引用,并排列成二维数组。
这段程序并不算复杂,但是还是可以发现其中存在一些问题。棋子的布局需要用两个数组才可表达清楚,这给编程增加了负担。造成这一状况的根本原因在于:每个位置上的棋子实际上存在三种状态:黑、白、无。而灯泡控件,只有两个值:真、假。用这两个值还不能完全表达棋子的状态,必须还要利用控件的可见属性。所以,要两个布尔类型的数组才能确定一个棋子的状态。
另外,要判断鼠标点击在棋盘哪个位置上,也比较繁琐。程序中只能得到鼠标相对于 VI 前面板左上角的位置信息。一旦棋盘挪动,用于计算鼠标点击位置的代码也需要做相应改动。
为此,考虑对这个界面再进行一点改进,使用一个具有更多值的控件来表示棋子。在这个程序中,可以使用 "经典 -> 经典下拉列表和枚举 -> 图片下拉列表" 控件。为图片下拉列表控件设定三个值:空白图片、黑色棋子图片、白色棋子图片: