Merge pull request #3357 from pypeclub/feature/OP-3420_Trigger-callback-on-workfile-open

Hosts: More options for in-host callbacks
This commit is contained in:
Jakub Trllo 2022-06-20 13:17:47 +02:00 committed by GitHub
commit c782c61c3c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 126 additions and 20 deletions

View file

@ -797,8 +797,14 @@ def update_current_task(task=None, asset=None, app=None, template_key=None):
else:
os.environ[key] = value
data = changes.copy()
# Convert env keys to human readable keys
data["project_name"] = legacy_io.Session["AVALON_PROJECT"]
data["asset_name"] = legacy_io.Session["AVALON_ASSET"]
data["task_name"] = legacy_io.Session["AVALON_TASK"]
# Emit session change
emit_event("taskChanged", changes.copy())
emit_event("taskChanged", data)
return changes

View file

@ -463,6 +463,25 @@ class OpenPypeModule:
pass
def on_host_install(self, host, host_name, project_name):
"""Host was installed which gives option to handle in-host logic.
It is a good option to register in-host event callbacks which are
specific for the module. The module is kept in memory for rest of
the process.
Arguments may change in future. E.g. 'host_name' should be possible
to receive from 'host' object.
Args:
host (ModuleType): Access to installed/registered host object.
host_name (str): Name of host.
project_name (str): Project name which is main part of host
context.
"""
pass
def cli(self, module_click_group):
"""Add commands to click group.

View file

@ -7,6 +7,7 @@ from openpype_interfaces import (
ITrayService,
ILaunchHookPaths
)
from openpype.lib.events import register_event_callback
from openpype.pipeline import AvalonMongoDB
from .exceptions import InvalidContextError
@ -422,3 +423,20 @@ class TimersManager(OpenPypeModule, ITrayService, ILaunchHookPaths):
}
return requests.post(rest_api_url, json=data)
def on_host_install(self, host, host_name, project_name):
self.log.debug("Installing task changed callback")
register_event_callback("taskChanged", self._on_host_task_change)
def _on_host_task_change(self, event):
project_name = event["project_name"]
asset_name = event["asset_name"]
task_name = event["task_name"]
self.log.debug((
"Sending message that timer should change to"
" Project: {} Asset: {} Task: {}"
).format(project_name, asset_name, task_name))
self.start_timer_with_webserver(
project_name, asset_name, task_name, self.log
)

View file

@ -16,9 +16,7 @@ from openpype.modules import load_modules, ModulesManager
from openpype.settings import get_project_settings
from openpype.lib import (
Anatomy,
register_event_callback,
filter_pyblish_plugins,
change_timer_to_current_context,
)
from . import (
@ -33,6 +31,9 @@ from . import (
_is_installed = False
_registered_root = {"_": ""}
_registered_host = {"_": None}
# Keep modules manager (and it's modules) in memory
# - that gives option to register modules' callbacks
_modules_manager = None
log = logging.getLogger(__name__)
@ -44,6 +45,23 @@ PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish")
LOAD_PATH = os.path.join(PLUGINS_DIR, "load")
def _get_modules_manager():
"""Get or create modules manager for host installation.
This is not meant for public usage. Reason is to keep modules
in memory of process to be able trigger their event callbacks if they
need any.
Returns:
ModulesManager: Manager wrapping discovered modules.
"""
global _modules_manager
if _modules_manager is None:
_modules_manager = ModulesManager()
return _modules_manager
def register_root(path):
"""Register currently active root"""
log.info("Registering root: %s" % path)
@ -74,6 +92,7 @@ def install_host(host):
_is_installed = True
legacy_io.install()
modules_manager = _get_modules_manager()
missing = list()
for key in ("AVALON_PROJECT", "AVALON_ASSET"):
@ -95,8 +114,6 @@ def install_host(host):
register_host(host)
register_event_callback("taskChanged", _on_task_change)
def modified_emit(obj, record):
"""Method replacing `emit` in Pyblish's MessageHandler."""
record.msg = record.getMessage()
@ -112,7 +129,14 @@ def install_host(host):
else:
pyblish.api.register_target("local")
install_openpype_plugins()
project_name = os.environ.get("AVALON_PROJECT")
host_name = os.environ.get("AVALON_APP")
# Give option to handle host installation
for module in modules_manager.get_enabled_modules():
module.on_host_install(host, host_name, project_name)
install_openpype_plugins(project_name, host_name)
def install_openpype_plugins(project_name=None, host_name=None):
@ -124,7 +148,7 @@ def install_openpype_plugins(project_name=None, host_name=None):
pyblish.api.register_discovery_filter(filter_pyblish_plugins)
register_loader_plugin_path(LOAD_PATH)
modules_manager = ModulesManager()
modules_manager = _get_modules_manager()
publish_plugin_dirs = modules_manager.collect_plugin_paths()["publish"]
for path in publish_plugin_dirs:
pyblish.api.register_plugin_path(path)
@ -168,10 +192,6 @@ def install_openpype_plugins(project_name=None, host_name=None):
register_inventory_action(path)
def _on_task_change():
change_timer_to_current_context()
def uninstall_host():
"""Undo all of what `install()` did"""
host = registered_host()

