2005年02月05日

这是我第二次编译Mozilla了,第一次首先是没有在我的VC6.0上安装SP5的包,结果到了一个Jpeg的地方报错。安装之后,编译继续。但最后出现了一个C1004错,是说wabtags.h文件有问题。原以为是我的环境有问题,没有细究,结果把Mozilla的源码删了,就没再编译。结果昨天我试着又编译了一次,仍然是报wabtags.h错,到网上查一查发现一篇文章:从一个微软的有意思的bug想到的,结果才发现,这个问题原来是VC本身的问题。在VC98的include目录下打开wabtags.h文件,结果发现有一些注释的结束*/变成了?/,全部改过来,保存再编译,终于成功了。

2005年01月31日

前面我们基本结束了界面生成部分,下面开始进入交互部分。

XUL的事件处理方式与HTML中的基本类似。对于需要响应事件的元素,只要在元素中增加相应的事件处理函数即可。如:

<menuitem label=”Close” accesskey=”c” oncommand=”window.close();”/>

 
<button id=”cancel-button” label=”Cancel”
     oncommand=”window.close();”/>

这个例子并未使用自定义的函数,而是直接使用DOM对象的方法进行调用。对于自定义的方法可以在XUL文件中设置script元素,将代码放在script代码中即可。或者在script中设置src属性,指向javascript代码文件。在XUL中目前都是使用javascript来进行程序处理的。

基本可响应的事件可以看原教程。

事件模型

XUL使用与DOM2 Events描述的事件模型一样。(至于DOM2 事件模型到底怎么回事我也没仔细看过,如果需要再了解好了)简单地说,一个事件的发送有两个状态:捕获状态和冒泡状态。在捕获状态,一个事件先发送到document,然后按照元素的分级结构层层传递,直到触发事件的元素。冒泡状态是反方向传递。

在处理路径上,如果事件被某个元素处理,那么事件传递即结束,如果没有被处理,则继续传到下一个元素。如果都没有处理,则缺省的处理被执行。这就意味着,不一定要把事件处理放在触发事件的元素上,而是放在它的父元素中进行处理。

如果一个事件被处理,处理函数应该返回true,如果返回false则表示未被处理。因此,有时可以在处理了事件后,仍然返回false,从而使事件可以继续传递。

你可以使用event对象的target属性来得到触发事件的元素,如:

<window
    onclick=”alert(event.target.tagName); return false;”
    .
    .
    .
>

当你在窗口中点击任何一个元素时,都会引发一个alert对话框,它将显示被点击的元素的名字。

2005年01月28日

模板可以让我们从RDF中自动生成需要的元素。当我们需要做特殊处理时,我们可以通过rule来进行不同的处理。那么前面我们学习了一些rule元素的使用,但那些其实都只是简写。完整的rule元素有三个子元素:conditions, bindings, action。

mozilla在处理的时候,模板生成器会先根据conditions中的条件对资源进行匹配,如果匹配成功,则调用相应的action中的内容进行复制处理。

条件

conditions元素是rule中用来进行匹配使用的条件,它有三个子元素:content, member, triple。

  • content
    只能写一次。它用来将包含模板的元素中的rel所指的起始根对应到一个变量中。如:

    <content uri=”?var”/>

    uri的属性值将对应rel中的根资源。?表示后面的标识符是一个变量。var是变量名,可以随便写。

  • member
    用来映射每条资源的变量。

    <tree id=”citiesTree” datasources=”weather.rdf”
        ref=”http://www.xulplanet.com/rdf/weather/cities”> 
        <template>
            <rule>
                <conditions>
                    <content uri=”?list”/>
                    <member container=”?list” child=”?city”/>
                </conditions>
            </rule>
        </template>
    </tree>

    从本例可以看出list对应http://www.xulplanet.com/rdf/weather/cities。这样container对应list变量,child对应的值放在city变量中。那么content与member给我的感觉就是用来定义变量。content用来定义根变量,child用来定义每条资源。

  • triple
    它是一个三元组,用来定义匹配规则。一个triple由三要素组成:主题(subject), 谓词(predicate), object(目标),这一点我们在讲RDF时讲过。不过这里使用triple是用来检查一条资源是否之定义的规则相匹配。

    <conditions>
        <content uri=”?list”/>
        <member container=”?list” child=”?city”/>
        <triple subject=”?city”
            predicate=”http://www.xulplanet.com/rdf/weather#prediction”
            object=”Cloud”/>
    </conditions>

  • 这里定义了一个triple,它用来检查一条资源的天气是否与目标相一致。主题就是city变量对应的资源,谓词就是http://www.xulplanet.com/rdf/weather#prediction,目标在这里就是”Cloud”。当目标是一个值时,那么做判断处理。但是当它设为一个变量时,如”?pred”,那么处理时就是将对应的值放在pred变量中,同时判断它是否不为空。条件中可以放置多个triple。

