Reputation: 163
I am a VFX Teacher and currently when I want to grab images of Node Graphs to use in Lecture slides I have to make the Node Graph full screen and do a screen capture, but as you can imagine with larger scripts I have to zoom out so far that sometimes its not recognisable.
It looks to me the way Nuke's Node Graph resizes when you zoom in and out, that's its probably a Vector image of some kind under the hood. I am looking for a way either export this image so I can get Higher Res version of the whole node graph. Either as Vector, or just a higher res rasterized image.
Does anyone know if there might be a way to do this with Python? Or is there some external script that can do this?
Upvotes: 3
Views: 673
Reputation: 310
I have wanted this for a long time, but it took your question to get me looking into it a bit deeper.
I don't believe there is an actual vector image available under the hood (it's openGL under the hood), but I also don't see why there wouldn't be a way to automate a full-res screenshot.
I have written a script that will check the size of your node graph, set the zoom to 100% and screenshot every piece of the DAG, then stitch it automatically. It takes a few seconds because if I try to do it too fast things get mixed up due to the threading, so I had to introduce artificial pauses in between each screenshot.
You should be able to run the code below. Don't forget to set your path on the second to last line!
from PySide2 import QtWidgets, QtOpenGL, QtGui
from math import ceil
import time
def get_dag():
stack = QtWidgets.QApplication.topLevelWidgets()
while stack:
widget = stack.pop()
if widget.objectName() == 'DAG.1':
for c in widget.children():
if isinstance(c, QtOpenGL.QGLWidget):
return c
stack.extend(c for c in widget.children() if c.isWidgetType())
def grab_dag(dag, path):
dag.updateGL() # This does some funky back and forth but function grabs the wrong thing without it
pix = dag.grabFrameBuffer()
pix.save(path)
class DagCapture(threading.Thread):
def __init__(self, path, margins=20, ignore_right=200):
self.path = path
threading.Thread.__init__(self)
self.margins = margins
self.ignore_right = ignore_right
def run(self):
# Store the current dag size and zoom
original_zoom = nuke.zoom()
original_center = nuke.center()
# Calculate the total size of the DAG
min_x = min([node.xpos() for node in nuke.allNodes()]) - self.margins
min_y = min([node.ypos() for node in nuke.allNodes()]) - self.margins
max_x = max([node.xpos() + node.screenWidth() for node in nuke.allNodes()]) + self.margins
max_y = max([node.ypos() + node.screenHeight() for node in nuke.allNodes()]) + self.margins
# Get the Dag Widget
dag = get_dag()
if not dag:
raise RuntimeError("Couldn't get DAG widget")
# Check the size of the current widget, excluding the right side (because of minimap)
capture_width = dag.width() - self.ignore_right
capture_height = dag.height()
# Calculate the number of tiles required to coveral all
image_width = max_x - min_x
image_height = max_y - min_y
horizontal_tiles = int(ceil(image_width / float(capture_width)))
vertical_tiles = int(ceil(image_height / float(capture_height)))
# Create a pixmap to store the results
pixmap = QtGui.QPixmap(image_width, image_height)
painter = QtGui.QPainter(pixmap)
painter.setCompositionMode(painter.CompositionMode_SourceOver)
# Move the dag so that the top left corner is in the top left corner, screenshot, paste in the pixmap, repeat
for xtile in range(horizontal_tiles):
left = min_x + capture_width * xtile
for ytile in range(vertical_tiles):
top = min_y + capture_height * ytile
nuke.executeInMainThread(nuke.zoom, (1, (left + (capture_width + self.ignore_right) / 2, top + capture_height / 2)))
time.sleep(.5)
nuke.executeInMainThread(grab_dag, (dag, self.path))
time.sleep(.5)
screengrab = QtGui.QImage(self.path)
painter.drawImage(capture_width * xtile, capture_height * ytile, screengrab)
painter.end()
pixmap.save(self.path)
nuke.executeInMainThread(nuke.zoom, (original_zoom, original_center))
print "Capture Complete"
t = DagCapture("C:\\Users\\erwan\\Downloads\\test.png")
t.start()
I'm sure this could be improved upon but, hopefully, it will save you time already!
Edit: I have now improved this code a bit: https://github.com/herronelou/nuke_dag_capture
Upvotes: 2