PyAPI: remove support for importing text blocks as modules
[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-80 compliant>
20
21 from _bpy import types as bpy_types
22 import _bpy
23
24 StructRNA = bpy_types.bpy_struct
25 StructMetaPropGroup = bpy_types.bpy_struct_meta_idprop
26 # StructRNA = bpy_types.Struct
27
28 bpy_types.BlendDataLibraries.load = _bpy._library_load
29 bpy_types.BlendDataLibraries.write = _bpy._library_write
30 bpy_types.BlendData.user_map = _bpy._rna_id_collection_user_map
31 bpy_types.BlendData.batch_remove = _bpy._rna_id_collection_batch_remove
32
33
34 class Context(StructRNA):
35     __slots__ = ()
36
37     def copy(self):
38         from types import BuiltinMethodType
39         new_context = {}
40         generic_attrs = (
41             *StructRNA.__dict__.keys(),
42             "bl_rna", "rna_type", "copy",
43         )
44         for attr in dir(self):
45             if not (attr.startswith("_") or attr in generic_attrs):
46                 value = getattr(self, attr)
47                 if type(value) != BuiltinMethodType:
48                     new_context[attr] = value
49
50         return new_context
51
52
53 class Library(bpy_types.ID):
54     __slots__ = ()
55
56     @property
57     def users_id(self):
58         """ID data blocks which use this library"""
59         import bpy
60
61         # See: readblenentry.c, IDTYPE_FLAGS_ISLINKABLE,
62         # we could make this an attribute in rna.
63         attr_links = ("actions", "armatures", "brushes", "cameras",
64                       "curves", "grease_pencil", "collections", "images",
65                       "lights", "lattices", "materials", "metaballs",
66                       "meshes", "node_groups", "objects", "scenes",
67                       "sounds", "speakers", "textures", "texts",
68                       "fonts", "worlds")
69
70         return tuple(id_block
71                      for attr in attr_links
72                      for id_block in getattr(bpy.data, attr)
73                      if id_block.library == self)
74
75
76 class Texture(bpy_types.ID):
77     __slots__ = ()
78
79     @property
80     def users_material(self):
81         """Materials that use this texture"""
82         import bpy
83         return tuple(mat for mat in bpy.data.materials
84                      if self in [slot.texture
85                                  for slot in mat.texture_slots
86                                  if slot]
87                      )
88
89     @property
90     def users_object_modifier(self):
91         """Object modifiers that use this texture"""
92         import bpy
93         return tuple(obj for obj in bpy.data.objects if
94                      self in [mod.texture
95                               for mod in obj.modifiers
96                               if mod.type == 'DISPLACE']
97                      )
98
99
100 class Collection(bpy_types.ID):
101     __slots__ = ()
102
103     @property
104     def users_dupli_group(self):
105         """The collection instance objects this collection is used in"""
106         import bpy
107         return tuple(obj for obj in bpy.data.objects
108                      if self == obj.instance_collection)
109
110
111 class Object(bpy_types.ID):
112     __slots__ = ()
113
114     @property
115     def children(self):
116         """All the children of this object. Warning: takes O(len(bpy.data.objects)) time."""
117         import bpy
118         return tuple(child for child in bpy.data.objects
119                      if child.parent == self)
120
121     @property
122     def users_collection(self):
123         """The collections this object is in. Warning: takes O(len(bpy.data.collections) + len(bpy.data.scenes)) time."""
124         import bpy
125         return tuple(collection for collection in bpy.data.collections
126                      if self in collection.objects[:]) + \
127                tuple(scene.collection for scene in bpy.data.scenes
128                      if self in scene.collection.objects[:])
129
130     @property
131     def users_scene(self):
132         """The scenes this object is in. Warning: takes O(len(bpy.data.scenes) * len(bpy.data.objects)) time."""
133         import bpy
134         return tuple(scene for scene in bpy.data.scenes
135                      if self in scene.objects[:])
136
137
138 class WindowManager(bpy_types.ID):
139     __slots__ = ()
140
141     def popup_menu(self, draw_func, title="", icon='NONE'):
142         import bpy
143         popup = self.popmenu_begin__internal(title, icon=icon)
144
145         try:
146             draw_func(popup, bpy.context)
147         finally:
148             self.popmenu_end__internal(popup)
149
150     def popover(
151             self, draw_func, *,
152             ui_units_x=0,
153             keymap=None,
154     ):
155         import bpy
156         popup = self.popover_begin__internal(
157             ui_units_x=ui_units_x,
158         )
159
160         try:
161             draw_func(popup, bpy.context)
162         finally:
163             self.popover_end__internal(popup, keymap=keymap)
164
165     def popup_menu_pie(self, event, draw_func, title="", icon='NONE'):
166         import bpy
167         pie = self.piemenu_begin__internal(title, icon=icon, event=event)
168
169         if pie:
170             try:
171                 draw_func(pie, bpy.context)
172             finally:
173                 self.piemenu_end__internal(pie)
174
175
176 class _GenericBone:
177     """
178     functions for bones, common between Armature/Pose/Edit bones.
179     internal subclassing use only.
180     """
181     __slots__ = ()
182
183     def translate(self, vec):
184         """Utility function to add *vec* to the head and tail of this bone"""
185         self.head += vec
186         self.tail += vec
187
188     def parent_index(self, parent_test):
189         """
190         The same as 'bone in other_bone.parent_recursive'
191         but saved generating a list.
192         """
193         # use the name so different types can be tested.
194         name = parent_test.name
195
196         parent = self.parent
197         i = 1
198         while parent:
199             if parent.name == name:
200                 return i
201             parent = parent.parent
202             i += 1
203
204         return 0
205
206     @property
207     def x_axis(self):
208         """ Vector pointing down the x-axis of the bone.
209         """
210         from mathutils import Vector
211         return self.matrix.to_3x3() @ Vector((1.0, 0.0, 0.0))
212
213     @property
214     def y_axis(self):
215         """ Vector pointing down the y-axis of the bone.
216         """
217         from mathutils import Vector
218         return self.matrix.to_3x3() @ Vector((0.0, 1.0, 0.0))
219
220     @property
221     def z_axis(self):
222         """ Vector pointing down the z-axis of the bone.
223         """
224         from mathutils import Vector
225         return self.matrix.to_3x3() @ Vector((0.0, 0.0, 1.0))
226
227     @property
228     def basename(self):
229         """The name of this bone before any '.' character"""
230         # return self.name.rsplit(".", 1)[0]
231         return self.name.split(".")[0]
232
233     @property
234     def parent_recursive(self):
235         """A list of parents, starting with the immediate parent"""
236         parent_list = []
237         parent = self.parent
238
239         while parent:
240             if parent:
241                 parent_list.append(parent)
242
243             parent = parent.parent
244
245         return parent_list
246
247     @property
248     def center(self):
249         """The midpoint between the head and the tail."""
250         return (self.head + self.tail) * 0.5
251
252     @property
253     def length(self):
254         """
255         The distance from head to tail,
256         when set the head is moved to fit the length.
257         """
258         return self.vector.length
259
260     @length.setter
261     def length(self, value):
262         self.tail = self.head + ((self.tail - self.head).normalized() * value)
263
264     @property
265     def vector(self):
266         """
267         The direction this bone is pointing.
268         Utility function for (tail - head)
269         """
270         return (self.tail - self.head)
271
272     @property
273     def children(self):
274         """A list of all the bones children. Warning: takes O(len(bones)) time."""
275         return [child for child in self._other_bones if child.parent == self]
276
277     @property
278     def children_recursive(self):
279         """A list of all children from this bone. Warning: takes O(len(bones)**2) time."""
280         bones_children = []
281         for bone in self._other_bones:
282             index = bone.parent_index(self)
283             if index:
284                 bones_children.append((index, bone))
285
286         # sort by distance to parent
287         bones_children.sort(key=lambda bone_pair: bone_pair[0])
288         return [bone for index, bone in bones_children]
289
290     @property
291     def children_recursive_basename(self):
292         """
293         Returns a chain of children with the same base name as this bone.
294         Only direct chains are supported, forks caused by multiple children
295         with matching base names will terminate the function
296         and not be returned. Warning: takes O(len(bones)**2) time.
297         """
298         basename = self.basename
299         chain = []
300
301         child = self
302         while True:
303             children = child.children
304             children_basename = []
305
306             for child in children:
307                 if basename == child.basename:
308                     children_basename.append(child)
309
310             if len(children_basename) == 1:
311                 child = children_basename[0]
312                 chain.append(child)
313             else:
314                 if children_basename:
315                     print("multiple basenames found, "
316                           "this is probably not what you want!",
317                           self.name, children_basename)
318
319                 break
320
321         return chain
322
323     @property
324     def _other_bones(self):
325         id_data = self.id_data
326         id_data_type = type(id_data)
327
328         if id_data_type == bpy_types.Object:
329             bones = id_data.pose.bones
330         elif id_data_type == bpy_types.Armature:
331             bones = id_data.edit_bones
332             if not bones:  # not in edit mode
333                 bones = id_data.bones
334
335         return bones
336
337
338 class PoseBone(StructRNA, _GenericBone, metaclass=StructMetaPropGroup):
339     __slots__ = ()
340
341     @property
342     def children(self):
343         obj = self.id_data
344         pbones = obj.pose.bones
345         self_bone = self.bone
346
347         return tuple(pbones[bone.name] for bone in obj.data.bones
348                      if bone.parent == self_bone)
349
350
351 class Bone(StructRNA, _GenericBone, metaclass=StructMetaPropGroup):
352     __slots__ = ()
353
354
355 class EditBone(StructRNA, _GenericBone, metaclass=StructMetaPropGroup):
356     __slots__ = ()
357
358     def align_orientation(self, other):
359         """
360         Align this bone to another by moving its tail and settings its roll
361         the length of the other bone is not used.
362         """
363         vec = other.vector.normalized() * self.length
364         self.tail = self.head + vec
365         self.roll = other.roll
366
367     def transform(self, matrix, scale=True, roll=True):
368         """
369         Transform the the bones head, tail, roll and envelope
370         (when the matrix has a scale component).
371
372         :arg matrix: 3x3 or 4x4 transformation matrix.
373         :type matrix: :class:`mathutils.Matrix`
374         :arg scale: Scale the bone envelope by the matrix.
375         :type scale: bool
376         :arg roll:
377
378            Correct the roll to point in the same relative
379            direction to the head and tail.
380
381         :type roll: bool
382         """
383         from mathutils import Vector
384         z_vec = self.matrix.to_3x3() @ Vector((0.0, 0.0, 1.0))
385         self.tail = matrix @ self.tail
386         self.head = matrix @ self.head
387
388         if scale:
389             scalar = matrix.median_scale
390             self.head_radius *= scalar
391             self.tail_radius *= scalar
392
393         if roll:
394             self.align_roll(matrix @ z_vec)
395
396
397 def ord_ind(i1, i2):
398     if i1 < i2:
399         return i1, i2
400     return i2, i1
401
402
403 class Mesh(bpy_types.ID):
404     __slots__ = ()
405
406     def from_pydata(self, vertices, edges, faces):
407         """
408         Make a mesh from a list of vertices/edges/faces
409         Until we have a nicer way to make geometry, use this.
410
411         :arg vertices:
412
413            float triplets each representing (X, Y, Z)
414            eg: [(0.0, 1.0, 0.5), ...].
415
416         :type vertices: iterable object
417         :arg edges:
418
419            int pairs, each pair contains two indices to the
420            *vertices* argument. eg: [(1, 2), ...]
421
422         :type edges: iterable object
423         :arg faces:
424
425            iterator of faces, each faces contains three or more indices to
426            the *vertices* argument. eg: [(5, 6, 8, 9), (1, 2, 3), ...]
427
428         :type faces: iterable object
429
430         .. warning::
431
432            Invalid mesh data
433            *(out of range indices, edges with matching indices,
434            2 sided faces... etc)* are **not** prevented.
435            If the data used for mesh creation isn't known to be valid,
436            run :class:`Mesh.validate` after this function.
437         """
438         from itertools import chain, islice, accumulate
439
440         face_lengths = tuple(map(len, faces))
441
442         self.vertices.add(len(vertices))
443         self.edges.add(len(edges))
444         self.loops.add(sum(face_lengths))
445         self.polygons.add(len(faces))
446
447         self.vertices.foreach_set("co", tuple(chain.from_iterable(vertices)))
448         self.edges.foreach_set("vertices", tuple(chain.from_iterable(edges)))
449
450         vertex_indices = tuple(chain.from_iterable(faces))
451         loop_starts = tuple(islice(chain([0], accumulate(face_lengths)), len(faces)))
452
453         self.polygons.foreach_set("loop_total", face_lengths)
454         self.polygons.foreach_set("loop_start", loop_starts)
455         self.polygons.foreach_set("vertices", vertex_indices)
456
457         # if no edges - calculate them
458         if faces and (not edges):
459             self.update(calc_edges=True)
460         elif edges:
461             self.update(calc_edges_loose=True)
462
463     @property
464     def edge_keys(self):
465         return [ed.key for ed in self.edges]
466
467
468 class MeshEdge(StructRNA):
469     __slots__ = ()
470
471     @property
472     def key(self):
473         return ord_ind(*tuple(self.vertices))
474
475
476 class MeshLoopTriangle(StructRNA):
477     __slots__ = ()
478
479     @property
480     def center(self):
481         """The midpoint of the face."""
482         face_verts = self.vertices[:]
483         mesh_verts = self.id_data.vertices
484         return (mesh_verts[face_verts[0]].co +
485                 mesh_verts[face_verts[1]].co +
486                 mesh_verts[face_verts[2]].co
487                 ) / 3.0
488
489     @property
490     def edge_keys(self):
491         verts = self.vertices[:]
492         return (ord_ind(verts[0], verts[1]),
493                 ord_ind(verts[1], verts[2]),
494                 ord_ind(verts[2], verts[0]),
495                 )
496
497
498 class MeshPolygon(StructRNA):
499     __slots__ = ()
500
501     @property
502     def edge_keys(self):
503         verts = self.vertices[:]
504         vlen = len(self.vertices)
505         return [ord_ind(verts[i], verts[(i + 1) % vlen]) for i in range(vlen)]
506
507     @property
508     def loop_indices(self):
509         start = self.loop_start
510         end = start + self.loop_total
511         return range(start, end)
512
513
514 class Text(bpy_types.ID):
515     __slots__ = ()
516
517     def as_string(self):
518         """Return the text as a string."""
519         return "\n".join(line.body for line in self.lines)
520
521     def from_string(self, string):
522         """Replace text with this string."""
523         self.clear()
524         self.write(string)
525
526     def as_module(self):
527         from os.path import splitext
528         from types import ModuleType
529         mod = ModuleType(splitext(self.name)[0])
530         # TODO: We could use Text.compiled (C struct member)
531         # if this is called often it will be much faster.
532         exec(self.as_string(), mod.__dict__)
533         return mod
534
535
536 class Sound(bpy_types.ID):
537     __slots__ = ()
538
539     @property
540     def factory(self):
541         """The aud.Factory object of the sound."""
542         import aud
543         return aud._sound_from_pointer(self.as_pointer())
544
545
546 class RNAMeta(type):
547     # TODO(campbell): move to C-API
548     @property
549     def is_registered(cls):
550         return "bl_rna" in cls.__dict__
551
552
553 class RNAMetaPropGroup(StructMetaPropGroup, RNAMeta):
554     pass
555
556
557 # Same as 'Operator'
558 # only without 'as_keywords'
559 class Gizmo(StructRNA):
560     __slots__ = ()
561
562     def __getattribute__(self, attr):
563         properties = StructRNA.path_resolve(self, "properties")
564         bl_rna = getattr(properties, "bl_rna", None)
565         if (bl_rna is not None) and (attr in bl_rna.properties):
566             return getattr(properties, attr)
567         return super().__getattribute__(attr)
568
569     def __setattr__(self, attr, value):
570         properties = StructRNA.path_resolve(self, "properties")
571         bl_rna = getattr(properties, "bl_rna", None)
572         if (bl_rna is not None) and (attr in bl_rna.properties):
573             return setattr(properties, attr, value)
574         return super().__setattr__(attr, value)
575
576     def __delattr__(self, attr):
577         properties = StructRNA.path_resolve(self, "properties")
578         bl_rna = getattr(properties, "bl_rna", None)
579         if (bl_rna is not None) and (attr in bl_rna.properties):
580             return delattr(properties, attr)
581         return super().__delattr__(attr)
582
583     from _bpy import (
584         _rna_gizmo_target_set_handler as target_set_handler,
585         _rna_gizmo_target_get_value as target_get_value,
586         _rna_gizmo_target_set_value as target_set_value,
587         _rna_gizmo_target_get_range as target_get_range,
588     )
589
590     # Convenience wrappers around private `_gpu` module.
591     def draw_custom_shape(self, shape, *, matrix=None, select_id=None):
592         """
593         Draw a shape created form :class:`bpy.types.Gizmo.draw_custom_shape`.
594
595         :arg shape: The cached shape to draw.
596         :type shape: Undefined.
597         :arg matrix: 4x4 matrix, when not given
598            :class:`bpy.types.Gizmo.matrix_world` is used.
599         :type matrix: :class:`mathutils.Matrix`
600         :arg select_id: The selection id.
601            Only use when drawing within :class:`bpy.types.Gizmo.draw_select`.
602         :type select_it: int
603         """
604         import gpu
605
606         if matrix is None:
607             matrix = self.matrix_world
608
609         batch, shader = shape
610         shader.bind()
611
612         if select_id is not None:
613             gpu.select.load_id(select_id)
614         else:
615             if self.is_highlight:
616                 color = (*self.color_highlight, self.alpha_highlight)
617             else:
618                 color = (*self.color, self.alpha)
619             shader.uniform_float("color", color)
620
621         with gpu.matrix.push_pop():
622             gpu.matrix.multiply_matrix(matrix)
623             batch.draw()
624
625     @staticmethod
626     def new_custom_shape(type, verts):
627         """
628         Create a new shape that can be passed to :class:`bpy.types.Gizmo.draw_custom_shape`.
629
630         :arg type: The type of shape to create in (POINTS, LINES, TRIS, LINE_STRIP).
631         :type type: string
632         :arg verts: Coordinates.
633         :type verts: sequence of of 2D or 3D coordinates.
634         :arg display_name: Optional callback that takes the full path, returns the name to display.
635         :type display_name: Callable that takes a string and returns a string.
636         :return: The newly created shape.
637         :rtype: Undefined (it may change).
638         """
639         import gpu
640         from gpu.types import (
641             GPUBatch,
642             GPUVertBuf,
643             GPUVertFormat,
644         )
645         dims = len(verts[0])
646         if dims not in {2, 3}:
647             raise ValueError("Expected 2D or 3D vertex")
648         fmt = GPUVertFormat()
649         pos_id = fmt.attr_add(id="pos", comp_type='F32', len=dims, fetch_mode='FLOAT')
650         vbo = GPUVertBuf(len=len(verts), format=fmt)
651         vbo.attr_fill(id=pos_id, data=verts)
652         batch = GPUBatch(type=type, buf=vbo)
653         shader = gpu.shader.from_builtin('3D_UNIFORM_COLOR' if dims == 3 else '2D_UNIFORM_COLOR')
654         batch.program_set(shader)
655         return (batch, shader)
656
657
658 # Only defined so operators members can be used by accessing self.order
659 # with doc generation 'self.properties.bl_rna.properties' can fail
660 class Operator(StructRNA, metaclass=RNAMeta):
661     __slots__ = ()
662
663     def __getattribute__(self, attr):
664         properties = StructRNA.path_resolve(self, "properties")
665         bl_rna = getattr(properties, "bl_rna", None)
666         if (bl_rna is not None) and (attr in bl_rna.properties):
667             return getattr(properties, attr)
668         return super().__getattribute__(attr)
669
670     def __setattr__(self, attr, value):
671         properties = StructRNA.path_resolve(self, "properties")
672         bl_rna = getattr(properties, "bl_rna", None)
673         if (bl_rna is not None) and (attr in bl_rna.properties):
674             return setattr(properties, attr, value)
675         return super().__setattr__(attr, value)
676
677     def __delattr__(self, attr):
678         properties = StructRNA.path_resolve(self, "properties")
679         bl_rna = getattr(properties, "bl_rna", None)
680         if (bl_rna is not None) and (attr in bl_rna.properties):
681             return delattr(properties, attr)
682         return super().__delattr__(attr)
683
684     def as_keywords(self, ignore=()):
685         """Return a copy of the properties as a dictionary"""
686         ignore = ignore + ("rna_type",)
687         return {attr: getattr(self, attr)
688                 for attr in self.properties.rna_type.properties.keys()
689                 if attr not in ignore}
690
691
692 class Macro(StructRNA):
693     # bpy_types is imported before ops is defined
694     # so we have to do a local import on each run
695     __slots__ = ()
696
697     @classmethod
698     def define(self, opname):
699         from _bpy import ops
700         return ops.macro_define(self, opname)
701
702
703 class PropertyGroup(StructRNA, metaclass=RNAMetaPropGroup):
704     __slots__ = ()
705
706
707 class RenderEngine(StructRNA, metaclass=RNAMeta):
708     __slots__ = ()
709
710
711 class KeyingSetInfo(StructRNA, metaclass=RNAMeta):
712     __slots__ = ()
713
714
715 class AddonPreferences(StructRNA, metaclass=RNAMeta):
716     __slots__ = ()
717
718
719 class _GenericUI:
720     __slots__ = ()
721
722     @classmethod
723     def _dyn_ui_initialize(cls):
724         draw_funcs = getattr(cls.draw, "_draw_funcs", None)
725
726         if draw_funcs is None:
727
728             def draw_ls(self, context):
729                 # ensure menus always get default context
730                 operator_context_default = self.layout.operator_context
731
732                 # Support filtering out by owner
733                 workspace = context.workspace
734                 if workspace.use_filter_by_owner:
735                     owner_names = {owner_id.name for owner_id in workspace.owner_ids}
736                 else:
737                     owner_names = None
738
739                 for func in draw_ls._draw_funcs:
740
741                     # Begin 'owner_id' filter.
742                     if owner_names is not None:
743                         owner_id = getattr(func, "_owner", None)
744                         if owner_id is not None:
745                             if func._owner not in owner_names:
746                                 continue
747                     # End 'owner_id' filter.
748
749                     # so bad menu functions don't stop
750                     # the entire menu from drawing
751                     try:
752                         func(self, context)
753                     except:
754                         import traceback
755                         traceback.print_exc()
756
757                     self.layout.operator_context = operator_context_default
758
759             draw_funcs = draw_ls._draw_funcs = [cls.draw]
760             cls.draw = draw_ls
761
762         return draw_funcs
763
764     @staticmethod
765     def _dyn_owner_apply(draw_func):
766         from _bpy import _bl_owner_id_get
767         owner_id = _bl_owner_id_get()
768         if owner_id is not None:
769             draw_func._owner = owner_id
770
771     @classmethod
772     def is_extended(cls):
773         return bool(getattr(cls.draw, "_draw_funcs", None))
774
775     @classmethod
776     def append(cls, draw_func):
777         """
778         Append a draw function to this menu,
779         takes the same arguments as the menus draw function
780         """
781         draw_funcs = cls._dyn_ui_initialize()
782         cls._dyn_owner_apply(draw_func)
783         draw_funcs.append(draw_func)
784
785     @classmethod
786     def prepend(cls, draw_func):
787         """
788         Prepend a draw function to this menu, takes the same arguments as
789         the menus draw function
790         """
791         draw_funcs = cls._dyn_ui_initialize()
792         cls._dyn_owner_apply(draw_func)
793         draw_funcs.insert(0, draw_func)
794
795     @classmethod
796     def remove(cls, draw_func):
797         """Remove a draw function that has been added to this menu"""
798         draw_funcs = cls._dyn_ui_initialize()
799         try:
800             draw_funcs.remove(draw_func)
801         except ValueError:
802             pass
803
804
805 class Panel(StructRNA, _GenericUI, metaclass=RNAMeta):
806     __slots__ = ()
807
808
809 class UIList(StructRNA, _GenericUI, metaclass=RNAMeta):
810     __slots__ = ()
811
812
813 class Header(StructRNA, _GenericUI, metaclass=RNAMeta):
814     __slots__ = ()
815
816
817 class Menu(StructRNA, _GenericUI, metaclass=RNAMeta):
818     __slots__ = ()
819
820     def path_menu(self, searchpaths, operator, *,
821                   props_default=None, prop_filepath="filepath",
822                   filter_ext=None, filter_path=None, display_name=None,
823                   add_operator=None):
824         """
825         Populate a menu from a list of paths.
826
827         :arg searchpaths: Paths to scan.
828         :type searchpaths: sequence of strings.
829         :arg operator: The operator id to use with each file.
830         :type operator: string
831         :arg prop_filepath: Optional operator filepath property (defaults to "filepath").
832         :type prop_filepath: string
833         :arg props_default: Properties to assign to each operator.
834         :type props_default: dict
835         :arg filter_ext: Optional callback that takes the file extensions.
836
837            Returning false excludes the file from the list.
838
839         :type filter_ext: Callable that takes a string and returns a bool.
840         :arg display_name: Optional callback that takes the full path, returns the name to display.
841         :type display_name: Callable that takes a string and returns a string.
842         """
843
844         layout = self.layout
845
846         import os
847         import bpy.utils
848
849         layout = self.layout
850
851         if not searchpaths:
852             layout.label(text="* Missing Paths *")
853
854         # collect paths
855         files = []
856         for directory in searchpaths:
857             files.extend(
858                 [(f, os.path.join(directory, f))
859                  for f in os.listdir(directory)
860                  if (not f.startswith("."))
861                  if ((filter_ext is None) or
862                      (filter_ext(os.path.splitext(f)[1])))
863                  if ((filter_path is None) or
864                      (filter_path(f)))
865                  ])
866
867         files.sort()
868
869         col = layout.column(align=True)
870
871         for f, filepath in files:
872             # Intentionally pass the full path to 'display_name' callback,
873             # since the callback may want to use part a directory in the name.
874             row = col.row(align=True)
875             name = display_name(filepath) if display_name else bpy.path.display_name(f)
876             props = row.operator(
877                 operator,
878                 text=name,
879                 translate=False,
880             )
881
882             if props_default is not None:
883                 for attr, value in props_default.items():
884                     setattr(props, attr, value)
885
886             setattr(props, prop_filepath, filepath)
887             if operator == "script.execute_preset":
888                 props.menu_idname = self.bl_idname
889
890             if add_operator:
891                 props = row.operator(add_operator, text="", icon='REMOVE')
892                 props.name = name
893                 props.remove_name = True
894
895         if add_operator:
896             wm = bpy.data.window_managers[0]
897
898             layout.separator()
899             row = layout.row()
900
901             sub = row.row()
902             sub.emboss = 'NORMAL'
903             sub.prop(wm, "preset_name", text="")
904
905             props = row.operator(add_operator, text="", icon='ADD')
906             props.name = wm.preset_name
907
908     def draw_preset(self, context):
909         """
910         Define these on the subclass:
911         - preset_operator (string)
912         - preset_subdir (string)
913
914         Optionally:
915         - preset_add_operator (string)
916         - preset_extensions (set of strings)
917         - preset_operator_defaults (dict of keyword args)
918         """
919         import bpy
920         ext_valid = getattr(self, "preset_extensions", {".py", ".xml"})
921         props_default = getattr(self, "preset_operator_defaults", None)
922         add_operator = getattr(self, "preset_add_operator", None)
923         self.path_menu(bpy.utils.preset_paths(self.preset_subdir),
924                        self.preset_operator,
925                        props_default=props_default,
926                        filter_ext=lambda ext: ext.lower() in ext_valid,
927                        add_operator=add_operator)
928
929     @classmethod
930     def draw_collapsible(cls, context, layout):
931         # helper function for (optionally) collapsed header menus
932         # only usable within headers
933         if context.area.show_menus:
934             # Align menus to space them closely.
935             layout.row(align=True).menu_contents(cls.__name__)
936         else:
937             layout.menu(cls.__name__, icon='COLLAPSEMENU')
938
939
940 class NodeTree(bpy_types.ID, metaclass=RNAMetaPropGroup):
941     __slots__ = ()
942
943
944 class Node(StructRNA, metaclass=RNAMetaPropGroup):
945     __slots__ = ()
946
947     @classmethod
948     def poll(cls, ntree):
949         return True
950
951
952 class NodeInternal(Node):
953     __slots__ = ()
954
955
956 class NodeSocket(StructRNA, metaclass=RNAMetaPropGroup):
957     __slots__ = ()
958
959     @property
960     def links(self):
961         """List of node links from or to this socket. Warning: takes O(len(nodetree.links)) time."""
962         return tuple(link for link in self.id_data.links
963                      if (link.from_socket == self or
964                          link.to_socket == self))
965
966
967 class NodeSocketInterface(StructRNA, metaclass=RNAMetaPropGroup):
968     __slots__ = ()
969
970
971 # These are intermediate subclasses, need a bpy type too
972 class CompositorNode(NodeInternal):
973     __slots__ = ()
974
975     @classmethod
976     def poll(cls, ntree):
977         return ntree.bl_idname == 'CompositorNodeTree'
978
979     def update(self):
980         self.tag_need_exec()
981
982
983 class ShaderNode(NodeInternal):
984     __slots__ = ()
985
986     @classmethod
987     def poll(cls, ntree):
988         return ntree.bl_idname == 'ShaderNodeTree'
989
990
991 class TextureNode(NodeInternal):
992     __slots__ = ()
993
994     @classmethod
995     def poll(cls, ntree):
996         return ntree.bl_idname == 'TextureNodeTree'