1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
22 "name": "Property Chart",
23 "author": "Campbell Barton (ideasman42)",
25 "blender": (2, 80, 0),
26 "location": "View3D > Sidebar > Item Tab",
27 "description": ("Edit arbitrary selected properties for "
28 "objects/sequence strips of the same type"),
30 "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
31 "Scripts/System/Object Property Chart",
35 """List properties of selected objects"""
38 from bl_operators
.presets
import AddPresetBase
39 from bpy
.types
import (
46 class AddPresetProperties(AddPresetBase
, Operator
):
47 """Add an properties preset"""
48 bl_idname
= "scene.properties_preset_add"
49 bl_label
= "Add Properties Preset"
50 preset_menu
= "SCENE_MT_properties_presets"
53 "scene = bpy.context.scene",
56 def pre_cb(self
, context
):
57 space_type
= context
.space_data
.type
58 if space_type
== 'VIEW_3D':
59 self
.preset_subdir
= "system_property_chart_view3d"
60 self
.preset_values
= ["scene.view3d_edit_props"]
62 self
.preset_subdir
= "system_property_chart_sequencer"
63 self
.preset_values
= ["scene.sequencer_edit_props"]
66 class SCENE_MT_properties_presets(Menu
):
67 bl_label
= "Properties Presets"
68 preset_operator
= "script.execute_preset"
70 def draw(self
, context
):
71 space_type
= context
.space_data
.type
73 if space_type
== 'VIEW_3D':
74 self
.preset_subdir
= "system_property_chart_view3d"
76 self
.preset_subdir
= "system_property_chart_sequencer"
78 Menu
.draw_preset(self
, context
)
81 def _property_chart_data_get(self
, context
):
82 # eg. context.active_object
83 obj
= eval("context.%s" % self
.context_data_path_active
)
88 # eg. context.selected_objects[:]
89 selected_objects
= eval("context.%s" % self
.context_data_path_selected
)[:]
91 if not selected_objects
:
94 return obj
, selected_objects
97 def _property_chart_draw(self
, context
):
99 This function can run for different types.
101 obj
, selected_objects
= _property_chart_data_get(self
, context
)
108 active_index
= selected_objects
.index(obj
)
112 if active_index
> 0: # not the first already
113 selected_objects
[0], selected_objects
[active_index
] = selected_objects
[active_index
], selected_objects
[0]
115 id_storage
= context
.scene
117 strings
= getattr(id_storage
, self
._PROP
_STORAGE
_ID
)
119 # Collected all props, now display them all
124 def obj_prop_get(obj
, attr_string
):
125 """return a pair (rna_base, "rna_property") to give to the rna UI property function"""
126 attrs
= attr_string
.split(".")
128 for i
, attr
in enumerate(attrs
):
130 val_new
= getattr(val_old
, attr
, Ellipsis)
132 if val_new
== Ellipsis:
134 return val_old
, attrs
[-1]
136 strings
= strings
.split()
140 for obj
in selected_objects
:
143 for attr_string
in strings
:
144 prop_pairs
.append(obj_prop_get(obj
, attr_string
))
145 if prop_found
is False and prop_pairs
[-1] != (None, None):
149 prop_all
.append((obj
, prop_pairs
))
151 row
= layout
.row(align
=True)
153 col
= row
.column(align
=True)
154 col
.label(text
="name")
155 for obj
, prop_pairs
in prop_all
:
156 col
.prop(obj
, "name", text
="")
158 for i
in range(len(strings
)):
159 col
= row
.column(align
=True)
161 # name and copy button
162 rowsub
= col
.row(align
=False)
163 rowsub
.label(text
=strings
[i
].rsplit(".", 1)[-1])
164 props
= rowsub
.operator("wm.chart_copy", text
="", icon
='PASTEDOWN', emboss
=False)
165 props
.data_path_active
= self
.context_data_path_active
166 props
.data_path_selected
= self
.context_data_path_selected
167 props
.data_path
= strings
[i
]
169 for obj
, prop_pairs
in prop_all
:
170 data
, attr
= prop_pairs
[i
]
171 # row is needed for vector buttons
173 col
.row().prop(data
, attr
, text
="") # , emboss=obj==active_object
175 col
.label(text
="<missing>")
177 # Presets for properties
178 col
= layout
.column()
179 col
.label(text
="Properties")
180 row
= col
.row(align
=True)
181 row
.menu("SCENE_MT_properties_presets", text
=bpy
.types
.SCENE_MT_properties_presets
.bl_label
)
182 row
.operator("scene.properties_preset_add", text
="", icon
='ADD')
183 row
.operator("scene.properties_preset_add", text
="", icon
='REMOVE').remove_active
= True
184 # edit the display props
185 col
.prop(id_storage
, self
._PROP
_STORAGE
_ID
, text
="")
188 class View3DEditProps(Panel
):
189 bl_idname
= "SYSPROP_CHART_PT_view3d"
190 bl_space_type
= 'VIEW_3D'
191 bl_region_type
= 'UI'
193 bl_label
= "Property Chart"
194 bl_context
= "objectmode"
195 bl_options
= {'DEFAULT_CLOSED'}
197 _PROP_STORAGE_ID
= "view3d_edit_props"
198 _PROP_STORAGE_ID_DESCR
= "Properties of objects in the context"
199 _PROP_STORAGE_DEFAULT
= "data data.use_auto_smooth"
201 # _property_chart_draw needs these
202 context_data_path_active
= "active_object"
203 context_data_path_selected
= "selected_objects"
205 draw
= _property_chart_draw
208 class SequencerEditProps(Panel
):
209 bl_idname
= "SYSPROP_CHART_PT_sequencer"
210 bl_space_type
= 'SEQUENCE_EDITOR'
211 bl_region_type
= 'UI'
213 bl_label
= "Property Chart"
214 bl_options
= {'DEFAULT_CLOSED'}
216 _PROP_STORAGE_ID
= "sequencer_edit_props"
217 _PROP_STORAGE_ID_DESCR
= "Properties of sequencer strips in the context"
218 _PROP_STORAGE_DEFAULT
= "blend_type blend_alpha"
220 # _property_chart_draw needs these
221 context_data_path_active
= "scene.sequence_editor.active_strip"
222 context_data_path_selected
= "selected_sequences"
224 draw
= _property_chart_draw
227 def poll(cls
, context
):
228 return context
.scene
.sequence_editor
is not None
230 # Operator to copy properties
233 def _property_chart_copy(self
, context
):
234 obj
, selected_objects
= _property_chart_data_get(self
, context
)
239 data_path
= self
.data_path
241 # quick & nasty method!
242 for obj_iter
in selected_objects
:
245 exec("obj_iter.%s = obj.%s" % (data_path
, data_path
))
247 # just in case we need to know what went wrong!
249 traceback
.print_exc()
251 from bpy
.props
import StringProperty
254 class CopyPropertyChart(Operator
):
255 "Open a path in a file browser"
256 bl_idname
= "wm.chart_copy"
257 bl_label
= "Copy properties from active to selected"
259 data_path_active
: StringProperty()
260 data_path_selected
: StringProperty()
261 data_path
: StringProperty()
263 def execute(self
, context
):
264 # so attributes are found for '_property_chart_data_get()'
265 self
.context_data_path_active
= self
.data_path_active
266 self
.context_data_path_selected
= self
.data_path_selected
268 _property_chart_copy(self
, context
)
276 SCENE_MT_properties_presets
,
284 bpy
.utils
.register_class(cls
)
286 Scene
= bpy
.types
.Scene
288 for cls
in View3DEditProps
, SequencerEditProps
:
291 cls
._PROP
_STORAGE
_ID
,
294 description
=cls
._PROP
_STORAGE
_ID
_DESCR
+ " (separated by spaces)",
295 default
=cls
._PROP
_STORAGE
_DEFAULT
, maxlen
=1024,
302 bpy
.utils
.unregister_class(cls
)
304 Scene
= bpy
.types
.Scene
306 for cls
in View3DEditProps
, SequencerEditProps
:
308 cls
._PROP
_STORAGE
_ID
,
312 if __name__
== "__main__":