那么conditions元素的作用一方面是进行条件的匹配,一方面是将相应的值放在变量中。不过现在还不清楚是否有更复杂的条件判断功能。

内容创建

在匹配成功后,生成器会自动调用action子元素中的内容进行处理。在action中你可以使用conditions中定义的变量。在rule的简单语法中,你使用uri=”rdf:*”来指明内容应该从哪里生成。在全语法中,你应该设置uri为conditions中的变量。通常使用member元素中的child属性所对应的变量。下面是一个完整的例子:

<tree id=”weatherTree” flex=”1″ datasources=”weather.rdf”
    ref=”http://www.xulplanet.com/rdf/weather/cities”>
    <treecols>
        <treecol id=”city” label=”City” primary=”true” flex=”1″/>
        <treecol id=”pred” label=”Prediction” flex=”1″/>
    </treecols>

<template>
    <rule>
        <conditions>
            <content uri=”?list”/>
            <member container=”?list” child=”?city”/>
            <triple subject=”?city”
                predicate=”http://www.xulplanet.com/rdf/weather#name”
                object=”?name”/>
            <triple subject=”?city”
                predicate=”http://www.xulplanet.com/rdf/weather#prediction”
                object=”?pred”/>
        </conditions>
        <action>
            <treechildren>
                <treeitem uri=”?city”>
                    <treerow>
                        <treecell label=”?name”/>
                        <treecell label=”?pred”/>
                    </treerow>
                </treeitem>
           </treechildren>
        </action>
    </rule>
</template>
</tree>

从例子我们可以看出,action元素中,uri=”?city”,而city是在member中的child属性对应的变量。treecell对应的值分别为”?name”和”?pred”。这个例子所对应的wheather.rdf文件在原教程中有,大家可以把它保存到自已的测试目录下,运行看一看。运行结果为:

从例子我们注意到这里没有设置flags=”dont-build-content “,因此这例子的内容不是直接从RDF文件中得到的。如果我们设置flags=”dont-build-content “,那么需要把上面的content元素改为treeitem。大家可以自行测试。

增加额外的绑定

bindings元素中可以定义多个binding元素,那么它的语法与triple很相似,主要的作用就是将信息与变量关联,如:

<bindings>
    <binding subject=”?city”
        predicate=”http://www.xulplanet.com/rdf/weather#temperature”
        object=”?temp”/>
</bindings>

那么它与triple不同之处就是不作判断处理。当指定的谓词不满足时,变量值为空。

2005年01月25日

Mozilla内置了一些数据源,它们与bookmarks相似,不过字段与可用的起始root可能有所区别。这里我不想多说了,看原教程吧。

那么我真正感兴趣的就是如何生成自已的RDF,但教程中并没有详细的介绍。考虑到RDF可以为列表和树之类的模板提供数据,那么它需要定义结点集,通过seq, bag, alt来定义。而真正的结点是通过description来定义的。这样一个RDF基本上需要定义两种东西:

  • 表示结点集合的seq, bag, alt
  • 表示结点的description

这样我们在使用这个RDF时,datasource即为这个RDF文件的URL,而rel为结点集的某个层次结点,如根结点或某个子结点。通过结点集就可以得到真正的结点数据。

从原教程所提供的RDF可以看出来,每种动物及分类都使用了一个description来定义,最后seq将分类与具体的动物结合起来。在使用模板时,就是从seq级别中选取某个层次作为起始的root进行处理。

不过XML这种格式的文件的确很冗长,如果没有好的工具来支持手工处理会很麻烦。

2005年01月24日

向tree增加数据源

从前面我们了解了从RDF读取数据后,利用模板生成元素有两种生成器,一是内容生成器用于绝大多数元素;二就是树生成器只用于tree元素。下面我们就介绍树和它的模板。象同其它的元素一样,你需要在tree元素中加入datasource和rel两个属性。下面一个例子是把history作为数据源:

<tree datasources=”rdf:history” ref=”NC:HistoryByDate”
          flags=”dont-build-content”>

前面我们已经知道,树生成器可以不生成每行数据,而是直接从数据源生成。上例的flags设为”dont-build-content”就是表示使用树生成器。如果没有flags属性,将会使用内容生成器。下面是一个完整的例子:

<tree id=”my-tree” flex=”1″
       datasources=”rdf:files” ref=”file:///” flags=”dont-build-content”>
  <treecols>
    <treecol id=”Name” label=”Name” primary=”true” flex=”1″/>
    <splitter/>
    <treecol id=”Date” label=”Date” flex=”1″/>
  </treecols>

    <template>
      <rule>
        <treechildren flex=”1″>
          <treeitem uri=”rdf:*”>
            <treerow>
              <treecell label=”rdf:http://home.netscape.com/NC-rdf#Name”/>
              <treecell label=”rdf:http://home.netscape.com/WEB-rdf#LastModifiedDate”/>
            </treerow>
          </treeitem>
        </treechildren>
      </rule>
    </template>
