added base of tvpaint worker implementation

This commit is contained in:
iLLiCiTiT 2021-10-11 12:07:31 +02:00
parent 9f68ee1aac
commit 6973584ca2
3 changed files with 269 additions and 2 deletions

View file

@ -0,0 +1,14 @@
from .worker_job import (
ExecuteSimpleGeorgeScript,
ExecuteGeorgeScript,
ExecuteGeorgeScriptWithResult,
TVPaintCommands
)
__all__ = (
"ExecuteSimpleGeorgeScript",
"ExecuteGeorgeScript",
"ExecuteGeorgeScriptWithResult",
"TVPaintCommands"
)

View file

@ -0,0 +1,242 @@
import os
import tempfile
import inspect
import copy
from uuid import uuid4
from abc import ABCMeta, abstractmethod, abstractproperty
import six
TMP_FILE_PREFIX = "opw_tvp_"
@six.add_metaclass(ABCMeta)
class BaseCommand:
@abstractproperty
def name(self):
"""Command name (must be unique)."""
pass
def __init__(self, parent, data):
if data is None:
data = {}
else:
data = copy.deepcopy(data)
command_id = data.get("id")
if command_id is None:
command_id = str(uuid4())
data["id"] = command_id
data["command"] = self.name
self._parent = parent
self._result = None
self._command_data = data
self._done = False
@property
def id(self):
return self._command_data["id"]
@property
def parent(self):
return self._parent
@property
def done(self):
return self._done
def set_done(self):
self._done = True
def set_result(self, result):
self._result = result
def result(self):
return {
"id": self.id,
"result": self._result,
"done": self._done
}
def command_data(self):
return copy.deepcopy(self._command_data)
@abstractmethod
def execute(self):
pass
@classmethod
@abstractmethod
def from_existing(cls, data):
pass
def execute_george(self, george_script):
return self.parent.execute_george(george_script)
def execute_george_through_file(self, george_script):
return self.parent.execute_george_through_file(george_script)
class ExecuteSimpleGeorgeScript(BaseCommand):
name = "execute_george_simple"
def __init__(self, parent, script, data=None):
data = data or {}
data["script"] = script
self._script = script
super().__init__(parent, data)
def execute(self):
self._result = self.execute_george(self._script)
@classmethod
def from_existing(cls, parent, data):
script = data.pop("script")
return cls(parent, script, data)
class ExecuteGeorgeScript(BaseCommand):
name = "execute_george_through_file"
def __init__(self, parent, script, data=None):
data = data or {}
data["script"] = script
self._script = script
super().__init__(parent, data)
def execute(self):
self.execute_george_through_file(self._script)
@classmethod
def from_existing(cls, parent, data):
script = data.pop("script")
return cls(parent, script, data)
class ExecuteGeorgeScriptWithResult(BaseCommand):
name = "execute_george_through_file_result"
def __init__(self, parent, script, tmp_file_keys, data=None):
data = data or {}
data["script"] = script
data["tmp_file_keys"] = tmp_file_keys
self._script = script
self._tmp_file_keys = tmp_file_keys
super().__init__(parent, data)
def execute(self):
filepath_by_key = {}
for key in self._tmp_file_keys:
output_file = tempfile.NamedTemporaryFile(
mode="w", prefix=TMP_FILE_PREFIX, suffix=".txt", delete=False
)
output_file.close()
filepath_by_key[key] = output_file.name.replace("\\", "/")
formatted_script = self._script.format(**filepath_by_key)
self.execute_george_through_file(formatted_script)
result = {}
for key, filepath in filepath_by_key.items():
with open(filepath, "r") as stream:
data = stream.read()
result[key] = data
os.remove(filepath)
self._result = result
@classmethod
def from_existing(cls, parent, data):
script = data.pop("script")
tmp_file_keys = data.pop("tmp_file_keys")
return cls(parent, script, tmp_file_keys, data)
class TVPaintCommands:
def __init__(self, workfile, commands=None, communicator=None):
if not commands:
commands = []
self._workfile = workfile
self._commands = []
self._communicator = communicator
self._command_classes_by_name = None
self.commands_from_data(commands)
@property
def communicator(self):
return self._communicator
@property
def classes_by_name(self):
if self._command_classes_by_name is None:
command_classes_by_name = {}
for attr in globals().values():
if (
not inspect.isclass(attr)
or not issubclass(attr, BaseCommand)
or attr is BaseCommand
):
continue
if inspect.isabstract(attr):
print("Skipping abstract class {}".format(attr.__name__))
command_classes_by_name[attr.name] = attr
self._command_classes_by_name = command_classes_by_name
return self._command_classes_by_name
def commands_from_data(self, commands_data):
for command_data in commands_data:
command_name = command_data["command"]
klass = self.classes_by_name[command_name]
command = klass.from_existing(command_data)
self.add_command(command)
def add_command(self, command):
self._commands.append(command)
def _open_workfile(self):
george_script = "tv_LoadProject '\"'\"{}\"'\"'".format(
self._workfile.replace("\\", "/")
)
self.execute_george_through_file(george_script)
def _close_workfile(self):
pass
def execute(self):
self._open_workfile()
for command in self._commands:
command.execute()
command.set_done()
self._close_workfile()
def commands_data(self):
return [
command.command_data()
for command in self._commands
]
def result(self):
return [
command.result()
for command in self._commands
]
def execute_george(self, george_script):
return self.communicator.execute_george(george_script)
def execute_george_through_file(self, george_script):
temporary_file = tempfile.NamedTemporaryFile(
mode="w", prefix=TMP_FILE_PREFIX, suffix=".grg", delete=False
)
temporary_file.write(george_script)
temporary_file.close()
temp_file_path = temporary_file.name.replace("\\", "/")
self.execute_george("tv_runscript {}".format(temp_file_path))
os.remove(temp_file_path)

View file

@ -2,6 +2,7 @@ import signal
import time
import asyncio
from openpype.hosts.tvpaint.worker import TVPaintCommands
from avalon.tvpaint.communication_server import (
BaseCommunicator,
CommunicationWrapper
@ -55,8 +56,18 @@ class WorkerCommunicator(BaseCommunicator):
if job is None:
return
print(job)
self._worker_connection.finish_job()
success = False
message = "Unknown function"
data = None
workfile = job["workfile"]
if job.data.get("function") == "commands":
commands = TVPaintCommands(workfile, job.data["commands"])
commands.execute()
success = True
message = "Executed"
data = commands.result()
self._worker_connection.finish_job(success, message, data)
def main_loop(self):
while self.server_is_running: