Проблемы PyQt4 / QProcess с Nuke v9 - PullRequest
0 голосов
/ 22 декабря 2019

Проблемы PyQt4 / QProcess с Nuke v9 ...

Я пытаюсь использовать QProcess для запуска рендеров в Nuke на моем рабочем месте. Причина, по которой я хочу использовать QProcess, заключается в том, что я настроил этот диспетчер задач с помощью сообщества в stackoverflow, которое принимает список команд и последовательно запускает его одну за другой, а также позволяет отображать вывод. Вы можете просмотреть вопрос, который я разместил здесь:

Как обновить пользовательский интерфейс с выводом из цикла QProcess без зависания пользовательского интерфейса?

Теперь я пытаюсь в основном запустить рендеринг Nukeчерез этот «Диспетчер задач». Но каждый раз, когда я делаю это, просто выдает ошибку, что QProcess уничтожается во время работы. Я имею в виду, я проверил это с подпроцессом, и это работало совершенно нормально. Поэтому я не уверен, почему рендеры не работают через QProcess.

Поэтому, чтобы провести дополнительное тестирование, я просто написал упрощенную версию дома. Первая проблема, с которой я столкнулся, заключается в том, что PyQt4, по-видимому, нельзя найти в python.exe Nuke. Хотя у меня есть PyQt4 для моей основной версии Python. Однако очевидно, что есть проблема совместимости с моим установленным PyQt4, так как моя основная версия Python - 2.7.12, а версия моего Nuke для Python - 2.7.3. Поэтому я подумал: «Хорошо, тогда я просто установлю PyQt4 внутри моей директории Nuke». Поэтому я взял эту ссылку и установил эту версию PyQt в свой каталог Nuke:

http://sourceforge.net/projects/pyqt/files/PyQt4/PyQt-4.10.3/PyQt4-4.10.3-gpl-Py2.7-Qt4.8.5-x64.exe

Итак, я запускаю свой маленький тест и, похоже, делаю то же самое, что и на своем рабочем месте. где QProcess просто уничтожается. Поэтому я подумал, что, возможно, добавление «waitForFinished ()» может сделать что-то другое, но тогда он выдаст мне эту ошибку, которая гласит:

The procedure entry point ??4QString@@QEAAAEAV0@$$QEAV0@@Z could not be located in the dynamic link library QtCore4.dll

И также выдаст мне эту ошибку:

ImportError: Не удалось загрузить C: \ Program Files \ Nuke9.0v8 \ nuke-9.0.8.dll

Сейчас я не могу больше проводить тестирование дома, и моя студия закрыта дляканикулы. Итак, у меня есть два вопроса, которые я хотел бы задать:

1) Что это за ошибка, которую я вижу о «точке входа в процедуру»? Это происходит только тогда, когда я пытаюсь вызвать что-то в экземпляре QProcess.

2) Почему мой QProcess уничтожается до завершения рендеринга? Почему это не происходит с подпроцессом? Как я могу отправить задание Nuke, получая те же результаты, что и подпроцесс?

Вот мой тестовый код:

import os
import sys
import subprocess
import PyQt4
from PyQt4 import QtCore

class Task:
    def __init__(self, program, args=None):
        self._program = program
        self._args = args or []

    @property
    def program(self):
        return self._program

    @property
    def args(self):
        return self._args


class SequentialManager(QtCore.QObject):
    started = QtCore.pyqtSignal()
    finished = QtCore.pyqtSignal()
    progressChanged = QtCore.pyqtSignal(int)
    dataChanged = QtCore.pyqtSignal(str)
    #^ this is how we can send a signal and can declare what type
    # of information we want to pass with this signal

    def __init__(self, parent=None):
        # super(SequentialManager, self).__init__(parent)
        # QtCore.QObject.__init__(self,parent)
        QtCore.QObject.__init__(self)

        self._progress = 0
        self._tasks = []
        self._process = QtCore.QProcess(self)
        self._process.setProcessChannelMode(QtCore.QProcess.MergedChannels)
        self._process.finished.connect(self._on_finished)
        self._process.readyReadStandardOutput.connect(self._on_readyReadStandardOutput)

    def execute(self, tasks):
        self._tasks = iter(tasks)
        #this 'iter()' method creates an iterator object
        self.started.emit()
        self._progress = 0
        self.progressChanged.emit(self._progress)
        self._execute_next()

    def _execute_next(self):
        try:
            task = next(self._tasks)
        except StopIteration:
            return False
        else:
            print 'starting %s' % task.args
            self._process.start(task.program, task.args)
            return True

    def _on_finished(self):
        self._process_task()
        if not self._execute_next():
            self.finished.emit()

    def _on_readyReadStandardOutput(self):
        output = self._process.readAllStandardOutput()
        result = output.data().decode()
        self.dataChanged.emit(result)

    def _process_task(self):
        self._progress += 1
        self.progressChanged.emit(self._progress)