</tree>

这个例子很有趣。在开始时,我一运行它,内容是空的,后来我仔细地看了一下。原来是被前面的history给误导了,因为这个例子与history根本没有关系。它的datasource是rdf:files,说明要处理的是文件系统。rel为file:///说明是从根目录开始。之所以出不来结果是因为我是在Windows XP下运行的,而这个例子是在Linux下运行的。只要我把rel写为正确的file:协议即可。如:file:///d:/newedit/,那么在我的机器上的运行结果为:

这个例子生成一个两列的树,第一列为文件或目录的名字,第二列为最后修改时间。注意uri放置的地方,它放在了treeitem中,这样模板将从这里开始生成。而整个模板的开始元素是treechildren,但由于treechildren在treeitem外面,因此它只用生成一次。这正是我们需要的,我们不需要多次生成treechildren。模板中使用了rule,但由于没有任何属性因此它会匹配全部数据。同时我们还可以看到当碰到一个目录时,树生成器也可以正确处理子元素。另外就是,更深层的结点不需要时并未创建,而是当用户展开时才真正创建。从这几点,我的确体会以Mozilla在这些方面的易用性和高效。以前做过的GUI哪一个不是自已一个个结点插入,如果有子结点还要考虑创建子结点,同时性能还很难保证。就是想实现与Mozilla相同的效率处理方式,但也要自已花费大量的编程时间。

列排序

试了上面的例子之后,你会发现列数据是无序的(在xp上好象已经排好顺序了,原教程是这样说的,我想可能是在Linux下吧)。树在从数据源生成数据的时间可以对数据进行排序。用户还可以在列头上点击进行排序。这种排序的特性,对于有静态内容的树来说是无效的。因此上面的flags属性还是很重要的。

有三个与排序有关的属性:

  • sort
    属性应设为一个RDF属性,用来指明排序所用的关键字。如果在某一列设置了这个属性,那么这一列将进行排序。
  • sortDirection
    用来指明缺省的排序的方向,有三个值:ascending(升序)、descending(降序)、natural(自然顺序)。
  • sortActive
    当某一列sortActive属性设置为”true”时,缺省将进行排序。

一般使用上面三个属性已经足够了,但通常还要使用样式类sortDirectionIndicator来指明哪一列正在被排序。正在被排序的列会出现一个三角型表示排序的方向。下面是一个上面的例子的修订版:

    <treecols>
      <treecol id=”Name” label=”Name” flex=”1″ primary=”true”
                class=”sortDirectionIndicator” sortActive=”true”
                sortDirection=”ascending”
                sort=”rdf:http://home.netscape.com/NC-rdf#Name”/>
      <splitter/>
      <treecol id=”Date” label=”Date” flex=”1″ class=”sortDirectionIndicator”
               sort=”rdf:http://home.netscape.com/WEB-rdf#LastModifiedDate”/>
    </treecols>

你也可以使用persist属性来保存某些属性的值。

对于rule元素还可以指定特殊的属性来匹配特殊的情况,如:

  • iscontainer
    如果设为”true”将用来匹配哪些拥有子元素的资源。例如象文件夹。
  • isempty
    如果设为”true”将用来匹配没有子结点的资源。

上面这两个属性是相反的。一个资源既可能是一个容器也可能没有子结点。不过它与一个资源不是一个容器是不同的。

大赛已经过去了,还是静下心来先学好XUL吧 :)

元素组装

那么在前面我们学习了生成列表、树这样的元素的生成,但是手工生成的。其中我还谈到可以通过RDF来生成,这对于大量数据是一个很方便的方式。这些数据或者来源于RDF文件,或者来源于内部的数据源(datasource)。Mozilla提供了象bookmarks, history和邮件信息这样的数据源。

为了从RDF中得到数据,我们需要提供第一个元素的模板(template)。这一点非常象xslt,通过提供模板,其实就是一个搜索条件,可以把目标XML文件中的满足条件的元素找到,不过xslt提供类似于循环、分支这样的操作,功能很强,与XUL的模板可能不太一样(反正现在是没见着)。

为了生成一个模板你需要使用template标签,它的内容就是要生成的元素。template需要放在一个容器内,如tree中。

下面让我们通过一个例子来学习。这个例子演示了将 bookmarks 中的顶层书签生成按钮的过程。

为了测试这个例子,你需要按chrome的要求配置好。不过前面我所做的任何测试都是在chrome下进行的,因此是没什么问题的。

示例代码为:

<vbox datasources=”rdf:bookmarks” ref=”NC:BookmarksRoot” flex=”1″>
  <template>
    <button uri=”rdf:*” label=”rdf:http://home.netscape.com/NC-rdf#Name”/>
  </template>
