开始动笔写书了

    上星期正式跟出版社签了约。交稿时间是明年三月,字数50万字。开始听到这个字数吓了我一跳,我以为这就是Word中显示出来的字数要五十万。累死我也写不出来这么多字啊。后来才打听明白,书籍出版的字数不是这么算的。它的算法很简单,基板上一页纸(16开)按1400~1600字计算,乘以页数,就是总字数。所以50万字的意思也就是最后出版的书在350页左右。我翻开身边几本计算机类的书籍一算,果然是这么回事。要是按照Word的统计方法,字数也许就只有一半了。而我写LabVIEW,程序片段都是图片,算不了字数,Word统计起来就更低了。

    书名一时也想不到有什么好的。要是到交稿时再想不出好名,就只好继续用博客上的名字了。

    两星期前就已经开始考虑书的内容了,已经列好了提纲。当然不能覆盖所有LabVIEW的内容,只能尽量多写一些用户的常见问题,和我擅长的方面。博客上的内容不是所有的都适合放到书中,大概挑下来也只有一半的内容能用。

    博客上的内容对于一本书来说,还是单薄了些。并且写博客是想到哪写到哪,内容并不连贯。写书的时候就要考虑内容连贯性,需要补充进大量的说明。估计最后成书的时候,来自现在博客的内容顶多只占1/4。

《我和 LabVIEW》目录

广告

开发 XControl 5 – 其他功能 VI

转换状态以保存功能VI用于保存XControl的状态数据。默认情况下,XControl外观功能VI中的状态(Display State)全部都会被保存在使用它的VI中。如果状态数据比较大,无疑会增加VI的大小。但是,这些状态也许并不需要保存。有些控件的状态,比如控件颜色,尺寸等信息,需要在VI关闭后仍然记得,在下次打开时,还可以保持上次的修改。但是有些状态中的数据,只是临时使用的,不需要保存。比如,我们的黑白棋控件状态中的任何数据,当前颜色,可落子的位置等等都是每次重新计算出来的,不需要保存下来共下次打开VI使用。所以,在转换状态以保存功能VI中,可以丢弃所有数据,保存一个空数据就可以了。

初始化VI,有两个作用,一是把保存在使用XControl的VI中的状态读取出来,付给XControl的状态。而是打开或初始化XControl需要使用到的资源。对于我们的黑白棋控件,由于没有保存任何状态数据在VI中,所以不需要读任何数据出来。而我们的黑白棋控件使用到了一个用户事件,所以需要在初始化功能VI中创建这个事件。

反初始化功能VI负责关闭XControl中打开的资源,我们在初始化功能VI中创建了一个事件,所以在这里要销毁它。

《我和 LabVIEW》目录

