File: builderhacks.py

package info (click to toggle)
mypaint 2.0.1-14
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 27,884 kB
  • sloc: python: 43,893; cpp: 6,931; xml: 2,475; sh: 473; makefile: 25
file content (130 lines) | stat: -rwxr-xr-x 4,354 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# -*- coding: utf-8 -*-
# This file is part of MyPaint.
# Copyright (C) 2013-2018 by the MyPaint Development Team.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.


"""Hacks for loading stuff from GtkBuilder files."""

## Imports
from __future__ import division, print_function

from lib.gibindings import Gtk

import lib.xml


## Public functions

def add_objects_from_template_string(builder, buffer_, object_ids, params):
    """Templatizes, parses, merges, and returns objects from a Builder UI-def

    This function wraps `Gtk.Builder.add_objects_from_string()`, with the
    addition that the `buffer_` parameter, and each element of `object_ids` is
    formatted using `str.format()` using `params` before use. This templatizing
    is required to produce a different result for the string buffer of XML
    data, and for each object ID.

    :param builder: a Gtk.Buider
    :param buffer_: the string to templatize then parse
    :param object_ids: list of object names to build (after templatizing)
    :param params: dict of template params
    :returns: a list of constructed objects

    The constructed objects are returned in a Python list if this wrapped
    method call is successful.

    When templatizing the XML fragment, parameter values will be escaped using
    `lib.xml.escape()`. Therefore `params` is limited to fairly simple
    dicts.

    """
    object_ids2 = []
    for oid in object_ids:
        oid2 = oid.format(**params)
        if oid == oid2:
            raise ValueError("object_id %s unchanged after .format()ing"
                             % oid)
        object_ids2.append(oid2)
    params_esc = {}
    for p, v in params.items():
        params_esc[p] = lib.xml.escape(v)
    buffer_2 = buffer_.format(**params_esc)
    if buffer_2 == buffer_:
        raise ValueError("buffer_ unchanged after .format()ing")
    result = []
    if builder.add_objects_from_string(buffer_2, object_ids2):
        for oid2 in object_ids2:
            obj2 = builder.get_object(oid2)
            assert obj2 is not None
            result.append(obj2)
    return result


## Module testing


_TEST_TEMPLATE = """
<interface>
    <object class="GtkLabel" id="never_instantiated">
        <property name="label">This should never be instantiated</property>
    </object>
    <object class="GtkButton" id="button_{id}">
        <property name="label">{label}</property>
        <signal name="clicked" handler="button_{id}_clicked"/>
    </object>
</interface>
"""


def _test():
    """Interactive module test function"""
    import os
    import sys
    vbox = Gtk.VBox()
    builder = Gtk.Builder()

    # Handlers can find out about their template values by parsing their
    # name (using the GtkBuildable interface). Alternatively, you can set
    # up private attributes in the instantiation loop.

    def _test_button_clicked_cb(widget):
        id_ = Gtk.Buildable.get_name(widget)
        if isinstance(id_, bytes):
            id_ = id_.decode("utf-8")
        print("Clicked: id=%r" % (id_, ))
        print("          i=%r" % (widget._i, ))

    # Unicode is supported in IDs and template values.
    # The XML template may be plain ASCII since escape() is used when
    # filling it.
    object_ids = [u"button_{id}"]
    words = [u"à", u"chacun", u"son", u"goût"]
    for i in words:
        params = {"id": i, "label": i.upper()}
        objs = add_objects_from_template_string(builder, _TEST_TEMPLATE,
                                                object_ids, params)
        for w in objs:
            w.connect("clicked", _test_button_clicked_cb)
            vbox.pack_start(w, True, True, 0)
            w._i = i
    # The label should never be instantiated by this code. In fact, only
    # the four buttons should.
    for obj in builder.get_objects():
        assert isinstance(obj, Gtk.Button)
    # Remainder of the demo code
    window = Gtk.Window()
    window.add(vbox)
    window.set_title(os.path.basename(sys.argv[0]))
    window.connect("destroy", lambda *a: Gtk.main_quit())
    window.set_size_request(250, 200)
    window.show_all()
    Gtk.main()


if __name__ == '__main__':
    _test()