</vbox>

上面的例子很有意思,如果你直接在命令行上运行。那么刚开始什么都没有。但一旦你打开浏览器,你会发现窗口增加了许多按钮都是与bookmarks相对应的。如果你在浏览器中增加或删除,这个窗口也会立刻发生变化。你也可以直接打开浏览器,在定位框中直接输入chrome链接,窗口内容就会显示在浏览器中。

从上面的例子我们可以看到,vbox就是一个容器,datasources属性用来设置数据源,通过值可以想象就是bookmarks。这同时也说明它是由Mozilla提供的,如果想使用其它的RDF可以使用不同的URL,如:

<box datasources=”chrome://zoo/content/animals.rdf”
     ref=”http://www.some-fictitious-zoo.com/all-animals”>

同时你还可以一次指定多个数据源,只要把它们用空格分开。

rel属性用来指明数据是从数据源何处来的。上面的例子中,NC:BookmarksRoot用来指明bookmarks的根元素。针对不同的数据源你可以使用不同的值。如果你使用自已的RDF文件,那么这个值就与Seq, Bag, Alt中的about属性值相对应。

通过加入datasources和rel这两个属性,就可以在元素内部通过模板来生成元素了。但是模板中的元素声明与平常有些不同。如上面例子中的button,增加了uri属性,label属性也不太一样。

在模板中的元素属性值如果是以rdf:开头的,表明这个值应该是从数据源来的。rdf:后面的值表示数据源中的属性名。

元素中的uri属性用来指明生成的内容从何处开始。在此之前的内容(即这个元素之外的其它内容)只生成一次,元素内部的内容会根据每个资源生成一次。下面是另一个例子:

<vbox datasources=”rdf:bookmarks” ref=”NC:BookmarksRoot” flex=”1″>
  <template>
    <vbox uri=”rdf:*”>
      <button label=”rdf:http://home.netscape.com/NC-rdf#Name”/>
      <label value=”rdf:http://home.netscape.com/NC-rdf#URL”/>
    </vbox>
  </template>
</vbox>

通过这个例子我们可以看出,这个模板将生成一个vbox的集合,每个vbox又有一个按钮和一个标签。

模板生成过程

当一个元素拥有datasource属性时,表示它的内容希望是从一个模板来的。当存在这个属性时,会在这个元素上增加一个builder的对象,这个对象负责从模板生成元素。在Javascript中你可以使用builder属性来访问这个builder对象。一般你不会使用它,通常是当自动生成有问题时,才会使用这个对象来重新生成内容。

有两种不同的生成器,一种是内容生成器用于绝大多数元素;另一种是树生成器只用于tree元素。

内容生成器会取出template元素中的内容,根据每行数据对它进行复制。在生成之后,你可以使用DOM来访问生成的元素,template元素本身不会显示出来,但它在DOM中仍然是存在的。每个从模板生成的元素都有一个与资源相对应的id属性。

内容生成器总是从放置uri=”rdf:*”的地方开始生成。从包含uri属性的元素算起,在此元素这外的元素只生成一次。如:

<template>
  <hbox>
    <label uri=”rdf:*” value=”rdf:http://home.netscape.com/NC-rdf#Name”/>
  </hbox>
</template>

上例中,uri=”rdf:*”是放在label中的,因此在label之外的hbox只生成一次,而label则根据resource的个数生成多个。

而对于树生成器,它并不直接生成DOM元素,而是当需要时直接从RDF数据源得到数据。

Rules

上面的例子对所有的资源都是相同的处理,而有时不同的资源需要不同的处理,这时我们可以使用rule元素来处理。从上面的bookmarks的例子我们可以看到,有些是书签,有些是分隔用的线(我的示例就是空白),那么我们可以分别对待:

<?xml version=”1.0″?>
<?xml-stylesheet href=”chrome://global/skin/” type=”text/css”?>
<window
    id=”findfile-window”
    title=”Find Files”
    orient=”horizontal”
    xmlns:rdf=”http://www.w3.org/1999/02/22-rdf-syntax-ns#” 
    xmlns=”http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul”>

 <vbox datasources=”rdf:bookmarks” ref=”NC:BookmarksRoot” flex=”1″>
   <template>

    <rule rdf:type=”http://home.netscape.com/NC-rdf#BookmarkSeparator”>
     <spacer uri=”rdf:*” height=”16″/>
    </rule>

    <rule>
      <button uri=”rdf:*” label=”rdf:http://home.netscape.com/NC-rdf#Name”/>
    </rule>
 
  </template>
 </vbox>
 
 </window>