View file

@ -1,6 +1,7 @@
import os
import logging
import shutil
import copy
import Qt
from Qt import QtWidgets, QtCore
@ -90,7 +91,9 @@ class FilesWidget(QtWidgets.QWidget):
self._task_type = None
# Pype's anatomy object for current project
self.anatomy = Anatomy(legacy_io.Session["AVALON_PROJECT"])
project_name = legacy_io.Session["AVALON_PROJECT"]
self.anatomy = Anatomy(project_name)
self.project_name = project_name
# Template key used to get work template from anatomy templates
self.template_key = "work"
@ -98,6 +101,7 @@ class FilesWidget(QtWidgets.QWidget):
self._workfiles_root = None
self._workdir_path = None
self.host = registered_host()
self.host_name = os.environ["AVALON_APP"]
# Whether to automatically select the latest modified
# file on a refresh of the files model.
@ -385,8 +389,9 @@ class FilesWidget(QtWidgets.QWidget):
return None
if self._asset_doc is None:
project_name = legacy_io.active_project()
self._asset_doc = get_asset_by_id(project_name, self._asset_id)
self._asset_doc = get_asset_by_id(
self.project_name, self._asset_id
)
return self._asset_doc
@ -396,8 +401,8 @@ class FilesWidget(QtWidgets.QWidget):
session = legacy_io.Session.copy()
self.template_key = get_workfile_template_key(
self._task_type,
session["AVALON_APP"],
project_name=session["AVALON_PROJECT"]
self.host_name,
project_name=self.project_name
)
changes = compute_session_changes(
session,
@ -430,6 +435,21 @@ class FilesWidget(QtWidgets.QWidget):
template_key=self.template_key
)
def _get_event_context_data(self):
asset_id = None
asset_name = None
asset_doc = self._get_asset_doc()
if asset_doc:
asset_id = asset_doc["_id"]
asset_name = asset_doc["name"]
return {
"project_name": self.project_name,
"asset_id": asset_id,
"asset_name": asset_name,
"task_name": self._task_name,
"host_name": self.host_name
}
def open_file(self, filepath):
host = self.host
if host.has_unsaved_changes():
@ -453,8 +473,21 @@ class FilesWidget(QtWidgets.QWidget):
# Save current scene, continue to open file
host.save_file(current_file)
event_data_before = self._get_event_context_data()
event_data_before["filepath"] = filepath
event_data_after = copy.deepcopy(event_data_before)
emit_event(
"workfile.open.before",
event_data_before,
source="workfiles.tool"
)
self._enter_session()
host.open_file(filepath)
emit_event(
"workfile.open.after",
event_data_after,
source="workfiles.tool"
)
self.file_opened.emit()
def save_changes_prompt(self):
@ -567,9 +600,14 @@ class FilesWidget(QtWidgets.QWidget):
src_path = self._get_selected_filepath()
# Trigger before save event
event_data_before = self._get_event_context_data()
event_data_before.update({
"filename": work_filename,
"workdir_path": self._workdir_path
})
emit_event(
"workfile.save.before",
{"filename": work_filename, "workdir_path": self._workdir_path},
event_data_before,
source="workfiles.tool"
)
@ -602,15 +640,20 @@ class FilesWidget(QtWidgets.QWidget):
# Create extra folders
create_workdir_extra_folders(
self._workdir_path,
legacy_io.Session["AVALON_APP"],
self.host_name,
self._task_type,
self._task_name,
legacy_io.Session["AVALON_PROJECT"]
self.project_name
)
event_data_after = self._get_event_context_data()
event_data_after.update({
"filename": work_filename,
"workdir_path": self._workdir_path
})
# Trigger after save events
emit_event(
"workfile.save.after",
{"filename": work_filename, "workdir_path": self._workdir_path},
event_data_after,
source="workfiles.tool"
)