Sun Position: fix error in HDRI mode when no env tex is selected
[blender-addons.git] / depsgraph_debug.py
blob816052a373c12f3f1d8fa4f71f935581dd05822d
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 import bpy
4 from bpy.types import (
5 Operator,
6 Panel,
8 from bpy.props import (StringProperty, )
10 bl_info = {
11 "name": "Dependency Graph Debug",
12 "author": "Sergey Sharybin",
13 "version": (0, 1),
14 "blender": (2, 80, 0),
15 "description": "Various dependency graph debugging tools",
16 "location": "Properties > View Layer > Dependency Graph",
17 "warning": "",
18 "doc_url": "",
19 "tracker_url": "",
20 "category": "Development",
24 def _get_depsgraph(context):
25 scene = context.scene
26 if bpy.app.version < (2, 80, 0,):
27 return scene.depsgraph
28 else:
29 view_layer = context.view_layer
30 return view_layer.depsgraph
33 ###############################################################################
34 # Save data from depsgraph to a specified file.
36 class SCENE_OT_depsgraph_save_common:
37 filepath: StringProperty(
38 name="File Path",
39 description="Filepath used for saving the file",
40 maxlen=1024,
41 subtype='FILE_PATH',
44 def _getExtension(self, context):
45 return ""
47 @classmethod
48 def poll(cls, context):
49 depsgraph = _get_depsgraph(context)
50 return depsgraph is not None
52 def invoke(self, context, event):
53 import os
54 if not self.filepath:
55 blend_filepath = context.blend_data.filepath
56 if not blend_filepath:
57 blend_filepath = "deg"
58 else:
59 blend_filepath = os.path.splitext(blend_filepath)[0]
61 self.filepath = blend_filepath + self._getExtension(context)
62 context.window_manager.fileselect_add(self)
63 return {'RUNNING_MODAL'}
65 def execute(self, context):
66 depsgraph = _get_depsgraph(context)
67 if not self.performSave(context, depsgraph):
68 return {'CANCELLED'}
69 return {'FINISHED'}
71 def performSave(self, context, depsgraph):
72 pass
75 class SCENE_OT_depsgraph_relations_graphviz(
76 Operator,
77 SCENE_OT_depsgraph_save_common,
79 bl_idname = "scene.depsgraph_relations_graphviz"
80 bl_label = "Save Depsgraph"
81 bl_description = "Save current scene's dependency graph to a graphviz file"
83 def _getExtension(self, context):
84 return ".dot"
86 def performSave(self, context, depsgraph):
87 import os
88 basename, extension = os.path.splitext(self.filepath)
89 depsgraph.debug_relations_graphviz(os.path.join(self.filepath, basename + ".dot"))
90 return True
93 class SCENE_OT_depsgraph_stats_gnuplot(
94 Operator,
95 SCENE_OT_depsgraph_save_common,
97 bl_idname = "scene.depsgraph_stats_gnuplot"
98 bl_label = "Save Depsgraph Stats"
99 bl_description = "Save current scene's evaluaiton stats to gnuplot file"
101 def _getExtension(self, context):
102 return ".plot"
104 def performSave(self, context, depsgraph):
105 depsgraph.debug_stats_gnuplot(self.filepath, "")
106 return True
109 ###############################################################################
110 # Visualize some depsgraph information as an image opening in image editor.
112 class SCENE_OT_depsgraph_image_common:
113 def _getOrCreateImageForAbsPath(self, filepath):
114 for image in bpy.data.images:
115 if image.filepath == filepath:
116 image.reload()
117 return image
118 return bpy.data.images.load(filepath, check_existing=True)
120 def _findBestImageEditor(self, context, image):
121 first_none_editor = None
122 for area in context.screen.areas:
123 if area.type != 'IMAGE_EDITOR':
124 continue
125 for space in area.spaces:
126 if space.type != 'IMAGE_EDITOR':
127 continue
128 if not space.image:
129 first_none_editor = space
130 else:
131 if space.image == image:
132 return space
133 return first_none_editor
135 def _createTempFile(self, suffix):
136 import os
137 import tempfile
138 fd, filepath = tempfile.mkstemp(suffix=suffix)
139 os.close(fd)
140 return filepath
142 def _openImageInEditor(self, context, image_filepath):
143 image = self._getOrCreateImageForAbsPath(image_filepath)
144 editor = self._findBestImageEditor(context, image)
145 if editor:
146 editor.image = image
148 def execute(self, context):
149 depsgraph = _get_depsgraph(context)
150 if not self.performSave(context, depsgraph):
151 return {'CANCELLED'}
152 return {'FINISHED'}
154 def performSave(self, context, depsgraph):
155 pass
158 class SCENE_OT_depsgraph_relations_image(Operator,
159 SCENE_OT_depsgraph_image_common):
160 bl_idname = "scene.depsgraph_relations_image"
161 bl_label = "Depsgraph as Image"
162 bl_description = "Create new image datablock from the dependency graph"
164 def performSave(self, context, depsgraph):
165 import os
166 import subprocess
167 # Create temporary file.
168 dot_filepath = self._createTempFile(suffix=".dot")
169 # Save dependency graph to graphviz file.
170 depsgraph.debug_relations_graphviz(dot_filepath)
171 # Convert graphviz to PNG image.
172 png_filepath = os.path.join(bpy.app.tempdir, "depsgraph.png")
173 command = ("dot", "-Tpng", dot_filepath, "-o", png_filepath)
174 try:
175 subprocess.run(command)
176 self._openImageInEditor(context, png_filepath)
177 except:
178 self.report({'ERROR'}, "Error invoking dot command")
179 return False
180 finally:
181 # Remove graphviz file.
182 os.remove(dot_filepath)
183 return True
186 class SCENE_OT_depsgraph_stats_image(Operator,
187 SCENE_OT_depsgraph_image_common):
188 bl_idname = "scene.depsgraph_stats_image"
189 bl_label = "Depsgraph Stats as Image"
190 bl_description = "Create new image datablock from the dependency graph " + \
191 "execution statistics"
193 def performSave(self, context, depsgraph):
194 import os
195 import subprocess
196 # Create temporary file.
197 plot_filepath = self._createTempFile(suffix=".plot")
198 png_filepath = os.path.join(bpy.app.tempdir, "depsgraph_stats.png")
199 # Save dependency graph stats to gnuplot file.
200 depsgraph.debug_stats_gnuplot(plot_filepath, png_filepath)
201 # Convert graphviz to PNG image.
202 command = ("gnuplot", plot_filepath)
203 try:
204 subprocess.run(command)
205 self._openImageInEditor(context, png_filepath)
206 except:
207 self.report({'ERROR'}, "Error invoking gnuplot command")
208 return False
209 finally:
210 # Remove graphviz file.
211 os.remove(plot_filepath)
212 return True
215 class SCENE_OT_depsgraph_relations_svg(Operator,
216 SCENE_OT_depsgraph_image_common):
217 bl_idname = "scene.depsgraph_relations_svg"
218 bl_label = "Depsgraph as SVG in Browser"
219 bl_description = "Create an SVG image from the dependency graph and open it in the web browser"
221 def performSave(self, context, depsgraph):
222 import os
223 import subprocess
224 import webbrowser
225 # Create temporary file.
226 dot_filepath = self._createTempFile(suffix=".dot")
227 # Save dependency graph to graphviz file.
228 depsgraph.debug_relations_graphviz(dot_filepath)
229 # Convert graphviz to SVG image.
230 svg_filepath = os.path.join(bpy.app.tempdir, "depsgraph.svg")
231 command = ("dot", "-Tsvg", dot_filepath, "-o", svg_filepath)
232 try:
233 subprocess.run(command)
234 webbrowser.open_new_tab("file://" + os.path.abspath(svg_filepath))
235 except:
236 self.report({'ERROR'}, "Error invoking dot command")
237 return False
238 finally:
239 # Remove graphviz file.
240 os.remove(dot_filepath)
241 return True
244 ###############################################################################
245 # Interface.
248 class SCENE_PT_depsgraph_common:
249 def draw(self, context):
250 layout = self.layout
251 col = layout.column()
252 # Everything related on relations and graph topology.
253 col.label(text="Relations:")
254 row = col.row()
255 row.operator("scene.depsgraph_relations_graphviz")
256 row.operator("scene.depsgraph_relations_image")
257 col.operator("scene.depsgraph_relations_svg")
258 # Everything related on evaluaiton statistics.
259 col.label(text="Statistics:")
260 row = col.row()
261 row.operator("scene.depsgraph_stats_gnuplot")
262 row.operator("scene.depsgraph_stats_image")
265 class SCENE_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 = "scene"
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 class RENDERLAYER_PT_depsgraph(bpy.types.Panel, SCENE_PT_depsgraph_common):
281 bl_label = "Dependency Graph"
282 bl_space_type = "PROPERTIES"
283 bl_region_type = "WINDOW"
284 bl_context = "view_layer"
285 bl_options = {'DEFAULT_CLOSED'}
287 @classmethod
288 def poll(cls, context):
289 if bpy.app.version < (2, 80, 0,):
290 return False
291 depsgraph = _get_depsgraph(context)
292 return depsgraph is not None
295 def register():
296 bpy.utils.register_class(SCENE_OT_depsgraph_relations_graphviz)
297 bpy.utils.register_class(SCENE_OT_depsgraph_relations_image)
298 bpy.utils.register_class(SCENE_OT_depsgraph_relations_svg)
299 bpy.utils.register_class(SCENE_OT_depsgraph_stats_gnuplot)
300 bpy.utils.register_class(SCENE_OT_depsgraph_stats_image)
301 bpy.utils.register_class(SCENE_PT_depsgraph)
302 bpy.utils.register_class(RENDERLAYER_PT_depsgraph)
305 def unregister():
306 bpy.utils.unregister_class(SCENE_OT_depsgraph_relations_graphviz)
307 bpy.utils.unregister_class(SCENE_OT_depsgraph_relations_image)
308 bpy.utils.unregister_class(SCENE_OT_depsgraph_relations_svg)
309 bpy.utils.unregister_class(SCENE_OT_depsgraph_stats_gnuplot)
310 bpy.utils.unregister_class(SCENE_OT_depsgraph_stats_image)
311 bpy.utils.unregister_class(SCENE_PT_depsgraph)
312 bpy.utils.unregister_class(RENDERLAYER_PT_depsgraph)
315 if __name__ == "__main__":
316 register()