第一条用来把分隔线生成空白。一个rule的所有属性将用来匹配条件。第二条因为没有任何属性,因此对每个元素都进行处理。在这个例子中要特别注意上面红字的内容,它引入了一个新的名字空间。原来我想就拷贝window中的内容到以前的例子中即可,但生成结果与原教程不同。仔细看了代码才发现,它引入了新的名字空间,加入后就可以了。测试效果为:

那么rule之间应该是依次执行,如果当前rule匹配成功,则处理完毕后跳到下一条数据。如果不成功,则跳到下一个rule再继续进行处理。

为什么上面我们要引入新的名字空间呢?因为我们使用rdf:type要匹配的条件并不在xul名字空间内,因此需要把http://home.netscape.com/NC-rdf#BookmarkSeparator所在的名字空间引入。

2005年01月19日

下面我们进入复杂的RDF知识的学习,说实在的,学了半天也只明白个大概,而且我还不能保证我理解得一定正确,因此建议大家多找资料学习,如果有心得一定分享一下啊。这里是一个链接,放在Mozilla中,它是一系列的文章。阅读这里

RDF介绍

什么叫RDF — Resource Description Framework(资源描述框架)

它是用来做什么的呢?

我的理解就是描述资源之间关系用的一种模型。那么它的存储表示其实有多种,我们常用的是XML格式的表示,因此也称为RDF/XML。也就是说RDF与XML不是一个东西,而RDF/XML只是RDF的一种表示的格式。在RDF中有两种东西,一种是资源(resource),一种是文本(literal)。文本相当于具体的字符串,而资源相当于是一个抽象概念。那么RDF主要就用来描述由这两者对象构成的世界中个体的关系。

RDF怎么进行描述呢?它使用Triple方法,可以称为三元组的方法,它们是:主语(或主题)、谓语(或谓词)和宾语(或目标)。其中主语与宾语就是资源。而谓语是两个资源之间的关系。举一个例子:

<http://www.xulplanet.com/rdf/people/Sandra>  ->  name     ->  Sandra

这里<http://www.xulplanet.com/rdf/people/Sandra>就是resource,它使用URI的表示方法,它只是一个名字,怎么起都行,使用尖括号括起来。Sandra就是literal(文本)。name就是关系。上面的意思就是<http://www.xulplanet.com/rdf/people/Sandra>的名字叫Sandra。三者之间使用->连起来。当然这并不是XML文件,这只是一种表示triple关系的方法。那么RDF基本上就是通过这种三元表示法来描述资源之间的关系的。注意,因为泛泛的资源(Resource)与具体的类型(resource)单词一样,不好区分。这样在描述具体的资源的类型时我使用英文resource,在泛泛谈资源而不区分它的类型时我就用中文。

使用URI来表示resource并不表示在处理RDF时,要从相应的链接得到数据,它只是一个标识符。 如果在一个RDF中出现一样的URI,那就表示是同一个resource。使用triple可以表示从resource到literal和resource到resource的关系。有时,谓词也可以使用resource的表示方法。

有时我们需要表示一个对象是属于某一类的情况,即实例与类的关系,那么我们可以使用rdf:type这个谓词来定义实例与类的关系,如:

<http://www.xulplanet.com/rdf/people/Sandra>  ->  rdf:type  ->
      http://xmlns.com/wordnet/1.6/Person

上面就表示Sandra是一个Person。

有时我们还需要表示一类事物,把它们组织成列表。那么RDF提供了seq、bag、alt来定义。seq为顺序列表,bag为无序表,alt定义了多个备选值,但应该只有一个可用。

我不想在这里讲得过细了,让我们看一看在RDF/XML中的一些规范吧。

RDF/XML

前面我已经说过,RDF/XML只算是RDF的一种表示或方言,但也是我们常用的表示方法。针对上面我们所讲的资源表示与关系表示,我们分别举例来说明如何使用。

使用RDF/XML要按XML的要求声明XML的相应的头和名字空间:

<?xml version=”1.0″?>

<rdf:RDF xmlns:rdf=”http://www.w3.org/1999/02/22-rdf-syntax-ns#”
         xmlns:people=”http://www.xulplanet.com/rdf/people/”>

</rdf:RDF>

其它的内容就放在这里面。

resource到literal三元组

  <rdf:Description rdf:about=”http://www.xulplanet.com/rdf/people/Sandra”
                   people:name=”Sandra”/>

可以看出,一个Description描述了一个三元组。resouce为它的rdf:about属性值。literal为people:name的属性值。而谓词就是people:name。这样我们了解了,在RDF/XML中三元组可以使用属性来描述。属性名可能表示资源的类型和关系。而属性值可能表示革resource的URI或literal的字符串。

因为一个resource可能与其它的资源之间有多个关系,那么可以把这些关系简单地写在一起,如:

<rdf:Description rdf:about=”http://www.xulplanet.com/rdf/people/Sandra”
                 people:name=”Sandra”
                 people:gender=”female”/>

这实际上定义了两个关系,名字和性别关系。

