Blender  V2.59
bpy_introspect_ui.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 
00003 # $Id: bpy_introspect_ui.py 34890 2011-02-16 02:51:56Z campbellbarton $
00004 # ***** BEGIN GPL LICENSE BLOCK *****
00005 #
00006 # This program is free software; you can redistribute it and/or
00007 # modify it under the terms of the GNU General Public License
00008 # as published by the Free Software Foundation; either version 2
00009 # of the License, or (at your option) any later version.
00010 #
00011 # This program is distributed in the hope that it will be useful,
00012 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 # GNU General Public License for more details.
00015 #
00016 # You should have received a copy of the GNU General Public License
00017 # along with this program; if not, write to the Free Software Foundation,
00018 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019 #
00020 # Contributor(s): Campbell Barton
00021 #
00022 # ***** END GPL LICENSE BLOCK *****
00023 
00024 # <pep8 compliant>
00025 
00026 # This script dumps ui definitions as XML.
00027 # useful for finding bad api usage.
00028 
00029 # Example usage:
00030 # python intern/tools/bpy_introspect_ui.py
00031 
00032 import sys
00033 ModuleType = type(sys)
00034 
00035 
00036 def module_add(name):
00037     mod = sys.modules[name] = ModuleType(name)
00038     return mod
00039 
00040 
00041 class AttributeBuilder(object):
00042     """__slots__ = (
00043         "_attr", "_attr_list", "_item_set", "_args",
00044         "active", "operator_context", "enabled", "index", "data"
00045         )"""
00046 
00047     def _as_py(self):
00048         data = [self._attr_single, self._args, [child._as_py() for child in self._attr_list]]
00049         return data
00050 
00051     def _as_xml(self, indent="    "):
00052 
00053         def to_xml_str(value):
00054             if type(value) == str:
00055                 # quick shoddy clean
00056                 value = value.replace("&", " ")
00057                 value = value.replace("<", " ")
00058                 value = value.replace(">", " ")
00059 
00060                 return '"' + value + '"'
00061             else:
00062                 return '"' + str(value) + '"'
00063 
00064         def dict_to_kw(args, dict_args):
00065             args_str = ""
00066             if args:
00067                 # args_str += " ".join([to_xml_str(a) for a in args])
00068                 for i, a in enumerate(args):
00069                     args_str += "arg" + str(i + 1) + "=" + to_xml_str(a) + " "
00070 
00071             if dict_args:
00072                 args_str += " ".join(["%s=%s" % (key, to_xml_str(value)) for key, value in sorted(dict_args.items())])
00073 
00074             if args_str:
00075                 return " " + args_str
00076 
00077             return ""
00078 
00079         lines = []
00080 
00081         def py_to_xml(item, indent_ctx):
00082             if item._attr_list:
00083                 lines.append("%s<%s%s>" % (indent_ctx, item._attr_single, dict_to_kw(item._args_tuple, item._args)))
00084                 for child in item._attr_list:
00085                     # print(child._attr)
00086                     py_to_xml(child, indent_ctx + indent)
00087                 lines.append("%s</%s>" % (indent_ctx, item._attr_single))
00088             else:
00089                 lines.append("%s<%s%s/>" % (indent_ctx, item._attr_single, dict_to_kw(item._args_tuple, item._args)))
00090 
00091         py_to_xml(self, indent)
00092 
00093         return "\n".join(lines)
00094 
00095     def __init__(self, attr, attr_single):
00096         self._attr = attr
00097         self._attr_single = attr_single
00098         self._attr_list = []
00099         self._item_set = []
00100         self._args = {}
00101         self._args_tuple = ()
00102 
00103     def __call__(self, *args, **kwargs):
00104         # print(self._attr, args, kwargs)
00105         self._args_tuple = args
00106         self._args = kwargs
00107         return self
00108 
00109     def __getattr__(self, attr):
00110         attr_obj = NewAttr(self._attr + "." + attr, attr)
00111         self._attr_list.append(attr_obj)
00112         return attr_obj
00113 
00114     # def __setattr__(self, attr, value):
00115     #     setatte
00116 
00117     def __getitem__(self, item):
00118         item_obj = NewAttr(self._attr + "[" + repr(item) + "]", item)
00119         self._item_set.append(item_obj)
00120         return item_obj
00121 
00122     def __setitem__(self, item, value):
00123         pass  # TODO?
00124 
00125     def __repr__(self):
00126         return self._attr
00127 
00128     def __iter__(self):
00129         return iter([])
00130 
00131     #def __len__(self):
00132     #    return 0
00133 
00134     def __int__(self):
00135         return 0
00136 
00137     def __cmp__(self, other):
00138         return -1
00139 
00140     def __lt__(self, other):
00141         return -1
00142 
00143     def __gt__(self, other):
00144         return -1
00145 
00146     def __le__(self, other):
00147         return -1
00148 
00149     def __add__(self, other):
00150         return self
00151 
00152     def __sub__(self, other):
00153         return self
00154 
00155     # Custom functions
00156     def lower(self):
00157         return ""
00158 
00159     def upper(self):
00160         return ""
00161 
00162     def keys(self):
00163         return []
00164 
00165 
00166 def NewAttr(attr, attr_single):
00167     obj = AttributeBuilder(attr, attr_single)
00168     return obj
00169 
00170 
00171 class BaseFakeUI():
00172     def __init__(self):
00173         self.layout = NewAttr("self.layout", "layout")
00174 
00175 
00176 class Panel(BaseFakeUI):
00177     pass
00178 
00179 
00180 class Header(BaseFakeUI):
00181     pass
00182 
00183 
00184 class Menu(BaseFakeUI):
00185     def draw_preset(self, context):
00186         pass
00187 
00188     def path_menu(self, a, b, c):
00189         pass
00190 
00191 
00192 class Operator(BaseFakeUI):
00193     pass
00194 
00195 
00196 class PropertyGroup():
00197     pass
00198 
00199 
00200 # setup fake module
00201 def fake_main():
00202     bpy = module_add("bpy")
00203 
00204     bpy.types = module_add("bpy.types")
00205     bpy.types.Panel = Panel
00206     bpy.types.Header = Header
00207     bpy.types.Menu = Menu
00208     bpy.types.PropertyGroup = PropertyGroup
00209     bpy.types.Operator = Operator
00210 
00211     bpy.types.Armature = type("Armature", (), {})
00212     bpy.types.Bone = type("Bone", (), {})
00213     bpy.types.EditBone = type("EditBone", (), {})
00214     bpy.types.PoseBone = type("PoseBone", (), {})
00215     bpy.types.Material = type("Material", (), {})
00216     bpy.types.Lamp = type("Lamp", (), {})
00217     bpy.types.Camera = type("Camera", (), {})
00218     bpy.types.Curve = type("Curve", (), {})
00219     bpy.types.Lattice = type("Lattice", (), {})
00220     bpy.types.Mesh = type("Mesh", (), {})
00221     bpy.types.MetaBall = type("MetaBall", (), {})
00222     bpy.types.Object = type("Object", (), {})
00223     bpy.types.Texture = type("Texture", (), {})
00224     bpy.types.ParticleSettings = type("ParticleSettings", (), {})
00225     bpy.types.World = type("World", (), {})
00226     bpy.types.Brush = type("Brush", (), {})
00227     bpy.types.WindowManager = type("WindowManager", (), {})
00228     bpy.types.Scene = type("Scene", (), {})
00229     bpy.types.Scene.EnumProperty = NewAttr("bpy.types.Scene.EnumProperty", "EnumProperty")
00230     bpy.types.Scene.StringProperty = NewAttr("bpy.types.Scene.StringProperty", "StringProperty")
00231 
00232     bpy.props = module_add("bpy.props")
00233     bpy.props.StringProperty = dict
00234     bpy.props.BoolProperty = dict
00235     bpy.props.IntProperty = dict
00236     bpy.props.EnumProperty = dict
00237 
00238     bpy.data = module_add("bpy.data")
00239     bpy.data.scenes = ()
00240     bpy.data.groups = ()
00241     bpy.data.meshes = ()
00242     bpy.data.shape_keys = ()
00243     bpy.data.materials = ()
00244     bpy.data.lamps = ()
00245     bpy.data.textures = ()
00246     bpy.data.cameras = ()
00247     bpy.data.curves = ()
00248     bpy.data.metaballs = ()
00249     bpy.data.armatures = ()
00250     bpy.data.particles = ()
00251 
00252     bpy.data.file_is_saved = True
00253 
00254     bpy.utils = module_add("bpy.utils")
00255     bpy.utils.smpte_from_frame = lambda f: ""
00256     bpy.utils.script_paths = lambda f: []
00257 
00258     bpy.app = module_add("bpy.app")
00259     bpy.app.debug = False
00260     bpy.app.version = 2, 55, 1
00261 
00262     bpy.path = module_add("bpy.path")
00263     bpy.path.display_name = lambda f: ""
00264 
00265 
00266 def fake_helper():
00267 
00268     class PropertyPanel():
00269         pass
00270 
00271     rna_prop_ui = module_add("rna_prop_ui")
00272     rna_prop_ui.PropertyPanel = PropertyPanel
00273     rna_prop_ui.draw = NewAttr("rna_prop_ui.draw", "draw")
00274 
00275     rigify = module_add("rigify")
00276     rigify.get_submodule_types = lambda: []
00277 
00278 
00279 fake_main()
00280 fake_helper()
00281 
00282 import bpy
00283 
00284 sys.path.insert(0, "/b/release/scripts/ui/")
00285 
00286 
00287 def module_classes(mod):
00288     classes = []
00289     for value in mod.__dict__.values():
00290         try:
00291             is_subclass = issubclass(value, BaseFakeUI)
00292         except:
00293             is_subclass = False
00294 
00295         if is_subclass:
00296             classes.append(value)
00297 
00298     return classes
00299 
00300 
00301 def main():
00302     import os
00303 
00304     base = os.path.join(os.path.dirname(__file__), "..", "..")
00305     base = os.path.normpath(base)
00306     base = os.path.abspath(base)
00307 
00308     scripts_dir = os.path.join(base, "release", "scripts", "ui")
00309     for f in sorted(os.listdir(scripts_dir)):
00310         if f.endswith(".py"):
00311             # print(f)
00312             mod = __import__(f[:-3])
00313 
00314             classes = module_classes(mod)
00315 
00316             for cls in classes:
00317                 setattr(bpy.types, cls.__name__, cls)
00318 
00319     # print("running...")
00320     print("<ui>")
00321     for f in sorted(os.listdir(scripts_dir)):
00322         if f.endswith(".py"):
00323             # print(f)
00324             mod = __import__(f[:-3])
00325 
00326             classes = module_classes(mod)
00327 
00328             for cls in classes:
00329                 # want to check if the draw function is directly in the class
00330                 # print("draw")
00331                 if "draw" in cls.__dict__:
00332                     self = cls()
00333                     self.draw(NewAttr("context", "context"))
00334                     # print(self.layout._as_py())
00335                     self.layout._args['id'] = mod.__name__ + "." + cls.__name__
00336                     print(self.layout._as_xml())
00337     print("</ui>")
00338 
00339 
00340 if __name__ == "__main__":
00341     main()