diff options
author | Campbell Barton <ideasman42@gmail.com> | 2010-08-07 20:21:15 +0400 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2010-08-07 20:21:15 +0400 |
commit | 057aac553b6f946f7993e8ce962ccc9f9fafe770 (patch) | |
tree | d8658bd99a172803d54930f24a7859cba6f22dfc /release | |
parent | 2b3c8bdc27121e6c9103b26cffd705cee9e20b4a (diff) |
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.
Diffstat (limited to 'release')
-rw-r--r-- | release/scripts/modules/bpy/utils.py | 35 | ||||
-rw-r--r-- | release/scripts/modules/bpy_xml_ui.py | 151 | ||||
-rw-r--r-- | release/scripts/ui/properties_render_test.xml | 79 | ||||
-rw-r--r-- | release/scripts/ui/space_userpref.py | 5 |
4 files changed, 266 insertions, 4 deletions
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 ##### + +# <pep8 compliant> + +""" +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 @@ +<ui> + <panel identifier="RENDER_PT_stamp_test" label="Stamp (XML)" space_type="PROPERTIES" region_type="WINDOW" context="render" default_closed="true"> + <draw_header> + <prop data="context.scene.render" property="render_stamp" text=""/> + </draw_header> + + <draw> + <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> + <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"/> + </column> + </split> + <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=""/> + </row> + </split> + </draw> + </panel> + + <panel identifier="RENDER_PT_dimensions_test" label="Dimensions (XML)" space_type="PROPERTIES" region_type="WINDOW" context="render"> + <draw> + <row align="true"> + <menu menu="RENDER_MT_presets"/> + <operator operator="render.preset_add" text="" icon="ZOOMIN"/> + </row> + <split> + <column> + <column align="true"> + <label text="Resolution:"/> + <prop data="context.scene.render" property="resolution_x" text="X"/> + <prop data="context.scene.render" property="resolution_y" text="Y"/> + <prop data="context.scene.render" property="resolution_percentage" text=""/> + + <label text="Aspect Ratio:"/> + <prop data="context.scene.render" property="pixel_aspect_x" text="X"/> + <prop data="context.scene.render" property="pixel_aspect_y" text="Y"/> + </column> + <row> + <prop data="context.scene.render" property="use_border" text="Border"/> + <row> + <active expr="context.scene.render.use_border"/> + <prop data="context.scene.render" property="crop_to_border" text="Crop"/> + </row> + </row> + </column> + <column> + <column align="true"> + <label text="Frame Range:"/> + <prop data="context.scene" property="frame_start" text="Start"/> + <prop data="context.scene" property="frame_end" text="End"/> + <prop data="context.scene" property="frame_step" text="Step"/> + + <label text="Frame Rate:"/> + <prop data="context.scene.render" property="fps"/> + <prop data="context.scene.render" property="fps_base" text="/"/> + </column> + </column> + </split> + </draw> + </panel> +</ui> 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 ''' |