resource到resource三元组

例如:

<rdf:Description rdf:about=”http://www.xulplanet.com/rdf/people/Sandra”
                 people:name=”Sandra”>
  <people:sibling rdf:resource=”http://www.xulplanet.com/rdf/people/Kevin”/>
</rdf:Description>

这里面定义了两个关系,一个是people:name,还有一个是people:sibling。而rdf:resource定义的是目标resource。

从这里我们还可以看到,三元组还可以使用子元素来描述。而且多种关系也可以放在一起混合进行表示:有的用属性表示,有的用子元素进行表示。非常灵活。当然,尽可能以简洁清晰为好。

还要提醒的是rdf:about表示主题,而rdf:resource表示目标。

type关系

前面说了rdf:type表示实例与类之间的关系,就是一个实例是属于某个类别。例如:

<rdf:Description rdf:about=”http://www.xulplanet.com/rdf/people/Sandra”
  <rdf:type resource=”http://xmlns.com/wordnet/1.6/Person”/>
</rdf:Description>

这里就不多说了。rdf:type是RDF内置的谓词。

列表类型关系

有三种列表形式:seq, bag, alt,举例如下:

<rdf:Seq rdf:about=”http://www.xulplanet.com/rdf/people/KarensKids”>
  <rdf:_1 rdf:resource=”http://www.xulplanet.com/rdf/people/Sandra”/>
  <rdf:_2 rdf:resource=”http://www.xulplanet.com/rdf/people/Kevin”/>
  <rdf:_3 rdf:resource=”http://www.xulplanet.com/rdf/people/Jack”/>
</rdf:Seq>

那么这里使用了rdf:_1来表示顺序。还有一种简化的方法就是把rdf:_1改为rdf:li,如:

<rdf:Seq rdf:about=”http://www.xulplanet.com/rdf/people/KarensKids”>
  <rdf:li rdf:resource=”http://www.xulplanet.com/rdf/people/Sandra”/>
  <rdf:li rdf:resource=”http://www.xulplanet.com/rdf/people/Kevin”/>
  <rdf:li rdf:resource=”http://www.xulplanet.com/rdf/people/Jack”/>
</rdf:Seq>

不过,你要是使用RDF Api的话你得不到li这个谓词,而你只能得到相应的数值。

由于Mozilla使用的RDF比较早,它与标准的处理有区别。一个就是about和resource属性不需要有rdf:的限定。另外就是对于rdf:type,在Mozilla是使用rdf:instanceOf。

xpcom是Mozilla中的一种组件技术,它可以允许Javascript调动本地对象,以完成Javascript所不具备的功能。那么它也有一个 Python 绑定,叫PyXPCOM,它是由ActiveState(发布ActiveState Python的公司)开发的,现在源代码已经并入Mozilla中。访问地址 

不过Mozilla并未在安装包中提供PyXPCOM的二进制包,因此,如果你想安装,那么要自已编译。(是真的吗?)这几天我尝试着编译Mozilla,但工具不全,因此一直还未成功。不过,在网上我发现一个叫mozpython的网站,它提供了一个叫MozPython的模块,可以在Mozilla中调用Python程序,不过我还是不清楚到底有什么用。因为它所演示的Python代码并不是嵌入到XUL页面中,而是独立的 Python 程序,它的执行结果要输出为HTML格式,然后供Mozilla进行显示。如果只把Mozilla当做 Python  代码的一个调用器还有点用,但现在我想实现的是用Python进行Mozilla的开发,象MozPython这样的效果想不出有什么用。希望Mozilla快点支持Python脚本的执行。不讲这个了,还是说PyXPCOM的下载吧。在MozPython的网站我发现了作者已经编译了几个版本,不过最新的是1.7版的。可以从这里下载。下载完的文件名是PyXPCOM_1.7_2.3_win32.zip。虽然版本有点低(我用的是1.7.5),但为了学习就无所谓了。怕有版本的问题,我还专门下载和安装了Mozilla 1.7版本。

安装PyXPCOM的经历并不轻松,安装说明在ActiveState网站上,可以阅读这里。不过它是从编译开始讲,我就只讲我是如何装的吧。

在讲述如何安装之前还要向大家交待一下:GRE。这里可不是出国考研要考的GRE英语。它是Gecko Runtime Environment的简称。Gecko就是Mozilla产品系统所用的引擎。那么大家可以读这篇文章来了解一下GRE的知识。那么它的作用就是将最小运行环境作为象Java的运行环境(JRE)一样的效果,公共的东西大家共享,这样可以减少安装所要的空间,便于维护。

安装XPCOM组件,有一个注册的步骤,就是要执行regxpcom.exe这个程序。但是在Mozilla 1.7下(它在Mozilla的安装目录下)如果你直接执行regxpcom.exe,那么很有可能不会成功,会报告:

