PLY: add missing type: short
[blender-addons.git] / ui_layer_manager.py
blobb4442d99bcd1cd764f968219cc67ad5f410ee24e
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": "Layer Management",
23 "author": "Alfonso Annarumma, Bastien Montagne",
24 "version": (1, 5, 2),
25 "blender": (2, 72, 0),
26 "location": "Toolshelf > Layers Tab",
27 "warning": "",
28 "description": "Display and Edit Layer Name",
29 "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/3D_interaction/layer_manager",
30 "category": "3D View",
33 import bpy
34 from bpy.types import Menu, Panel, UIList, PropertyGroup
35 from bpy.props import StringProperty, BoolProperty, IntProperty, CollectionProperty, BoolVectorProperty, PointerProperty
36 from bpy.app.handlers import persistent
38 EDIT_MODES = {'EDIT_MESH', 'EDIT_CURVE', 'EDIT_SURFACE', 'EDIT_METABALL', 'EDIT_TEXT', 'EDIT_ARMATURE'}
40 NUM_LAYERS = 20
42 FAKE_LAYER_GROUP = [True] * NUM_LAYERS
44 class NamedLayer(PropertyGroup):
45 name = StringProperty(name="Layer Name")
46 use_lock = BoolProperty(name="Lock Layer", default=False)
47 use_object_select = BoolProperty(name="Object Select", default=True)
48 use_wire = BoolProperty(name="Wire Layer", default=False)
51 class NamedLayers(PropertyGroup):
52 layers = CollectionProperty(type=NamedLayer)
53 use_hide_empty_layers = BoolProperty(name="Hide Empty Layer", default=False)
54 use_extra_options = BoolProperty(name="Show Extra Options", default=True)
55 use_layer_indices = BoolProperty(name="Show Layer Indices", default=False)
56 use_classic = BoolProperty(name="Classic", default=False, description="Use a classic layer selection visibility")
58 use_init = BoolProperty(default=True, options={'HIDDEN'})
61 # Stupid, but only solution currently is to use a handler to init that layers collection...
62 @persistent
63 def check_init_data(scene):
64 namedlayers = scene.namedlayers
65 if namedlayers.use_init:
66 while namedlayers.layers:
67 namedlayers.layers.remove(0)
68 for i in range(NUM_LAYERS):
69 layer = namedlayers.layers.add()
70 layer.name = "Layer%.2d" % (i + 1) # Blender use layer nums starting from 1, not 0.
71 namedlayers.use_init = False
74 class LayerGroup(PropertyGroup):
75 use_toggle = BoolProperty(name="", default=False)
76 use_wire = BoolProperty(name="", default=False)
77 use_lock = BoolProperty(name="", default=False)
79 layers = BoolVectorProperty(name="Layers", default=([False] * NUM_LAYERS), size=NUM_LAYERS, subtype='LAYER')
82 class SCENE_OT_namedlayer_group_add(bpy.types.Operator):
83 """Add and select a new layer group"""
84 bl_idname = "scene.namedlayer_group_add"
85 bl_label = "Add Layer Group"
87 layers = BoolVectorProperty(name="Layers", default=([False] * NUM_LAYERS), size=NUM_LAYERS)
89 @classmethod
90 def poll(cls, context):
91 return bool(context.scene)
93 def execute(self, context):
94 scene = context.scene
95 layergroups = scene.layergroups
96 layers = self.layers
98 group_idx = len(layergroups)
99 layer_group = layergroups.add()
100 layer_group.name = "LayerGroup.%.3d" % group_idx
101 layer_group.layers = layers
102 scene.layergroups_index = group_idx
104 return {'FINISHED'}
107 class SCENE_OT_namedlayer_group_remove(bpy.types.Operator):
108 """Remove selected layer group"""
109 bl_idname = "scene.namedlayer_group_remove"
110 bl_label = "Remove Layer Group"
112 group_idx = bpy.props.IntProperty()
114 @classmethod
115 def poll(cls, context):
116 return bool(context.scene)
118 def execute(self, context):
119 scene = context.scene
120 group_idx = self.group_idx
122 scene.layergroups.remove(group_idx)
123 if scene.layergroups_index > len(scene.layergroups) - 1:
124 scene.layergroups_index = len(scene.layergroups) - 1
126 return {'FINISHED'}
129 class SCENE_OT_namedlayer_toggle_visibility(bpy.types.Operator):
130 """Show or hide given layer (shift to extend)"""
131 bl_idname = "scene.namedlayer_toggle_visibility"
132 bl_label = "Show/Hide Layer"
134 layer_idx = IntProperty()
135 group_idx = IntProperty()
136 use_spacecheck = BoolProperty()
137 extend = BoolProperty(options={'SKIP_SAVE'})
139 @classmethod
140 def poll(cls, context):
141 return context.scene and (context.area.spaces.active.type == 'VIEW_3D')
143 def execute(self, context):
144 scene = context.scene
145 layer_cont = context.area.spaces.active if self.use_spacecheck else context.scene
146 layer_idx = self.layer_idx
148 if layer_idx == -1:
149 group_idx = self.group_idx
150 layergroups = scene.layergroups[group_idx]
151 group_layers = layergroups.layers
152 layers = layer_cont.layers
154 if layergroups.use_toggle:
155 layer_cont.layers = [not group_layer and layer for group_layer, layer in zip(group_layers, layers)]
156 layergroups.use_toggle = False
157 else:
158 layer_cont.layers = [group_layer or layer for group_layer, layer in zip(group_layers, layers)]
159 layergroups.use_toggle = True
160 else:
161 if self.extend:
162 layer_cont.layers[layer_idx] = not layer_cont.layers[layer_idx]
163 else:
164 layers = [False] * NUM_LAYERS
165 layers[layer_idx] = True
166 layer_cont.layers = layers
167 return {'FINISHED'}
169 def invoke(self, context, event):
170 self.extend = event.shift
171 return self.execute(context)
174 class SCENE_OT_namedlayer_move_to_layer(bpy.types.Operator):
175 """Move selected objects to this Layer (shift to extend)"""
176 bl_idname = "scene.namedlayer_move_to_layer"
177 bl_label = "Move Objects To Layer"
179 layer_idx = IntProperty()
180 extend = BoolProperty(options={'SKIP_SAVE'})
182 @classmethod
183 def poll(cls, context):
184 return context.scene
186 def execute(self, context):
187 layer_idx = self.layer_idx
188 scene = context.scene
190 # Cycle all objects in the layer
191 for obj in scene.objects:
192 if obj.select:
193 # If object is in at least one of the scene's visible layers...
194 if True in {ob_layer and sce_layer for ob_layer, sce_layer in zip(obj.layers, scene.layers)}:
195 if self.extend:
196 obj.layers[layer_idx] = not obj.layers[layer_idx]
197 else:
198 layer = [False] * NUM_LAYERS
199 layer[layer_idx] = True
200 obj.layers = layer
201 return {'FINISHED'}
203 def invoke(self, context, event):
204 self.extend = event.shift
205 return self.execute(context)
208 class SCENE_OT_namedlayer_toggle_wire(bpy.types.Operator):
209 """Toggle all objects on this layer draw as wire"""
210 bl_idname = "scene.namedlayer_toggle_wire"
211 bl_label = "Toggle Objects Draw Wire"
213 layer_idx = IntProperty()
214 use_wire = BoolProperty()
215 group_idx = IntProperty()
217 @classmethod
218 def poll(cls, context):
219 return context.scene and (context.area.spaces.active.type == 'VIEW_3D')
221 def execute(self, context):
222 scene = context.scene
223 layer_idx = self.layer_idx
224 use_wire = self.use_wire
226 view_3d = context.area.spaces.active
228 # Check if layer have some thing
229 if view_3d.layers_used[layer_idx] or layer_idx == -1:
230 display = 'WIRE' if use_wire else 'TEXTURED'
231 # Cycle all objects in the layer.
232 for obj in context.scene.objects:
233 if layer_idx == -1:
234 group_idx = self.group_idx
235 group_layers = scene.layergroups[group_idx].layers
236 layers = obj.layers
237 if True in {layer and group_layer for layer, group_layer in zip(layers, group_layers)}:
238 obj.draw_type = display
239 scene.layergroups[group_idx].use_wire = use_wire
240 else:
241 if obj.layers[layer_idx]:
242 obj.draw_type = display
243 scene.namedlayers.layers[layer_idx].use_wire = use_wire
245 return {'FINISHED'}
248 class SCENE_OT_namedlayer_lock_all(bpy.types.Operator):
249 """Lock all objects on this layer"""
250 bl_idname = "scene.namedlayer_lock_all"
251 bl_label = "Lock Objects"
253 layer_idx = IntProperty()
254 use_lock = BoolProperty()
255 group_idx = IntProperty()
257 @classmethod
258 def poll(cls, context):
259 return context.scene and (context.area.spaces.active.type == 'VIEW_3D')
261 def execute(self, context):
262 scene = context.scene
263 view_3d = context.area.spaces.active
264 layer_idx = self.layer_idx
265 group_idx = self.group_idx
266 group_layers = FAKE_LAYER_GROUP if group_idx < 0 else scene.layergroups[group_idx].layers
267 use_lock = self.use_lock
269 # check if layer have some thing
270 if layer_idx == -1 or view_3d.layers_used[layer_idx]:
271 # Cycle all objects in the layer.
272 for obj in context.scene.objects:
273 if layer_idx == -1:
274 layers = obj.layers
275 if True in {layer and group_layer for layer, group_layer in zip(layers, group_layers)}:
276 obj.hide_select = not use_lock
277 obj.select = False
278 scene.layergroups[group_idx].use_lock = not use_lock
279 else:
280 if obj.layers[layer_idx]:
281 obj.hide_select = not use_lock
282 obj.select = False
283 scene.namedlayers.layers[layer_idx].use_lock = not use_lock
285 return {'FINISHED'}
288 class SCENE_OT_namedlayer_select_objects_by_layer(bpy.types.Operator):
289 """Select all the objects on this Layer (shift for multi selection, ctrl to make active the last selected object)"""
290 bl_idname = "scene.namedlayer_select_objects_by_layer"
291 bl_label = "Select Objects In Layer"
293 select_obj = BoolProperty()
294 layer_idx = IntProperty()
296 extend = BoolProperty(options={'SKIP_SAVE'})
297 active = BoolProperty(options={'SKIP_SAVE'})
299 @classmethod
300 def poll(cls, context):
301 return context.scene and (context.area.spaces.active.type == 'VIEW_3D')
303 def execute(self, context):
304 scene = context.scene
305 view_3d = context.area.spaces.active
306 select_obj = self.select_obj
307 layer_idx = self.layer_idx
309 not_all_selected = 0
310 # check if layer have some thing
311 if view_3d.layers_used[layer_idx]:
312 objects = []
313 for obj in context.scene.objects:
314 if obj.layers[layer_idx]:
315 objects.append(obj)
316 not_all_selected -= 1
317 if self.active:
318 context.scene.objects.active = obj
319 if obj.select:
320 not_all_selected += 1
321 if not not_all_selected:
322 for obj in objects:
323 obj.select = False
324 else:
325 bpy.ops.object.select_by_layer(extend=self.extend, layers=layer_idx + 1)
327 return {'FINISHED'}
329 def invoke(self, context, event):
330 self.extend = event.shift
331 self.active = event.ctrl
332 return self.execute(context)
335 class SCENE_OT_namedlayer_show_all(bpy.types.Operator):
336 """Show or hide all layers in the scene"""
337 bl_idname = "scene.namedlayer_show_all"
338 bl_label = "Select All Layers"
340 show = BoolProperty()
342 @classmethod
343 def poll(cls, context):
344 return context.scene and (context.area.spaces.active.type == 'VIEW_3D')
346 def execute(self, context):
347 scene = context.scene
348 view_3d = context.area.spaces.active
349 show = self.show
350 active_layer = scene.active_layer
352 # check for lock camera and layer is active
353 layer_cont = scene if view_3d.lock_camera_and_layers else view_3d
355 if show:
356 layer_cont.layers[:] = [True] * NUM_LAYERS
357 # Restore active layer (stupid, but Scene.active_layer is readonly).
358 layer_cont.layers[active_layer] = False
359 layer_cont.layers[active_layer] = True
360 else:
361 layers = [False] * NUM_LAYERS
362 # Keep selection of active layer
363 layers[active_layer] = True
364 layer_cont.layers[:] = layers
366 return {'FINISHED'}
369 class SCENE_PT_namedlayer_layers(bpy.types.Panel):
370 bl_space_type = 'VIEW_3D'
371 bl_region_type = 'TOOLS'
372 bl_label = "Layer Management"
373 bl_options = {'DEFAULT_CLOSED'}
374 bl_category = "Layers"
376 @classmethod
377 def poll(self, context):
378 return ((getattr(context, "mode", 'EDIT_MESH') not in EDIT_MODES) and
379 (context.area.spaces.active.type == 'VIEW_3D'))
381 def draw(self, context):
382 scene = context.scene
383 view_3d = context.area.spaces.active
384 actob = context.object
385 namedlayers = scene.namedlayers
386 use_extra = namedlayers.use_extra_options
387 use_hide = namedlayers.use_hide_empty_layers
388 use_indices = namedlayers.use_layer_indices
389 use_classic = namedlayers.use_classic
391 # Check for lock camera and layer is active
392 if view_3d.lock_camera_and_layers:
393 layer_cont = scene
394 use_spacecheck = False
395 else:
396 layer_cont = view_3d
397 use_spacecheck = True
399 layout = self.layout
400 row = layout.row()
401 col = row.column()
402 col.prop(view_3d, "lock_camera_and_layers", text="")
403 # Check if there is a layer off
404 show = (False in {layer for layer in layer_cont.layers})
405 icon = 'RESTRICT_VIEW_ON' if show else 'RESTRICT_VIEW_OFF'
406 col.operator("scene.namedlayer_show_all", emboss=False, icon=icon, text="").show = show
408 col = row.column()
409 col.prop(namedlayers, "use_classic")
410 col.prop(namedlayers, "use_extra_options", text="Options")
412 col = row.column()
413 col.prop(namedlayers, "use_layer_indices", text="Indices")
414 col.prop(namedlayers, "use_hide_empty_layers", text="Hide Empty")
416 col = layout.column()
417 for layer_idx in range(NUM_LAYERS):
418 namedlayer = namedlayers.layers[layer_idx]
419 is_layer_used = view_3d.layers_used[layer_idx]
421 if (use_hide and not is_layer_used):
422 # Hide unused layers and this one is unused, skip.
423 continue
425 row = col.row(align=True)
427 # layer index
428 if use_indices:
429 sub = row.row(align=True)
430 sub.alignment = 'LEFT'
431 sub.label(text="%.2d." % (layer_idx + 1))
433 # visualization
434 icon = 'RESTRICT_VIEW_OFF' if layer_cont.layers[layer_idx] else 'RESTRICT_VIEW_ON'
435 if use_classic:
436 op = row.operator("scene.namedlayer_toggle_visibility", text="", icon=icon, emboss=True)
437 op.layer_idx = layer_idx
438 op.use_spacecheck = use_spacecheck
439 else:
440 row.prop(layer_cont, "layers", index=layer_idx, emboss=True, icon=icon, toggle=True, text="")
442 # Name (use special icon for active layer)
443 icon = 'FILE_TICK' if (getattr(layer_cont, "active_layer", -1) == layer_idx) else 'NONE'
444 row.prop(namedlayer, "name", text="", icon=icon)
446 if use_extra:
447 use_lock = namedlayer.use_lock
449 # Select by type operator
450 sub = row.column(align=True)
451 sub.enabled = not use_lock
452 sub.operator("scene.namedlayer_select_objects_by_layer", icon='RESTRICT_SELECT_OFF',
453 text="", emboss=True).layer_idx = layer_idx
455 # Lock operator
456 icon = 'LOCKED' if use_lock else 'UNLOCKED'
457 op = row.operator("scene.namedlayer_lock_all", text="", emboss=True, icon=icon)
458 op.layer_idx = layer_idx
459 op.group_idx = -1
460 op.use_lock = use_lock
462 # Merge layer
463 # check if layer has something
464 has_active = (actob and actob.layers[layer_idx])
465 icon = ('LAYER_ACTIVE' if has_active else 'LAYER_USED') if is_layer_used else 'RADIOBUT_OFF'
466 row.operator("scene.namedlayer_move_to_layer", text="", emboss=True, icon=icon).layer_idx = layer_idx
468 # Wire view
469 use_wire = namedlayer.use_wire
470 icon = 'WIRE' if use_wire else 'POTATO'
471 op = row.operator("scene.namedlayer_toggle_wire", text="", emboss=True, icon=icon)
472 op.layer_idx = layer_idx
473 op.use_wire = not use_wire
475 if not (layer_idx + 1) % 5:
476 col.separator()
478 if len(scene.objects) == 0:
479 layout.label(text="No objects in scene")
482 class SCENE_UL_namedlayer_groups(UIList):
483 def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
484 layer_group = item
486 # check for lock camera and layer is active
487 view_3d = context.area.spaces.active # Ensured it is a 'VIEW_3D' in panel's poll(), weak... :/
488 use_spacecheck = False if view_3d.lock_camera_and_layers else True
490 if self.layout_type in {'DEFAULT', 'COMPACT'}:
491 layout.prop(layer_group, "name", text="", emboss=False)
492 # lock operator
493 use_lock = layer_group.use_lock
494 icon = 'LOCKED' if use_lock else 'UNLOCKED'
495 op = layout.operator("scene.namedlayer_lock_all", text="", emboss=False, icon=icon)
496 op.use_lock = use_lock
497 op.group_idx = index
498 op.layer_idx = -1
500 # view operator
501 icon = 'RESTRICT_VIEW_OFF' if layer_group.use_toggle else 'RESTRICT_VIEW_ON'
502 op = layout.operator("scene.namedlayer_toggle_visibility", text="", emboss=False, icon=icon)
503 op.use_spacecheck = use_spacecheck
504 op.group_idx = index
505 op.layer_idx = -1
507 # wire operator
508 use_wire = layer_group.use_wire
509 icon = 'WIRE' if use_wire else 'POTATO'
510 op = layout.operator("scene.namedlayer_toggle_wire", text="", emboss=False, icon=icon)
511 op.use_wire = not use_wire
512 op.group_idx = index
513 op.layer_idx = -1
515 elif self.layout_type in {'GRID'}:
516 layout.alignment = 'CENTER'
519 class SCENE_PT_namedlayer_groups(bpy.types.Panel):
520 bl_space_type = 'VIEW_3D'
521 bl_region_type = 'TOOLS'
522 bl_category = "Layers"
523 bl_label = "Layer Groups"
524 bl_options = {'DEFAULT_CLOSED'}
526 @classmethod
527 def poll(self, context):
528 return ((getattr(context, "mode", 'EDIT_MESH') not in EDIT_MODES) and
529 (context.area.spaces.active.type == 'VIEW_3D'))
531 def draw(self, context):
532 scene = context.scene
533 group_idx = scene.layergroups_index
535 layout = self.layout
536 row = layout.row()
537 row.template_list("SCENE_UL_namedlayer_groups", "", scene, "layergroups", scene, "layergroups_index")
539 col = row.column(align=True)
540 col.operator("scene.namedlayer_group_add", icon='ZOOMIN', text="").layers = scene.layers
541 col.operator("scene.namedlayer_group_remove", icon='ZOOMOUT', text="").group_idx = group_idx
543 if bool(scene.layergroups):
544 layout.prop(scene.layergroups[group_idx], "layers", text="", toggle=True)
545 layout.prop(scene.layergroups[group_idx], "name", text="Name:")
548 def register():
549 bpy.utils.register_module(__name__)
550 bpy.types.Scene.layergroups = CollectionProperty(type=LayerGroup)
551 # Unused, but this is needed for the TemplateList to work...
552 bpy.types.Scene.layergroups_index = IntProperty(default=-1)
553 bpy.types.Scene.namedlayers = PointerProperty(type=NamedLayers)
554 bpy.app.handlers.scene_update_post.append(check_init_data)
557 def unregister():
558 bpy.app.handlers.scene_update_post.remove(check_init_data)
559 del bpy.types.Scene.layergroups
560 del bpy.types.Scene.layergroups_index
561 del bpy.types.Scene.namedlayers
562 bpy.utils.unregister_module(__name__)
565 if __name__ == "__main__":
566 register()