nodeitems_utils.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  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. # <pep8 compliant>
  19. import bpy
  20. class NodeCategory:
  21. @classmethod
  22. def poll(cls, _context):
  23. return True
  24. def __init__(self, identifier, name, description="", items=None):
  25. self.identifier = identifier
  26. self.name = name
  27. self.description = description
  28. if items is None:
  29. self.items = lambda context: []
  30. elif callable(items):
  31. self.items = items
  32. else:
  33. def items_gen(context):
  34. for item in items:
  35. if item.poll is None or context is None or item.poll(context):
  36. yield item
  37. self.items = items_gen
  38. class NodeItem:
  39. def __init__(self, nodetype, label=None, settings=None, poll=None):
  40. if settings is None:
  41. settings = {}
  42. self.nodetype = nodetype
  43. self._label = label
  44. self.settings = settings
  45. self.poll = poll
  46. @property
  47. def label(self):
  48. if self._label:
  49. return self._label
  50. else:
  51. # if no custom label is defined, fall back to the node type UI name
  52. bl_rna = bpy.types.Node.bl_rna_get_subclass(self.nodetype)
  53. if bl_rna is not None:
  54. return bl_rna.name
  55. else:
  56. return "Unknown"
  57. @property
  58. def translation_context(self):
  59. if self._label:
  60. return bpy.app.translations.contexts.default
  61. else:
  62. # if no custom label is defined, fall back to the node type UI name
  63. bl_rna = bpy.types.Node.bl_rna_get_subclass(self.nodetype)
  64. if bl_rna is not None:
  65. return bl_rna.translation_context
  66. else:
  67. return bpy.app.translations.contexts.default
  68. # NB: is a staticmethod because called with an explicit self argument
  69. # NodeItemCustom sets this as a variable attribute in __init__
  70. @staticmethod
  71. def draw(self, layout, _context):
  72. props = layout.operator("node.add_node", text=self.label, text_ctxt=self.translation_context)
  73. props.type = self.nodetype
  74. props.use_transform = True
  75. for setting in self.settings.items():
  76. ops = props.settings.add()
  77. ops.name = setting[0]
  78. ops.value = setting[1]
  79. class NodeItemCustom:
  80. def __init__(self, poll=None, draw=None):
  81. self.poll = poll
  82. self.draw = draw
  83. _node_categories = {}
  84. def register_node_categories(identifier, cat_list):
  85. if identifier in _node_categories:
  86. raise KeyError("Node categories list '%s' already registered" % identifier)
  87. return
  88. # works as draw function for menus
  89. def draw_node_item(self, context):
  90. layout = self.layout
  91. col = layout.column()
  92. for item in self.category.items(context):
  93. item.draw(item, col, context)
  94. menu_types = []
  95. for cat in cat_list:
  96. menu_type = type("NODE_MT_category_" + cat.identifier, (bpy.types.Menu,), {
  97. "bl_space_type": 'NODE_EDITOR',
  98. "bl_label": cat.name,
  99. "category": cat,
  100. "poll": cat.poll,
  101. "draw": draw_node_item,
  102. })
  103. menu_types.append(menu_type)
  104. bpy.utils.register_class(menu_type)
  105. def draw_add_menu(self, context):
  106. layout = self.layout
  107. for cat in cat_list:
  108. if cat.poll(context):
  109. layout.menu("NODE_MT_category_%s" % cat.identifier)
  110. # stores: (categories list, menu draw function, submenu types)
  111. _node_categories[identifier] = (cat_list, draw_add_menu, menu_types)
  112. def node_categories_iter(context):
  113. for cat_type in _node_categories.values():
  114. for cat in cat_type[0]:
  115. if cat.poll and ((context is None) or cat.poll(context)):
  116. yield cat
  117. def node_items_iter(context):
  118. for cat in node_categories_iter(context):
  119. for item in cat.items(context):
  120. yield item
  121. def unregister_node_cat_types(cats):
  122. for mt in cats[2]:
  123. bpy.utils.unregister_class(mt)
  124. def unregister_node_categories(identifier=None):
  125. # unregister existing UI classes
  126. if identifier:
  127. cat_types = _node_categories.get(identifier, None)
  128. if cat_types:
  129. unregister_node_cat_types(cat_types)
  130. del _node_categories[identifier]
  131. else:
  132. for cat_types in _node_categories.values():
  133. unregister_node_cat_types(cat_types)
  134. _node_categories.clear()
  135. def draw_node_categories_menu(self, context):
  136. for cats in _node_categories.values():
  137. cats[1](self, context)