开发 XControl 4 – 外观功能VI

    外观功能 VI 用于定义 XControl 的界面,这里可以直接使用我们之前已经设计好的界面。把界面设计技巧 4 – 改进界面实现方法中设计好的棋盘棋子界面拷贝过来就可以了。这个外观功能VI窗口的大小,就是将来XControl控件的大小。所以这个VI的大小要刚好包裹住棋盘。

    外观功能VI的程序框图定义了当有事件发生时,XControl的反应。这是一个典型的事件处理结构。但是大家要注意到它的超时事件处理(下图),程序在这里设置了退出循环。实际上,这个程序框图并不是持续运行的,只有当XControl有事件发生时,LabVIEW才调用这一程序框图,在处理完这个事件后,立即退出这一程序框图。所以千万不要试图在这里添加持续执行的代码,比如控制XControl上的动画等等。

    从上图中还可以看到,外观功能VI有三个输入,和三个输出:
    Data In / Data Out,是XControl的数据,它的数据类型由数据功能控件定义。外观功能VI的程序框图开始运行时,Data In 输入的是XControl当前的值。程序框图运行过程中可以对这一值进行修改。修改后的值由Data Out 输出,返回给LabVIEW。
    Display State In / Display State Out,是XControl运行是用到的内部数据,在这里我们就把它成为状态。它的数据类型由状态功能控件定义。它也可以在程序运行中被改变,的输入输出方式与Data类似。
    Container State,是一个簇,用于表明XControl实例在VI面板上的状态。它有三个元素: Indicator? 表明XControl实例是否是一个显示控件,它的值为假时,表明XControl实例是一个控制控件。Run Mode? 表示XControl实例所在的VI是否处于运行状态。Refnum是指向XControl实例的引用。
    Action 用于通知LabVIEW程序在这次执行中对XControl所做的修改。它有三个元素:Data Changed?,State Changed?,Action Name。如果我们在程序中改变了Data,那么就一定要把Data Changed?设置为真,通知LabVIEW,这样改变的数据才会生效。同样,如果改变了State,则一定要把State Changed?设置为真。Action Name是一个字符串,可以给他输入一个表明这次程序运行的简短文字。这段文字会在LabVIEW的菜单项“编辑->撤销”中出现。

   外观功能VI中的事件处理结构主要处理两类事件,一类是针对XControl的特殊事件,另一类是用户在界面上操作产生的事件。

    针对XControl的特殊事件有4个:数据更改、显示状态更改、方向更改、执行状态更改。

    当把一个数值输入给XControl的实例时,就会触发数据更新事件。对于数据更新事件的处理一般是根据新的数据更新界面上的控件,和XControl的状态(Display State)。下图是黑白棋控件对数据更新事件的处理:根据新的期盼布局,刷新棋盘在界面上的显示;重新计算黑白子可以落下的位置。由于我们在处理这一事件的过程中,更新了XControl的状态,所以一定要把State Changed?设置为真。

     如果通过调用XControl的属性和方法,改变了XControl的状态的值,就会触发显示状态更改事件。对于数据更新事件的处理一般是根据新的状态值更新界面上的控件,XControl的数据和状态。下图是当用户调用黑白棋控件的“走子”这个方法,放下一颗新棋子后,外观功能VI对其的处理:放下新棋子,更新棋盘,计算新布局下黑白棋子可以放置的位置。因为这个操作及更新了黑白棋XControl数据,也更新了它的状态,所以Data Changed?和State Changed?都要被设置为真。

    当XControl实例控件由控制控件变为显示控件,或反过来变化的时候,就会触发方向更改时间。当XControl实例控件所在的VI由运行态变为编辑状态,或反向变化时,就会触发执行状态更改事件。对这两个事件的处理是类似的:在某些状态下,需要禁止用户在界面上的操作。在我们的黑白棋控件中,对这两个事件的处理是相同的。当控件为显示控件,并在运行状态时,禁止用户对界面点击。

    我们的黑白棋XControl只处理一个用户界面事件:当用户在棋盘上合法的位置点击鼠标时,走一步棋。当用户在即面上点击,首先判断这里可否落子,如果可以,则落下一子,更新XControl的数据和和状态,并产生一个事件。

《我和 LabVIEW》目录

开发 XControl 3 – 实现功能控件

    XControl 有两个功能控件,本别定义XControl的数据类型,和XControl使用到的内部数据的数据类型。

    首先考虑数据功能控件,它用于定义XControl的接线端的数据类型。我们使用一个二维的U16数组表示棋盘布局,所以在数据功能控件中要使用一个二维数组。

 

 

     其次就要来考虑状态功能控件,这个控件类型的数据在XControl的功能VI中又被称为显示状态。但它实际上的用途并不局限于帮助显示,实际上,XControl运行所需的全部变量,都应当被包含在这个功能控件中。

    左面这幅图就是我所列出的运行一个黑白棋XControl所需的一些变量。

    在我们编写的黑白棋程控件中,将会用到一下内部数据:

    method,当用户运行一个XControl的方法时,设置这一变量。这一变量对应每个方法有不同的值。这样,在XControl的外观功能VI中,就可以知道用户调用的是什么方法了。

    current color,用于表明当前应该落什么颜色的棋子。

    available black position,黑色棋子可以防止的位置。

    available white position,白色棋子可以防止的位置。

    Interactive Action,是一个用户自定义事件。当用户在棋盘上落下一子时,XControl就产生这个事件,通知使用了它的VI。

    row 和 column 用于记录上次落子的位置。

    先不需了解这里边每一个数据具体的含义和用法。在后面使用到它们的时候还会详细介绍。实际在编写XControl的时候,也不需要一次把状态功能控件就设计好。可以一边实现XControl的功能,一边对其进行补充。

 