Can not initialize XPCOM Glue
Can not aquire component registrar

这是为什么?我查了半天的资料(向大家提供一个邮件列表归档的网站很不错Mail Archive,其中我找到了mozilla.xpcom邮件列表)才大概明白,regxpcom.exe的执行需要有xpcom.dll才可以。但是在我的Mozilla安装目录下并没有。这是怎么回事,难到是Mozilla根本不包括这个东西吗?其实它就是放在GRE中的。那么这个GRE目录在我的机器上是:

C:\Program Files\Common Files\mozilla.org\GRE

它有一个子目录是跟版本相一致的。那么在这个目录下你会找到所有想要的东西。(我发现Mozilla越学东西越多,学习曲线是否有些太陡了吧。)那么把xpcom.dll所在的目录加入PATH环境变量中就可以了。在我的机器上是:

MOZ_GRE=C:\Program Files\Common Files\mozilla.org\GRE\1.7_2004061609
PATH=%PATH%;%MOZ_GRE%

那么regxpcom已经可以执行了。下一步就是把下载的PyXPCOM_1.7_2.3_win32.zip解压到上面这个目录下。那么因为PyXPCOM有Python代码,因此需要Python来执行,并且可以被Python进行调用。因此还要把解开后的python子目录加入到PYTHONPATH中。在我的机器上就是:

PYTHONPATH=%MOZ_GRE%\python

然后,我的做法是把Mozilla安装目录下的components子目录中的compreg.dat和xpti.dat文件删除。启动Mozilla,这样就会自动进行注册。这样就应该安装完毕了,剩下的找些例子试一试吧。

下面我小结一下:

  1. 如果不自行编译那么先要下载PyXPCOM
  2. 把GRE路径加进PATH中
  3. 解压PyXPCOM包到GRE目录中
  4. 把GRE的python目录加入到PYTHONPATH
  5. 删除Mozilla安装目录/components下的compreg.dat和xpti.dat文件
  6. 重启Mozilla

有些步骤也是多次试出来的,希望以后的Mozilla版本能够内置这一组件,简化操作。有问题欢迎交流。

2005年01月18日

XUL中的Tree元素是一种比较复杂的元素。它除了可以生成分级结构,而且还可以生成分级的列表结构。这在许多GUI中都是比较复杂的东西。象windows下的资源管理器,就只是树状结点,而不是分级列表。那么它既有分级的特性,又可以同时具备列表特性。不过,tree只能包括文本或图片,而不象列表可以包含任何元素。

有几种生成tree的方法,最简单、直接的就是直接生成tree的结点。其它的方法还可以使用数据源,利用模板来生成,这是后话。我们先学习简单的方法。

简单树

我们先不考虑分级的情况。先看一个例子:

<tree flex=”1″>
  <treecols>
    <treecol id=”sender” label=”Sender” flex=”1″/>
    <treecol id=”subject” label=”Subject” flex=”2″/>
  </treecols>

  <treechildren>
    <treeitem>
      <treerow>
        <treecell label=”joe@somewhere.com”/>
        <treecell label=”Top secret plans”/>
      </treerow>
    </treeitem>
    <treeitem>
      <treerow>
        <treecell label=”mel@whereever.com”/>
        <treecell label=”Let’s do lunch”/>
      </treerow>
    </treeitem>
  </treechildren>
</tree>

  • tree
    用来生成树结构。可以指定rows属性来设置缺省显示的记录条数。如果不指定缺省为0。如果指定了flex属性,说明树是可变的,就不必再指定rows属性了。
  • treecols
    用来生成树的表头。相当于listbox当中listhead。
  • treecol
    用来生成表头列,这里定义了两列,同时定义的宽度的占比。
  • treechildren
    用来生成表格体。所有记录行都包含在这个标签内。
  • treeitem
    用来生成一条记录, 它可以包含用treerow表示的单行,也可以包含用treechildren表示的多行。
  • treerow
    用来定义一条记录,应该放在treeitem内。
  • treecell
    用来定义一条记录的某个字段。应该放在treerow内。

例子的效果为:

从上图我们可以看到,这个简单的tree与列表框差别不大,因为它没有分级。不过,当一列的内容显示不完全时,会采用chop方式来显示其中的元素。同时在表头的最右边还有一个按钮可以调整要显示的列。Mozilla已经为我们提供了许多好的特性。

如果你不想显示右上角的按钮(叫columnpicker),可以设置tree的hidecolumnpicker为”true”即可。不过要保证你已经设置了id属性。

此时用户可以在一个tree中进行多选(点击时按shift或ctrl键),如果不想允许多选,则可以把tree的single属性设为”true”即可。

分级树

