From 057aac553b6f946f7993e8ce962ccc9f9fafe770 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 7 Aug 2010 16:21:15 +0000 Subject: Experemental XML UI, define panels/menus/headers which load at startup like python scripts. - 2 panels implimented in properties_render_test.xml (Render Dimensions and Stamp) - only enabled in debug mode. - poll() functions are not supported yet. - as stated above experemental, we'll see if this is at all useful, remove if not. - XML could be replaced with JSON or YAML. --- release/scripts/modules/bpy/utils.py | 35 ++++++ release/scripts/modules/bpy_xml_ui.py | 151 ++++++++++++++++++++++++++ release/scripts/ui/properties_render_test.xml | 79 ++++++++++++++ release/scripts/ui/space_userpref.py | 5 +- 4 files changed, 266 insertions(+), 4 deletions(-) create mode 100644 release/scripts/modules/bpy_xml_ui.py create mode 100644 release/scripts/ui/properties_render_test.xml (limited to 'release') diff --git a/release/scripts/modules/bpy/utils.py b/release/scripts/modules/bpy/utils.py index 6c1c669d1f2..c7ee23264e8 100644 --- a/release/scripts/modules/bpy/utils.py +++ b/release/scripts/modules/bpy/utils.py @@ -30,6 +30,8 @@ import sys as _sys from _bpy import blend_paths from _bpy import script_paths as _bpy_script_paths +_TEST_XML = _bpy.app.debug + def _test_import(module_name, loaded_modules): import traceback import time @@ -52,6 +54,35 @@ def _test_import(module_name, loaded_modules): loaded_modules.add(mod.__name__) # should match mod.__name__ too return mod +if _TEST_XML: + # TEST CODE + def _test_import_xml(path, f, loaded_modules): + import bpy_xml_ui + import traceback + + f_full = _os.path.join(path, f) + _bpy_types._register_immediate = True + try: + classes = bpy_xml_ui.load_xml(f_full) + except: + traceback.print_exc() + classes = [] + _bpy_types._register_immediate = False + + if classes: + mod_name = f.split(".")[0] + + # fake module + mod = type(traceback)(mod_name) + mod.__file__ = f_full + for cls in classes: + setattr(mod, cls.__name__, cls) + + loaded_modules.add(mod_name) + _sys.modules[mod_name] = mod + mod.register = lambda: None # quiet errors + return mod + def modules_from_path(path, loaded_modules): """ @@ -79,6 +110,10 @@ def modules_from_path(path, loaded_modules): else: mod = None + if _TEST_XML: + if mod is None and f.endswith(".xml"): + mod = _test_import_xml(path, f, loaded_modules) + if mod: modules.append(mod) diff --git a/release/scripts/modules/bpy_xml_ui.py b/release/scripts/modules/bpy_xml_ui.py new file mode 100644 index 00000000000..18eec9c4ef4 --- /dev/null +++ b/release/scripts/modules/bpy_xml_ui.py @@ -0,0 +1,151 @@ +# ##### 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 XML into blender/ui function calls. +""" + +import xml.dom.minidom +import bpy as _bpy + +def parse_rna(prop, value): + if prop.type == 'FLOAT': + value = float(value) + elif prop.type == 'INT': + value = int(value) + elif prop.type == 'BOOLEAN': + if value not in ("true", "false"): + raise Exception("invalid bool value: %s", value) + value = bool(value == "true") + elif prop.type in ('STRING', 'ENUM'): + pass + elif prop.type == 'POINTER': + value = eval("_bpy." + value) + else: + raise Exception("type not supported %s.%s" % (prop.identifier, prop.type)) + return value + +def parse_args(base, xml_node): + args = {} + rna_params = base.bl_rna.functions[xml_node.tagName].parameters + for key, value in xml_node.attributes.items(): + args[key] = parse_rna(rna_params[key], value) + return args + +def ui_xml(base, xml_node): + name = xml_node.tagName + prop = base.bl_rna.properties.get(name) + if name in base.bl_rna.properties: + attr = xml_node.attributes.get("expr") + if attr: + value = attr.value + value = eval(value, {"context": _bpy.context}) + setattr(base, name, value) + else: + attr = xml_node.attributes['value'] + value = attr.value + value = parse_rna(prop, value) + setattr(base, name, value) + else: + func_new = getattr(base, name) + kw_args = parse_args(base, xml_node) + base_new = func_new(**kw_args) # call blender func + if xml_node.hasChildNodes(): + ui_xml_list(base_new, xml_node.childNodes) + +def ui_xml_list(base, xml_nodes): + import bpy + for node in xml_nodes: + if node.nodeType not in (node.TEXT_NODE, node.COMMENT_NODE): + ui_xml(base, node) + bpy.N = node + +def test(layout): + uixml = xml.dom.minidom.parseString(open("/mnt/test/blender-svn/blender/release/scripts/ui/test.xml", 'r').read()) + panel = uixml.getElementsByTagName('panel')[0] + ui_xml_list(layout, panel.childNodes) + +def load_xml(filepath): + classes = [] + fn = open(filepath, 'r') + data = fn.read() + uixml = xml.dom.minidom.parseString(data).getElementsByTagName("ui")[0] + fn.close() + + def draw_xml(self, context): + node = self._xml_node.getElementsByTagName("draw")[0] + ui_xml_list(self.layout, node.childNodes) + + def draw_header_xml(self, context): + node = self._xml_node.getElementsByTagName("draw_header")[0] + ui_xml_list(self.layout, node.childNodes) + + for node in uixml.childNodes: + if node.nodeType not in (node.TEXT_NODE, node.COMMENT_NODE): + name = node.tagName + class_name = node.attributes["identifier"].value + + if name == "panel": + class_dict = { + "bl_label": node.attributes["label"].value, + "bl_region_type": node.attributes["region_type"].value, + "bl_space_type": node.attributes["space_type"].value, + "bl_context": node.attributes["context"].value, + "bl_default_closed": ((node.attributes["default_closed"].value == "true") if "default_closed" in node.attributes else False), + + "draw": draw_xml, + "_xml_node": node + } + + if node.getElementsByTagName("draw_header"): + class_dict["draw_header"] = draw_header_xml + + # will register instantly + class_new = type(class_name, (_bpy.types.Panel,), class_dict) + + elif name == "menu": + class_dict = { + "bl_label": node.attributes["label"].value, + + "draw": draw_xml, + "_xml_node": node + } + + # will register instantly + class_new = type(class_name, (_bpy.types.Menu,), class_dict) + + elif name == "header": + class_dict = { + "bl_label": node.attributes["label"].value, + "bl_space_type": node.attributes["space_type"].value, + + "draw": draw_xml, + "_xml_node": node + } + + # will register instantly + class_new = type(class_name, (_bpy.types.Header,), class_dict) + else: + raise Exception("invalid id found '%s': expected a value in ('header', 'panel', 'menu)'" % name) + + classes.append(class_new) + + + return classes diff --git a/release/scripts/ui/properties_render_test.xml b/release/scripts/ui/properties_render_test.xml new file mode 100644 index 00000000000..f8a77e37e21 --- /dev/null +++ b/release/scripts/ui/properties_render_test.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/release/scripts/ui/space_userpref.py b/release/scripts/ui/space_userpref.py index 6f521ede722..9d0a3405354 100644 --- a/release/scripts/ui/space_userpref.py +++ b/release/scripts/ui/space_userpref.py @@ -922,12 +922,11 @@ class USERPREF_PT_addons(bpy.types.Panel): return list(USERPREF_PT_addons._addons_fake_modules.values()) else: - # never run this!, before it used ast + # never run this!, before it used 'ast' module pass ''' # note, this still gets added to _bpy_types.TypeMap import bpy_types as _bpy_types - _bpy_types._register_override = True # sys.path.insert(0, None) for path in paths: @@ -937,8 +936,6 @@ class USERPREF_PT_addons(bpy.types.Panel): if bpy.app.debug: print("Addon Script Load Time %.4f" % (time.time() - t_main)) - _bpy_types._register_override = False - # del sys.path[0] return modules ''' -- cgit v1.2.3