《我和 LabVIEW》目录

如何学习 LabVIEW

    根据我自己的观察,学习LabVIEW一般有以下三种方式:系统型学习方法、探索型学习方法和目标驱动型学习方法。这三种方法之间并不矛盾,可以在不同的时段使用不同的方法。每个人可以根据自己的个性特点和所处环境选择一个适合自己的学习方案。

系统型学习方案是传统的学习方法,学生学习多是按此方法。它是指按照别人制定好的学习方案一步一步学习掌握一门知识。学习效果如何,主要取决于教师和教材的水平。若选此方案学习LabVIEW,最高效的方法莫过于参加NI公司的LabVIEW培训课程。基本上,完全没接触过LabVIEW的学员可以在一星期的时间内达到编写简单程序的程度。此外,现在很多大学都开有LabVIEW课程,方便了在校生学习。
自学也可以采用此方案。找一本教程类的书籍,按照书中指导一步一步学习。教程类的书籍应当侧重于解释LabVIEW的编程思想以及原理;有些书仅偏重于罗列LabVIEW中每个函数或VI的功能,则不适合用于此种学习方案。

探索型学习方法适合喜好自己钻研的人。同样一个技巧,如果是自己发现的,比从他人那里的来会更有成就感。任何一个教程都不可能覆盖到LabVIEW的全部功能,有心得学员不妨自己打开书中未曾介绍到的那些菜单或者函数选板,尝试一下它们都是做什么用的。在真正动手摆弄每个新东西之前,打开LabVIEW的即时帮助窗口,阅读一下相关说明可以大大加快学习过程。
比如,打开“应用程序控制”函数选板,发现这里有一项“选板编辑”。好像没有任何一本书里介绍过这个功能嘛,这是干啥用的呢?如果没任何提示,也是无从下手去尝试的。打开LabVIEW的及时帮助,可以看到它对这个功能的简单介绍。进入“详细帮助信息”,会得到更全面的说明。再自己动手实践一下,就基本可以掌握此功能了。

    阅读他人代码也是一个很好的学习方法。自己的探索总是有思维局限性的,他人解决问题的方法可以大大拓宽自己思路。我介绍过的编程经验中,很大一部分都不是我自己凭空想出来的,而是借鉴与别人的LabVIEW代码。

    目标驱动型学习方法是公司员工中最常见的学习方式了。工作后,如果不是个人有兴趣,多数人不会浪费时间去学习工作中用不到的知识。等老板布置了具体项目或者工作任务后再学习相关知识,效率更高。学也只要够解决眼前问题就行了。针对这种情况,请教身边牛人或者公司前辈是最好的学习方法。如果周围的人不能解决问题,到论坛上发贴,寻求更广泛的帮助。
推荐一个论坛。首先是NI的官方论坛,这里会有NI的技术支持和研发工程师来回答问题。如果英文够好,最好是到它的英文版面去提问,英文讨论区人气更旺,容易找到答案。LAVA 是官方之外最大的LabVIEW社区,也是寻求帮助的好地方。如果平时用Windows Live Messenger,可以加入 http://labview.groups.live.com/,这是个msn讨论LabVIEW 的群。在它上面讨论问题最大的好处是可以及时得到回应。
我见过几个工程师在项目中遇到了难题,于是来报名参加LabVIEW的培训课程,以为上完课可以解决自己的问题。但实际上完全误解了培训课程的意义,培训课程是为了帮助那些想要系统学习LabVIEW知识的人,而不专注于任何一个具体问题。

博客版《我和 LabVIEW》目录

开发 XControl 2 – 创建

    在项目浏览器上,点击鼠标右键,选择“新建->XControl”,就可以创建一个新的XControl。

    XControl 在结构上是一种特殊的库,他包含一些特定的更能VI,和一些可选的属性、方法VI及其它相关文件。在新建的 XControl 上已经包含了4个必须的功能VI(控件):数据、状态、外观、初始化。XControl 还有两个可选的功能VI:反初始化和转换状态以保存。
    简要介绍这几个功能VI的功能是:
    数据:用来定义XControl的数据类型。
    状态:定义所有XControl内部使用到的数据。
    外观:这是XControl中最主要的功能VI,用以实现XControl的界面和界面上的行为。
    初始化:设置XControl的初始状态。
    反初始化:负责清理工作。
    转换状态以保存:用于把XControl内部的某些数据保存在使用它的VI中。

    下面对XControl做一些基础的设置,比如修改它的图标、版本号等,然后保存。
    XControl功能VI的文件名并不一定与其功能名相同。比如,为了方便更多人使用,我使用了英文名称来保存我的XControl:

 

《我和 LabVIEW》目录

开发 XControl 1 – 设计

XControl 是 LabVIEW 8 开始出现的一个制作 LabVIEW 控件的工具。与之前的用户自定义控件相比,用户自定义控件只能定义控件的界面,而 XControl 还允许通过编写代码来定义控件的行为。因此 XControl 功能更加强大。
XControl 的主要优点是可以把界面元素与相关的代码封装在一起,从而方便发布和重用这些界面组件。
XControl 也有比用户自定义控件不足的地方,它开发起来更加困难;设计不合理的XControl会导致程序更加严重的问题。

需要开发一个新的控件之前,首先要考虑一下以何种方式实现这个控件。
如果这个控件极为特殊,只会用在某个特定的程序中,那么也许没有必要将其作为单独的控件;
如果这个控件需要被多次使用,那么就应该考虑把它做成可重用的独立控件。这个控件也许不包含任何特殊行为,比如一个用于表示坐标位置的控件由两个数值控件组成,程序只是使用它的值就可以了;或者一个新型按钮,进外观与旧按钮不同,其它行为都与传统的按钮一模一样。这样的控件适合使用用户自定义控件来制作。
如果新的控件需要重用,行为与已有其它控件又有较大差别,那么就要考虑XControl了。比如:制作一个新按钮,但它比传统按钮多一个状态;或者它的界面带有动画效果;制作数值类控件但是用中国本土度量单位;基于图片控件,专用于绘制某种特殊曲线等。

我们前面提到的黑白棋的控件,既有特殊界面,又有特殊行为,又可以应用于不同软件中,非常适合做成XControl。我们先来具体设计一下这个XControl所需的界面和行为。
它的界面部分前面已经设计好了,直接拿来用就可以了。不过在前文提到的几个设计方案中,我个人觉得 Pict Ring 数组控件的那个解决方案,最能简化编程代码,所以我们采用这个界面方案。
XControl 在应在程序框图上的端点的输入输出数据应该是应用程序最经常需要与XControl交互的数据。本例中,应用程序最常使用的数据就是棋盘的布局信息。因此,这个XControl的数据应当是一个8×8的整型数组,表示棋盘上棋子的布局。
黑白棋控件的属性应当包括:当前该下什么颜色的子、可落子的位置、盘面上每种颜色的子数、上次落子的位置。
它的方法有:落下一子(这个方法需要包含以下具体的操作:在新位置放置一个棋子;翻转被吃掉的棋子;更新数据和所有属性的值)。
它还要在当用户在交互界面上摆下一子之后,发个事件通知应用程序。

在这里下载这个XControl的代码:http://decibel.ni.com/content/docs/DOC-1801

博客版《我和 LabVIEW》

