Merge pull request #2504 from pypeclub/bugfix/OP-2347_Maya-Workdirs--Workspacemel-get-nested-down-when-switching-via-Work-Files-tool

Workfiles Tool can handle when workfile is not created directly in workdir path
This commit is contained in:
Jakub Trllo 2022-02-01 10:57:02 +01:00 committed by GitHub
commit 7d8b36ac7d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 99 additions and 78 deletions

View file

@ -218,12 +218,10 @@ def on_task_changed(*args):
)
def before_workfile_save(workfile_path):
if not workfile_path:
return
workdir = os.path.dirname(workfile_path)
copy_workspace_mel(workdir)
def before_workfile_save(event):
workdir_path = event.workdir_path
if workdir_path:
copy_workspace_mel(workdir_path)
class MayaDirmap(HostDirmap):

View file

@ -1,3 +1,8 @@
from .events import (
BaseEvent,
BeforeWorkfileSave
)
from .attribute_definitions import (
AbtractAttrDef,
UnknownDef,
@ -9,6 +14,9 @@ from .attribute_definitions import (
__all__ = (
"BaseEvent",
"BeforeWorkfileSave",
"AbtractAttrDef",
"UnknownDef",
"NumberDef",

View file

@ -0,0 +1,51 @@
"""Events holding data about specific event."""
# Inherit from 'object' for Python 2 hosts
class BaseEvent(object):
"""Base event object.
Can be used to anything because data are not much specific. Only required
argument is topic which defines why event is happening and may be used for
filtering.
Arg:
topic (str): Identifier of event.
data (Any): Data specific for event. Dictionary is recommended.
"""
_data = {}
def __init__(self, topic, data=None):
self._topic = topic
if data is None:
data = {}
self._data = data
@property
def data(self):
return self._data
@property
def topic(self):
return self._topic
@classmethod
def emit(cls, *args, **kwargs):
"""Create object of event and emit.
Args:
Same args as '__init__' expects which may be class specific.
"""
from avalon import pipeline
obj = cls(*args, **kwargs)
pipeline.emit(obj.topic, [obj])
return obj
class BeforeWorkfileSave(BaseEvent):
"""Before workfile changes event data."""
def __init__(self, filename, workdir):
super(BeforeWorkfileSave, self).__init__("before.workfile.save")
self.filename = filename
self.workdir_path = workdir

View file

@ -11,6 +11,7 @@ from Qt import QtWidgets, QtCore
from avalon import io, api, pipeline
from openpype import style
from openpype.pipeline.lib import BeforeWorkfileSave
from openpype.tools.utils.lib import (
qt_app_context
)
@ -367,7 +368,8 @@ class FilesWidget(QtWidgets.QWidget):
self.template_key = "work"
# This is not root but workfile directory
self.root = None
self._workfiles_root = None
self._workdir_path = None
self.host = api.registered_host()
# Whether to automatically select the latest modified
@ -465,8 +467,9 @@ class FilesWidget(QtWidgets.QWidget):
# This way we can browse it even before we enter it.
if self._asset_id and self._task_name and self._task_type:
session = self._get_session()
self.root = self.host.work_root(session)
self.files_model.set_root(self.root)
self._workdir_path = session["AVALON_WORKDIR"]
self._workfiles_root = self.host.work_root(session)
self.files_model.set_root(self._workfiles_root)
else:
self.files_model.set_root(None)
@ -590,7 +593,7 @@ class FilesWidget(QtWidgets.QWidget):
window = NameWindow(
parent=self,
root=self.root,
root=self._workfiles_root,
anatomy=self.anatomy,
template_key=self.template_key,
session=session
@ -605,7 +608,7 @@ class FilesWidget(QtWidgets.QWidget):
return
src = self._get_selected_filepath()
dst = os.path.join(self.root, work_file)
dst = os.path.join(self._workfiles_root, work_file)
shutil.copy(src, dst)
self.workfile_created.emit(dst)
@ -638,98 +641,59 @@ class FilesWidget(QtWidgets.QWidget):
"filter": ext_filter
}
if Qt.__binding__ in ("PySide", "PySide2"):
kwargs["dir"] = self.root
kwargs["dir"] = self._workfiles_root
else:
kwargs["directory"] = self.root
kwargs["directory"] = self._workfiles_root
work_file = QtWidgets.QFileDialog.getOpenFileName(**kwargs)[0]
if work_file:
self.open_file(work_file)
def on_save_as_pressed(self):
work_file = self.get_filename()
if not work_file:
work_filename = self.get_filename()
if not work_filename:
return
# Initialize work directory if it has not been initialized before
if not os.path.exists(self.root):
log.debug("Initializing Work Directory: %s", self.root)
self.initialize_work_directory()
if not os.path.exists(self.root):
# Failed to initialize Work Directory
log.error(
"Failed to initialize Work Directory: {}".format(self.root)
)
return
file_path = os.path.join(os.path.normpath(self.root), work_file)
pipeline.emit("before.workfile.save", [file_path])
self._enter_session() # Make sure we are in the right session
self.host.save_file(file_path)
# Trigger before save event
BeforeWorkfileSave.emit(work_filename, self._workdir_path)
# Make sure workfiles root is updated
# - this triggers 'workio.work_root(...)' which may change value of
# '_workfiles_root'
self.set_asset_task(
self._asset_id, self._task_name, self._task_type
)
# Create workfiles root folder
if not os.path.exists(self._workfiles_root):
log.debug("Initializing Work Directory: %s", self._workfiles_root)
os.makedirs(self._workfiles_root)
# Update session if context has changed
self._enter_session()
# Prepare full path to workfile and save it
filepath = os.path.join(
os.path.normpath(self._workfiles_root), work_filename
)
self.host.save_file(filepath)
# Create extra folders
create_workdir_extra_folders(
self.root,
self._workdir_path,
api.Session["AVALON_APP"],
self._task_type,
self._task_name,
api.Session["AVALON_PROJECT"]
)
pipeline.emit("after.workfile.save", [file_path])
self.workfile_created.emit(file_path)
# Trigger after save events
pipeline.emit("after.workfile.save", [filepath])
self.workfile_created.emit(filepath)
# Refresh files model
self.refresh()
def on_file_select(self):
self.file_selected.emit(self._get_selected_filepath())
def initialize_work_directory(self):
"""Initialize Work Directory.
This is used when the Work Directory does not exist yet.
This finds the current AVALON_APP_NAME and tries to triggers its
`.toml` initialization step. Note that this will only be valid
whenever `AVALON_APP_NAME` is actually set in the current session.
"""
# Inputs (from the switched session and running app)
session = api.Session.copy()
changes = pipeline.compute_session_changes(
session,
asset=self._get_asset_doc(),
task=self._task_name,
template_key=self.template_key
)
session.update(changes)
# Prepare documents to get workdir data
project_doc = io.find_one({"type": "project"})
asset_doc = io.find_one(
{
"type": "asset",
"name": session["AVALON_ASSET"]
}
)
task_name = session["AVALON_TASK"]
host_name = session["AVALON_APP"]
# Get workdir from collected documents
workdir = get_workdir(project_doc, asset_doc, task_name, host_name)
# Create workdir if does not exist yet
if not os.path.exists(workdir):
os.makedirs(workdir)
# Force a full to the asset as opposed to just self.refresh() so
# that it will actually check again whether the Work directory exists
self.set_asset_task(self._asset_id, self._task_name, self._task_type)
def refresh(self):
"""Refresh listed files for current selection in the interface"""
self.files_model.refresh()