Merge branch 'blender-v2.81-release'
[blender-addons.git] / system_property_chart.py
blob1aefb6438141f5d0c215264993db8973cf6e85c5
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 #####
19 # <pep8 compliant>
21 bl_info = {
22 "name": "Property Chart",
23 "author": "Campbell Barton (ideasman42)",
24 "version": (0, 1, 1),
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"),
29 "warning": "",
30 "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
31 "Scripts/System/Object Property Chart",
32 "category": "System",
35 """List properties of selected objects"""
37 import bpy
38 from bl_operators.presets import AddPresetBase
39 from bpy.types import (
40 Menu,
41 Operator,
42 Panel,
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"
52 preset_defines = [
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"]
61 else:
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"
75 else:
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)
85 if obj is None:
86 return None, None
88 # eg. context.selected_objects[:]
89 selected_objects = eval("context.%s" % self.context_data_path_selected)[:]
91 if not selected_objects:
92 return None, None
94 return obj, selected_objects
97 def _property_chart_draw(self, context):
98 """
99 This function can run for different types.
101 obj, selected_objects = _property_chart_data_get(self, context)
103 if not obj:
104 return
106 # active first
107 try:
108 active_index = selected_objects.index(obj)
109 except ValueError:
110 active_index = -1
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
120 layout = self.layout
122 if strings:
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(".")
127 val_new = obj
128 for i, attr in enumerate(attrs):
129 val_old = val_new
130 val_new = getattr(val_old, attr, Ellipsis)
132 if val_new == Ellipsis:
133 return None, None
134 return val_old, attrs[-1]
136 strings = strings.split()
138 prop_all = []
140 for obj in selected_objects:
141 prop_pairs = []
142 prop_found = False
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):
146 prop_found = True
148 if prop_found:
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
172 if data:
173 col.row().prop(data, attr, text="") # , emboss=obj==active_object
174 else:
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'
192 bl_category = "Item"
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'
212 bl_category = "Item"
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
226 @classmethod
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)
236 if not obj:
237 return
239 data_path = self.data_path
241 # quick & nasty method!
242 for obj_iter in selected_objects:
243 if obj != obj_iter:
244 try:
245 exec("obj_iter.%s = obj.%s" % (data_path, data_path))
246 except:
247 # just in case we need to know what went wrong!
248 import traceback
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)
270 return {'FINISHED'}
272 # List The Classes #
274 classes = (
275 AddPresetProperties,
276 SCENE_MT_properties_presets,
277 View3DEditProps,
278 SequencerEditProps,
279 CopyPropertyChart
282 def register():
283 for cls in classes:
284 bpy.utils.register_class(cls)
286 Scene = bpy.types.Scene
288 for cls in View3DEditProps, SequencerEditProps:
289 setattr(
290 Scene,
291 cls._PROP_STORAGE_ID,
292 StringProperty(
293 name="Properties",
294 description=cls._PROP_STORAGE_ID_DESCR + " (separated by spaces)",
295 default=cls._PROP_STORAGE_DEFAULT, maxlen=1024,
300 def unregister():
301 for cls in classes:
302 bpy.utils.unregister_class(cls)
304 Scene = bpy.types.Scene
306 for cls in View3DEditProps, SequencerEditProps:
307 delattr(Scene,
308 cls._PROP_STORAGE_ID,
312 if __name__ == "__main__":
313 register()