界面编程技巧 5 – 使用绘图控件

    有时候需要画一个比较复杂图形或曲线,而 LabVIEW 没有提供相应的控件。可以借用 LabVIEW 已有的基本功能的控件,配上一些代码,实现一个具有特定功能的控件。
    常被用来做这种基本控件有 XY Graph、3D Picture Control、Picture 控件等。
    例如需要做一个绘制极坐标函数曲线的控件,就可以在 XY Graph 的基础上改造。一共一个转换用的VI,把点的极坐标转换成直角坐标系下的值,在 XY Graph 上绘制出来就可以了。需要某个支持某种特定三维绘图方式的控件,可以通过改造 3D Picture Control 得到。
    Picture 控件是个更为基础的控件,很多具有特殊效果的界面元素都可以利用 Picture 控件制作。比如,需要制作带图标的菜单,或类似LabVIEW函数选板的菜单等。LabVIEW没有为它们提供现成的控件,就可以在Picture控件上自己把这些效果都画出来。我们前面介绍的棋盘棋子界面也可以使用Picture控件来制作。

    下面介绍一下实现这个界面的具体过程。
    第一步,创建一个空白的Picture控件,针对Picture控件的常用操作都在Picture Functions 函数选板中。

 

    与前面介绍的方法不同,使用Picture控件制作棋盘棋子的过程,不是在VI编辑状态下进行的,而是需要在程序运行时绘制。所以下面的界面设计工作都要通过编程来完成了。先介绍一下Picture控件的Erase First属性,它有3个值:0表示从不擦除,也就是说每次传一个数据给这个控件,比如一个圆环图案,Picture上显示的并非只有这个圆环,而是把圆环叠加在原本的内容之上。如果我想画一个有三个矩形组成的图案,可以分三次画,每一次传递一个矩形图案给Picture控件;2表示每次都擦除,每次传递一个图案给Picture控件,它都会将原来的图案擦掉,仅保留这一次的图形。擦除图案后Picture控件会显示默认的白色。所以,使用这种方式,用户在切换图案时会看到Picture闪烁一下。若非必要,尽量不要使用这种方式;1表示程序第一次运行时把Picture上的内容清除,等于自动帮你做了初始化工作,我们这里也使用这种方式。
    就是说,棋盘布局发生变化时,进更新发生了变动的位置。不要重绘整幅图。

    棋盘再棋子下层,所以要先画棋盘。画棋盘可以使用LabVIEW提供的划线函数,一条线一条线画出来。因为我们之前已经制作了棋盘的图片,所以可以直接把这张图片显示出来。代码如下:

    下面再画上棋盘初始时的四个棋子。画棋子的方法与棋盘相同,可以使用画圆函数,已可以使用已经制作好的图片:

    到目前为止,界面设计的几种方法就已经介绍好了。如果能够把这个黑白棋的相关界面和操作(比如放置棋子,反转棋子等)提取出来,合成一个组件,公布出来,其他有类似需求的人就可以直接利用这个组件,不再需要自己重新设计了。
    然而,在 LabVIEW 8 之前是无法实现这一功能呢,因为控制棋子行为的代码分散在程序的各处,而棋盘棋子也是主VI的一部分,很难将它们提取出来组成独立模块。LabVIEW 8 中出现的 XControl 可以把控件的界面及行为封装在一起,成为一个既有界面,又有运行代码一个组件。

    下面我就会重启一个先话题,讨论如何把这个黑白棋做成一个可以独立发布的组件。

《我和 LabVIEW》目录

界面设计技巧 4 – 改进界面实现方法

    到目前为止,棋盘棋子的界面已经基本成型。下面我们实现一小部分代码,来看看这个界面设计方案是否可行,是否可以改进。
    以棋盘的初始化为例,在游戏开始时,只有两黑两白四颗子,摆在期盼最中间。实现这个操作的代码和执行结果如下图所示:

  

    代码中的子VI(Get All Chess.vi)中的代码,就是我们在第一节图8中看到的那段代码。它负责得到所有棋子的引用,并排列成二维数组。

    这段初始化的代码并不算复杂,但是我们还是可以从中看出一些问题:棋子的布局需要用两个数组才可表达清楚,这给编程增加了负担。造成这一状况的根本原因在于:每个位置上的棋子实际上有三个状态:黑、白、无;而我们选用的灯泡控件,只有两个值:真、假。用这两个值不足以完全表达棋子的状态,所以,要两个布尔类型才能确定一个棋子的状态。
    另外,棋盘只是一张背景图片,这样,判断鼠标在棋盘哪个位置上进行的点击,也比较繁琐。一旦棋盘挪动,代码也需要做相应改动。
    改进的方法,就是使用一个有更多值的控件来表示棋子。在我们的程序中,要求每个棋子位置有多个不同图片,这正好可以使用 Pict Ring  控件。Pict Ring 控件包含三个值:空白图片、黑色棋子图片、白色棋子图片。这样即便是没有棋子的位置,Pict Ring 控件还在,可以感知用户的鼠标点击事件。

    上面的代码还有一处不足,每个棋子都是一个独立的控件,造成界面控件太多,不好管理。对于更复杂的程序,比如围棋游戏,如果使用这种方式,界面上就有将近400个控件,这是不可接受的。
    改进的方法是使用数组控件,把所有的棋子组成一个数组。

    具体的实现步骤如下:
    首先创建一个经典风格的 Pict Ring 控件,添加三幅图片:空白、黑棋子和白棋子。棋子采用的都是png文件格式的图片,以实现透明和阴影效果。

