1 # SPDX-FileCopyrightText: 2010-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
6 "name": "Property Chart",
7 "author": "Campbell Barton (ideasman42)",
10 "location": "View3D > Sidebar > Item Tab",
11 "description": ("Edit arbitrary selected properties for "
12 "objects/sequence strips of the same type"),
14 "doc_url": "{BLENDER_MANUAL_URL}/addons/system/property_chart.html",
18 """List properties of selected objects"""
21 from bl_operators
.presets
import AddPresetBase
22 from bpy
.types
import (
29 class AddPresetProperties(AddPresetBase
, Operator
):
30 """Add an properties preset"""
31 bl_idname
= "scene.properties_preset_add"
32 bl_label
= "Add Properties Preset"
33 preset_menu
= "SCENE_MT_properties_presets"
36 "scene = bpy.context.scene",
39 def pre_cb(self
, context
):
40 space_type
= context
.space_data
.type
41 if space_type
== 'VIEW_3D':
42 self
.preset_subdir
= "system_property_chart_view3d"
43 self
.preset_values
= ["scene.view3d_edit_props"]
45 self
.preset_subdir
= "system_property_chart_sequencer"
46 self
.preset_values
= ["scene.sequencer_edit_props"]
49 class SCENE_MT_properties_presets(Menu
):
50 bl_label
= "Properties Presets"
51 preset_operator
= "script.execute_preset"
53 def draw(self
, context
):
54 space_type
= context
.space_data
.type
56 if space_type
== 'VIEW_3D':
57 self
.preset_subdir
= "system_property_chart_view3d"
59 self
.preset_subdir
= "system_property_chart_sequencer"
61 Menu
.draw_preset(self
, context
)
64 def _property_chart_data_get(self
, context
):
65 # eg. context.active_object
66 obj
= eval("context.%s" % self
.context_data_path_active
)
71 # eg. context.selected_objects[:]
72 selected_objects
= eval("context.%s" % self
.context_data_path_selected
)[:]
74 if not selected_objects
:
77 return obj
, selected_objects
80 def _property_chart_draw(self
, context
):
82 This function can run for different types.
84 obj
, selected_objects
= _property_chart_data_get(self
, context
)
91 active_index
= selected_objects
.index(obj
)
95 if active_index
> 0: # not the first already
96 selected_objects
[0], selected_objects
[active_index
] = selected_objects
[active_index
], selected_objects
[0]
98 id_storage
= context
.scene
100 strings
= getattr(id_storage
, self
._PROP
_STORAGE
_ID
)
102 # Collected all props, now display them all
107 def obj_prop_get(obj
, attr_string
):
108 """return a pair (rna_base, "rna_property") to give to the rna UI property function"""
109 attrs
= attr_string
.split(".")
111 for i
, attr
in enumerate(attrs
):
113 val_new
= getattr(val_old
, attr
, Ellipsis)
115 if val_new
== Ellipsis:
117 return val_old
, attrs
[-1]
119 strings
= strings
.split()
123 for obj
in selected_objects
:
126 for attr_string
in strings
:
127 prop_pairs
.append(obj_prop_get(obj
, attr_string
))
128 if prop_found
is False and prop_pairs
[-1] != (None, None):
132 prop_all
.append((obj
, prop_pairs
))
134 row
= layout
.row(align
=True)
136 col
= row
.column(align
=True)
137 col
.label(text
="name")
138 for obj
, prop_pairs
in prop_all
:
139 col
.prop(obj
, "name", text
="")
141 for i
in range(len(strings
)):
142 col
= row
.column(align
=True)
144 # name and copy button
145 rowsub
= col
.row(align
=False)
146 rowsub
.label(text
=strings
[i
].rsplit(".", 1)[-1])
147 props
= rowsub
.operator("wm.chart_copy", text
="", icon
='PASTEDOWN', emboss
=False)
148 props
.data_path_active
= self
.context_data_path_active
149 props
.data_path_selected
= self
.context_data_path_selected
150 props
.data_path
= strings
[i
]
152 for obj
, prop_pairs
in prop_all
:
153 data
, attr
= prop_pairs
[i
]
154 # row is needed for vector buttons
156 col
.row().prop(data
, attr
, text
="") # , emboss=obj==active_object
158 col
.label(text
="<missing>")
160 # Presets for properties
161 col
= layout
.column()
162 col
.label(text
="Properties")
163 row
= col
.row(align
=True)
164 row
.menu("SCENE_MT_properties_presets", text
=bpy
.types
.SCENE_MT_properties_presets
.bl_label
)
165 row
.operator("scene.properties_preset_add", text
="", icon
='ADD')
166 row
.operator("scene.properties_preset_add", text
="", icon
='REMOVE').remove_active
= True
167 # edit the display props
168 col
.prop(id_storage
, self
._PROP
_STORAGE
_ID
, text
="")
171 class View3DEditProps(Panel
):
172 bl_idname
= "SYSPROP_CHART_PT_view3d"
173 bl_space_type
= 'VIEW_3D'
174 bl_region_type
= 'UI'
176 bl_label
= "Property Chart"
177 bl_context
= "objectmode"
178 bl_options
= {'DEFAULT_CLOSED'}
180 _PROP_STORAGE_ID
= "view3d_edit_props"
181 _PROP_STORAGE_ID_DESCR
= "Properties of objects in the context"
182 _PROP_STORAGE_DEFAULT
= "data data.use_auto_smooth"
184 # _property_chart_draw needs these
185 context_data_path_active
= "active_object"
186 context_data_path_selected
= "selected_objects"
188 draw
= _property_chart_draw
191 class SequencerEditProps(Panel
):
192 bl_idname
= "SYSPROP_CHART_PT_sequencer"
193 bl_space_type
= 'SEQUENCE_EDITOR'
194 bl_region_type
= 'UI'
196 bl_label
= "Property Chart"
197 bl_options
= {'DEFAULT_CLOSED'}
199 _PROP_STORAGE_ID
= "sequencer_edit_props"
200 _PROP_STORAGE_ID_DESCR
= "Properties of sequencer strips in the context"
201 _PROP_STORAGE_DEFAULT
= "blend_type blend_alpha"
203 # _property_chart_draw needs these
204 context_data_path_active
= "scene.sequence_editor.active_strip"
205 context_data_path_selected
= "selected_sequences"
207 draw
= _property_chart_draw
210 def poll(cls
, context
):
211 return context
.scene
.sequence_editor
is not None
213 # Operator to copy properties
216 def _property_chart_copy(self
, context
):
217 obj
, selected_objects
= _property_chart_data_get(self
, context
)
222 data_path
= self
.data_path
224 data_path_with_dot
= data_path
225 if not data_path_with_dot
.startswith("["):
226 data_path_with_dot
= "." + data_path_with_dot
229 "obj_iter%s = obj%s" % (data_path_with_dot
, data_path_with_dot
),
234 for obj_iter
in selected_objects
:
237 exec(code
, {}, {"obj": obj
, "obj_iter": obj_iter
})
239 # just in case we need to know what went wrong!
241 traceback
.print_exc()
244 from bpy
.props
import StringProperty
247 class CopyPropertyChart(Operator
):
248 "Open a path in a file browser"
249 bl_idname
= "wm.chart_copy"
250 bl_label
= "Copy properties from active to selected"
252 data_path_active
: StringProperty()
253 data_path_selected
: StringProperty()
254 data_path
: StringProperty()
256 def execute(self
, context
):
257 # so attributes are found for '_property_chart_data_get()'
258 self
.context_data_path_active
= self
.data_path_active
259 self
.context_data_path_selected
= self
.data_path_selected
261 _property_chart_copy(self
, context
)
270 SCENE_MT_properties_presets
,
279 bpy
.utils
.register_class(cls
)
281 Scene
= bpy
.types
.Scene
283 for cls
in View3DEditProps
, SequencerEditProps
:
286 cls
._PROP
_STORAGE
_ID
,
289 description
=cls
._PROP
_STORAGE
_ID
_DESCR
+ " (separated by spaces)",
290 default
=cls
._PROP
_STORAGE
_DEFAULT
, maxlen
=1024,
297 bpy
.utils
.unregister_class(cls
)
299 Scene
= bpy
.types
.Scene
301 for cls
in View3DEditProps
, SequencerEditProps
:
303 cls
._PROP
_STORAGE
_ID
,
307 if __name__
== "__main__":