最近要做一个Python的基于Eclipse的界面设计器,因此我对各种GUI设计工具做了一下分析,发现GUI设计工具也有一个门派。
在那个懵懂的年代,一切界面代码都是要开发人员手工书写,这无疑增加了开发难度,Delphi、VB等工具的出现扭转了这个局面,使用这些工具开发人员只要在控件面板上拖拖拽拽就可以完成界面的设计,做到了“所见即所得”的开发方式。仔细分析,GUI设计工具有如下几个门派:基于界面文件的纯代码生成、代码生成与界面文件结合、无界面文件方式。
基于界面文件的纯代码生成:NetBeans是这类工具的典型代表(如果我没记错的话JBuilder也是这样实现的),NetBeans中与界面设计有关的有两个文件:.java文件和.form文件。.form文件中是以XML格式描述界面布局和控件的属性等信息;.java文件则是通过解析.form文件生成的代码,生成的界面代码主要位于initComponents方法中,这个方法在NetBeans IDE中是无法手工编辑的。在用户拖拉控件的时候,NetBeans就将拖拉的控件描述增加到.form文件中,并且即时将新的代码生成到.java文件中。这样实现的好处有如下几点:IDE实现容易,IDE的开发人员只要关注于如何将界面信息转化为.form文件和如何将.form文件解析生成.java代码即可,无需关心用户修改.java代码造成的反向解析问题;.java文件可以脱离.form而存在,也就是.form文件只是在设计期有意义,而在运行期是无用的。缺点是:用户无法手工修改生成的代码。
代码生成与界面文件结合:Delphi和VB是这类工具的典型代表。以Delphi为例,在Delphi中新建以后界面以后将会存在两个文件:.dfm和.pas,.dfm描述了界面布局和控件的属性等信息,.pas则定义了控件的变量和事件处理函数。在编译的时候.dfm被编译到可执行文件中,运行的时候动态解析.dfm文件来构建界面。与NetBeans不同的就是.dfm文件是有运行期的意义的,如果没有.dfm文件文件,程序将无法编译运行。这样的方式通常只适用于Delphi、VB这样代码和IDE结合过于紧密的语言,很难将生成的代码进行手工修改。
无界面文件方式:Eclipse的Visual Editor是最经典的例子。使用Visual Editor进行GUI绘制的时候,只存在一个.java文件,Visual Editor将用户绘制的界面直接解析为.java代码,如果用户修改了.java代码,Visual Editor会运行一个虚拟机,在虚拟机中运行用户修改后的文件,得到运行时的程序界面,然后将这个界面绘制到窗口设计器中。这样做可以将所有的界面信息都集成到一个文件中,并且支持用户手工修改生成的代码;由于设计器中的界面是通过另外一个虚拟机运行而得到的,在界面设计器中看到的界面就是运行时的界面,这样保证了真正的“所见即所得”。这样做的坏处也是明显的,由于需要重新启动一个虚拟机,导致了速度很慢,资源占用比较高,使用Visual Editor的时候经常造成Eclipse内存不足退出。
我在开发界面设计器的早期采用的基于界面文件的纯代码生成方式,系统中有一个.aui文件和对应生成的.py源代码文件,后来由于系统需求(主要是要求允许开发人员修改生成的代码),我就准备改用无界面文件方式。如果采用Visual Editor的无界面文件方式难度是比较大的,而且会导致资源占用太大,因此我采用了另外一种思路,也就是在内存中为每个界面维护一个对象模型(树状结构),在用户绘制界面的时候去修改这个对象模型,在用户保存界面的时候去解析这个对象模型生成.py源代码;在由.py源代码加载绘制设计器中的界面的时候,首先通过解析.py 源代码生成源代码的抽象语法树(AST),然后解析这个AST生成界面的对象模型,这样就可以很轻松的绘制界面了。这样做不仅有Visual Editor的优点,而且占用资源比较小;不过由于手工修改代码的千差万别,如果开发人员修改的代码采用了比较生僻的语法,有可能造成用户修改的代码无法正确的解析为对象模型,造成.py源代码加载绘制设计器中的界面的时候发生异常,解决这个问题的唯一一个办法就是建议开发人员尽量采用常用的代码来修改生成的界面代码。
由于HTML代码本身就是一个树状模型,无需进行代码和模型间的转换,所以网页设计器就不存在上边说的这些帮派了。