#python #nuke
#python #nuke
Вопрос:
Я преподаватель VFX, и в настоящее время, когда я хочу получить изображения графов узлов для использования в слайдах лекций, я должен сделать график узлов полноэкранным и сделать снимок экрана, но, как вы можете себе представить, с большими сценариями мне приходится уменьшать масштаб настолько, что иногда его невозможно распознать.
Мне кажется, что размер графа узлов Nuke изменяется при увеличении и уменьшении масштаба, это, вероятно, какое-то векторное изображение под капотом. Я ищу способ либо экспортировать это изображение, чтобы я мог получить версию всего графа узлов с более высоким разрешением. Либо как векторное, либо просто растеризованное изображение с более высоким разрешением.
Кто-нибудь знает, может ли быть способ сделать это с помощью Python? Или есть какой-то внешний скрипт, который может это сделать?
Ответ №1:
Я давно этого хотел, но ваш вопрос заставил меня разобраться в этом немного глубже.
Я не верю, что под капотом доступно реальное векторное изображение (это OpenGL под капотом), но я также не понимаю, почему не было бы способа автоматизировать скриншот в полном разрешении.
Я написал скрипт, который проверит размер вашего графа узлов, установит масштабирование на 100% и сделает скриншот каждого фрагмента DAG, а затем автоматически прошьет его. Это занимает несколько секунд, потому что, если я пытаюсь сделать это слишком быстро, все путается из-за многопоточности, поэтому мне пришлось вводить искусственные паузы между каждым снимком экрана.
Вы должны быть в состоянии выполнить приведенный ниже код. Не забудьте указать свой путь в предпоследней строке!
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()
Я уверен, что это можно было бы улучшить, но, надеюсь, это уже сэкономит вам время!
Редактировать: теперь я немного улучшил этот код: https://github.com/herronelou/nuke_dag_capture
Комментарии:
1. Вау, это выглядит потрясающе. Я просто проверяю это сейчас. Я не получаю никаких ошибок, это доходит до печати «захват завершен», но я также не получаю вывод файла. Я изменил путь, по которому вызывается класс DagCapture, в мой локальный каталог загрузки, но ничего не генерируется. Нужно ли мне устанавливать какие-либо другие библиотеки или те, которые вы используете, являются стандартными для Nuke (12.1v2)?
2. Здравствуйте. Я заметил, что у меня нет какой-либо проверки, чтобы узнать, успешно ли выполнено сохранение на диск или нет. Я побежал по неправильному пути, и все это вело себя так, как будто это было успешно, но потом ничего не произошло. Сейчас у меня нет времени его изменять, но добавьте печать перед pix.save(), чтобы увидеть, выводит ли он False или True
3. Он возвращает False …. коррекция, он возвращает x4 False
4. Правильно, так что это будет означать, что он не сохранится. Дважды проверьте путь, если в Windows убедитесь, что вы поставили двойные обратные косые черты, а не одиночные, убедитесь, что каталог существует (это не приведет к созданию каталогов), и что вы вводите имя файла с .png в конце.
5. Я отредактировал ответ, включив ссылку на GitHub, где я улучшил код, который я изначально написал для вас, чтобы добавить пользовательский интерфейс и разрешить пользовательские уровни масштабирования, поля и т.д…