首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

在 PyGTK 中管理部件几何结构(3)实现

在 PyGTK 中管理部件几何结构(3)实现

实现 GTK+ 几何结构管理模型由两个阶段组成:
  • 请求(requisition):在此阶段,容器遍历它的 child,并询问每个 child 所需的空间。
  • 分配(allocation):在此阶段,为容器指定子矩形像素空间,并将其划分给它的 child。
要注意,这两个阶段都是递归的,有些 child 本身也是容器。
gtk.Widget(所有 PyGTK 部件的基类)的一些 foo 方法在内部调用虚方法        do_foo。目前,这一点没有明确整理成文档,但是本文和  小节中的例子应该清楚地展示了这一点。同样,我们的实现覆盖了其中一些 do_foo 虚方法。
请求 请求阶段通过以下方法实现:
清单 9. WTable.do_size_request
1
2
3
4
5
6
7
8
9
10
11
12
13
14
def do_size_request(self, oreq):
    reqMgrs = (req.Manager(), req.Manager()) # (X,Y)
    for child in self.gChildren:
        request = child.widget.size_request() # compute!
        for xyi in (X, Y):
            be = child.lrtb[xyi]
            glue1d = child.glue.xy[xyi]
            sz = request[xyi] + glue1d.base()
            rr = req.RangeSize(be, sz)
            reqMgrs[xyi].addRangeReq(rr)
    self.reqs = map(lambda m: m.solve(), reqMgrs) # (X,Y)
    bw2 = 2 * self.border_width
    oreq.width  = min(sum(self.reqs[X]) + bw2, self.maxsize[0])
    oreq.height = min(sum(self.reqs[Y]) + bw2, self.maxsize[1])




其他注意作为演示,这里忽略了很多可能的改进。例如,参数验证和 Python 的标准的、可选的方法字符串文档均被忽略。

类似地,我们并没有优雅地处理用户错误,例如检查无效的区间。

Glue 对象可以安全地被共享,因为 WTable 既不会扩大它们,也不会更改它们的值。

wGrow 权重值主要用于扩展给定的额外空间。对于像素不足的特殊情况,实际上需要元素有更大的 wGrow,从而缩小得更少。        因此,可以以递减函数的方式转换这些数字。而更聪明的 Glue        类可以有显式的缩小权重。

它通过 oreq.width 和        oreq.height 字段返回值。        reqMgrs 对象收集 child 所需的大小,然后调用类 req.Manager 的 solve() 方法。它确定并返回每个列和行所需的大小。现在,我们感兴趣的是通过        oreq 返回的总数。注意,即使我们得到每个列和行的大小的解决方法,需求可能是以行和列的区间 的形式给出的。区间可能包含不止一个列或一个行。
分配 在此阶段,您得到一部分珍贵的屏幕空间。这里所得到的分配是之前询问的空间请求和祖先(ancestor)容器策略(其中之一就是桌面窗口管理器,它可以以用户交互的方式调整顶级窗口的大小)的结果。
如果分配的空间刚好符合建议的 WTable 需求,那么只需按照前一步中的 req.Manager 类提供的方法在 WTable 的 child 之间分配空间。                                                                                            否则,会得到额外的空间,或者出现像素不足,后一种情况较少见。在这两种情况下,都需要对差额进行分配。
glue 和 widget 的 wGrow 值现在被用作伪(pseudo)需求;可以以类似的方法收集和解决这些需求。这一次,单位不是像素,而是相对权重,它们的相对份额被用于扩展或缩小列和行(参见 )。
清单 10. WTable.do_size_allocate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def do_size_allocate(self, allocation):
    self.allocation = allocation
    allocs = (allocation.width, allocation.height)
    alloc_offsets = (self.border_width, self.border_width)
    self.crSizes = [None, None]  # 2-lists: columns size, rows sizes.
    self.offsets = [None, None]  # alloc_offsets + partial sums of crSizes
    for xyi in (X, Y):
        a = allocs[xyi]
        reqs = self.reqs[xyi]
        gWeights = self._getGrowWeights(xyi)
        self.crSizes[xyi] = given = self._divide(allocs[xyi], reqs, gWeights)
        offsets = len(given) * [None]
        offsets[0] = alloc_offsets[xyi]
        for oi in range(1, len(offsets)):
            offsets[oi] = offsets[oi - 1] + given[oi - 1]
        self.offsets[xyi] = offsets
    for child in self.gChildren:
        self._allocate_child(child)

    if self.flags() & GTK.REALIZED:
        self.window.move_resize(*allocation)




对于清单 10 中的步骤,do_size_allocate()        方法:
  • 确定每个维上的每个分段(列或行)的大小。将这些大小的部分和与          allocation.x 和          allocation.y 相加,便得到偏移量。
  • 确定行和列的分配大小之后,就可以设置每个 child 的分配。同样要注意,一个 child 不一定就占用一个 “单元”,它可能占用由一个行和列区间 组成的一个矩形。
