我现在开始用上Beta版的Firefox了,它增加了一个我非常喜欢的功能,就是Tab分组管理功能。估计凭借这个功能,Firefox能从Chrome那里抢回一点客户。
网页越开越多,是很难管理的,分了组管理起来就方便多了。这有一个官方的视频介绍:http://videos.mozilla.org/serv/firefox4beta/grouptabs.webm
我现在开始用上Beta版的Firefox了,它增加了一个我非常喜欢的功能,就是Tab分组管理功能。估计凭借这个功能,Firefox能从Chrome那里抢回一点客户。
网页越开越多,是很难管理的,分了组管理起来就方便多了。这有一个官方的视频介绍:http://videos.mozilla.org/serv/firefox4beta/grouptabs.webm
虽然我以前研究过动态注册事件这一功能(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分别是程序两个不同风格的界面。
我为程序设计了一个非常简单的功能,在界面上点一下按钮,就返回一个随机数值。程序的功能是在Main.vi中实现的,它采用的是经典的事件结构。与一般程序不同的是,它没有界面控件,它所捕获的事件都是通过动态注册生成的。
Main.vi的前面板用于把界面VI上控件的引用传递过来。
Interface1.vi,它的前面板就是程序界面:
它的程序框图不需要做任何实质工作,只要把界面上的控件的引用传递给Main.vi就可以了。
Interface2.vi只是与第一种界面风格略有不同:
它的程序框图与前一VI完全相同:
通过这种设计,把程序的界面与功能完全分离到了两个不同的VI中。因此,可以方便的只改变程序的界面而又完全不动程序的功能代码部分。
示例程序可以从这里下载:http://decibel.ni.com/content/docs/DOC-15583
(2.3.6) (13.3.5)
编程的时候经常遇到这种情况:需要保存一些数据,这些数据的组织类似一张表格。表格有很多类型相同的条目组成,每个条目由一个标识符(比如ID,名称等),和其它一些数据组成。程序会保存这张表格、对表格中的条目做修改、根据标识符快速找到某的条目。
实现这样的程序功能在C++或C#中非常简单,只要使用他们提供的Map,Dictionary容器就可以了,这些容器已经把维护和查找这张表的功能都实现好了,用户只需使用它们的几个简单接口函数。遗憾的是,LabVIEW中没有提供类似的容器。LabVIEW通常自己定义一个簇的数组来保存表单中的数据,然后再自己编写查询数据的代码。这样做一是编程复杂,而是查询效率太低。
我曾经想过给LabVIEW添加一个这样功能的容器,但是没有得到批准。不过,我最近发现,原来LabVIEW中有三个函数,可以实现类似的功能,虽然它们原本可能并不是用来做这个功能的。这三个函数的本职工作是为变体数据类型设置属性,它们在“Programming->Classter,Class&Variant->Variant”函数选板下,分别是Get Variant Attribute, Set Variant Attribute, Delete Variant Attribute 几个函数。从它们的名字就能知道他们是做什么的了。
变体数据的的属性由一个名字和一个数据组成。每个属性的名字都必须是唯一的,数据可以是任何数据类型的。这样,它恰好适合于前文提到测程序需求。我们可以把表单中的每一个条目看作是一个属性;条目的标识就是属性的名称;条目的其它部分是属性的数据。创建和修改表单使用Set Variant Attribute,Delete Variant Attribute 函数;查询表单的内容使用Get Variant Attribute函数就可以了。
变体的属性在LabVIEW中是以哈希表格式存储的,它的查询效率极高,所以特别适合用于需要大量查询的程序。
它毕竟不是一个真正的容器,还有一些局限性,比如标识只能使用字符串。
https://lv.qizhen.xyz/pattern_ui
一般来说一个程序的主程序,就是他的主界面。编写这样的程序是有固定套路的,也就是它的设计模式。
在主程序中,少不了对用户界面操作的处理,因此事件结构时必须的。同时,它也会处理一些非界面上的任务,因此程序也必须还有一个选择结构以应对其它工作。这样一来,就有两种程序结构可供选择了:1.选择结构在外,事件结构在内;2.事件结构在外,选择结构在内。
今天听说Google放弃Wave这个产品了,真是可惜。我还是挺喜欢这个产品的,它比Email功能强大多了。
不过喜欢归喜欢,我自己也是只有在Wave新出来的时候玩了几下,之后就再也没去看过。不去的主要原因是因为我希望能够在一个地方看到自己所有的email,而不是到多个网站上去挨个查收,既然已经有了Gmail,还去看Wave干嘛呢。
要是Google能够在Gmail中提供一个选项,允许在Gmail中启用Wave一样的界面就好了。
这里说的框架插件结构是指程序启动时或运行中,去查看硬盘某个路径下,有哪些插件模块,然后把它们调用起来的这样一种程序结构。LabVIEW中实现这样的程序机构有两种方法,一是利用LabVIEW Scripting中动的态调用VI,二是利用LVClass。下面比较一下这两种方法各自的优缺点。
动态调用VI | LVClass | |
LabVIEW版本 | LabVIEW很早的版本就具备了动态调用VI的功能。 | LabVIEW 8.2开始才支持面向对象的程序设计。 |
开发的难度 | 相对来说比较直观易懂。 | 对于已经了解了LabVIEW面向对象编程的用户来说,做一个插件也是比较简单的。但是如果完全没接触过面向对象这个概念,还要先花不少时间去学习面向对象编程。 |
插件的形式 | 插件是一个单独的VI,插件所有的功能都必须在这个VI中实现。 | 插件是一个对象(类的实例)。插件对象可以拥有多个方法。一个新的插件被添加进来后,它的所有方法可以自动应用于框架程序中任何调用了插件方法的地方。 |
总结 | 适合简单程序 | 适合大型程序 |