在程序中,我们不希望看见 Pict Ring 控件的边框和背景。我们可以用透明画笔,把边框和背景画为透明。

    造一个二维数组用来放置棋子元素。由于界面上不希望看到这个数组的边框和背景,所以同样用透明画笔把它们画为透明。数组的标签、索引显示可以通过数组的右键菜单->显示一项进行隐藏。
    把棋子元素放置于数组中,并把数组拉成8×8大小,放置在棋盘上放。一个棋盘棋子控件就做好了。而这种做法又大大简化了编码的复杂度,比如同样是初始化设置,只要一个赋值语句就可完成:

《我和 LabVIEW》目录

界面设计技巧 3 – 用户自定义控件

    现在棋盘已经很漂亮了,相对来说,棋子似乎残留了比较多的LabVIEW控件的痕迹,与我们要编写的游戏程序风格不符。我们可以利用用户自定义控件来造出更漂亮的棋子。

    制作自定义控件,可以从一个全新的ctl文件开始,也可以在某个已有的控件基础上进行修改。比如这里我们想把棋子周围一圈光效移走,右键点击一颗棋子,选择 Advanced->Customized,弹出控件编辑界面。按工具栏上扳手一样的按钮,切换到自定义模式,即可修改控件上的元素。这个控件有三个元素:标签、灯泡的主体部分、和边框。选中最外面那个白色的框,即边框,删除即可。编辑完成保存,新的棋子就不再有边框了。

    自定义控件也可以贴图,布尔型控件,比如按钮一般有4个状态,可以贴上4张不同的图片,做成复杂形状的按钮。下图就是通过贴图做成的一个有阴影效果的棋子按钮。

    最好所有的控件都使用严格类型定义,这样以后再需要改变界面的时候,只要在类型定义ctl文件中改动,所有的棋子就都会改变。

《我和 LabVIEW》目录

界面设计技巧 2 – 装饰和背景图片

    现在棋子都已经摆放到位了,下面考虑如何把棋盘加上去。由于棋盘是静态不动的,所以设计起来要比棋子简单。LabVIEW 自带了这种形状的装饰组件,比如线条、方块之类的,利用这些装饰图案,很容易搭出一个棋盘来。如下图,就是由几根被画成黑色的线条搭出来的部分棋盘。

  

    在编写程序界面时,装饰最常被用来将一组相关联的控件包围起来,或把不相关的控件个离开。

    不过呢,用LabVIEW自带的简单图形拼出来的棋盘始终是不够漂亮。我们可以先用专业的画图工具,比如画图板(也不怎么专业吗)画一个漂亮的棋盘,保存成图片文件。然后把图片贴到VI的前面板,当作背景图片。这样,就可以得到一个漂亮的多的棋盘了。
    贴图这个操作,可以用Ctrl+C,Ctrl+V,也可以直接在文件浏览器中,用鼠标把图片文件拖拽到VI前面板。
    拖拽到VI上的图片,是处在界面最上层的,覆盖住了棋子。利用Reorder工具中的“Move to Back”,把它挪到最下层。在调整好棋子和棋盘的位置,整个一个棋子棋盘界面就做好了。如下图:

 

    棋盘棋子都摆好之后,它们的相对位置应该固定下来。如果需要它在界面上挪动,应该是所有的棋子和棋盘一起动。先用鼠标把棋盘和全部棋子选中,再在Reorder工具中,选择group就可以把它们设定为一组。之后他们之间的相对位置就固定下来了。

    咱们刚刚贴上来的图片是个矩形的,可是有时候,需要背景图片是不规则形状的。这种情况下需要使用支持透明色的图片格式,比如gif格式,把不规则图片空白部分设为透明即可。还有一种常用的文件格式png格式,支持像素点透明度的设置,利用不同的透明度设置还可以给背景图片做出阴影等效果。例如下图VI界面中两个带粉色的带阴影效果的解说框,使用的就是png文件格式的图片。

    这样子贴上来的背景图片大小尺寸是固定的。有时候我们希望整个前面板被某一背景图片铺满。这个设置是在窗格的属性中设定的。鼠标右键点击前面板窗口的滚动条,选择“属性”,在“背景”设置中选择一个背景,或添加一幅自己的图片,就可以为VI前面板设置上背景图片了。下图是使用的“Clouds”背景的效果:

 《我和 LabVIEW》目录