最后,调用 window.move_resize() 方法。注意,allocation 参数被加上        * 前缀,以利用 gtk.gdk.Rectangle 类提供的排序方法(参见  中的 fields 元素,它用于自动生成定义 PyGdkRectangle_Type 的 C 代码。这将        allocation 转换成与类 class gtk.gdk.Window 的 move_resize() 方法匹配的元组)。
表的空间分配划分 在前面的请求阶段中,确定了每个列和行所需的像素大小。现在需要划分一个给定的总分配。如果刚好与需求相符,那么可以直接使用它们,否则需要进行调整,加上或减去(较少见)额外的像素。
应该根据权重来划分差额。同样,用户不会显式地将权重赋给列和行,而是通过一个 “Glue” 将        wGrow 值赋给每个 child。这样做可以在 req.Manager 类的帮助下推算出所需的权重。
清单 11. WTable._getGrowWeights
1
2
3
4
5
6
7
8
9
10
11
12
def _getGrowWeights(self, xyi):
    wMgr = req.Manager()
    for child in self.gChildren:
        be = child.lrtb[xyi]
        glue1d = child.glue.xy[xyi]
        rr = req.RangeSize(be, glue1d.total_grow_weight())
        wMgr.addRangeReq(rr)
    wMgr.solve()
    gws = wMgr.reqs
    if sum(gws) == 0:
        gws = len(gws) * [1]  # if zero weights then equalize
    return gws




对于 “cake” 分配空间(清单 12),根据需求和权重对其进行划分,以划分差额。注意,当像素不足时,要对权重进行转换,这样较大的权重损失较小。另外,还需要注意全 0 权重的情况,这种情况被视作全等。
清单 12. WTable._divide
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def _divide(self, cake, requirements, growWeights):
    n = len(requirements)   # == len(growWeights)
    given = requirements[:] # start with exact satisfaction
    reqTotal = sum(requirements)
    delta = cake - reqTotal
    if delta < 0: # rarely, "invert" weights
        growWeights = map(lambda x: max(growWeights) - x, growWeights)
        if sum(growWeights) == 0:
            growWeights = n * [1]; # equalize
    i = 0
    gwTotal = sum(growWeights)
    while gwTotal > 0 and delta != 0:
        add = (delta * growWeights + gwTotal/2) / gwTotal
        gwTotal -= growWeights
        given += add
        delta -= add
        i += 1
    return given




child 的空间分配 现在,列和行的分配已确定,并且用        crSizes 和 offsets 表示,接下来为每个 child 分配空间就很简单了。惟一要考虑的是为 child 提供的分配空间与它的需求之间的差额。
清单 13. WTable._allocate_child
1
2
3
4
5
6
7
8
9
10
11
12
13
def _allocate_child(self, child):
    offsetsxy = [None, None]
    req = list( child.widget.get_child_requisition() ) # pre-calculated
    for xyi in (X, Y):
        segRange = child.lrtb[xyi]
        g1d = child.glue.xy[xyi]
        supply = sum( self.crSizes[xyi][segRange[0]: segRange[1]] )
        (oadd, cadd) = g1d.place(req[xyi], supply)
        offsetsxy[xyi] = self.offsets[xyi][ segRange[0] ] + oadd
        req[xyi] += cadd
    allocation = gdk.Rectangle(x=offsetsxy[0], y=offsetsxy[1],
                               width=req[0], height=req[1])
    child.widget.size_allocate(allocation)




为了确定 child 与边距要增长(或缩小)多少,使用 Glue1D.place 方法。对于每个 child,该方法被调用两次:每个 (X,Y) 维调用一次。对于一个给定的维,有 3 个 wGrow 值要考虑:两个边(左和右,或者上和下)和 child 值本身。
清单 14. Glue1D.place
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def place(self, cneed, supply):
    pads = self.base()
    need = cneed + pads
    delta = supply - need;
    if delta >= 0:
        gwTotal = self.total_grow_weight()
        oadd = self.springs[0].pad
        if gwTotal == 0:
            cadd = delta
        else:
            oadd += round_div(delta * self.springs[0].wGrow, gwTotal)
            cadd = round_div(delta * self.wGrow, gwTotal)
    else: # rare
        shrink = -delta
        if pads >= shrink:  # Cutting from the pads is sufficient
            oadd = round_div(self.springs[0].pad * delta, pads)
            cadd = 0
        else:
            oadd = 0
            cadd = delta    # reduce the child as well
    return (oadd, cadd)




Glue1D.place 方法返回整数 (oadd,cadd) 的一个二元组。
  • oadd 被加到偏移量中。同样,这意味着第一个边距(左边距或上边距)将增长多少。
  • cadd 被加到 child 的 widget          分配空间(宽度或高度)中。
它使用前面提到的 total_grow_weight()        方法和下面的 round_div 方法(参见  中的 PEP 238):
清单 15. round_div
1
2
def round_div(n, d):
    return (n + d/2) / d

返回列表