def test_dot_lookup(self): self.assertEqual(dot_lookup({'a': {'b': 'hello'}}, 'a.b'), 'hello') self.assertEqual(dot_lookup({'a': {'b': 'hello'}}, ['a', 'b']), 'hello') self.assertEqual(dot_lookup({'a': {'b': 'hello'}}, 'a.b', parent=True), {'b': 'hello'}) self.assertEqual(dot_lookup({'a': {'b': 'hello'}}, ''), {'a': {'b': 'hello'}})
def render(self): # TODO: Fix it so only changed components are updated # for widget in self._frame.winfo_children(): # widget.destroy() # Recursively build component tree dict root_tree = parse_component_tree(self._root_comp()) if self._old_tree is not None: delta = diff(self._old_tree, root_tree, ignore=('_comp_obj', '_comp_update')) for diff_type, index, data in delta: if isinstance(index, str) or len(index) == 1: # Top-level change old_node = self._old_tree else: # Sub-node change old_node = dot_lookup(self._old_tree, index[:-1]) # Update node props if diff_type == 'change': update_fn = old_node['_comp_update'] new_node = dot_lookup(root_tree, index[:-1]) new_node['_comp_update'] = update_fn new_node['_comp_obj'] = old_node['_comp_obj'] if update_fn is not None: update_fn(text=new_node['text'], **new_node['_props']) # # Copy component info # Update new node in root tree (if new one created) # patch([('change', index[:-1], (old_node2, new_node))], root_tree) else: render_list = [(root_tree, None)] self.render_component(root_tree) # TODO: Diff tree against existing tree # Instantiate or modify components as needed (pack(), update() or destroy()) self._dirty = False self._old_tree = root_tree
def patch(differences, lTree, rTree, lDict, rDict, out): root = etree.Element('diff') for difference in differences: type = difference[0] path = difference[1] change = difference[2] if type == 'change': if 'attrib' in path: split = path.split('.attrib.') parent_path = path.split('.attrib.')[0] attrib = path.split('.attrib.')[1] lnode = dot_lookup(lDict, parent_path) replace = etree.SubElement( root, 'replace', attrib={'sel': lnode['path'] + '/@' + attrib}) replace.text = change[1] else: lnode = dot_lookup(lDict, path, parent=True) replace = etree.SubElement( root, 'replace', attrib={'sel': lnode['path'] + '/text()'}) replace.text = change[1] elif type == 'add': if 'attrib' in path: for c in change: lnode = dot_lookup(lDict, path, parent=True) add = etree.SubElement(root, 'add', attrib={ 'sel': lnode['path'], 'type': '@' + c[0] }) add.text = c[1] else: for c in change: xpath = c[0] relement = rTree.xpath(xpath)[0] lelement = relement.getprevious() if lelement is not None: xpath = better_xpath(lelement, rTree) add = etree.SubElement(root, 'add', attrib={ 'sel': xpath, 'pos': 'after' }) add.insert(0, deepcopy(relement)) else: lelement = relement.getparent() xpath = better_xpath(lelement, rTree) add = etree.SubElement(root, 'add', attrib={ 'sel': xpath, 'pos': 'prepend' }) add.insert(0, deepcopy(relement)) elif type == 'remove': if 'attrib' in path: for c in change: lnode = dot_lookup(lDict, path, parent=True) remove = etree.SubElement( root, 'remove', attrib={'sel': lnode['path'] + '/@' + c[0]}) else: for c in change: xpath = c[0] etree.SubElement(root, 'remove', attrib={'sel': xpath}) et = etree.ElementTree(root) et.write(out, xml_declaration=True, encoding='utf-8', pretty_print=True)