设计:同一功能对应多种不同界面的应用程序

虽然我以前研究过动态注册事件这一功能(3.7.6),但是还没有想到,这个功能有什么实际用途。直到前几天,一个客户向我介绍了他们工作中遇到的困难,我才突然想到,他们的问题恰好可以使用动态注册事件来解决。

他们的问题是这样的:他们开发了一套软件销售给多个用户。用户们对软件功能的需求是一致的,但他们对软件的界面却有各自不同的需求。比如界面的语言、控件的位置、尺寸、颜色等都不同。对于软件开发人员来说,最好的解决方案是程序的代码(程序框图)只有一份,而界面(VI前面板)有多份。常规方法是无法解决这个问题的,因为LabVIEW中每个VI只能对应一个前面板和一个程序框图。
程序中实现界面的那个VI通常是主VI,这个VI中的代码都是比较复杂的。一个程序维护多份功能相同而又复杂的主VI并不是一种优化的方法,发现一个bug,要到每个VI中去改。

动态注册事件的一个用途就是把界面和程序代码完全分离开来。遇到上文客户提出的需求,可以编写多个只有界面的VI,和一个没有界面只有程序框图的VI(这里说的只有界面和只有程序框图,并不是说那个VI真的没有程序框图或界面,只是说它的程序框图或界面特别简单,没有实质内容)。不同界面VI中控件的标签要有统一命名,以便被实现功能那个VI按照控件名来设置或读取控件参数。每个界面上其它的设置,都可以各不相同。界面VI的程序框图需要有一个空循环,以便它可以持续的运行。

程序真正实现功能的那个主VI不需要有界面,它要在后台运行。主程序的主体框架还是传统的循环事件结构。主程序开启后,把界面VI运行起来,需要使用不同的界面,只要在这里修改一下界面VI的路径就可以了。然后,主程序再通过控件的标签得到每一个控件的引用,再把它们注册到事件结构中去。这样一来,主程序就具备了和传统的主VI一样的能力了,读写界面上控件的值,接收控件发出的事件。

这样一来,程序中不再有代码重复的VI,可维护性大大加强。

————————————————————————

 

2011.3.29 今天应网友要求,给这个设计写了一个示例:

示例由三个VI组成。Main.vi实现软件的功能,Interface1,和Interface2分别是程序两个不同风格的界面。

image

我为程序设计了一个非常简单的功能,在界面上点一下按钮,就返回一个随机数值。程序的功能是在Main.vi中实现的,它采用的是经典的事件结构。与一般程序不同的是,它没有界面控件,它所捕获的事件都是通过动态注册生成的。

image

Main.vi的前面板用于把界面VI上控件的引用传递过来。

image

Interface1.vi,它的前面板就是程序界面:

image

它的程序框图不需要做任何实质工作,只要把界面上的控件的引用传递给Main.vi就可以了。

image

Interface2.vi只是与第一种界面风格略有不同:

image

它的程序框图与前一VI完全相同:

image

 

通过这种设计,把程序的界面与功能完全分离到了两个不同的VI中。因此,可以方便的只改变程序的界面而又完全不动程序的功能代码部分。

示例程序可以从这里下载:http://decibel.ni.com/content/docs/DOC-15583

Advertisements

12 thoughts on “设计:同一功能对应多种不同界面的应用程序

  1. 我之前有个这相发,觉得是可行的,也没有真正给它写代码。既然你提出来需要一个范例,我今天就抽空做了一个简单的例子。我会把这个例子的介绍就添加在原文的下面。

    • 阮工:
      你好!你给的例程是通过在界面VI中调用代码VI实现的,如果界面上的控件比较多的话,那么传给代码VI的输入参数就比较多,有没有方法不通过直接调用代码VI,而是通过控件名来传值,比如通过VI的Set Value或Get Value方法等,或有更好的方法。谢谢!

      • 你可以把界面VI的引用传给Main.vi,然后再Main.vi中通过控件名就可以得到对应控件的引用,之后就可以通过来做赋值、事件响应等的操作了。
        你可以参考这篇文章里关于得到控件引用的部分:https://ruanqizhen.wordpress.com/2008/07/10/%E7%95%8C%E9%9D%A2%E8%AE%BE%E8%AE%A1%E6%8A%80%E5%B7%A7-1-%E5%88%A9%E7%94%A8-labview-%E8%87%AA%E5%B8%A6%E6%8E%A7%E4%BB%B6/
        或者你有我的书的话,对应的内容在6.2.3和10.3.2节中。

        • 阮工:
          你好!那如果通过这种方法,那在主VI中注册动态事件时,只能注册面板中所有Control Value Change事件,就不能像你例程中那样对每个控件注册一个动态事件了,只能在事件结构中再判断是哪个控件发生了变化,是这样吗?顺便问一下,在LabVIEW的安装目录下有哪几个目录下的VI是非常有用,但在程序面板中没有显示出来的?就像你的一个例程中Get Control这样的VI。你对LabVIEW的文件结构可能更了解。非常感谢!

          • 我喜欢把界面上的控件组合成一个一个的簇来存放,这些簇全都用严格类型定义。把这些簇的引用传入子vi再去做处理,跟用Get Control差不多,不过会稍微好用一点,毕竟你自己做过分门别类,不知道这个用法怎么样。

          • 我的意思是把界面VI的引用作为参数。再通过VI的引用,得到VI上面每个控件的引用。在Main.vi中得到Interface.vi上控件的引用之后,就既可以使用这个引用注册事件,也可以使用这个引用修改属性值了。

            • 阮工:
              你好!可能我还是没有说明我的意思,我的意思是你在interface.vi中是把Main.vi作为子vi调用的,你的例程中interface.vi上是三个控件,你的Main.vi的输入参数就是三个,因为Main.vi中你注册动态事件时是对每个控件的引用分别注册动态事件的,因为对每个控件事件的处理是不一样的,如果interface.vi上的控件比较多时,Main.vi就要有很多输入参数,我的意思是能否有一种方法,当interface.vi上的控件比较多时,Main.vi的输入参数比较少,但又能实现你例程的功能。谢谢!

              • 我的意思是:在interface.vi与Main.vi之间,只有一个参数,是interface.vi的引用。
                在Main.vi中通过这一个引用就可以得到interface.vi上所有的控件。
                建议你看一下我发给你的链接。

            • 对,是这样,阮老师是从宏观的角度,我的角度比较微观一点:)。我的意思是,在Interface1和2里,如果控件数量很多,如果不用簇来规整的话,直接去Get Control,注册事件的时候就会非常多,比如例子里是2个按钮,就是注册两个value change,如果是10个的话就得下拉10个。如果把这10个按钮组成一个簇,注册的时候只需一次,事件处理的时候,可以写在一个事件里(cluster-All Element)。如果用簇把控件组合成几块,在Main.vi里注册和处理事件的时候就会相对少一点。另外,再把这些簇的引用做成type definition,如果interface上的控件有更改的话,修改起来也比较方便。

  2. 阮工:
    你好!我想要利用这个功能,能否给一个简单的例子,尤其是真正执行代码的那个VI,多谢了!从界面VI我是利用了VI的Set Value和Get Value方法,虽然我了解了一些动态注册事件,但代码VI我还是不知怎样去实现。

  3. 使用属性节点的确比直接读写值效率低下。但是,界面不同于数学运算,在界面操作中,效率的瓶颈是人的反应速度,而不是计算机的云算速度。一个界面,假设有上千个控件,全部使用属性节点,可能效率最多低几个毫秒,这几个毫秒,人眼根本无法察觉。

  4. 阮大侠,这样做的话,因为只有控件的引用,读取和赋值都得通过属性节点,岂不是程序效率会大大降低?

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s