class outputLog(QtCore.QObject):
    def __init__(self, parent=None, parentWindow=None):
        QtCore.QObject.__init__(self)
        self._manager = SequentialManager(self)

    def startProcess(self, tasks):
        # self._manager.progressChanged.connect(self._progressbar.setValue)
        self._manager.dataChanged.connect(self.on_dataChanged)
        self._manager.started.connect(self.on_started)
        self._manager.finished.connect(self.on_finished)
        self._manager.execute(tasks)

    @QtCore.pyqtSlot()
    def on_started(self):
        print 'process started'

    @QtCore.pyqtSlot()
    def on_finished(self):
        print 'finished'

    @QtCore.pyqtSlot(str)
    def on_dataChanged(self, message):
        if message:
            print message

def nukeTestRender():
    import nuke

    nuke.scriptOpen('D:/PC6/Documents/nukeTestRender/nukeTestRender.nk')

    writeNode = None
    for node in nuke.allNodes():
        if node.Class() == 'Write':
            writeNode = node

    framesList = [1, 20, 30, 40]
    fr = nuke.FrameRanges(framesList)
    # nuke.execute(writeNode, fr)

    for x in range(20):
        print 'random'

def run():
    nukePythonEXE = 'C:/Program Files/Nuke9.0v8/python.exe'
    thisFile = os.path.dirname(os.path.abspath("__file__"))
    print thisFile
    cmd = '"%s" %s renderCheck' %(nukePythonEXE, __file__)
    cmd2 = [__file__, 'renderCheck']
    cmdList = [Task(nukePythonEXE, cmd2)]
    # subprocess.call(cmd, stdin=None, stdout=None, stderr=None, shell=False)
    taskManager = outputLog()
    taskManager.startProcess(cmdList)
    taskManager._manager._process.waitForFinished()

if __name__ == "__main__":
    print sys.argv
    if len(sys.argv) == 1:
        run()
    elif len(sys.argv) == 2:
        nukeTestRender()

1 Ответ

0 голосов
/ 03 января 2020

Мне удалось найти ответ, поэтому я напишу подробности ниже:

В основном, я получал ошибку с установленным PyQt4, потому что он не был совместим с моей версией Nuke,так что, по-видимому, более рекомендуется использовать PySide, включенный в Nuke. Однако исполняемый файл Python для Nuke не может найти PySide, необходимо добавить несколько путей к sys.path:

paths = ['C:\\Program Files\\Nuke9.0v8\\lib\\site-packages,
C:\\Users\\Desktop02\\.nuke',
'C:\\Program Files\\Nuke9.0v8\\plugins',
'C:\\Program Files\\Nuke9.0v8\\pythonextensions\\site-packages\\setuptools-0.6c11-py2.6.egg',
'C:\\Program Files\\Nuke9.0v8\\pythonextensions\\site-packages\\protobuf-2.5.0-py2.6.egg',
'C:\\Program Files\\Nuke9.0v8\\pythonextensions\\site-packages',
'C:\\Program Files\\Nuke9.0v8\\plugins\\modules',
'C:\\Program Files\\Nuke9.0v8\\configs\\Python\\site-packages',
'C:\\Users\\Desktop02\\.nuke\\Python\\site-packages']

for path in paths:
   sys.path.append(path)

Я нашел недостающие пути, открыв Nuke в режиме графического интерфейса и исполняемый файл Python, исравнивая оба файла sys.path, чтобы увидеть, чего не хватало исполняемому файлу Python.

И чтобы ответить на мой главный вопрос: если я вызываю waitForFinished (-1) для экземпляра QProcess, это игнорирует ограничение по умолчанию 30 секунд для этой функции... Ответ пришел из этой темы:

