Skip to content

Instantly share code, notes, and snippets.

@hansent
Forked from tito/implem-nested-prop.patch
Created October 10, 2012 21:47
Show Gist options
  • Save hansent/3868666 to your computer and use it in GitHub Desktop.
Save hansent/3868666 to your computer and use it in GitHub Desktop.
diff --git a/kivy/lang.py b/kivy/lang.py
index 10cdd21..5fa43bc 100644
--- a/kivy/lang.py
+++ b/kivy/lang.py
@@ -613,6 +613,7 @@ class ParserRule(object):
'''
__slots__ = ('ctx', 'line', 'name', 'children', 'id', 'properties',
+ 'nested_properties',
'canvas_before', 'canvas_root', 'canvas_after',
'handlers', 'level', 'cache_marked')
@@ -632,6 +633,8 @@ class ParserRule(object):
self.id = None
#: Properties associated to the rule
self.properties = OrderedDict()
+ #: Nested properties
+ self.nested_properties = OrderedDict()
#: Canvas normal
self.canvas_root = None
#: Canvas before
@@ -651,6 +654,8 @@ class ParserRule(object):
def precompile(self):
for x in self.properties.itervalues():
x.precompile()
+ for x in self.nested_properties.itervalues():
+ x.precompile()
for x in self.handlers:
x.precompile()
for x in self.children:
@@ -671,6 +676,12 @@ class ParserRule(object):
for name in self.properties:
if not hasattr(widget, name):
widget.create_property(name)
+ for name in self.nested_properties:
+ key = name.split('.', 1)[0]
+ if not hasattr(widget, key):
+ rule = self.nested_properties[name]
+ raise ParserException(rule.ctx, rule.line,
+ 'No property {0} in {1}'.format(key, widget.__class__))
def _forbid_selectors(self):
c = self.name[0]
@@ -737,7 +748,7 @@ class Parser(object):
CLASS_RANGE = range(ord('A'), ord('Z') + 1)
PROP_RANGE = range(ord('A'), ord('Z') + 1) + \
range(ord('a'), ord('z') + 1) + \
- range(ord('0'), ord('9') + 1) + [ord('_')]
+ range(ord('0'), ord('9') + 1) + [ord('_'), ord('.')]
__slots__ = ('rules', 'templates', 'root', 'sourcecode',
'directives', 'filename')
@@ -940,7 +951,10 @@ class Parser(object):
if name[:3] == 'on_':
current_object.handlers.append(rule)
else:
- current_object.properties[name] = rule
+ if '.' in name:
+ current_object.nested_properties[name] = rule
+ else:
+ current_object.properties[name] = rule
else:
current_property = name
current_propobject = None
@@ -968,8 +982,12 @@ class Parser(object):
if current_property[:3] == 'on_':
current_object.handlers.append(current_propobject)
else:
- current_object.properties[current_property] = \
- current_propobject
+ if '.' in name:
+ current_object.nested_properties[current_property] = \
+ current_propobject
+ else:
+ current_object.properties[current_property] = \
+ current_propobject
else:
current_propobject.value += '\n' + content
@@ -1010,7 +1028,14 @@ def create_handler(iself, element, key, value, rule, idmap):
e_value = eval(value, idmap)
if __debug__:
trace('Builder: call_fn => value=%r' % (e_value, ))
- setattr(element, key, e_value)
+ if type(key) is tuple:
+ _key, subkey = key
+ subelement = getattr(element, _key)
+ if subelement is None:
+ return
+ setattr(subelement, subkey, e_value)
+ else:
+ setattr(element, key, e_value)
# bind every key.value
if rule.watched_keys is not None:
@@ -1228,6 +1253,7 @@ class BuilderBase(object):
assert(rule not in self.rulectx)
self.rulectx[rule] = rctx = {
'ids': {'root': widget},
+ 'nset': [],
'set': [], 'hdl': []}
# extract the context of the rootrule (not rule!)
@@ -1311,6 +1337,8 @@ class BuilderBase(object):
# append the properties and handlers to our final resolution task
if rule.properties:
rctx['set'].append((widget, rule.properties.values()))
+ if rule.nested_properties:
+ rctx['nset'].append((widget, rule.nested_properties.values()))
if rule.handlers:
rctx['hdl'].append((widget, rule.handlers))
@@ -1331,6 +1359,19 @@ class BuilderBase(object):
value, rule, rctx['ids'])
setattr(widget_set, key, value)
+ # now apply nested handlers
+ for widget_set, rules in reversed(rctx['nset']):
+ for rule in rules:
+ assert(isinstance(rule, ParserRuleProperty))
+ key, subkey = rule.name.split('.', 1)
+ value = rule.co_value
+ if type(value) is CodeType:
+ value = create_handler(widget_set, widget_set, (key, subkey),
+ value, rule, rctx['ids'])
+ element = getattr(widget_set, key)
+ if element is not None:
+ setattr(element, subkey, value)
+
# build handlers
for widget_set, rules in rctx['hdl']:
for crule in rules:
from kivy.app import App
from kivy.lang import Builder
from kivy.clock import Clock
'''
Facts:
1. can't use "self.texture_1.tex_coords", texture_1 have a bind() method, but doesn't correspond to the Property bind().
2. self.tex_coords_1 or (0, 0, 0, 0, 0, 0, 0, 0) is needed because self.tex_coords_1 is None by default
3. can't use "self.texture_1.tex_coords" because texture_1 is None at the start
'''
root = Builder.load_string(
'''
#:import Image kivy.core.image.Image
#:import time time.time
Widget:
boottime: 0
texture_1: Image('Sky_back_layer.png').texture
texture_2: Image('Vegetation_(middle_layer).png').texture
texture_3: Image('Ground_(front_layer).png').texture
texture_1.wrap: 'repeat'
texture_2.wrap: 'repeat'
texture_3.wrap: 'repeat'
texture_1.uvpos: root.boottime * 0.001, 0
texture_2.uvpos: root.boottime * 0.01, 0
texture_3.uvpos: root.boottime * 0.1, 0
tex_coords_1: (getattr(self.texture_1, 'tex_coords'), root.boottime)[0]
tex_coords_2: (getattr(self.texture_2, 'tex_coords'), root.boottime)[0]
tex_coords_3: (getattr(self.texture_3, 'tex_coords'), root.boottime)[0]
canvas:
Rectangle:
pos: self.pos
size: self.size
texture: self.texture_1
tex_coords: self.tex_coords_1 or (0, 0, 0, 0, 0, 0, 0, 0)
Rectangle:
pos: self.pos
size: self.size
texture: self.texture_2
tex_coords: self.tex_coords_2 or (0, 0, 0, 0, 0, 0, 0, 0)
Rectangle:
pos: self.pos
size: self.size
texture: self.texture_3
tex_coords: self.tex_coords_3 or (0, 0, 0, 0, 0, 0, 0, 0)
''')
class MyApp(App):
def build(self):
Clock.schedule_interval(self.set_time, 0)
return root
def set_time(self, dt):
self.root.boottime = Clock.get_boottime()
if __name__ == '__main__':
MyApp().run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment