1 # SPDX-License-Identifier: GPL-2.0-or-later
4 "name": "Property Chart",
5 "author": "Campbell Barton (ideasman42)",
8 "location": "View3D > Sidebar > Item Tab",
9 "description": ("Edit arbitrary selected properties for "
10 "objects/sequence strips of the same type"),
12 "doc_url": "{BLENDER_MANUAL_URL}/addons/system/property_chart.html",
16 """List properties of selected objects"""
19 from bl_operators
.presets
import AddPresetBase
20 from bpy
.types
import (
27 class AddPresetProperties(AddPresetBase
, Operator
):
28 """Add an properties preset"""
29 bl_idname
= "scene.properties_preset_add"
30 bl_label
= "Add Properties Preset"
31 preset_menu
= "SCENE_MT_properties_presets"
34 "scene = bpy.context.scene",
37 def pre_cb(self
, context
):
38 space_type
= context
.space_data
.type
39 if space_type
== 'VIEW_3D':
40 self
.preset_subdir
= "system_property_chart_view3d"
41 self
.preset_values
= ["scene.view3d_edit_props"]
43 self
.preset_subdir
= "system_property_chart_sequencer"
44 self
.preset_values
= ["scene.sequencer_edit_props"]
47 class SCENE_MT_properties_presets(Menu
):
48 bl_label
= "Properties Presets"
49 preset_operator
= "script.execute_preset"
51 def draw(self
, context
):
52 space_type
= context
.space_data
.type
54 if space_type
== 'VIEW_3D':
55 self
.preset_subdir
= "system_property_chart_view3d"
57 self
.preset_subdir
= "system_property_chart_sequencer"
59 Menu
.draw_preset(self
, context
)
62 def _property_chart_data_get(self
, context
):
63 # eg. context.active_object
64 obj
= eval("context.%s" % self
.context_data_path_active
)
69 # eg. context.selected_objects[:]
70 selected_objects
= eval("context.%s" % self
.context_data_path_selected
)[:]
72 if not selected_objects
:
75 return obj
, selected_objects
78 def _property_chart_draw(self
, context
):
80 This function can run for different types.
82 obj
, selected_objects
= _property_chart_data_get(self
, context
)
89 active_index
= selected_objects
.index(obj
)
93 if active_index
> 0: # not the first already
94 selected_objects
[0], selected_objects
[active_index
] = selected_objects
[active_index
], selected_objects
[0]
96 id_storage
= context
.scene
98 strings
= getattr(id_storage
, self
._PROP
_STORAGE
_ID
)
100 # Collected all props, now display them all
105 def obj_prop_get(obj
, attr_string
):
106 """return a pair (rna_base, "rna_property") to give to the rna UI property function"""
107 attrs
= attr_string
.split(".")
109 for i
, attr
in enumerate(attrs
):
111 val_new
= getattr(val_old
, attr
, Ellipsis)
113 if val_new
== Ellipsis:
115 return val_old
, attrs
[-1]
117 strings
= strings
.split()
121 for obj
in selected_objects
:
124 for attr_string
in strings
:
125 prop_pairs
.append(obj_prop_get(obj
, attr_string
))
126 if prop_found
is False and prop_pairs
[-1] != (None, None):
130 prop_all
.append((obj
, prop_pairs
))
132 row
= layout
.row(align
=True)
134 col
= row
.column(align
=True)
135 col
.label(text
="name")
136 for obj
, prop_pairs
in prop_all
:
137 col
.prop(obj
, "name", text
="")
139 for i
in range(len(strings
)):
140 col
= row
.column(align
=True)
142 # name and copy button
143 rowsub
= col
.row(align
=False)
144 rowsub
.label(text
=strings
[i
].rsplit(".", 1)[-1])
145 props
= rowsub
.operator("wm.chart_copy", text
="", icon
='PASTEDOWN', emboss
=False)
146 props
.data_path_active
= self
.context_data_path_active
147 props
.data_path_selected
= self
.context_data_path_selected
148 props
.data_path
= strings
[i
]
150 for obj
, prop_pairs
in prop_all
:
151 data
, attr
= prop_pairs
[i
]
152 # row is needed for vector buttons
154 col
.row().prop(data
, attr
, text
="") # , emboss=obj==active_object
156 col
.label(text
="<missing>")
158 # Presets for properties
159 col
= layout
.column()
160 col
.label(text
="Properties")
161 row
= col
.row(align
=True)
162 row
.menu("SCENE_MT_properties_presets", text
=bpy
.types
.SCENE_MT_properties_presets
.bl_label
)
163 row
.operator("scene.properties_preset_add", text
="", icon
='ADD')
164 row
.operator("scene.properties_preset_add", text
="", icon
='REMOVE').remove_active
= True
165 # edit the display props
166 col
.prop(id_storage
, self
._PROP
_STORAGE
_ID
, text
="")
169 class View3DEditProps(Panel
):
170 bl_idname
= "SYSPROP_CHART_PT_view3d"
171 bl_space_type
= 'VIEW_3D'
172 bl_region_type
= 'UI'
174 bl_label
= "Property Chart"
175 bl_context
= "objectmode"
176 bl_options
= {'DEFAULT_CLOSED'}
178 _PROP_STORAGE_ID
= "view3d_edit_props"
179 _PROP_STORAGE_ID_DESCR
= "Properties of objects in the context"
180 _PROP_STORAGE_DEFAULT
= "data data.use_auto_smooth"
182 # _property_chart_draw needs these
183 context_data_path_active
= "active_object"
184 context_data_path_selected
= "selected_objects"
186 draw
= _property_chart_draw
189 class SequencerEditProps(Panel
):
190 bl_idname
= "SYSPROP_CHART_PT_sequencer"
191 bl_space_type
= 'SEQUENCE_EDITOR'
192 bl_region_type
= 'UI'
194 bl_label
= "Property Chart"
195 bl_options
= {'DEFAULT_CLOSED'}
197 _PROP_STORAGE_ID
= "sequencer_edit_props"
198 _PROP_STORAGE_ID_DESCR
= "Properties of sequencer strips in the context"
199 _PROP_STORAGE_DEFAULT
= "blend_type blend_alpha"
201 # _property_chart_draw needs these
202 context_data_path_active
= "scene.sequence_editor.active_strip"
203 context_data_path_selected
= "selected_sequences"
205 draw
= _property_chart_draw
208 def poll(cls
, context
):
209 return context
.scene
.sequence_editor
is not None
211 # Operator to copy properties
214 def _property_chart_copy(self
, context
):
215 obj
, selected_objects
= _property_chart_data_get(self
, context
)
220 data_path
= self
.data_path
222 data_path_with_dot
= data_path
223 if not data_path_with_dot
.startswith("["):
224 data_path_with_dot
= "." + data_path_with_dot
227 "obj_iter%s = obj%s" % (data_path_with_dot
, data_path_with_dot
),
232 for obj_iter
in selected_objects
:
235 exec(code
, {}, {"obj": obj
, "obj_iter": obj_iter
})
237 # just in case we need to know what went wrong!
239 traceback
.print_exc()
242 from bpy
.props
import StringProperty
245 class CopyPropertyChart(Operator
):
246 "Open a path in a file browser"
247 bl_idname
= "wm.chart_copy"
248 bl_label
= "Copy properties from active to selected"
250 data_path_active
: StringProperty()
251 data_path_selected
: StringProperty()
252 data_path
: StringProperty()
254 def execute(self
, context
):
255 # so attributes are found for '_property_chart_data_get()'
256 self
.context_data_path_active
= self
.data_path_active
257 self
.context_data_path_selected
= self
.data_path_selected
259 _property_chart_copy(self
, context
)
268 SCENE_MT_properties_presets
,
277 bpy
.utils
.register_class(cls
)
279 Scene
= bpy
.types
.Scene
281 for cls
in View3DEditProps
, SequencerEditProps
:
284 cls
._PROP
_STORAGE
_ID
,
287 description
=cls
._PROP
_STORAGE
_ID
_DESCR
+ " (separated by spaces)",
288 default
=cls
._PROP
_STORAGE
_DEFAULT
, maxlen
=1024,
295 bpy
.utils
.unregister_class(cls
)
297 Scene
= bpy
.types
.Scene
299 for cls
in View3DEditProps
, SequencerEditProps
:
301 cls
._PROP
_STORAGE
_ID
,
305 if __name__
== "__main__":