From ab8ccaa7098f6fee887f2a249fddada7864cd6c5 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 11 Aug 2010 15:11:30 +0000 Subject: python declarative UI - remove XML testing feature - add 2 modules: bpyml - generic, bpyml_ui - blender spesific. nothing uses these now. ==bpyml_ui module== defines BPyML_BaseUI and its draw() function which uses the bpyml member of the class instance self.draw_data & self.draw_header_data. This way declarative ui is opt-in and easy to use by using BPyML_BaseUI as a mix-in class. ==bpyml module== This module translates a python like XML representation into XML or simple python blender/ui function calls. sometag(arg=10) [ another(), another(key="value") ] # converts into ... --- release/scripts/modules/bpyml.py | 204 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 release/scripts/modules/bpyml.py (limited to 'release/scripts/modules/bpyml.py') diff --git a/release/scripts/modules/bpyml.py b/release/scripts/modules/bpyml.py new file mode 100644 index 00000000000..f30a4b8d9ac --- /dev/null +++ b/release/scripts/modules/bpyml.py @@ -0,0 +1,204 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# 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. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# + +""" +This module translates a python like XML representation into XML +or simple python blender/ui function calls. + + sometag(arg=10) [ + another() + another(key="value") + ] + +# converts into ... + + + + + + +""" + +TAG, ARGS, CHILDREN = range(3) +class ReturnStore(tuple): + def __getitem__(self, key): + + # single item get's + if type(key) is ReturnStore: + key = (key, ) + + if type(key) is tuple: + children = self[CHILDREN] + if children: + raise Exception("Only a single __getitem__ is allowed on the ReturnStore") + else: + children[:] = key + return self + else: + return tuple.__getitem__(self, key) + + +class FunctionStore(object): + def __call__(self, **kwargs): + return ReturnStore((self.__class__.__name__, kwargs, [])) + + +def tag_vars(tags, module=__name__): + return {tag: type(tag, (FunctionStore, ), {"__module__": module})() for tag in tags} + + +def tag_module(mod_name, tags): + import sys + from types import ModuleType + mod = ModuleType(mod_name) + sys.modules[mod_name] = mod + dict_values = tag_vars(tags, mod_name) + mod.__dict__.update(dict_values) + return mod + + +def toxml(py_data, indent=" "): + + if len(py_data) != 1 or type(py_data) != list: + raise Exception("Expected a list with one member") + + def _to_xml(py_item, xml_node=None): + if xml_node is None: + xml_node = newdoc.createElement(py_item[TAG]) + + for key, value in py_item[ARGS].items(): + xml_node.setAttribute(key, str(value)) + + for py_item_child in py_item[CHILDREN]: + xml_node.appendChild(_to_xml(py_item_child)) + + return xml_node + + def _to_xml_iter(xml_parent, data_ls): + for py_item in data_ls: + xml_node = newdoc.createElement(py_item[TAG]) + + + # ok if its empty + _to_xml_iter(xml_node, py_item[CHILDREN]) + + import xml.dom.minidom + impl = xml.dom.minidom.getDOMImplementation() + newdoc = impl.createDocument(None, py_data[0][TAG], None) + + _to_xml(py_data[0], newdoc.documentElement) + + return newdoc.documentElement.toprettyxml(indent=" ") + + +def fromxml(data): + def _fromxml_kwargs(xml_node): + kwargs = {} + for key, value in xml_node.attributes.items(): + kwargs[key] = value + return kwargs + + + def _fromxml(xml_node): + py_item = (xml_node.tagName, _fromxml_kwargs(xml_node), []) + #_fromxml_iter(py_item, xml_node.childNodes) + for xml_node_child in xml_node.childNodes: + if xml_node_child.nodeType not in (xml_node_child.TEXT_NODE, xml_node_child.COMMENT_NODE): + py_item[CHILDREN].append(_fromxml(xml_node_child)) + return py_item + + import xml.dom.minidom + xml_doc = xml.dom.minidom.parseString(data) + return [_fromxml(xml_doc.documentElement)] + + +def topretty_py(py_data, indent=" "): + + if len(py_data) != 1: + raise Exception("Expected a list with one member") + + lines = [] + + def _to_kwargs(kwargs): + return ", ".join([("%s=%s" % (key, repr(value))) for key, value in sorted(kwargs.items())]) + + def _topretty(py_item, indent_ctx, last): + if py_item[CHILDREN]: + lines.append("%s%s(%s) [" % (indent_ctx, py_item[TAG], _to_kwargs(py_item[ARGS]))) + py_item_last = py_item[CHILDREN][-1] + for py_item_child in py_item[CHILDREN]: + _topretty(py_item_child, indent_ctx + indent, (py_item_child is py_item_last)) + lines.append("%s]%s" % (indent_ctx, ("" if last else ","))) + else: + lines.append("%s%s(%s)%s" % (indent_ctx, py_item[TAG], _to_kwargs(py_item[ARGS]), ("" if last else ","))) + + _topretty(py_data[0], "", True) + + return "\n".join(lines) + +if __name__ == "__main__": + # testing code. + + tag_module("bpyml_test", ("ui", "prop", "row", "column", "active", "separator", "split")) + from bpyml_test import * + + draw = [ + ui() [ + split() [ + column() [ + prop(data='context.scene.render', property='stamp_time', text='Time'), + prop(data='context.scene.render', property='stamp_date', text='Date'), + prop(data='context.scene.render', property='stamp_render_time', text='RenderTime'), + prop(data='context.scene.render', property='stamp_frame', text='Frame'), + prop(data='context.scene.render', property='stamp_scene', text='Scene'), + prop(data='context.scene.render', property='stamp_camera', text='Camera'), + prop(data='context.scene.render', property='stamp_filename', text='Filename'), + prop(data='context.scene.render', property='stamp_marker', text='Marker'), + prop(data='context.scene.render', property='stamp_sequencer_strip', text='Seq. Strip') + ], + column() [ + active(expr='context.scene.render.render_stamp'), + prop(data='context.scene.render', property='stamp_foreground', slider=True), + prop(data='context.scene.render', property='stamp_background', slider=True), + separator(), + prop(data='context.scene.render', property='stamp_font_size', text='Font Size') + ] + ], + split(percentage=0.2) [ + prop(data='context.scene.render', property='stamp_note', text='Note'), + row() [ + active(expr='context.scene.render.stamp_note'), + prop(data='context.scene.render', property='stamp_note_text', text='') + ] + ] + ] + ] + + xml_data = toxml(draw) + print(xml_data) # xml version + + py_data = fromxml(xml_data) + print(py_data) # converted back to py + + xml_data = toxml(py_data) + print(xml_data) # again back to xml + + py_data = fromxml(xml_data) # pretty python version + print(topretty_py(py_data)) -- cgit v1.2.3