Pose Library: update for rename of asset_library to asset_library_ref
[blender-addons.git] / depsgraph_debug.py
blobc0906005a89c39dc1e838bd4f326c171033c4e9d
1 # ##### BEGIN GPL LICENSE BLOCK #####
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.
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.
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.
17 # ##### END GPL LICENSE BLOCK #####
19 import bpy
20 from bpy.types import (
21 Operator,
22 Panel,
24 from bpy.props import (StringProperty, )
26 bl_info = {
27 "name": "Dependency Graph Debug",
28 "author": "Sergey Sharybin",
29 "version": (0, 1),
30 "blender": (2, 80, 0),
31 "description": "Various dependency graph debugging tools",
32 "warning": "",
33 "doc_url": "",
34 "tracker_url": "",
35 "category": "Development",
39 def _get_depsgraph(context):
40 scene = context.scene
41 if bpy.app.version < (2, 80, 0,):
42 return scene.depsgraph
43 else:
44 view_layer = context.view_layer
45 return view_layer.depsgraph
48 ###############################################################################
49 # Save data from depsgraph to a specified file.
51 class SCENE_OT_depsgraph_save_common:
52 filepath: StringProperty(
53 name="File Path",
54 description="Filepath used for saving the file",
55 maxlen=1024,
56 subtype='FILE_PATH',
59 def _getExtension(self, context):
60 return ""
62 @classmethod
63 def poll(cls, context):
64 depsgraph = _get_depsgraph(context)
65 return depsgraph is not None
67 def invoke(self, context, event):
68 import os
69 if not self.filepath:
70 blend_filepath = context.blend_data.filepath
71 if not blend_filepath:
72 blend_filepath = "deg"
73 else:
74 blend_filepath = os.path.splitext(blend_filepath)[0]
76 self.filepath = blend_filepath + self._getExtension(context)
77 context.window_manager.fileselect_add(self)
78 return {'RUNNING_MODAL'}
80 def execute(self, context):
81 depsgraph = _get_depsgraph(context)
82 if not self.performSave(context, depsgraph):
83 return {'CANCELLED'}
84 return {'FINISHED'}
86 def performSave(self, context, depsgraph):
87 pass
90 class SCENE_OT_depsgraph_relations_graphviz(
91 Operator,
92 SCENE_OT_depsgraph_save_common,
94 bl_idname = "scene.depsgraph_relations_graphviz"
95 bl_label = "Save Depsgraph"
96 bl_description = "Save current scene's dependency graph to a graphviz file"
98 def _getExtension(self, context):
99 return ".dot"
101 def performSave(self, context, depsgraph):
102 import os
103 basename, extension = os.path.splitext(self.filepath)
104 depsgraph.debug_relations_graphviz(os.path.join(self.filepath, basename + ".dot"))
105 return True
108 class SCENE_OT_depsgraph_stats_gnuplot(
109 Operator,
110 SCENE_OT_depsgraph_save_common,
112 bl_idname = "scene.depsgraph_stats_gnuplot"
113 bl_label = "Save Depsgraph Stats"
114 bl_description = "Save current scene's evaluaiton stats to gnuplot file"
116 def _getExtension(self, context):
117 return ".plot"
119 def performSave(self, context, depsgraph):
120 depsgraph.debug_stats_gnuplot(self.filepath, "")
121 return True
124 ###############################################################################
125 # Visualize some depsgraph information as an image opening in image editor.
127 class SCENE_OT_depsgraph_image_common:
128 def _getOrCreateImageForAbsPath(self, filepath):
129 for image in bpy.data.images:
130 if image.filepath == filepath:
131 image.reload()
132 return image
133 return bpy.data.images.load(filepath, check_existing=True)
135 def _findBestImageEditor(self, context, image):
136 first_none_editor = None
137 for area in context.screen.areas:
138 if area.type != 'IMAGE_EDITOR':
139 continue
140 for space in area.spaces:
141 if space.type != 'IMAGE_EDITOR':
142 continue
143 if not space.image:
144 first_none_editor = space
145 else:
146 if space.image == image:
147 return space
148 return first_none_editor
150 def _createTempFile(self, suffix):
151 import os
152 import tempfile
153 fd, filepath = tempfile.mkstemp(suffix=suffix)
154 os.close(fd)
155 return filepath
157 def _openImageInEditor(self, context, image_filepath):
158 image = self._getOrCreateImageForAbsPath(image_filepath)
159 editor = self._findBestImageEditor(context, image)
160 if editor:
161 editor.image = image
163 def execute(self, context):
164 depsgraph = _get_depsgraph(context)
165 if not self.performSave(context, depsgraph):
166 return {'CANCELLED'}
167 return {'FINISHED'}
169 def performSave(self, context, depsgraph):
170 pass
173 class SCENE_OT_depsgraph_relations_image(Operator,
174 SCENE_OT_depsgraph_image_common):
175 bl_idname = "scene.depsgraph_relations_image"
176 bl_label = "Depsgraph as Image"
177 bl_description = "Create new image datablock from the dependency graph"
179 def performSave(self, context, depsgraph):
180 import os
181 import subprocess
182 # Create temporary file.
183 dot_filepath = self._createTempFile(suffix=".dot")
184 # Save dependency graph to graphviz file.
185 depsgraph.debug_relations_graphviz(dot_filepath)
186 # Convert graphviz to PNG image.
187 png_filepath = os.path.join(bpy.app.tempdir, "depsgraph.png")
188 command = ("dot", "-Tpng", dot_filepath, "-o", png_filepath)
189 try:
190 subprocess.run(command)
191 self._openImageInEditor(context, png_filepath)
192 except:
193 self.report({'ERROR'}, "Error invoking dot command")
194 return False
195 finally:
196 # Remove graphviz file.
197 os.remove(dot_filepath)
198 return True
201 class SCENE_OT_depsgraph_stats_image(Operator,
202 SCENE_OT_depsgraph_image_common):
203 bl_idname = "scene.depsgraph_stats_image"
204 bl_label = "Depsgraph Stats as Image"
205 bl_description = "Create new image datablock from the dependency graph " + \
206 "execution statistics"
208 def performSave(self, context, depsgraph):
209 import os
210 import subprocess
211 # Create temporary file.
212 plot_filepath = self._createTempFile(suffix=".plot")
213 png_filepath = os.path.join(bpy.app.tempdir, "depsgraph_stats.png")
214 # Save dependency graph stats to gnuplot file.
215 depsgraph.debug_stats_gnuplot(plot_filepath, png_filepath)
216 # Convert graphviz to PNG image.
217 command = ("gnuplot", plot_filepath)
218 try:
219 subprocess.run(command)
220 self._openImageInEditor(context, png_filepath)
221 except:
222 self.report({'ERROR'}, "Error invoking gnuplot command")
223 return False
224 finally:
225 # Remove graphviz file.
226 os.remove(plot_filepath)
227 return True
230 ###############################################################################
231 # Interface.
234 class SCENE_PT_depsgraph_common:
235 def draw(self, context):
236 layout = self.layout
237 col = layout.column()
238 # Everything related on relations and graph topology.
239 col.label(text="Relations:")
240 row = col.row()
241 row.operator("scene.depsgraph_relations_graphviz")
242 row.operator("scene.depsgraph_relations_image")
243 # Everything related on evaluaiton statistics.
244 col.label(text="Statistics:")
245 row = col.row()
246 row.operator("scene.depsgraph_stats_gnuplot")
247 row.operator("scene.depsgraph_stats_image")
250 class SCENE_PT_depsgraph(bpy.types.Panel, SCENE_PT_depsgraph_common):
251 bl_label = "Dependency Graph"
252 bl_space_type = "PROPERTIES"
253 bl_region_type = "WINDOW"
254 bl_context = "scene"
255 bl_options = {'DEFAULT_CLOSED'}
257 @classmethod
258 def poll(cls, context):
259 if bpy.app.version >= (2, 80, 0,):
260 return False
261 depsgraph = _get_depsgraph(context)
262 return depsgraph is not None
265 class RENDERLAYER_PT_depsgraph(bpy.types.Panel, SCENE_PT_depsgraph_common):
266 bl_label = "Dependency Graph"
267 bl_space_type = "PROPERTIES"
268 bl_region_type = "WINDOW"
269 bl_context = "view_layer"
270 bl_options = {'DEFAULT_CLOSED'}
272 @classmethod
273 def poll(cls, context):
274 if bpy.app.version < (2, 80, 0,):
275 return False
276 depsgraph = _get_depsgraph(context)
277 return depsgraph is not None
280 def register():
281 bpy.utils.register_class(SCENE_OT_depsgraph_relations_graphviz)
282 bpy.utils.register_class(SCENE_OT_depsgraph_relations_image)
283 bpy.utils.register_class(SCENE_OT_depsgraph_stats_gnuplot)
284 bpy.utils.register_class(SCENE_OT_depsgraph_stats_image)
285 bpy.utils.register_class(SCENE_PT_depsgraph)
286 bpy.utils.register_class(RENDERLAYER_PT_depsgraph)
289 def unregister():
290 bpy.utils.unregister_class(SCENE_OT_depsgraph_relations_graphviz)
291 bpy.utils.unregister_class(SCENE_OT_depsgraph_relations_image)
292 bpy.utils.unregister_class(SCENE_OT_depsgraph_stats_gnuplot)
293 bpy.utils.unregister_class(SCENE_OT_depsgraph_stats_image)
294 bpy.utils.unregister_class(SCENE_PT_depsgraph)
295 bpy.utils.unregister_class(RENDERLAYER_PT_depsgraph)
298 if __name__ == "__main__":
299 register()