Blender V2.61 - r43446

ui_snapshot.py

Go to the documentation of this file.
00001 # ***** BEGIN GPL LICENSE BLOCK *****
00002 #
00003 # This program is free software; you can redistribute it and/or
00004 # modify it under the terms of the GNU General Public License
00005 # as published by the Free Software Foundation; either version 2
00006 # of the License, or (at your option) any later version.
00007 #
00008 # This program is distributed in the hope that it will be useful,
00009 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00010 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011 # GNU General Public License for more details.
00012 #
00013 # You should have received a copy of the GNU General Public License
00014 # along with this program; if not, write to the Free Software Foundation,
00015 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00016 #
00017 # Contributor(s): Campbell Barton
00018 #
00019 # ***** END GPL LICENSE BLOCK *****
00020 
00021 # screenshot panels, corrently only works for render, scene and world.
00022 # needs some further work to setup blender contexts for all possible panels.
00023 
00024 import os
00025 
00026 REMOVE_CMP_IMAGES = True
00027 TEMP_DIR = "/tmp"
00028 
00029 PROPERTY_MAPPING = {
00030     "armature_edit": 'DATA',
00031     "bone": 'BONE',
00032     "bone_constraint": '',
00033     "constraint": '',
00034     "curve_edit": '',
00035     "data": '',
00036     "imagepaint": '',
00037     "lattice_edit": 'DATA',
00038     "material": 'MATERIAL',
00039     "mball_edit": '',
00040     "mesh_edit": '',
00041     "modifier": '',
00042     "object": 'OBJECT',
00043     "objectmode": '',
00044     "particle": '',
00045     "particlemode": '',
00046     "physics": '',
00047     "posemode": '',  # toolbar
00048     "render": 'RENDER',
00049     "scene": 'SCENE',
00050     "surface_edit": '',
00051     "text_edit": '',
00052     "texture": '',
00053     "vertexpaint": '',
00054     "weightpaint": '',
00055     "world": 'WORLD',
00056 }
00057 
00058 # format: % (new, blank, out)
00059 magick_command = 'convert "%s" "%s" \( -clone 0 -clone 1 -compose difference -composite -threshold 0 \) -delete 1 -alpha off -compose copy_opacity -composite -trim "%s" '
00060 
00061 import bpy
00062 
00063 
00064 def clear_startup_blend():
00065     import bpy
00066     if bpy.ops.object.mode_set.poll():
00067         bpy.ops.object.mode_set(mode='OBJECT')
00068 
00069     import bpy
00070 
00071     for scene in bpy.data.scenes:
00072         for obj in scene.objects:
00073             scene.objects.unlink(obj)
00074 
00075 
00076 def force_redraw():
00077     bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)
00078 
00079 def fake_poll(cls, context):
00080     return True
00081 
00082 def screenshot(path):
00083     force_redraw()
00084     bpy.ops.screen.screenshot(filepath=path)
00085 
00086 def context_setup(bl_context, class_name):    
00087     if bl_context == "object":
00088         bpy.ops.object.add(type='EMPTY')
00089     elif bl_context == "bone":
00090         bpy.ops.object.armature_add()
00091         bpy.ops.object.mode_set(mode='EDIT')
00092     elif bl_context == "armature_edit":
00093         bpy.ops.object.armature_add()
00094         bpy.ops.object.mode_set(mode='EDIT')
00095     elif bl_context == "posemode":
00096         bpy.ops.object.armature_add()
00097         bpy.ops.object.mode_set(mode='POSE')
00098     elif bl_context == "lattice_edit":
00099         bpy.ops.object.add(type='LATTICE')
00100         bpy.ops.object.mode_set(mode='EDIT')
00101     elif bl_context == "material":
00102         bpy.ops.object.add(type='MESH')
00103         bpy.context.object.data.materials.append(bpy.data.materials.new("Material"))
00104         bpy.ops.object.mode_set(mode='EDIT')
00105     
00106 
00107 def main():
00108     panel_subclasses = []
00109 
00110     for cls_name in dir(bpy.types):
00111         cls = getattr(bpy.types, cls_name)
00112         if issubclass(cls, bpy.types.Panel):
00113             if bpy.types.Panel is cls:
00114                 continue
00115 
00116             panel_subclasses.append((cls, getattr(cls, "poll", None)))
00117 
00118     for cls, poll in panel_subclasses:
00119         cls.poll = classmethod(fake_poll)
00120         cls.bl_options = set()  # so we dont get 'DEFAULT_CLOSED'
00121         bpy.utils.unregister_class(cls)
00122 
00123     # collect context types
00124     button_contexts = {None}
00125     for cls, poll in panel_subclasses:
00126         button_contexts.add(getattr(cls, "bl_context", None))
00127     button_contexts.remove(None)
00128 
00129     # get the properties space
00130     space_props = None
00131     for sa in bpy.context.screen.areas:
00132         space = sa.spaces.active
00133         if space.type == 'PROPERTIES':
00134             space_props = space
00135             break
00136     if space_props is None:
00137         raise Exception("no properties space type found")
00138     
00139     for bl_context in sorted(button_contexts):
00140         print(list(sorted(button_contexts)))
00141         # TODO
00142         # if bl_context in PROPERTY_SKIP:
00143         #     continue
00144 
00145         ## TESTING ONLY
00146         #if bl_context != "material":
00147         #    continue
00148 
00149         prop_context = PROPERTY_MAPPING[bl_context]
00150         if not prop_context:
00151             print("    TODO, skipping", bl_context)
00152             continue
00153 
00154         space_props.context = prop_context
00155 
00156         for cls, poll in panel_subclasses:
00157             if cls.bl_space_type == 'PROPERTIES':
00158                 if cls.bl_region_type == 'WINDOW':
00159                     if cls.bl_context == bl_context:
00160                         
00161                         clear_startup_blend()
00162                         context_setup(bl_context, cls.__name__)
00163                         
00164                         file_base = os.path.join(TEMP_DIR, "%s_%s" % (bl_context, "_" + cls.__name__.replace(".", "_")))
00165                         file_old = file_base + "_old.png"
00166                         file_new = file_base + "_new.png"
00167                         file_crop = file_base + ".png"
00168                         
00169                         screenshot(file_old)
00170                         
00171                         # we need a new unique name so old 'closed' settings dont get applied
00172                         idname = getattr(cls, "bl_idname", cls.__name__.split(".")[-1])
00173                         cls.bl_idname = idname + "_"
00174                         
00175                         bpy.utils.register_class(cls)
00176 
00177                         screenshot(file_new)
00178 
00179                         bpy.utils.unregister_class(cls)
00180 
00181                         # screenshot magic
00182                         from os import system
00183                         system(magick_command % (file_new, file_old, file_crop))
00184                         
00185                         if REMOVE_CMP_IMAGES:
00186                             from os import remove
00187                             remove(file_old)
00188                             remove(file_new)
00189 
00190 
00191 if __name__ == "__main__":
00192     main()