级别: 中级 David Mertz, Ph.D. (mertz@gnosis.cx), 用户, Gnosis Software, Inc.
2004 年 4 月 01 日 随着时间的迁移,XML 渗透到了许多领域。XML 应用越来越多的一个领域是图形用户界面配置,尤其是那些持久存储而不应该在编译期修复的成分。本文中,David 考察了 XML 在 Mac OS X 的 Aqua GUI 和 K Desktop Environment (KDE) 中的应用,这两种平台在多数现代 Linux 版本中都作为标准或者可以使用。
操作系统 -- 特别是在其 GUI 中 -- 通过各种不同的机制、以各种不同的格式,维护着结构化的、持久性的数据。Windows 版本使用 INI 文件,后来改为二进制的(也是统一的)注册表;早期版本的 Mac OS 在文件系统中有资源叉和供 Finder 使用的桌面文件;Linux 和其他类 Unix 系统通常使用主目录中分散的、隐藏的文件,窗体管理工具和应用程序之间的详细配置有多种互不兼容的方式。
随着时间的变化,我越来越相信,文件系统特定位置的 XML 文件将代替所有这些早期的机制和格式。我知道,还没有哪种 GUI
完全使用 XML,但是多数都在不断增加对 XML 的使用。目前应用 XML 较广泛的两种 GUI 是 Aqua(在 Mac OS X 平台上)和 KDE(任何 XWindow 系统上,如 Linux)。
Mac OS X
Aqua GUI for Mac OS X 使用一个 XML DTD 列举和控制各种不同的属性。
属性列表
Mac OS X 中所用的 XML 格式是一种简单的 DTD,称为“属性列表” -- 具体而言就是 http://www.apple.com/DTDs/PropertyList-1.0.dtd。该 DTD 把数据序列化为通常在面向对象编程语言中才会见到的(嵌套的)数据结构集:词典、数组、字符串、数字、Boolean 值、日期和二进制数据。这种格式看起来与为 Python、Perl 和 Ruby 这类脚本语言创建的 XML 序列化格式很相似,其中一些在本专栏以前的文章中曾经介绍过。我认为属性列表的对象模型可能主要借自 Objective-C,Mac OS 的开发中大量使用了这个动态的 C 语言超集。
属性列表 DTD 非常短,完全可以抄录在下面(稍微改变了格式,增加了一些简短的注释):
清单 1.PropertyList-1.0.dtd
<!ENTITY % plistObject "(array | data | date | dict | real |
integer | string | true | false )" >
<!ELEMENT plist %plistObject;>
<!ATTLIST plist version CDATA "1.0" >
<!-- Collections -->
<!ELEMENT array (%plistObject;)*>
<!ELEMENT dict (key, %plistObject;)*>
<!ELEMENT key (#PCDATA)>
<!--- Primitive types -->
<!ELEMENT string (#PCDATA)>
<!ELEMENT data (#PCDATA)>
<!-- Contents interpreted as Base-64 encoded -->
<!ELEMENT date (#PCDATA)>
<!-- Contents should conform to a subset of ISO 8601 -->
<!-- Numerical primitives -->
<!ELEMENT true EMPTY> <!-- Boolean constant true -->
<!ELEMENT false EMPTY> <!-- Boolean constant false -->
<!ELEMENT real (#PCDATA)>
<!-- Contents should represent a floating point number -->
<!ELEMENT integer (#PCDATA)>
<!-- Contents should represent a (possibly signed) int number -->
|
符合这个 DTD 的 XML 文件并不真正是面向 XML 的,XML 仅仅作为编程数据的一种方便的序列化。当然,使用属性列表使得配置数据更容易被所有 XML 工具处理。可免费获得的 Developers Tools(开发人员工具集)包含编辑属性列表的工具,直接称为
Property List Editor;但是任何 XML 编辑工具 -- 包括基本的文本编辑器 -- 也可以很好地工作。
应用程序捆绑
属性列表起着多种不同的作用,我将考察其中的两种 -- 可能是最重要的两种。首先,必须了解现代 Mac OS 应用程序的结构:许多应用程序不是单一文件,而是名称以
.app 结束的
目录。在这些目录中,OS X 希望找到特定的托管子目录和文件(以及各种不同的可选项)。对于 Aqua GUI,这些包/捆绑就像是单一的可执行文件。在这个层次体系中,属性列表 XML 文件名为
Info.plist ,控制着应用程序显示的图标、
Get Info对话框中提供的信息、应用程序首选项存放在哪儿,等等诸如此类的功能。这些首选项本身保存在另一个属性列表 XML 文件中。
Info.plist 仅限于保存特定的词典键和值的集合(请参阅
参考资料)。但是,包含在首选项属性列表中的数据是应用程序专有的。可以包含应用程序配置所需要的多数信息。我决定 Aqua 化(Aqua-ify)一个 PyQT 应用程序,这个程序是我所参与的 Open Voting Consortium (OVC) 项目的一部分;应用程序 BRP (Ballot Reconciliation) 不是 Mac 专用的,一般在 Linux 上运行,但我认为我可以使它适应 Mac 平台。为此,我创建了下列文件和目录:
清单 2. 创建一个 Mac OS 应用程序包
$ ls -R BallotReconciliation.app/
Contents
BallotReconciliation.app//Contents:
Info.plist MacOS PkgInfo Resources
BallotReconciliation.app//Contents/MacOS:
run_brp
BallotReconciliation.app//Contents/Resources:
BRP.icns
|
这里有几点需要说明一下。文件
PkgInfo 包含 8 个字节,告诉查找程序这个包是可执行的,还是要用其他应用程序打开。它还指定了一个创建者代码,您一般可以使用字符串
APPL---- 。在这里,文件
run_brp 是一个 bash 脚本,以
#!/bin/bash 开始,主要用于使用适当的脚本启动 Python 解释器的运行。任何可执行文件都能运行,包括 Python、perl、bash 或 Java 脚本和程序。如果不同的平台需要不同的可执行文件,可以将其放在
Contents/MacOSClassic/ 之类的目录中;同一个包可以包含用于不同平台的多个可执行文件。文件
BRP.icns 是 Mac OS 多种解析格式的图标数据。(这一点并没有严格的要求,但是定制的图标看起来要好一些。)
本专栏读者最感兴趣的是文件
Info.plist ,我们仔细地看一看它:
清单 3. BallotReconciliation.app/Contents/Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>run_brp</string>
<key>CFBundleGetInfoString</key>
<string>? 2004 Open Voting Consortium (EVMPLv.1.1)</string>
<key>CFBundleIconFile</key>
<string>BRP.icns</string>
<key>CFBundleIdentifier</key>
<string>com.openvoting.BRP</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>0.1</string>
<key>CFBundleName</key>
<string>BallotReconciliation</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.10</string>
<key>CFBundleSignature</key>
<string>----</string>
<key>CFBundleVersion</key>
<string>0.10</string>
</dict>
</plist>
|
如前所述,这些数据是关于应用程序的元数据词典:
-
CFBundleExecutable 指定可执行文件
。
-
CFBundleIconFile 给出图标
。
-
CFBundleGetInfoString 规定显示的关于应用程序的信息。
键
CFBundleIdentifier 比较有趣 -- 它说明到哪里寻找应用程序的配置首选项。首选项属性列表一般存在于
~/Library/Preferences 目录下,应该按照 Java 语言的风格使用完整的所有者前缀命名。因此,我结合使用了 OVC 域和应用程序名:
com.openvoting.BRP 。这个名字现在只是虚构的,因为 BRP 没有在该文件中保存任何配置信息。但是,其他应用程序确实在像
com.apple.Terminal.plist (在前述的用户目录中)这样的文件中保存配置首选项。
配置首选项
作为一个例子,我们来看看 Terminal.app 配置属性列表中的部分内容:
清单 4.~/Library/Preferences/com.apple.Terminal.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AppleSavePanelExpanded</key>
<string>NO</string>
<key>Autowrap</key>
<string>YES</string>
<key>BlinkCursor</key>
<string>NO</string>
<key>CleanCommands</key>
<string>rlogin;telnet;ssh;slogin</string>
<key>Columns</key>
<string>80</string>
<key>FontAntialiasing</key>
<string>YES</string>
<key>NSFixedPitchFontSize</key>
<integer>15</integer>
<key>NSTableView Sort Ordering NSNavOutlineColumnSettings.v1</key>
<array>
<data>
BAt0eXBlZHN0cmVhbYED6IQBQISEhAhOU1N0cmluZwGEhAhOU09iamVjdACF
hAErHE5TTmF2RGlzcGxheU5hbWVGaWxlUHJvcGVydHmG
</data>
<true/>
</array>
<key>NSWindow Frame Inspector</key>
<string>102 397 268 435 0 0 1280 832 </string>
<key>TermCapString</key>
<string>vt102</string>
</dict>
</plist>
|
从键名中可以看出,一些配置是关于应用程序的实际显示的(所用的字体、消除锯齿、窗体大小,等等),而另一些具有更多的功能性(终端仿真,专用 sub-shell)。这里也留下了一个嵌套数据的例子,说明可以自由地将一个集合嵌套在另一个集合中。
K Desktop Environment
KDE 项目在多个地方使用 XML:
- 使用 XML 为多个 KDE 应用程序建立进程。
- 可以使用 XML-RPC 描述应用程序的行为。
- 使用 XML 配置 Kate 中的语法高亮显示。
- 各种不同的应用程序如 KOffice 使用 XML 作为数据格式。
但对于本文而言,我所关心的是使用 XML 动态改变 GUI 元素。
kpartgui DTD
根据我的判断,根本没有现成可用的称为
kpartgui.dtd 的 DTD。(它就像一个占位用的名字。)但是,KDE 中控制(可选的)下拉菜单项和弹出菜单项、快捷键和工具条动作的许多资源,都有一个声明
<!DOCTYPE kpartgui> (或者
<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> )。多数 KDE 应用程序都有自己的配置对话框向这些 XML 资源写入数据,但是也可在外部文本编辑器、XML 编辑器中或者通过程序修改这些信息。无论用哪种方式,下一次再启动配置的应用程序时,它的菜单和工具栏都会相应地变化。
并非 KDE 应用程序配置的每个方面 -- 甚至应该成为 GUI 的那些方面 -- 都保存在 XML 文件中。我预料 KDE 将继续走向 XML,可能在将来的某个 KDE 版本中,绝大部分都采用 XML 配置。但现在还不是。比如,其中的
~/.kde/share/config/keditrc 文件是一个 INI 风格的配置文件,用于设置图标的大小和标题。但是在
KEdit 中实际显示的工具栏操作在
.kde/share/apps/kedit/keditui.rc 中设置,该文件使用了
kpartgui XML。
这些 XML 配置文件中列出的菜单项被
添加到编译进应用程序的菜单上。基本的菜单仍然在编译时确定 -- 运行时仅仅改变额外的可选项。菜单、工具栏项和快捷键都指向给定应用程序中已有的动作 -- 或者动态生成的
ActionList 集合(比如当前打开的窗口上的动作)。KDE 应用程序通常
可用不同的语言描述,但这是另一个话题。一般而言,菜单、工具栏和快捷键必须指向应用程序开发人员在编译时已经预期到的动作。
配置 KEdit
为了使您对
kpartgui 配置文件有一定了解,这里介绍我创建的一个经过适当修改的版本。清单 5 中的代码使用
KEdit 中的配置对话框生成,然后在文本编辑器中作了修改。注意,为了向 KDE 标准下拉菜单中增加菜单项,菜单的名称必须和原来的匹配。
清单 5.keditui.rc 的例子
<!DOCTYPE kpartgui>
<kpartgui version="2" name="
" >
<MenuBar>
<Menu name="edit" >
<text>&Edit</text>
<Action name="insert_file" />
<Action name="insert_date" />
</Menu>
<Menu name="tools">
<text>&Tools</text>
<Action name="file_mail" />
</Menu>
</MenuBar>
<ActionProperties>
<Action shortcut="F1" name="help_about_kde" />
</ActionProperties>
<ToolBar noMerge="1" name="mainToolBar" >
<text>Main Toolbar</text>
<Action name="file_new" />
<Action name="file_open" />
<Action name="file_print" />
<Action name="edit_cut" />
<Action name="edit_copy" />
<Action name="edit_paste" />
<Action name="help_about_app" />
</ToolBar>
</kpartgui>
|
KDE 很好的一个特点是精心设计、风格一致的指南。很容易向可能
不应该添加菜单项的地方添加菜单项。(比如,您可能不希望在“Windows”顶级菜单下添加“Open File”菜单。)我认为上面的例子基本上能够切中要害,但是也非常简单。
添加弹出菜单和嵌套菜单
KEdit 是一个简单的纯文本编辑器,不使用上下文感知的弹出菜单。
KWord 是一个完善的字处理程序(是 KOffice 软件包的一部分),对各种文档元素使用弹出菜单。配置弹出菜单和配置下拉菜单惟一的区别是菜单名,在
name 属性中设置。比如:
清单 6. 在 kword.rc 中配置弹出菜单
<Menu name="footnote_popup" >
<Action name="text_default" />
<Action name="format_font" />
<Action name="format_paragraph" />
<Separator/>
<Action name="border_backgroundcolor" />
<Separator/>
<Action name="edit_cut" />
<Action name="edit_copy" />
<Action name="edit_paste" />
<Separator/>
<Action name="edit_footendnote" />
<Action name="change_footendtype" />
</Menu>
|
嵌套菜单的设置和您预期的差不多,使用嵌套的
<Menu> 标签实现。比如:
清单 7. kword.rc 中的嵌套菜单
<Menu name="table" >
<text>Ta&ble</text>
<Action name="table_propertiesmenu" />
<Separator/>
<Menu name="Row" >
<text>Row</text>
<Action name="table_insrow" />
<Action name="table_delrow" />
</Menu>
<Menu name="Column" >
<text>Column</text>
<Action name="table_inscol" />
<Action name="table_delcol" />
<Action name="table_resizecol" />
</Menu>
<!-- ...and so on... -->
|
KWord 这样的复杂应用程序往往允许配置大部分的菜单,而简单的应用程序除了标准菜单之外往往没有多少好设置的了。因此,您可以创建一个
KWord 副本,为仅使用少量功能的用户提供最少的菜单项。
结束语
迄今为止,现代 GUI 配置中使用 XML 还只是个别现象。多数界面都在某个地方使用 XML,而在其他地方使用另外的机制。但是显然,一般的趋势是转向 XML。
显然,Mac OS X Aqua 和 KDE 都有各自的 XML 观点。Mac OS X 仅仅使用对应大量数据类型的 XML 元素,而把实际上属于应用程序或者 GUI 专用的内容都放在容器元素的 PCDATA 中。相反,KDE 的 XML 则令人感到是针对具体用法的。标签的命名明确反映了 GUI 的用途(比如,
<dict> 与
<Menu> ),多数内容规范都放在
name 属性中。
参考资料
关于作者
对本文的评价
|