sun_position: fix warning from deleted prop in User Preferences
[blender-addons.git] / space_view3d_modifier_tools.py
blobba71532e9500fdb8f9940597d5ef457702567a29
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 #####
18 # by meta-androcto, saidenka #
20 bl_info = {
21 "name": "Modifier Tools",
22 "author": "Meta Androcto, saidenka",
23 "version": (0, 2, 6),
24 "blender": (2, 80, 0),
25 "location": "Properties > Modifiers",
26 "description": "Modifiers Specials Show/Hide/Apply Selected",
27 "warning": "",
28 "wiki_url": "https://docs.blender.org/manual/en/dev/addons/"
29 "interface/modifier_tools.html",
30 "category": "Interface"
33 import bpy
34 from bpy.types import Operator
37 class ApplyAllModifiers(Operator):
38 bl_idname = "object.apply_all_modifiers"
39 bl_label = "Apply All Modifiers"
40 bl_description = ("Apply All modifiers of the selected object(s) \n"
41 "Active object has to have modifiers for the option to show up")
42 bl_options = {'REGISTER', 'UNDO'}
44 def execute(self, context):
45 is_select, is_mod = False, False
46 message_a, message_b = "", ""
47 # collect names for objects failed to apply modifiers
48 collect_names = []
50 for obj in bpy.context.selected_objects:
51 is_select = True
53 # copying context for the operator's override
54 contx = bpy.context.copy()
55 contx['object'] = obj
57 for mod in obj.modifiers[:]:
58 contx['modifier'] = mod
59 is_mod = True
60 try:
61 bpy.ops.object.modifier_apply(
62 contx, apply_as='DATA',
63 modifier=contx['modifier'].name
65 except:
66 obj_name = getattr(obj, "name", "NO NAME")
67 collect_names.append(obj_name)
68 message_b = True
69 pass
71 if is_select:
72 if is_mod:
73 message_a = "Applying modifiers on all Selected Objects"
74 else:
75 message_a = "No Modifiers on Selected Objects"
76 else:
77 self.report({"INFO"}, "No Selection. No changes applied")
78 return {'CANCELLED'}
80 # applying failed for some objects, show report
81 message_obj = (",".join(collect_names) if collect_names and
82 len(collect_names) < 8 else "some objects (Check System Console)")
84 self.report({"INFO"},
85 (message_a if not message_b else
86 "Applying modifiers failed for {}".format(message_obj)))
88 if (collect_names and message_obj == "some objects (Check System Console)"):
89 print("\n[Modifier Tools]\n\nApplying failed on:"
90 "\n\n{} \n".format(", ".join(collect_names)))
92 return {'FINISHED'}
95 class DeleteAllModifiers(Operator):
96 bl_idname = "object.delete_all_modifiers"
97 bl_label = "Remove All Modifiers"
98 bl_description = "Remove All modifiers of the selected object(s)"
99 bl_options = {'REGISTER', 'UNDO'}
101 def invoke(self, context, event):
102 return context.window_manager.invoke_confirm(self, event)
104 def execute(self, context):
105 is_select, is_mod = False, False
106 message_a = ""
108 for obj in context.selected_objects:
109 is_select = True
110 modifiers = obj.modifiers[:]
111 for modi in modifiers:
112 is_mod = True
113 obj.modifiers.remove(modi)
115 if is_select:
116 if is_mod:
117 message_a = "Removing modifiers on all Selected Objects"
118 else:
119 message_a = "No Modifiers on Selected Objects"
120 else:
121 self.report({"INFO"}, "No Selection. No changes applied")
122 return {'CANCELLED'}
124 self.report({"INFO"}, message_a)
125 return {'FINISHED'}
128 class ToggleApplyModifiersView(Operator):
129 bl_idname = "object.toggle_apply_modifiers_view"
130 bl_label = "Toggle Visibility of Modifiers"
131 bl_description = "Shows/Hide modifiers of the active / selected object(s) in the 3D View"
132 bl_options = {'REGISTER'}
134 @classmethod
135 def poll(cls, context):
136 return context.active_object is not None
138 def execute(self, context):
139 is_apply = True
140 message_a = ""
142 # avoid toggling not exposed modifiers (currently only Collision, see T53406)
143 skip_type = ["COLLISION"] # types of modifiers to skip
144 skipped = set() # collect names
145 count_modifiers = 0 # check for message_a (all skipped)
147 # check if the active object has only one non exposed modifier as the logic will fail
148 if len(context.active_object.modifiers) == 1 and \
149 context.active_object.modifiers[0].type in skip_type:
151 for obj in context.selected_objects:
152 for mod in obj.modifiers:
153 if mod.type in skip_type:
154 skipped.add(mod.name)
155 continue
157 if mod.show_viewport:
158 is_apply = False
159 break
160 else:
161 for mod in context.active_object.modifiers:
162 if mod.type in skip_type:
163 skipped.add(mod.name)
164 continue
166 if mod.show_viewport:
167 is_apply = False
168 break
170 count_modifiers = len(context.active_object.modifiers)
171 # active object - no selection
172 for mod in context.active_object.modifiers:
173 if mod.type in skip_type:
174 count_modifiers -= 1
175 skipped.add(mod.name)
176 continue
178 mod.show_viewport = is_apply
180 for obj in context.selected_objects:
181 count_modifiers += len(obj.modifiers)
183 for mod in obj.modifiers:
184 if mod.type in skip_type:
185 skipped.add(mod.name)
186 count_modifiers -= 1
187 continue
189 mod.show_viewport = is_apply
191 message_a = "{} modifiers in the 3D View".format("Displaying" if is_apply else "Hiding")
193 if skipped:
194 message_a = "{}, {}".format(message_a, "skipping: " + ", ".join(skipped)) if \
195 count_modifiers > 0 else "No change of Modifiers' Visibility, all skipped"
197 self.report({"INFO"}, message_a)
199 return {'FINISHED'}
202 class ToggleAllShowExpanded(Operator):
203 bl_idname = "wm.toggle_all_show_expanded"
204 bl_label = "Expand/Collapse All Modifiers"
205 bl_description = "Expand/Collapse Modifier Stack"
206 bl_options = {'REGISTER'}
208 @classmethod
209 def poll(cls, context):
210 return context.active_object is not None
212 def execute(self, context):
213 obj = context.active_object
214 if (len(obj.modifiers)):
215 vs = 0
216 for mod in obj.modifiers:
217 if (mod.show_expanded):
218 vs += 1
219 else:
220 vs -= 1
221 is_close = False
222 if (0 < vs):
223 is_close = True
224 for mod in obj.modifiers:
225 mod.show_expanded = not is_close
226 else:
227 self.report({'WARNING'}, "Not a single modifier to Expand/Collapse")
228 return {'CANCELLED'}
230 for area in context.screen.areas:
231 area.tag_redraw()
232 return {'FINISHED'}
235 # Menus #
236 def menu(self, context):
237 if (context.active_object):
238 if (len(context.active_object.modifiers)):
239 col = self.layout.column(align=True)
241 row = col.row(align=True)
242 row.operator(ApplyAllModifiers.bl_idname,
243 icon='IMPORT', text="Apply All")
244 row.operator(DeleteAllModifiers.bl_idname,
245 icon='X', text="Delete All")
247 row = col.row(align=True)
248 row.operator(ToggleApplyModifiersView.bl_idname,
249 icon='RESTRICT_VIEW_OFF',
250 text="Viewport Vis")
251 row.operator(ToggleAllShowExpanded.bl_idname,
252 icon='FULLSCREEN_ENTER',
253 text="Toggle Stack")
256 def menu_func(self, context):
257 if (context.active_object):
258 if (len(context.active_object.modifiers)):
259 layout = self.layout
260 layout.separator()
261 layout.operator(ApplyAllModifiers.bl_idname,
262 icon='IMPORT',
263 text="Apply All Modifiers")
265 # Register
266 classes = [
267 ApplyAllModifiers,
268 DeleteAllModifiers,
269 ToggleApplyModifiersView,
270 ToggleAllShowExpanded,
273 def register():
274 from bpy.utils import register_class
275 for cls in classes:
276 register_class(cls)
278 # Add "Specials" menu to the "Modifiers" menu
279 bpy.types.DATA_PT_modifiers.prepend(menu)
281 # Add apply operator to the Apply 3D View Menu
282 bpy.types.VIEW3D_MT_object_apply.append(menu_func)
285 def unregister():
286 # Remove "Specials" menu from the "Modifiers" menu.
287 bpy.types.DATA_PT_modifiers.remove(menu)
289 # Remove apply operator to the Apply 3D View Menu
290 bpy.types.VIEW3D_MT_object_apply.remove(menu_func)
292 from bpy.utils import unregister_class
293 for cls in reversed(classes):
294 unregister_class(cls)
296 if __name__ == "__main__":
297 register()