QProcess и shell: Уничтожено, пока процесс еще выполняется

Итак, вот мой полученный рабочий код:

import os
import sys
import subprocess
sysArgs = sys.argv
try:
    import nuke
    from PySide import QtCore
except ImportError:
    raise ImportError('nuke not currently importable')

class Task:
    def __init__(self, program, args=None):
        self._program = program
        self._args = args or []

    @property
    def program(self):
        return self._program

    @property
    def args(self):
        return self._args


class SequentialManager(QtCore.QObject):
    started = QtCore.Signal()
    finished = QtCore.Signal()
    progressChanged = QtCore.Signal(int)
    dataChanged = QtCore.Signal(str)
    #^ this is how we can send a signal and can declare what type
    # of information we want to pass with this signal

    def __init__(self, parent=None):
        # super(SequentialManager, self).__init__(parent)
        # QtCore.QObject.__init__(self,parent)
        QtCore.QObject.__init__(self)

        self._progress = 0
        self._tasks = []
        self._process = QtCore.QProcess(self)
        self._process.setProcessChannelMode(QtCore.QProcess.MergedChannels)
        self._process.finished.connect(self._on_finished)
        self._process.readyReadStandardOutput.connect(self._on_readyReadStandardOutput)

    def execute(self, tasks):
        self._tasks = iter(tasks)
        #this 'iter()' method creates an iterator object
        self.started.emit()
        self._progress = 0
        self.progressChanged.emit(self._progress)
        self._execute_next()

    def _execute_next(self):
        try:
            task = next(self._tasks)
        except StopIteration:
            return False
        else:
            print 'starting %s' % task.args
            self._process.start(task.program, task.args)
            return True

    def _on_finished(self):
        self._process_task()
        if not self._execute_next():
            self.finished.emit()

    def _on_readyReadStandardOutput(self):
        output = self._process.readAllStandardOutput()
        result = output.data().decode()
        self.dataChanged.emit(result)

    def _process_task(self):
        self._progress += 1
        self.progressChanged.emit(self._progress)


class outputLog(QtCore.QObject):
    def __init__(self, parent=None, parentWindow=None):
        QtCore.QObject.__init__(self)
        self._manager = SequentialManager(self)

    def startProcess(self, tasks):
        # self._manager.progressChanged.connect(self._progressbar.setValue)
        self._manager.dataChanged.connect(self.on_dataChanged)
        self._manager.started.connect(self.on_started)
        self._manager.finished.connect(self.on_finished)
        self._manager.execute(tasks)

    @QtCore.Slot()
    def on_started(self):
        print 'process started'

    @QtCore.Slot()
    def on_finished(self):
        print 'finished'

    @QtCore.Slot(str)
    def on_dataChanged(self, message):
        if message:
            print message

def nukeTestRender():
    import nuke

    nuke.scriptOpen('D:/PC6/Documents/nukeTestRender/nukeTestRender.nk')

    writeNode = None
    for node in nuke.allNodes():
        if node.Class() == 'Write':
            writeNode = node

    framesList = [1, 20, 30, 40]
    fr = nuke.FrameRanges(framesList)
    nuke.execute(writeNode, fr)
    # nuke.execute(writeNode, start=1, end=285)

    for x in range(20):
        print 'random'

def run():
    nukePythonEXE = 'C:/Program Files/Nuke9.0v8/python.exe'
    thisFile = os.path.dirname(os.path.abspath("__file__"))
    print thisFile
    cmd = '"%s" %s renderCheck' %(nukePythonEXE, sysArgs[0])
    cmd2 = [sysArgs[0], 'renderCheck']
    cmdList = [Task(nukePythonEXE, cmd2)]
    # subprocess.call(cmd, stdin=None, stdout=None, stderr=None, shell=False)
    taskManager = outputLog()
    taskManager.startProcess(cmdList)
    taskManager._manager._process.waitForFinished(-1)

if __name__ == "__main__":
    print sys.argv
    if len(sysArgs) == 1:
        run()
    elif len(sysArgs) == 2:
        nukeTestRender()

По какой-то причине PySide отказывается загружаться без предварительного импорта модуля nuke. а также есть известная ошибка при импорте nuke, при этом удаляются все аргументы sys.argv, так что они должны быть сохранены где-то сначала до импорта nuke ...

...