界面设计技巧 1 – 利用 LabVIEW 自带控件

我前面讲了一堆设计界面的规范和原则,下面介绍一些具体的技巧,可以让界面编写更快捷、美观。
我们需要一个具体示例来帮助介绍这些的技巧,我打算以编写一个黑白棋游戏的界面为例。选择黑白棋是因为这个游戏的界面在常见棋类中比较简单,适合做范例。另外,它也是我最开始学习LabVIEW时的练习程序之一,比较有感情:) 黑白棋的棋盘由8×8个正方格组成,旗子为黑白两色,放置在方格中。(黑白棋游戏详细规则:http://www.othello.cn/rules.htm
编写这样一个界面可以使用到多种不同的思路和技巧,我会按照从简到繁的顺序,分几次来介绍几个不同的方法。

界面设计的时候,首先要调查一下看能不能使用已有的控件。借用已有控件可以大大节省我们自己的开发时间了。我们这个游戏界面上的按钮、文本框等自然可以使用LabVIEW自带的控件;黑白棋的棋盘棋子,也可以上网去找找看有没有别人已经做好的可供使用。
假如没有现成的棋盘棋子控件,那就要我们自己来做一个了。虽然作为整体,没有现成的东西可用,但把它细分成小的基础部分,还是有可能利用一些已有控件的。

比如说棋子:这个游戏的棋子为圆形,只有黑白两色,个数最多64个。这个特点很适合用 LabVIEW 中的圆形LED灯泡来表示。圆形LED灯泡控件如下图所示:

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

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

下面我们要把这些棋子排列整齐。如果有耐心,可以用鼠标一个一个的调整每个棋子的位置。LabVIEW 提供了几个小工具来帮我们整理界面控件的位置和大小,它们就是工具条上,字体调整按钮右侧的四个按钮。这四个个按钮分别用于对齐控件,调整控件间距调整控件大小和控件前后次序。这几个工具在编辑界面时会经常使用到。

我们先把首先利用对齐工具把首行和首列棋子对齐、再利用间距调整按钮使它们间距均匀。再利用对齐工具让其它棋子都与首行首列对齐即可。调整好的界面如下:

到此为止,棋子的界面部分就完全设计好了。但是我们还要考虑一下相关的代码。棋子在程序运行过程中时发生变化的。
64颗棋子并不都是显示在屏幕上的。游戏一开始,屏幕上只有四颗棋子,以后每走一步多一颗棋子。LabVIEW 每个控件都有一个属性“Visible”,控制控件是否在前面板上显示出来。棋盘的某个位置还没有放棋子时,可将该位置的棋子控件隐藏。

设计界面时,经常遇到有些控件只在某种特定情况下出现。这样的问题有两种最常见解决方案,一是我们刚刚提到的,可以在不需要看见某个控件时设置它的Visible属性,将其隐藏。这种方法代码编写比较简单,但是不利于界面编辑。尤其当界面某一位置需要在不同情况下出现多种不同控件的情况下。几个几个控件需要在那个位置上重叠摆放,不利于对控件进行编辑调整。
第二种方法是通过控件的Position属性,设置它在界面上的位置。需要显示控件时,把它设定到应该出现的位置;需要隐藏它的时候,把它挪到VI前面板可视范围之外的某个位置上,这样就看不到它了。使用这种方法,始终可以在VI前面板上找到这个控件进行编辑修改。但是编程的时候相对繁琐,需要在程序中设定控件的位置。
如果有一组控件需要同时出现或隐藏,那还可以考虑利用tab控件。把这组控件加在tab的某个页面上,然后通过调整tab的显示页面,控制控件出现与否。

打开程序的框图,64个控件端子排布在那里。对它们分别进行操作,程序代码将会非常杂乱难懂。为了让程序更清晰,最好把这64个控件按照在棋盘上的位置,组织成一个8×8的二维数组。之后,程序对哪个位置的棋子进行操作就一目了然了。

直接把它们组成数组的方法是:为每个控件建立一个引用,然后使用 build array 函数把它们组织起来。但是对64个控件进行一一操作还是够烦的,最好可以编程解决。由于这64个棋子的名字是有规律的,因此我们可编程,按照名字一一等到这些控件的引用。再将得到的引用转换成8×8的数组。如下图所示的代码

这里使用了一个关键的子VI,Get Control.vi。这是LabVIEW自带的一个VI([LabVIEW]\resource\importtools\Common\VI Scripting\VI\Front Panel\Method\Get Control.vi),它用来按名称得到前面板上控件的引用。
这段代码输出的 chess array 是一个8×8数组,包含了所有64个控件。之后程序再对棋子进行操作,从这里得到相应位置的棋子的引用即可对其进行操作了。

实际工作中,有些应用程序有比较复杂的界面,为了简化它的代码,对界面控件的操作被放置在子VI中完成。直观的做法也是:程序开始时为主程序的控件建立引用,把这些引用捆绑成一个簇,传递到子VI中去。但是,一旦界面发生变化,所有使用到这个簇的VI都可能需要被修改,相当不便。所以,这样的程序也可以使用上段文字介绍的方案,只把主VI的引用传递给子VI,在使用到某个主VI控件的时候,按照名字得到它的引用再对其进行操作。

博客版《我和 LabVIEW》

用户界面设计 5 – 限制

    保障软件的可靠性是软件开发者的责任。如果用户误操作,或者提供了错误的数据给程序,稳定的程序可以组织程序继续运行并报告错误。但这毕竟是亡羊补牢的做法,更完美的解决方案应倒是从根源上就杜绝误操作和错误的输入数据。
    所以,在做界面设计时,还应考虑如何限制用户的输入数据和操作。禁止误操作出现,把输入数据都限制在合理的范围内。

一、限制输入数据

    LabVIEW 的某些控件本身就带有对输入数据进行限制的功能。比如数值型控件,在它的属性对话框中的 Data Entry 页,可以设置这个控件接受的数据的范围。我有一个控件用来表示选取某个通道,可供使用的合法数据为通道0至通道3,我们就可以在这一页把控件的最大最小值分别设为3和0。如下图:

 
图1:数据范围限制

    这样设置后,用户也许还会输入一个不合理的数值,比如99,但LabVIEW 会立即忽略这个不合理数值。
    有时,还有更好的限制方法:让用户根本没办法选择不合理的数据。比如本例,我们在设计时,可以考虑使用 Enum 或 Ring 型控件来表示通道号,这样用户只能在正确的值中选择一个。如下图:


图2:枚举型数据

    除了 Enum 或 Ring 型控件,单选按钮也可以起到同样的效果。单选按钮可以直接就在界面上显示出所有可供选择的值,并且可以附带对每个选项的详细解释。不经常被用到的对话框可以采用这种控件。比如下图,是VI属性中设置密码的页面。

 
图3:使用选择按钮的界面

二、防止误操作

    一个简单的规则:让所有但是不应被改动的控件都失效。大家看图3中的修改密码按钮是灰色的。因为这是用户选择的是无密码,所以当时不应出现修改密码的操作出现。与其让用户判断是否可以按这个按钮,不如直接禁止它的使用,以防用户错误的按下它发生不可预期的错误。当用户有密码设置后,再允许这个按键被使用。

《我和 LabVIEW》目录

怎样根据错误代码得到错误信息

    大多数 VI 都会带有错误处理机制,所以 VI 的前面板上会有 error in/error out 控件。如果发现有返回错误代码,之间在空间边缘处点击鼠标右键,选择 Explain Error 就可以看到详细的错误信息。
    在 Explain Error 对话框上改变错误代码,即可查看到任意一个错误代码的相关信息。也可以通过菜单 Help->Explain Error 打开这个对话框。


图1:使用 Explain Error 对话框

    如果是在程序当中需要动态的得到一个错误代码的信息,可以使用 [LabVIEW]\vi.lib\Utility\error.llb\Error Code Database.vi。这个 VI 可以根据输入的错误代码返回错误信息。

《我和 LabVIEW》目录