-
Notifications
You must be signed in to change notification settings - Fork 385
/
builderhacks.py
executable file
·130 lines (106 loc) · 4.25 KB
/
builderhacks.py
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()