use try/except when unloading modules too.
[blender.git] / release / scripts / modules / bpy_types.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
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.
7 #
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.
12 #
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.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20
21 from _bpy import types as bpy_types
22 import _bpy
23 from mathutils import Vector
24
25 StructRNA = bpy_types.Struct.__bases__[0]
26 # StructRNA = bpy_types.Struct
27
28
29 class Context(StructRNA):
30     __slots__ = ()
31
32     def copy(self):
33         from types import BuiltinMethodType
34         new_context = {}
35         generic_attrs = list(StructRNA.__dict__.keys()) + ["bl_rna", "rna_type", "copy"]
36         for attr in dir(self):
37             if not (attr.startswith("_") or attr in generic_attrs):
38                 value = getattr(self, attr)
39                 if type(value) != BuiltinMethodType:
40                     new_context[attr] = value
41
42         return new_context
43
44
45 class Library(bpy_types.ID):
46     __slots__ = ()
47
48     @property
49     def users_id(self):
50         """ID datablocks which use this library"""
51         import bpy
52
53         # See: readblenentry.c, IDTYPE_FLAGS_ISLINKABLE, we could make this an attribute in rna.
54         attr_links = "actions", "armatures", "brushes", "cameras", \
55                 "curves", "gpencil", "groups", "images", \
56                 "lamps", "lattices", "materials", "metaballs", \
57                 "meshes", "node_groups", "objects", "scenes", \
58                 "sounds", "textures", "texts", "fonts", "worlds"
59
60         return tuple(id_block for attr in attr_links for id_block in getattr(bpy.data, attr) if id_block.library == self)
61
62
63 class Texture(bpy_types.ID):
64     __slots__ = ()
65
66     @property
67     def users_material(self):
68         """Materials that use this texture"""
69         import bpy
70         return tuple(mat for mat in bpy.data.materials if self in [slot.texture for slot in mat.texture_slots if slot])
71
72     @property
73     def users_object_modifier(self):
74         """Object modifiers that use this texture"""
75         import bpy
76         return tuple(obj for obj in bpy.data.objects if self in [mod.texture for mod in obj.modifiers if mod.type == 'DISPLACE'])
77
78
79 class Group(bpy_types.ID):
80     __slots__ = ()
81
82     @property
83     def users_dupli_group(self):
84         """The dupli group this group is used in"""
85         import bpy
86         return tuple(obj for obj in bpy.data.objects if self == obj.dupli_group)
87
88
89 class Object(bpy_types.ID):
90     __slots__ = ()
91
92     @property
93     def children(self):
94         """All the children of this object"""
95         import bpy
96         return tuple(child for child in bpy.data.objects if child.parent == self)
97
98     @property
99     def users_group(self):
100         """The groups this object is in"""
101         import bpy
102         return tuple(group for group in bpy.data.groups if self in group.objects[:])
103
104     @property
105     def users_scene(self):
106         """The scenes this object is in"""
107         import bpy
108         return tuple(scene for scene in bpy.data.scenes if self in scene.objects[:])
109
110
111 class _GenericBone:
112     """
113     functions for bones, common between Armature/Pose/Edit bones.
114     internal subclassing use only.
115     """
116     __slots__ = ()
117
118     def translate(self, vec):
119         """Utility function to add *vec* to the head and tail of this bone."""
120         self.head += vec
121         self.tail += vec
122
123     def parent_index(self, parent_test):
124         """
125         The same as 'bone in other_bone.parent_recursive' but saved generating a list.
126         """
127         # use the name so different types can be tested.
128         name = parent_test.name
129
130         parent = self.parent
131         i = 1
132         while parent:
133             if parent.name == name:
134                 return i
135             parent = parent.parent
136             i += 1
137
138         return 0
139
140     @property
141     def x_axis(self):
142         """ Vector pointing down the x-axis of the bone.
143         """
144         return self.matrix.rotation_part() * Vector((1.0, 0.0, 0.0))
145
146     @property
147     def y_axis(self):
148         """ Vector pointing down the x-axis of the bone.
149         """
150         return self.matrix.rotation_part() * Vector((0.0, 1.0, 0.0))
151
152     @property
153     def z_axis(self):
154         """ Vector pointing down the x-axis of the bone.
155         """
156         return self.matrix.rotation_part() * Vector((0.0, 0.0, 1.0))
157
158     @property
159     def basename(self):
160         """The name of this bone before any '.' character"""
161         #return self.name.rsplit(".", 1)[0]
162         return self.name.split(".")[0]
163
164     @property
165     def parent_recursive(self):
166         """A list of parents, starting with the immediate parent"""
167         parent_list = []
168         parent = self.parent
169
170         while parent:
171             if parent:
172                 parent_list.append(parent)
173
174             parent = parent.parent
175
176         return parent_list
177
178     @property
179     def center(self):
180         """The midpoint between the head and the tail."""
181         return (self.head + self.tail) * 0.5
182
183     @property
184     def length(self):
185         """The distance from head to tail, when set the head is moved to fit the length."""
186         return self.vector.length
187
188     @length.setter
189     def length(self, value):
190         self.tail = self.head + ((self.tail - self.head).normalize() * value)
191
192     @property
193     def vector(self):
194         """The direction this bone is pointing. Utility function for (tail - head)"""
195         return (self.tail - self.head)
196
197     @property
198     def children(self):
199         """A list of all the bones children."""
200         return [child for child in self._other_bones if child.parent == self]
201
202     @property
203     def children_recursive(self):
204         """a list of all children from this bone."""
205         bones_children = []
206         for bone in self._other_bones:
207             index = bone.parent_index(self)
208             if index:
209                 bones_children.append((index, bone))
210
211         # sort by distance to parent
212         bones_children.sort(key=lambda bone_pair: bone_pair[0])
213         return [bone for index, bone in bones_children]
214
215     @property
216     def children_recursive_basename(self):
217         """
218         Returns a chain of children with the same base name as this bone
219         Only direct chains are supported, forks caused by multiple children with matching basenames will
220         terminate the function and not be returned.
221         """
222         basename = self.basename
223         chain = []
224
225         child = self
226         while True:
227             children = child.children
228             children_basename = []
229
230             for child in children:
231                 if basename == child.basename:
232                     children_basename.append(child)
233
234             if len(children_basename) == 1:
235                 child = children_basename[0]
236                 chain.append(child)
237             else:
238                 if len(children_basename):
239                     print("multiple basenames found, this is probably not what you want!", bone.name, children_basename)
240
241                 break
242
243         return chain
244
245     @property
246     def _other_bones(self):
247         id_data = self.id_data
248         id_data_type = type(id_data)
249
250         if id_data_type == bpy_types.Object:
251             bones = id_data.pose.bones
252         elif id_data_type == bpy_types.Armature:
253             bones = id_data.edit_bones
254             if not bones: # not in editmode
255                 bones = id_data.bones
256
257         return bones
258
259
260 class PoseBone(StructRNA, _GenericBone):
261     __slots__ = ()
262
263
264 class Bone(StructRNA, _GenericBone):
265     __slots__ = ()
266
267
268 class EditBone(StructRNA, _GenericBone):
269     __slots__ = ()
270
271     def align_orientation(self, other):
272         """
273         Align this bone to another by moving its tail and settings its roll
274         the length of the other bone is not used.
275         """
276         vec = other.vector.normalize() * self.length
277         self.tail = self.head + vec
278         self.roll = other.roll
279
280     def transform(self, matrix):
281         """
282         Transform the the bones head, tail, roll and envalope (when the matrix has a scale component).
283         Expects a 4x4 or 3x3 matrix.
284         """
285         from mathutils import Vector
286         z_vec = self.matrix.rotation_part() * Vector((0.0, 0.0, 1.0))
287         self.tail = matrix * self.tail
288         self.head = matrix * self.head
289         scalar = matrix.median_scale
290         self.head_radius *= scalar
291         self.tail_radius *= scalar
292         self.align_roll(matrix * z_vec)
293
294
295 def ord_ind(i1, i2):
296     if i1 < i2:
297         return i1, i2
298     return i2, i1
299
300
301 class Mesh(bpy_types.ID):
302     __slots__ = ()
303
304     def from_pydata(self, verts, edges, faces):
305         """
306         Make a mesh from a list of verts/edges/faces
307         Until we have a nicer way to make geometry, use this.
308         """
309         self.add_geometry(len(verts), len(edges), len(faces))
310
311         verts_flat = [f for v in verts for f in v]
312         self.verts.foreach_set("co", verts_flat)
313         del verts_flat
314
315         edges_flat = [i for e in edges for i in e]
316         self.edges.foreach_set("verts", edges_flat)
317         del edges_flat
318
319         def treat_face(f):
320             if len(f) == 3:
321                 return f[0], f[1], f[2], 0
322             elif f[3] == 0:
323                 return f[3], f[0], f[1], f[2]
324             return f
325
326         faces_flat = [v for f in faces for v in treat_face(f)]
327         self.faces.foreach_set("verts_raw", faces_flat)
328         del faces_flat
329
330     @property
331     def edge_keys(self):
332         return [edge_key for face in self.faces for edge_key in face.edge_keys]
333
334     @property
335     def edge_face_count_dict(self):
336         face_edge_keys = [face.edge_keys for face in self.faces]
337         face_edge_count = {}
338         for face_keys in face_edge_keys:
339             for key in face_keys:
340                 try:
341                     face_edge_count[key] += 1
342                 except:
343                     face_edge_count[key] = 1
344
345         return face_edge_count
346
347     @property
348     def edge_face_count(self):
349         edge_face_count_dict = self.edge_face_count_dict
350         return [edge_face_count_dict.get(ed.key, 0) for ed in self.edges]
351
352     def edge_loops_from_faces(self, faces=None, seams=()):
353         """
354         Edge loops defined by faces
355
356         Takes me.faces or a list of faces and returns the edge loops
357         These edge loops are the edges that sit between quads, so they dont touch
358         1 quad, note: not connected will make 2 edge loops, both only containing 2 edges.
359
360         return a list of edge key lists
361         [ [(0,1), (4, 8), (3,8)], ...]
362
363         return a list of edge vertex index lists
364         """
365
366         OTHER_INDEX = 2, 3, 0, 1 # opposite face index
367
368         if faces is None:
369             faces = self.faces
370
371         edges = {}
372
373         for f in faces:
374 #            if len(f) == 4:
375             if f.verts_raw[3] != 0:
376                 edge_keys = f.edge_keys
377                 for i, edkey in enumerate(f.edge_keys):
378                     edges.setdefault(edkey, []).append(edge_keys[OTHER_INDEX[i]])
379
380         for edkey in seams:
381             edges[edkey] = []
382
383         # Collect edge loops here
384         edge_loops = []
385
386         for edkey, ed_adj in edges.items():
387             if 0 < len(ed_adj) < 3: # 1 or 2
388                 # Seek the first edge
389                 context_loop = [edkey, ed_adj[0]]
390                 edge_loops.append(context_loop)
391                 if len(ed_adj) == 2:
392                     other_dir = ed_adj[1]
393                 else:
394                     other_dir = None
395
396                 ed_adj[:] = []
397
398                 flipped = False
399
400                 while 1:
401                     # from knowing the last 2, look for th next.
402                     ed_adj = edges[context_loop[-1]]
403                     if len(ed_adj) != 2:
404
405                         if other_dir and flipped == False: # the original edge had 2 other edges
406                             flipped = True # only flip the list once
407                             context_loop.reverse()
408                             ed_adj[:] = []
409                             context_loop.append(other_dir) # save 1 lookiup
410
411                             ed_adj = edges[context_loop[-1]]
412                             if len(ed_adj) != 2:
413                                 ed_adj[:] = []
414                                 break
415                         else:
416                             ed_adj[:] = []
417                             break
418
419                     i = ed_adj.index(context_loop[-2])
420                     context_loop.append(ed_adj[not  i])
421
422                     # Dont look at this again
423                     ed_adj[:] = []
424
425
426         return edge_loops
427
428     def edge_loops_from_edges(self, edges=None):
429         """
430         Edge loops defined by edges
431
432         Takes me.edges or a list of edges and returns the edge loops
433
434         return a list of vertex indices.
435         [ [1, 6, 7, 2], ...]
436
437         closed loops have matching start and end values.
438         """
439         line_polys = []
440
441         # Get edges not used by a face
442         if edges is None:
443             edges = self.edges
444
445         if not hasattr(edges, "pop"):
446             edges = edges[:]
447
448         edge_dict = {ed.key: ed for ed in self.edges if ed.select}
449
450         while edges:
451             current_edge = edges.pop()
452             vert_end, vert_start = current_edge.verts[:]
453             line_poly = [vert_start, vert_end]
454
455             ok = True
456             while ok:
457                 ok = False
458                 #for i, ed in enumerate(edges):
459                 i = len(edges)
460                 while i:
461                     i -= 1
462                     ed = edges[i]
463                     v1, v2 = ed.verts
464                     if v1 == vert_end:
465                         line_poly.append(v2)
466                         vert_end = line_poly[-1]
467                         ok = 1
468                         del edges[i]
469                         # break
470                     elif v2 == vert_end:
471                         line_poly.append(v1)
472                         vert_end = line_poly[-1]
473                         ok = 1
474                         del edges[i]
475                         #break
476                     elif v1 == vert_start:
477                         line_poly.insert(0, v2)
478                         vert_start = line_poly[0]
479                         ok = 1
480                         del edges[i]
481                         # break
482                     elif v2 == vert_start:
483                         line_poly.insert(0, v1)
484                         vert_start = line_poly[0]
485                         ok = 1
486                         del edges[i]
487                         #break
488             line_polys.append(line_poly)
489
490         return line_polys
491
492
493 class MeshEdge(StructRNA):
494     __slots__ = ()
495
496     @property
497     def key(self):
498         return ord_ind(*tuple(self.verts))
499
500
501 class MeshFace(StructRNA):
502     __slots__ = ()
503
504     @property
505     def center(self):
506         """The midpoint of the face."""
507         face_verts = self.verts[:]
508         mesh_verts = self.id_data.verts
509         if len(face_verts) == 3:
510             return (mesh_verts[face_verts[0]].co + mesh_verts[face_verts[1]].co + mesh_verts[face_verts[2]].co) / 3.0
511         else:
512             return (mesh_verts[face_verts[0]].co + mesh_verts[face_verts[1]].co + mesh_verts[face_verts[2]].co + mesh_verts[face_verts[3]].co) / 4.0
513
514     @property
515     def edge_keys(self):
516         verts = self.verts[:]
517         if len(verts) == 3:
518             return ord_ind(verts[0], verts[1]), ord_ind(verts[1], verts[2]), ord_ind(verts[2], verts[0])
519
520         return ord_ind(verts[0], verts[1]), ord_ind(verts[1], verts[2]), ord_ind(verts[2], verts[3]), ord_ind(verts[3], verts[0])
521
522
523 class Text(bpy_types.ID):
524     __slots__ = ()
525
526     def as_string(self):
527         """Return the text as a string."""
528         return "\n".join(line.line for line in self.lines)
529
530     def from_string(self, string):
531         """Replace text with this string."""
532         self.clear()
533         self.write(string)
534
535     @property
536     def users_logic(self):
537         """Logic bricks that use this text"""
538         import bpy
539         return tuple(obj for obj in bpy.data.objects if self in [cont.text for cont in obj.game.controllers if cont.type == 'PYTHON'])
540
541 import collections
542
543 TypeMap = {}
544 # Properties (IDPropertyGroup) are different from types because they need to be registered
545 # before adding sub properties to them, so they are registered on definition
546 # and unregistered on unload
547 PropertiesMap = {}
548
549 # Using our own loading function we set this to false
550 # so when running a script directly in the text editor
551 # registers moduals instantly.
552 _register_immediate = True
553
554 def _unload_module(module, free=True):
555     for t in TypeMap.get(module, ()):
556         try:
557             bpy_types.unregister(t)
558         except:
559             import traceback
560             print("bpy.utils._unload_module(): Module '%s' failed to unregister class '%s.%s'" % (module, t.__module__, t.__name__))
561             traceback.print_exc()
562
563     if free == True and module in TypeMap:
564         del TypeMap[module]
565
566
567     for t in PropertiesMap.get(module, ()):
568         try:
569             bpy_types.unregister(t)
570         except:
571             import traceback
572             print("bpy.utils._unload_module(): Module '%s' failed to unregister class '%s.%s'" % (module, t.__module__, t.__name__))
573             traceback.print_exc()
574
575     if free == True and module in PropertiesMap:
576         del PropertiesMap[module]
577
578 def _load_module(module, force=False):
579     for t in TypeMap.get(module, ()):
580         try:
581             bpy_types.register(t)
582         except:
583             import traceback
584             print("bpy.utils._load_module(): Module '%s' failed to register class '%s.%s'" % (module, t.__module__, t.__name__))
585             traceback.print_exc()
586
587 _bpy._load_module = _load_module
588 _bpy._unload_module = _unload_module
589
590 class RNAMeta(type):
591     @classmethod
592     def _register_immediate(cls):
593         return _register_immediate
594     
595     def __new__(cls, name, bases, classdict, **args):
596         result = type.__new__(cls, name, bases, classdict)
597         if bases and bases[0] != StructRNA:
598             module = result.__module__
599
600             ClassMap = TypeMap
601
602             # Register right away if needed
603             if cls._register_immediate():
604                 bpy_types.register(result)
605                 ClassMap = PropertiesMap 
606
607             # first part of packages only
608             if "." in module:
609                 module = module[:module.index(".")]
610             
611             ClassMap.setdefault(module, []).append(result)
612
613         return result
614
615 class RNAMetaRegister(RNAMeta):
616     @classmethod
617     def _register_immediate(cls):
618         return True
619
620 class OrderedMeta(RNAMeta):
621
622     def __init__(cls, name, bases, attributes):
623         super(OrderedMeta, cls).__init__(name, bases, attributes)
624         cls.order = list(attributes.keys())
625
626     def __prepare__(name, bases, **kwargs):
627         return collections.OrderedDict()
628
629 # Only defined so operators members can be used by accessing self.order
630 class Operator(StructRNA, metaclass=OrderedMeta):
631     __slots__ = ()
632
633
634 class Macro(StructRNA, metaclass=OrderedMeta):
635     # bpy_types is imported before ops is defined
636     # so we have to do a local import on each run
637     __slots__ = ()
638
639     @classmethod
640     def define(self, opname):
641         from _bpy import ops
642         return ops.macro_define(self, opname)
643     
644 class IDPropertyGroup(StructRNA, metaclass=RNAMetaRegister):
645         __slots__ = ()
646
647 class RenderEngine(StructRNA, metaclass=RNAMeta):
648     __slots__ = ()
649
650 class _GenericUI:
651     __slots__ = ()
652
653     @classmethod
654     def _dyn_ui_initialize(cls):
655         draw_funcs = getattr(cls.draw, "_draw_funcs", None)
656
657         if draw_funcs is None:
658
659             def draw_ls(self, context):
660                 for func in draw_ls._draw_funcs:
661                     func(self, context)
662
663             draw_funcs = draw_ls._draw_funcs = [cls.draw]
664             cls.draw = draw_ls
665
666         return draw_funcs
667
668     @classmethod
669     def append(cls, draw_func):
670         """Prepend an draw function to this menu, takes the same arguments as the menus draw function."""
671         draw_funcs = cls._dyn_ui_initialize()
672         draw_funcs.append(draw_func)
673
674     @classmethod
675     def prepend(cls, draw_func):
676         """Prepend a draw function to this menu, takes the same arguments as the menus draw function."""
677         draw_funcs = cls._dyn_ui_initialize()
678         draw_funcs.insert(0, draw_func)
679
680     @classmethod
681     def remove(cls, draw_func):
682         """Remove a draw function that has been added to this menu"""
683         draw_funcs = cls._dyn_ui_initialize()
684         try:
685             draw_funcs.remove(draw_func)
686         except:
687             pass
688
689
690 class Panel(StructRNA, _GenericUI, metaclass=RNAMeta):
691     __slots__ = ()
692
693
694 class Header(StructRNA, _GenericUI, metaclass=RNAMeta):
695     __slots__ = ()
696
697
698 class Menu(StructRNA, _GenericUI, metaclass=RNAMeta):
699     __slots__ = ()
700
701     def path_menu(self, searchpaths, operator, props_default={}):
702         layout = self.layout
703         # hard coded to set the operators 'filepath' to the filename.
704
705         import os
706         import bpy.utils
707
708         layout = self.layout
709
710         # collect paths
711         files = []
712         for directory in searchpaths:
713             files.extend([(f, os.path.join(directory, f)) for f in os.listdir(directory)])
714
715         files.sort()
716
717         for f, filepath in files:
718
719             if f.startswith("."):
720                 continue
721
722             preset_name = bpy.utils.display_name(f)
723             props = layout.operator(operator, text=preset_name)
724
725             for attr, value in props_default.items():
726                 setattr(props, attr, value)
727
728             props.filepath = filepath
729             if operator == "script.execute_preset":
730                 props.menu_idname = self.bl_idname
731                 props.preset_name = preset_name
732
733     def draw_preset(self, context):
734         """Define these on the subclass
735          - preset_operator
736          - preset_subdir
737         """
738         import bpy
739         self.path_menu(bpy.utils.preset_paths(self.preset_subdir), self.preset_operator)