Fix T67009, T67013: crash with Intel HD Graphics and some older drivers
[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_pencils", "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     # Dummy class to keep the reference in `bpy_types_dict` and avoid
659 # erros like: "TypeError: expected GizmoGroup subclass of class ..."
660 class GizmoGroup(StructRNA):
661     __slots__ = ()
662
663
664 # Only defined so operators members can be used by accessing self.order
665 # with doc generation 'self.properties.bl_rna.properties' can fail
666 class Operator(StructRNA, metaclass=RNAMeta):
667     __slots__ = ()
668
669     def __getattribute__(self, attr):
670         properties = StructRNA.path_resolve(self, "properties")
671         bl_rna = getattr(properties, "bl_rna", None)
672         if (bl_rna is not None) and (attr in bl_rna.properties):
673             return getattr(properties, attr)
674         return super().__getattribute__(attr)
675
676     def __setattr__(self, attr, value):
677         properties = StructRNA.path_resolve(self, "properties")
678         bl_rna = getattr(properties, "bl_rna", None)
679         if (bl_rna is not None) and (attr in bl_rna.properties):
680             return setattr(properties, attr, value)
681         return super().__setattr__(attr, value)
682
683     def __delattr__(self, attr):
684         properties = StructRNA.path_resolve(self, "properties")
685         bl_rna = getattr(properties, "bl_rna", None)
686         if (bl_rna is not None) and (attr in bl_rna.properties):
687             return delattr(properties, attr)
688         return super().__delattr__(attr)
689
690     def as_keywords(self, ignore=()):
691         """Return a copy of the properties as a dictionary"""
692         ignore = ignore + ("rna_type",)
693         return {attr: getattr(self, attr)
694                 for attr in self.properties.rna_type.properties.keys()
695                 if attr not in ignore}
696
697
698 class Macro(StructRNA):
699     # bpy_types is imported before ops is defined
700     # so we have to do a local import on each run
701     __slots__ = ()
702
703     @classmethod
704     def define(self, opname):
705         from _bpy import ops
706         return ops.macro_define(self, opname)
707
708
709 class PropertyGroup(StructRNA, metaclass=RNAMetaPropGroup):
710     __slots__ = ()
711
712
713 class RenderEngine(StructRNA, metaclass=RNAMeta):
714     __slots__ = ()
715
716
717 class KeyingSetInfo(StructRNA, metaclass=RNAMeta):
718     __slots__ = ()
719
720
721 class AddonPreferences(StructRNA, metaclass=RNAMeta):
722     __slots__ = ()
723
724
725 class _GenericUI:
726     __slots__ = ()
727
728     @classmethod
729     def _dyn_ui_initialize(cls):
730         draw_funcs = getattr(cls.draw, "_draw_funcs", None)
731
732         if draw_funcs is None:
733
734             def draw_ls(self, context):
735                 # ensure menus always get default context
736                 operator_context_default = self.layout.operator_context
737
738                 # Support filtering out by owner
739                 workspace = context.workspace
740                 if workspace.use_filter_by_owner:
741                     owner_names = {owner_id.name for owner_id in workspace.owner_ids}
742                 else:
743                     owner_names = None
744
745                 for func in draw_ls._draw_funcs:
746
747                     # Begin 'owner_id' filter.
748                     if owner_names is not None:
749                         owner_id = getattr(func, "_owner", None)
750                         if owner_id is not None:
751                             if func._owner not in owner_names:
752                                 continue
753                     # End 'owner_id' filter.
754
755                     # so bad menu functions don't stop
756                     # the entire menu from drawing
757                     try:
758                         func(self, context)
759                     except:
760                         import traceback
761                         traceback.print_exc()
762
763                     self.layout.operator_context = operator_context_default
764
765             draw_funcs = draw_ls._draw_funcs = [cls.draw]
766             cls.draw = draw_ls
767
768         return draw_funcs
769
770     @staticmethod
771     def _dyn_owner_apply(draw_func):
772         from _bpy import _bl_owner_id_get
773         owner_id = _bl_owner_id_get()
774         if owner_id is not None:
775             draw_func._owner = owner_id
776
777     @classmethod
778     def is_extended(cls):
779         return bool(getattr(cls.draw, "_draw_funcs", None))
780
781     @classmethod
782     def append(cls, draw_func):
783         """
784         Append a draw function to this menu,
785         takes the same arguments as the menus draw function
786         """
787         draw_funcs = cls._dyn_ui_initialize()
788         cls._dyn_owner_apply(draw_func)
789         draw_funcs.append(draw_func)
790
791     @classmethod
792     def prepend(cls, draw_func):
793         """
794         Prepend a draw function to this menu, takes the same arguments as
795         the menus draw function
796         """
797         draw_funcs = cls._dyn_ui_initialize()
798         cls._dyn_owner_apply(draw_func)
799         draw_funcs.insert(0, draw_func)
800
801     @classmethod
802     def remove(cls, draw_func):
803         """Remove a draw function that has been added to this menu"""
804         draw_funcs = cls._dyn_ui_initialize()
805         try:
806             draw_funcs.remove(draw_func)
807         except ValueError:
808             pass
809
810
811 class Panel(StructRNA, _GenericUI, metaclass=RNAMeta):
812     __slots__ = ()
813
814
815 class UIList(StructRNA, _GenericUI, metaclass=RNAMeta):
816     __slots__ = ()
817
818
819 class Header(StructRNA, _GenericUI, metaclass=RNAMeta):
820     __slots__ = ()
821
822
823 class Menu(StructRNA, _GenericUI, metaclass=RNAMeta):
824     __slots__ = ()
825
826     def path_menu(self, searchpaths, operator, *,
827                   props_default=None, prop_filepath="filepath",
828                   filter_ext=None, filter_path=None, display_name=None,
829                   add_operator=None):
830         """
831         Populate a menu from a list of paths.
832
833         :arg searchpaths: Paths to scan.
834         :type searchpaths: sequence of strings.
835         :arg operator: The operator id to use with each file.
836         :type operator: string
837         :arg prop_filepath: Optional operator filepath property (defaults to "filepath").
838         :type prop_filepath: string
839         :arg props_default: Properties to assign to each operator.
840         :type props_default: dict
841         :arg filter_ext: Optional callback that takes the file extensions.
842
843            Returning false excludes the file from the list.
844
845         :type filter_ext: Callable that takes a string and returns a bool.
846         :arg display_name: Optional callback that takes the full path, returns the name to display.
847         :type display_name: Callable that takes a string and returns a string.
848         """
849
850         layout = self.layout
851
852         import os
853         import bpy.utils
854
855         layout = self.layout
856
857         if not searchpaths:
858             layout.label(text="* Missing Paths *")
859
860         # collect paths
861         files = []
862         for directory in searchpaths:
863             files.extend(
864                 [(f, os.path.join(directory, f))
865                  for f in os.listdir(directory)
866                  if (not f.startswith("."))
867                  if ((filter_ext is None) or
868                      (filter_ext(os.path.splitext(f)[1])))
869                  if ((filter_path is None) or
870                      (filter_path(f)))
871                  ])
872
873         files.sort()
874
875         col = layout.column(align=True)
876
877         for f, filepath in files:
878             # Intentionally pass the full path to 'display_name' callback,
879             # since the callback may want to use part a directory in the name.
880             row = col.row(align=True)
881             name = display_name(filepath) if display_name else bpy.path.display_name(f)
882             props = row.operator(
883                 operator,
884                 text=name,
885                 translate=False,
886             )
887
888             if props_default is not None:
889                 for attr, value in props_default.items():
890                     setattr(props, attr, value)
891
892             setattr(props, prop_filepath, filepath)
893             if operator == "script.execute_preset":
894                 props.menu_idname = self.bl_idname
895
896             if add_operator:
897                 props = row.operator(add_operator, text="", icon='REMOVE')
898                 props.name = name
899                 props.remove_name = True
900
901         if add_operator:
902             wm = bpy.data.window_managers[0]
903
904             layout.separator()
905             row = layout.row()
906
907             sub = row.row()
908             sub.emboss = 'NORMAL'
909             sub.prop(wm, "preset_name", text="")
910
911             props = row.operator(add_operator, text="", icon='ADD')
912             props.name = wm.preset_name
913
914     def draw_preset(self, _context):
915         """
916         Define these on the subclass:
917         - preset_operator (string)
918         - preset_subdir (string)
919
920         Optionally:
921         - preset_add_operator (string)
922         - preset_extensions (set of strings)
923         - preset_operator_defaults (dict of keyword args)
924         """
925         import bpy
926         ext_valid = getattr(self, "preset_extensions", {".py", ".xml"})
927         props_default = getattr(self, "preset_operator_defaults", None)
928         add_operator = getattr(self, "preset_add_operator", None)
929         self.path_menu(bpy.utils.preset_paths(self.preset_subdir),
930                        self.preset_operator,
931                        props_default=props_default,
932                        filter_ext=lambda ext: ext.lower() in ext_valid,
933                        add_operator=add_operator)
934
935     @classmethod
936     def draw_collapsible(cls, context, layout):
937         # helper function for (optionally) collapsed header menus
938         # only usable within headers
939         if context.area.show_menus:
940             # Align menus to space them closely.
941             layout.row(align=True).menu_contents(cls.__name__)
942         else:
943             layout.menu(cls.__name__, icon='COLLAPSEMENU')
944
945
946 class NodeTree(bpy_types.ID, metaclass=RNAMetaPropGroup):
947     __slots__ = ()
948
949
950 class Node(StructRNA, metaclass=RNAMetaPropGroup):
951     __slots__ = ()
952
953     @classmethod
954     def poll(cls, _ntree):
955         return True
956
957
958 class NodeInternal(Node):
959     __slots__ = ()
960
961
962 class NodeSocket(StructRNA, metaclass=RNAMetaPropGroup):
963     __slots__ = ()
964
965     @property
966     def links(self):
967         """List of node links from or to this socket. Warning: takes O(len(nodetree.links)) time."""
968         return tuple(link for link in self.id_data.links
969                      if (link.from_socket == self or
970                          link.to_socket == self))
971
972
973 class NodeSocketInterface(StructRNA, metaclass=RNAMetaPropGroup):
974     __slots__ = ()
975
976
977 # These are intermediate subclasses, need a bpy type too
978 class CompositorNode(NodeInternal):
979     __slots__ = ()
980
981     @classmethod
982     def poll(cls, ntree):
983         return ntree.bl_idname == 'CompositorNodeTree'
984
985     def update(self):
986         self.tag_need_exec()
987
988
989 class ShaderNode(NodeInternal):
990     __slots__ = ()
991
992     @classmethod
993     def poll(cls, ntree):
994         return ntree.bl_idname == 'ShaderNodeTree'
995
996
997 class TextureNode(NodeInternal):
998     __slots__ = ()
999
1000     @classmethod
1001     def poll(cls, ntree):
1002         return ntree.bl_idname == 'TextureNodeTree'