__init__.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  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-80 compliant>
  19. """
  20. Module to manage overriding various parts of Blender.
  21. Intended for use with 'app_templates', though it can be used from anywhere.
  22. """
  23. # TODO, how to check these aren't from add-ons.
  24. # templates might need to un-register while filtering.
  25. def class_filter(cls_parent, **kw):
  26. whitelist = kw.pop("whitelist", None)
  27. blacklist = kw.pop("blacklist", None)
  28. kw_items = tuple(kw.items())
  29. for cls in cls_parent.__subclasses__():
  30. # same as is_registered()
  31. if "bl_rna" in cls.__dict__:
  32. if blacklist is not None and cls.__name__ in blacklist:
  33. continue
  34. if ((whitelist is not None and cls.__name__ is whitelist) or
  35. all((getattr(cls, attr) in expect) for attr, expect in kw_items)):
  36. yield cls
  37. def ui_draw_filter_register(
  38. *,
  39. ui_ignore_classes=None,
  40. ui_ignore_operator=None,
  41. ui_ignore_property=None,
  42. ui_ignore_menu=None,
  43. ui_ignore_label=None
  44. ):
  45. import bpy
  46. UILayout = bpy.types.UILayout
  47. if ui_ignore_classes is None:
  48. ui_ignore_classes = (
  49. bpy.types.Panel,
  50. bpy.types.Menu,
  51. bpy.types.Header,
  52. )
  53. class OperatorProperties_Fake:
  54. pass
  55. class UILayout_Fake(bpy.types.UILayout):
  56. __slots__ = ()
  57. def __getattribute__(self, attr):
  58. # ensure we always pass down UILayout_Fake instances
  59. if attr in {"row", "split", "column", "box", "column_flow"}:
  60. real_func = UILayout.__getattribute__(self, attr)
  61. def dummy_func(*args, **kw):
  62. # print("wrapped", attr)
  63. ret = real_func(*args, **kw)
  64. return UILayout_Fake(ret)
  65. return dummy_func
  66. elif attr in {"operator", "operator_menu_enum", "operator_enum", "operator_menu_hold"}:
  67. if ui_ignore_operator is None:
  68. return UILayout.__getattribute__(self, attr)
  69. real_func = UILayout.__getattribute__(self, attr)
  70. def dummy_func(*args, **kw):
  71. # print("wrapped", attr)
  72. ui_test = ui_ignore_operator(args[0])
  73. if ui_test is False:
  74. ret = real_func(*args, **kw)
  75. else:
  76. if ui_test is None:
  77. UILayout.__getattribute__(self, "label")("")
  78. else:
  79. assert(ui_test is True)
  80. # may need to be set
  81. ret = OperatorProperties_Fake()
  82. return ret
  83. return dummy_func
  84. elif attr in {"prop", "prop_enum"}:
  85. if ui_ignore_property is None:
  86. return UILayout.__getattribute__(self, attr)
  87. real_func = UILayout.__getattribute__(self, attr)
  88. def dummy_func(*args, **kw):
  89. # print("wrapped", attr)
  90. ui_test = ui_ignore_property(args[0].__class__.__name__, args[1])
  91. if ui_test is False:
  92. ret = real_func(*args, **kw)
  93. else:
  94. if ui_test is None:
  95. UILayout.__getattribute__(self, "label")("")
  96. else:
  97. assert(ui_test is True)
  98. ret = None
  99. return ret
  100. return dummy_func
  101. elif attr == "menu":
  102. if ui_ignore_menu is None:
  103. return UILayout.__getattribute__(self, attr)
  104. real_func = UILayout.__getattribute__(self, attr)
  105. def dummy_func(*args, **kw):
  106. # print("wrapped", attr)
  107. ui_test = ui_ignore_menu(args[0])
  108. if ui_test is False:
  109. ret = real_func(*args, **kw)
  110. else:
  111. if ui_test is None:
  112. UILayout.__getattribute__(self, "label")("")
  113. else:
  114. assert(ui_test is True)
  115. ret = None
  116. return ret
  117. return dummy_func
  118. elif attr == "label":
  119. if ui_ignore_label is None:
  120. return UILayout.__getattribute__(self, attr)
  121. real_func = UILayout.__getattribute__(self, attr)
  122. def dummy_func(*args, **kw):
  123. # print("wrapped", attr)
  124. ui_test = ui_ignore_label(args[0] if args else kw.get("text", ""))
  125. if ui_test is False:
  126. ret = real_func(*args, **kw)
  127. else:
  128. if ui_test is None:
  129. real_func("")
  130. else:
  131. assert(ui_test is True)
  132. ret = None
  133. return ret
  134. return dummy_func
  135. else:
  136. return UILayout.__getattribute__(self, attr)
  137. # print(self, attr)
  138. def operator(*args, **kw):
  139. return super().operator(*args, **kw)
  140. def draw_override(func_orig, self_real, context):
  141. cls_real = self_real.__class__
  142. if cls_real is super:
  143. # simple, no wrapping
  144. return func_orig(self_real, context)
  145. class Wrapper(cls_real):
  146. __slots__ = ()
  147. def __getattribute__(self, attr):
  148. if attr == "layout":
  149. return UILayout_Fake(self_real.layout)
  150. else:
  151. cls = super()
  152. try:
  153. return cls.__getattr__(self, attr)
  154. except AttributeError:
  155. # class variable
  156. try:
  157. return getattr(cls, attr)
  158. except AttributeError:
  159. # for preset bl_idname access
  160. return getattr(UILayout(self), attr)
  161. @property
  162. def layout(self):
  163. # print("wrapped")
  164. return self_real.layout
  165. return func_orig(Wrapper(self_real), context)
  166. ui_ignore_store = []
  167. for cls in ui_ignore_classes:
  168. for subcls in list(cls.__subclasses__()):
  169. if "draw" in subcls.__dict__: # don't want to get parents draw()
  170. def replace_draw():
  171. # function also serves to hold draw_old in a local name-space
  172. draw_orig = subcls.draw
  173. def draw(self, context):
  174. return draw_override(draw_orig, self, context)
  175. subcls.draw = draw
  176. ui_ignore_store.append((subcls, "draw", subcls.draw))
  177. replace_draw()
  178. return ui_ignore_store
  179. def ui_draw_filter_unregister(ui_ignore_store):
  180. for (obj, attr, value) in ui_ignore_store:
  181. setattr(obj, attr, value)