# ##### 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 ##### # import bpy def rna_idprop_ui_get(item, create=True): try: return item['_RNA_UI'] except: if create: item['_RNA_UI'] = {} return item['_RNA_UI'] else: return None def rna_idprop_ui_prop_get(item, prop, create=True): rna_ui = rna_idprop_ui_get(item, create) if rna_ui == None: return None try: return rna_ui[prop] except: rna_ui[prop] = {} return rna_ui[prop] def rna_idprop_ui_prop_clear(item, prop): rna_ui = rna_idprop_ui_get(item, False) if rna_ui == None: return try: del rna_ui[prop] except: pass def draw(layout, context, context_member, use_edit=True): def assign_props(prop, val, key): prop.path = context_member prop.property = key try: prop.value = str(val) except: pass rna_item = eval("context." + context_member) # poll should really get this... if not rna_item: return items = rna_item.items() items.sort() if use_edit: row = layout.row() props = row.operator("wm.properties_add", text="Add") props.path = context_member del row for key, val in items: if key == '_RNA_UI': continue row = layout.row() convert_to_pyobject = getattr(val, "convert_to_pyobject", None) val_orig = val if convert_to_pyobject: val_draw = val = val.convert_to_pyobject() val_draw = str(val_draw) else: val_draw = val box = row.box() if use_edit: split = box.split(percentage=0.75) row = split.row() else: row = box.row() row.label(text=key) # explicit exception for arrays if convert_to_pyobject and not hasattr(val_orig, "len"): row.label(text=val_draw) else: row.prop(rna_item, '["%s"]' % key, text="") if use_edit: row = split.row(align=True) prop = row.operator("wm.properties_edit", text="edit") assign_props(prop, val_draw, key) prop = row.operator("wm.properties_remove", text="", icon='ZOOMOUT') assign_props(prop, val_draw, key) class PropertyPanel(bpy.types.Panel): """ The subclass should have its own poll function and the variable '_context_path' MUST be set. """ bl_label = "Custom Properties" bl_default_closed = True def draw(self, context): draw(self.layout, context, self._context_path) from bpy.props import * rna_path = StringProperty(name="Property Edit", description="Property path edit", maxlen=1024, default="", options={'HIDDEN'}) rna_value = StringProperty(name="Property Value", description="Property value edit", maxlen=1024, default="") rna_property = StringProperty(name="Property Name", description="Property name edit", maxlen=1024, default="") rna_min = FloatProperty(name="Min", default=0.0, precision=3) rna_max = FloatProperty(name="Max", default=1.0, precision=3) class WM_OT_properties_edit(bpy.types.Operator): '''Internal use (edit a property path)''' bl_idname = "wm.properties_edit" bl_label = "Edit Property" path = rna_path property = rna_property value = rna_value min = rna_min max = rna_max description = StringProperty(name="Tip", default="") # the class instance is not persistant, need to store in the class # not ideal but changes as the op runs. _last_prop = [''] def execute(self, context): path = self.properties.path value = self.properties.value prop = self.properties.property prop_old = self._last_prop[0] try: value_eval = eval(value) except: value_eval = value if type(value_eval) == str: value_eval = '"' + value_eval + '"' # First remove item = eval("context.%s" % path) rna_idprop_ui_prop_clear(item, prop_old) exec_str = "del item['%s']" % prop_old # print(exec_str) exec(exec_str) # Reassign exec_str = "item['%s'] = %s" % (prop, value_eval) # print(exec_str) exec(exec_str) self._last_prop[:] = [prop] prop_type = type(item[prop]) prop_ui = rna_idprop_ui_prop_get(item, prop) if prop_type in (float, int): prop_ui['soft_min'] = prop_ui['min'] = prop_type(self.properties.min) prop_ui['soft_max'] = prop_ui['max'] = prop_type(self.properties.max) prop_ui['description'] = self.properties.description return {'FINISHED'} def invoke(self, context, event): self._last_prop[:] = [self.properties.property] item = eval("context.%s" % self.properties.path) # setup defaults prop_ui = rna_idprop_ui_prop_get(item, self.properties.property, False) # dont create if prop_ui: self.properties.min = prop_ui.get("min", -1000000000) self.properties.max = prop_ui.get("max", 1000000000) self.properties.description = prop_ui.get("description", "") wm = context.manager # This crashes, TODO - fix #return wm.invoke_props_popup(self, event) wm.invoke_props_popup(self, event) return {'RUNNING_MODAL'} class WM_OT_properties_add(bpy.types.Operator): '''Internal use (edit a property path)''' bl_idname = "wm.properties_add" bl_label = "Add Property" path = rna_path def execute(self, context): item = eval("context.%s" % self.properties.path) def unique_name(names): prop = 'prop' prop_new = prop i = 1 while prop_new in names: prop_new = prop + str(i) i += 1 return prop_new property = unique_name(item.keys()) item[property] = 1.0 return {'FINISHED'} class WM_OT_properties_remove(bpy.types.Operator): '''Internal use (edit a property path)''' bl_idname = "wm.properties_remove" bl_label = "Add Property" path = rna_path property = rna_property def execute(self, context): item = eval("context.%s" % self.properties.path) del item[self.properties.property] return {'FINISHED'}