分级树是通过在某个treeitem中嵌套treechildren来实现的。

  1. 在需要分级的treeitem元素中,设置containter属性为”true”。这样就允许用户可以通过双击来打开和关闭内部的行。通过设置open属性为”true”或”false”,就可以设置初始状态下内部行的打开和关闭。
  2. 将表头中的主要列设置primary属性,这样就可能在有分级情况的这一列单元格的前面显示一个小三角或加号。一旦某列被设为primary,用户是无法关闭的。
  3. 在某个需要分级的treeitem中嵌入treechildren元素。注意不要放在treerow中,那样做无效。

下面是一个分级树的例子:

<tree flex=”1″>
  <treecols>
      <treecol id=”firstname” label=”First Name” primary=”true” flex=”3″/>
      <treecol id=”lastname” label=”Last Name” flex=”7″/>
  </treecols>

  <treechildren>
    <treeitem container=”true” open=”true”>
      <treerow>
        <treecell label=”Guys”/>
      </treerow>

      <treechildren>
        <treeitem>
          <treerow>
            <treecell label=”Bob”/>
            <treecell label=”Carpenter”/>
          </treerow>
        </treeitem>
        <treeitem>
          <treerow>
            <treecell label=”Jerry”/>
            <treecell label=”Hodge”/>
          </treerow>
        </treeitem>
      </treechildren>
    </treeitem>
  </treechildren>
</tree>

其中红字是我们要注意的。从上面蓝色背景的代码可以看出,只定义了第一个字段。如果第二个字段不定义则为空。定义也是可以的。

执行效果为:

上面的例子是把第一列设为primary,你也可以把其它列设为primary。这样小三角或加号就显示在相应的列上。可以试一试,很有趣。

其它特性

如果设置tree元素的enableColumnDrag,则用户可以拖拽表格头重新安装每列的顺序。

如果想改变列的宽度,你可以在表头处的每个treecol之间放置一个splitter。这样一个小条就会出现的每列之间。然后你还可以使用CSS来设置splitter的max-width样式为0,这样,小条就看不见了,但你仍然可以拖拽。你也可以使用全局CSS样式类tree-splitter来实现这一目的。

你可以在treecol中设置hidden属性为”true”来初始显示时隐藏某一列。用户可以通过修改clumnpicker来显示隐藏列。

好,让我们继续完善findfile.xul,加入如下代码:

  <tree flex=”1″>
    <treecols>
      <treecol id=”name” label=”Filename” flex=”1″/>
      <treecol id=”location” label=”Location” flex=”2″/>
      <treecol id=”size” label=”Size” flex=”1″/>
    </treecols>
  
    <treechildren>
     <treeitem>
       <treerow>
         <treecell label=”mozilla”/>
         <treecell label=”/usr/local”/>
         <treecell label=”2520 bytes”/>
       </treerow>
     </treeitem>
    </treechildren>
  </tree>
  <splitter collapse=”before” resizeafter=”grow”/>

红色部分将替换掉iframe元素。splitter也增加了collapse属性。运行效果为:

2005年01月17日

列表框使用listbox元素,还记得吗?看到这里我的确已经忘了,看来学习与熟练区别很大。那么它除了生成单列的列表框,还可以生成多列的列表框。在内部是通过grid来实现的,但列表框有自已的元素标签,还是有一些区别的。列表框中的每个单元可以放置任何元素,不过通常只是用来放置文本。

下面是一个列子:

<listbox>
  <listcols>
    <listcol flex=”1″/>
    <listcol flex=”2″/>
  </listcols>
  <listitem>
    <listcell label=”George”/>
    <listcell label=”House Painter”/>
  </listitem>
  <listitem>
    <listcell label=”Mary Ellen”/>
    <listcell label=”Candle Maker”/>
  </listitem>
  <listitem>
    <listcell label=”Roger”/>
    <listcell label=”Swashbuckler”/>
  </listitem>
</listbox>

它生成二列三行的一个列表。类似grid,listcols用来定义列字段集。listcol用来定义每列的外观,这里定义了第一列的宽度是第二列的一半。它没有rows这样的行记录集的定义,直接使用listitem定义一行(单列列表框就是使用这个元素来定义每一行的),而每个单元使用listcell来定义。

上面的例子如果你运行,你会发现它没有表头。

如果想要表头,使用listhead和listheader来定义表头。例如:

<listbox>
  <listhead>
    <listheader label=”Name”/>
    <listheader label=”Occupation”/>
  </listhead>

  <listcols>
    <listcol flex=”1″/>
    <listcol flex=”2″/>
  </listcols>
  <listitem>
    <listcell label=”George”/>
    <listcell label=”House Painter”/>
  </listitem>
  <listitem>
    <listcell label=”Mary Ellen”/>
    <listcell label=”Candle Maker”/>
  </listitem>
  <listitem>
    <listcell label=”Roger”/>
    <listcell label=”Swashbuckler”/>
  </listitem>
</listbox>

运行效果为: