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 #
21 "name": "Modifier Tools",
22 "author": "Meta Androcto, saidenka",
24 "blender": (2, 77, 0),
25 "location": "Properties > Modifiers",
26 "description": "Modifiers Specials Show/Hide/Apply Selected",
28 "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6"
29 "/Py/Scripts/3D_interaction/modifier_tools",
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
50 for obj
in bpy
.context
.selected_objects
:
53 # copying context for the operator's override
54 contx
= bpy
.context
.copy()
57 for mod
in obj
.modifiers
[:]:
58 contx
['modifier'] = mod
61 bpy
.ops
.object.modifier_apply(
62 contx
, apply_as
='DATA',
63 modifier
=contx
['modifier'].name
66 obj_name
= getattr(obj
, "name", "NO NAME")
67 collect_names
.append(obj_name
)
73 message_a
= "Applying modifiers on all Selected Objects"
75 message_a
= "No Modifiers on Selected Objects"
77 self
.report({"INFO"}, "No Selection. No changes applied")
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)")
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
)))
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
108 for obj
in context
.selected_objects
:
110 modifiers
= obj
.modifiers
[:]
111 for modi
in modifiers
:
113 obj
.modifiers
.remove(modi
)
117 message_a
= "Removing modifiers on all Selected Objects"
119 message_a
= "No Modifiers on Selected Objects"
121 self
.report({"INFO"}, "No Selection. No changes applied")
124 self
.report({"INFO"}, message_a
)
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'}
135 def poll(cls
, context
):
136 return context
.active_object
is not None
138 def execute(self
, context
):
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
)
157 if mod
.show_viewport
:
161 for mod
in context
.active_object
.modifiers
:
162 if mod
.type in skip_type
:
163 skipped
.add(mod
.name
)
166 if mod
.show_viewport
:
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
:
175 skipped
.add(mod
.name
)
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
)
189 mod
.show_viewport
= is_apply
191 message_a
= "{} modifiers in the 3D View".format("Displaying" if is_apply
else "Hidding")
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
)
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'}
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
)):
216 for mod
in obj
.modifiers
:
217 if (mod
.show_expanded
):
224 for mod
in obj
.modifiers
:
225 mod
.show_expanded
= not is_close
227 self
.report({'WARNING'}, "Not a single modifier to Expand/Collapse")
230 for area
in context
.screen
.areas
:
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',
251 row
.operator(ToggleAllShowExpanded
.bl_idname
,
252 icon
='FULLSCREEN_ENTER',
256 def menu_func(self
, context
):
257 if (context
.active_object
):
258 if (len(context
.active_object
.modifiers
)):
261 layout
.operator(ApplyAllModifiers
.bl_idname
,
263 text
="Apply All Modifiers")
267 bpy
.utils
.register_module(__name__
)
269 # Add "Specials" menu to the "Modifiers" menu
270 bpy
.types
.DATA_PT_modifiers
.prepend(menu
)
272 # Add apply operator to the Apply 3D View Menu
273 bpy
.types
.VIEW3D_MT_object_apply
.append(menu_func
)
277 # Remove "Specials" menu from the "Modifiers" menu.
278 bpy
.types
.DATA_PT_modifiers
.remove(menu
)
280 # Remove apply operator to the Apply 3D View Menu
281 bpy
.types
.VIEW3D_MT_object_apply
.remove(menu_func
)
283 bpy
.utils
.unregister_module(__name__
)
286 if __name__
== "__main__":