Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Publisher: Add thumbnail sources #4042

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
b6a2be5
removed unused imports
iLLiCiTiT Oct 26, 2022
2a91415
Create widget has thumbnail
iLLiCiTiT Oct 26, 2022
2afc531
modified thumbnail to paint the content on own
iLLiCiTiT Oct 26, 2022
89edb5c
use private attribute
iLLiCiTiT Oct 26, 2022
b6a2b51
thumbnail widget can adapt to size changes
iLLiCiTiT Oct 26, 2022
cc4d158
moved thumbnail widget to separated file
iLLiCiTiT Oct 27, 2022
c4432bf
fix variant input style
iLLiCiTiT Oct 27, 2022
bd5121b
traypublisher has REVIEW_EXTENSIONS as set
iLLiCiTiT Oct 27, 2022
48c4c23
added helper function to get fake process id
iLLiCiTiT Oct 27, 2022
8cf23ec
create context can store thumbnails
iLLiCiTiT Oct 27, 2022
d71a1f8
creators can set thumbnail path and allow to pass thumbnail in precre…
iLLiCiTiT Oct 27, 2022
f5c73f5
create context collector also adds thumbnail source to instances
iLLiCiTiT Oct 27, 2022
52c8322
enhanced caching of instance data in tray publisher
iLLiCiTiT Oct 27, 2022
3489a71
settings creator allows thumbnail in precreation
iLLiCiTiT Oct 27, 2022
9c04847
added small comment
iLLiCiTiT Oct 27, 2022
90222b1
publisher has temp dir for thumbnails which is cleared up on publishe…
iLLiCiTiT Oct 27, 2022
7c09494
implemented getter and setters for thumbnails
iLLiCiTiT Oct 27, 2022
82aea56
added forgotter abstract methods
iLLiCiTiT Oct 27, 2022
334ec33
added potential implementation of remote qt publisher controller
iLLiCiTiT Oct 27, 2022
7a21dc8
thumbnail widget is using potential of controller
iLLiCiTiT Oct 27, 2022
25d8139
creator adds thumbnail to creators create
iLLiCiTiT Oct 27, 2022
e537d2d
handle thumbnail changes in subset widget
iLLiCiTiT Oct 27, 2022
eff9b57
CreateItem knows if support drop of thumbnails in create page
iLLiCiTiT Oct 27, 2022
ba84990
thumbnail widdget can disable dropping
iLLiCiTiT Oct 27, 2022
40fbc3a
create widget is handling enabled dropping of thumbnails
iLLiCiTiT Oct 27, 2022
f18e5c5
moved extract thumbnail from tray publisher to global plugins
iLLiCiTiT Oct 28, 2022
b42346e
use faster checks first
iLLiCiTiT Oct 28, 2022
7a18d3d
removed hosts filter
iLLiCiTiT Oct 28, 2022
fec7df1
use colors from style data
iLLiCiTiT Oct 28, 2022
f7dec32
draw disabled drop with slashed circle
iLLiCiTiT Oct 28, 2022
4cf0fe9
disable drop when no instance is selected
iLLiCiTiT Oct 28, 2022
b3ca4ab
allow the drop if instances are selected
iLLiCiTiT Oct 28, 2022
6c80a7f
context thumbnail is not used directly in extract thumbnail from sour…
iLLiCiTiT Oct 28, 2022
831023b
integrate thumbnail can use context thumbnail (if is available)
iLLiCiTiT Oct 28, 2022
b8719c6
don't remove last path
iLLiCiTiT Oct 28, 2022
5861480
add missing argument
iLLiCiTiT Oct 28, 2022
1b040af
removed unused import
iLLiCiTiT Oct 28, 2022
7647176
hide thumbnail widget if drop is disabled
iLLiCiTiT Oct 31, 2022
0a7c203
draw dashes if user can drop thumbnails
iLLiCiTiT Oct 31, 2022
1c604ee
define max thumbnails in class variable
iLLiCiTiT Oct 31, 2022
72e729f
fix mapping on multiselection
iLLiCiTiT Oct 31, 2022
c9d255c
separated thumbnail painter widget and thumbnail widget to be able ha…
iLLiCiTiT Oct 31, 2022
1f2ad7c
don't use alpha on button hover color
iLLiCiTiT Oct 31, 2022
44f6d1c
set render hint for paint image with color
iLLiCiTiT Oct 31, 2022
1d827f9
draw backgroup only final image
iLLiCiTiT Oct 31, 2022
53c3ae8
added helper function to draw checker
iLLiCiTiT Oct 31, 2022
2d4e13a
implemented new pixmap button which is not pushbutton based
iLLiCiTiT Oct 31, 2022
2ed10e4
separated painting into smaller methods
iLLiCiTiT Oct 31, 2022
01279cc
change thumbnail bg color
iLLiCiTiT Oct 31, 2022
bbaf811
added image for clear thumbnail button and use pixmap button
iLLiCiTiT Oct 31, 2022
33178d1
add different styles for button
iLLiCiTiT Oct 31, 2022
78a725c
chnage the object name
iLLiCiTiT Oct 31, 2022
bebb903
change type of 'IMAGE_EXTENSIONS' and 'VIDEO_EXTENSIONS' to set
iLLiCiTiT Nov 1, 2022
32b91ef
Integrate thumbnails plugin is context plugin without family filters
iLLiCiTiT Nov 1, 2022
7df622d
fix thumbnail refreshing
iLLiCiTiT Nov 1, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
45 changes: 28 additions & 17 deletions openpype/hosts/traypublisher/api/plugin.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import collections

from openpype.lib.attribute_definitions import FileDef
from openpype.pipeline.create import (
Creator,
HiddenCreator,
CreatedInstance
CreatedInstance,
PRE_CREATE_THUMBNAIL_KEY,
)

from .pipeline import (
Expand All @@ -14,7 +17,7 @@
from openpype.lib.transcoding import IMAGE_EXTENSIONS, VIDEO_EXTENSIONS


REVIEW_EXTENSIONS = IMAGE_EXTENSIONS + VIDEO_EXTENSIONS
REVIEW_EXTENSIONS = set(IMAGE_EXTENSIONS) | set(VIDEO_EXTENSIONS)


def _cache_and_get_instances(creator):
Expand All @@ -29,21 +32,24 @@ def _cache_and_get_instances(creator):

shared_key = "openpype.traypublisher.instances"
if shared_key not in creator.collection_shared_data:
creator.collection_shared_data[shared_key] = list_instances()
instances_by_creator_id = collections.defaultdict(list)
for instance_data in list_instances():
creator_id = instance_data.get("creator_identifier")
instances_by_creator_id[creator_id].append(instance_data)
creator.collection_shared_data[shared_key] = instances_by_creator_id
return creator.collection_shared_data[shared_key]


class HiddenTrayPublishCreator(HiddenCreator):
host_name = "traypublisher"

def collect_instances(self):
for instance_data in _cache_and_get_instances(self):
creator_id = instance_data.get("creator_identifier")
if creator_id == self.identifier:
instance = CreatedInstance.from_existing(
instance_data, self
)
self._add_instance_to_context(instance)
instance_data_by_identifier = _cache_and_get_instances(self)
for instance_data in instance_data_by_identifier[self.identifier]:
instance = CreatedInstance.from_existing(
instance_data, self
)
self._add_instance_to_context(instance)

def update_instances(self, update_list):
update_instances(update_list)
Expand Down Expand Up @@ -74,13 +80,12 @@ class TrayPublishCreator(Creator):
host_name = "traypublisher"

def collect_instances(self):
for instance_data in _cache_and_get_instances(self):
creator_id = instance_data.get("creator_identifier")
if creator_id == self.identifier:
instance = CreatedInstance.from_existing(
instance_data, self
)
self._add_instance_to_context(instance)
instance_data_by_identifier = _cache_and_get_instances(self)
for instance_data in instance_data_by_identifier[self.identifier]:
instance = CreatedInstance.from_existing(
instance_data, self
)
self._add_instance_to_context(instance)

def update_instances(self, update_list):
update_instances(update_list)
Expand Down Expand Up @@ -110,18 +115,24 @@ def _store_new_instance(self, new_instance):

class SettingsCreator(TrayPublishCreator):
create_allow_context_change = True
create_allow_thumbnail = True

extensions = []

def create(self, subset_name, data, pre_create_data):
# Pass precreate data to creator attributes
thumbnail_path = pre_create_data.pop(PRE_CREATE_THUMBNAIL_KEY, None)

data["creator_attributes"] = pre_create_data
data["settings_creator"] = True
# Create new instance
new_instance = CreatedInstance(self.family, subset_name, data, self)

self._store_new_instance(new_instance)

if thumbnail_path:
self.set_instance_thumbnail_path(new_instance.id, thumbnail_path)

def get_instance_attr_defs(self):
return [
FileDef(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ def process(self, instance):
if creator_attributes["add_review_family"]:
repre["tags"].append("review")
instance.data["families"].append("review")
instance.data["thumbnailSource"] = file_url
if not instance.data.get("thumbnailSource"):
instance.data["thumbnailSource"] = file_url

instance.data["source"] = file_url

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,8 @@ def _create_review_representation(
if "review" not in instance.data["families"]:
instance.data["families"].append("review")

instance.data["thumbnailSource"] = first_filepath
if not instance.data.get("thumbnailSource"):
instance.data["thumbnailSource"] = first_filepath

review_representation["tags"].append("review")
self.log.debug("Representation {} was marked for review. {}".format(
Expand Down
8 changes: 4 additions & 4 deletions openpype/lib/transcoding.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
# Regex to parse array attributes
ARRAY_TYPE_REGEX = re.compile(r"^(int|float|string)\[\d+\]$")

IMAGE_EXTENSIONS = [
IMAGE_EXTENSIONS = {
".ani", ".anim", ".apng", ".art", ".bmp", ".bpg", ".bsave", ".cal",
".cin", ".cpc", ".cpt", ".dds", ".dpx", ".ecw", ".exr", ".fits",
".flic", ".flif", ".fpx", ".gif", ".hdri", ".hevc", ".icer",
Expand All @@ -54,15 +54,15 @@
".rgbe", ".logluv", ".tiff", ".sgi", ".tga", ".tiff", ".tiff/ep",
".tiff/it", ".ufo", ".ufp", ".wbmp", ".webp", ".xbm", ".xcf",
".xpm", ".xwd"
]
}

VIDEO_EXTENSIONS = [
VIDEO_EXTENSIONS = {
".3g2", ".3gp", ".amv", ".asf", ".avi", ".drc", ".f4a", ".f4b",
".f4p", ".f4v", ".flv", ".gif", ".gifv", ".m2v", ".m4p", ".m4v",
".mkv", ".mng", ".mov", ".mp2", ".mp4", ".mpe", ".mpeg", ".mpg",
".mpv", ".mxf", ".nsv", ".ogg", ".ogv", ".qt", ".rm", ".rmvb",
".roq", ".svi", ".vob", ".webm", ".wmv", ".yuv"
]
}


def get_transcode_temp_directory():
Expand Down
1 change: 1 addition & 0 deletions openpype/pipeline/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
register_host,
registered_host,
deregister_host,
get_process_id,
)
install = install_host
uninstall = uninstall_host
Expand Down
17 changes: 17 additions & 0 deletions openpype/pipeline/context_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import types
import logging
import platform
import uuid

import pyblish.api
from pyblish.lib import MessageHandler
Expand Down Expand Up @@ -37,6 +38,7 @@


_is_installed = False
_process_id = None
_registered_root = {"_": ""}
_registered_host = {"_": None}
# Keep modules manager (and it's modules) in memory
Expand Down Expand Up @@ -546,3 +548,18 @@ def change_current_context(asset_doc, task_name, template_key=None):
emit_event("taskChanged", data)

return changes


def get_process_id():
"""Fake process id created on demand using uuid.

Can be used to create process specific folders in temp directory.

Returns:
str: Process id.
"""

global _process_id
if _process_id is None:
_process_id = str(uuid.uuid4())
return _process_id
2 changes: 2 additions & 0 deletions openpype/pipeline/create/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from .constants import (
SUBSET_NAME_ALLOWED_SYMBOLS,
DEFAULT_SUBSET_TEMPLATE,
PRE_CREATE_THUMBNAIL_KEY,
)

from .subset_name import (
Expand Down Expand Up @@ -40,6 +41,7 @@
__all__ = (
"SUBSET_NAME_ALLOWED_SYMBOLS",
"DEFAULT_SUBSET_TEMPLATE",
"PRE_CREATE_THUMBNAIL_KEY",

"TaskNotSetError",
"get_subset_name",
Expand Down
2 changes: 2 additions & 0 deletions openpype/pipeline/create/constants.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
SUBSET_NAME_ALLOWED_SYMBOLS = "a-zA-Z0-9_."
DEFAULT_SUBSET_TEMPLATE = "{family}{Variant}"
PRE_CREATE_THUMBNAIL_KEY = "thumbnail_source"


__all__ = (
"SUBSET_NAME_ALLOWED_SYMBOLS",
"DEFAULT_SUBSET_TEMPLATE",
"PRE_CREATE_THUMBNAIL_KEY",
)
26 changes: 26 additions & 0 deletions openpype/pipeline/create/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -1077,6 +1077,8 @@ def __init__(
# Shared data across creators during collection phase
self._collection_shared_data = None

self.thumbnail_paths_by_instance_id = {}

# Trigger reset if was enabled
if reset:
self.reset(discover_publish_plugins)
Expand Down Expand Up @@ -1146,6 +1148,29 @@ def reset(self, discover_publish_plugins=True):

self.reset_finalization()

def refresh_thumbnails(self):
"""Cleanup thumbnail paths.

Remove all thumbnail filepaths that are empty or lead to files which
does not exists or of instances that are not available anymore.
"""

invalid = set()
for instance_id, path in self.thumbnail_paths_by_instance_id.items():
instance_available = True
if instance_id is not None:
instance_available = instance_id in self._instances_by_id

if (
not instance_available
or not path
or not os.path.exists(path)
):
invalid.add(instance_id)

for instance_id in invalid:
self.thumbnail_paths_by_instance_id.pop(instance_id)

def reset_preparation(self):
"""Prepare attributes that must be prepared/cleaned before reset."""

Expand All @@ -1157,6 +1182,7 @@ def reset_finalization(self):

# Stop access to collection shared data
self._collection_shared_data = None
self.refresh_thumbnails()

def reset_avalon_context(self):
"""Give ability to reset avalon context.
Expand Down
14 changes: 14 additions & 0 deletions openpype/pipeline/create/creator_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,13 @@ def collection_shared_data(self):

return self.create_context.collection_shared_data

def set_instance_thumbnail_path(self, instance_id, thumbnail_path=None):
"""Set path to thumbnail for instance."""

self.create_context.thumbnail_paths_by_instance_id[instance_id] = (
thumbnail_path
)


class Creator(BaseCreator):
"""Creator that has more information for artist to show in UI.
Expand All @@ -468,6 +475,13 @@ class Creator(BaseCreator):
# - in some cases it may confuse artists because it would not be used
# e.g. for buld creators
create_allow_context_change = True
# A thumbnail can be passed in precreate attributes
# - if is set to True is should expect that a thumbnail path under key
# PRE_CREATE_THUMBNAIL_KEY can be sent in data with precreate data
# - is disabled by default because the feature was added in later stages
# and creators who would not expect PRE_CREATE_THUMBNAIL_KEY could
# cause issues with instance data
create_allow_thumbnail = False

# Precreate attribute definitions showed before creation
# - similar to instance attribute definitions
Expand Down
14 changes: 3 additions & 11 deletions openpype/pipeline/workfile/lock_workfile.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import os
import json
from uuid import uuid4
from openpype.lib import Logger, filter_profiles
from openpype.lib.pype_info import get_workstation_info
from openpype.settings import get_project_settings
from openpype.pipeline import get_process_id


def _read_lock_file(lock_filepath):
Expand Down Expand Up @@ -37,7 +37,7 @@ def is_workfile_locked_for_current_process(filepath):

lock_filepath = _get_lock_file(filepath)
data = _read_lock_file(lock_filepath)
return data["process_id"] == _get_process_id()
return data["process_id"] == get_process_id()


def delete_workfile_lock(filepath):
Expand All @@ -49,7 +49,7 @@ def delete_workfile_lock(filepath):
def create_workfile_lock(filepath):
lock_filepath = _get_lock_file(filepath)
info = get_workstation_info()
info["process_id"] = _get_process_id()
info["process_id"] = get_process_id()
with open(lock_filepath, "w") as stream:
json.dump(info, stream)

Expand All @@ -59,14 +59,6 @@ def remove_workfile_lock(filepath):
delete_workfile_lock(filepath)


def _get_process_id():
process_id = os.environ.get("OPENPYPE_PROCESS_ID")
if not process_id:
process_id = str(uuid4())
os.environ["OPENPYPE_PROCESS_ID"] = process_id
return process_id


def is_workfile_lock_enabled(host_name, project_name, project_setting=None):
if project_setting is None:
project_setting = get_project_settings(project_name)
Expand Down
27 changes: 24 additions & 3 deletions openpype/plugins/publish/collect_from_create_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,28 @@ def process(self, context):
if not create_context:
return

thumbnail_paths_by_instance_id = (
create_context.thumbnail_paths_by_instance_id
)
context.data["thumbnailSource"] = (
thumbnail_paths_by_instance_id.get(None)
)

project_name = create_context.project_name
if project_name:
context.data["projectName"] = project_name

for created_instance in create_context.instances:
instance_data = created_instance.data_to_store()
if instance_data["active"]:
thumbnail_path = thumbnail_paths_by_instance_id.get(
created_instance.id
)
self.create_instance(
context, instance_data, created_instance.transient_data
context,
instance_data,
created_instance.transient_data,
thumbnail_path
)

# Update global data to context
Expand All @@ -39,7 +53,13 @@ def process(self, context):
legacy_io.Session[key] = value
os.environ[key] = value

def create_instance(self, context, in_data, transient_data):
def create_instance(
self,
context,
in_data,
transient_data,
thumbnail_path
):
subset = in_data["subset"]
# If instance data already contain families then use it
instance_families = in_data.get("families") or []
Expand All @@ -53,7 +73,8 @@ def create_instance(self, context, in_data, transient_data):
"name": subset,
"family": in_data["family"],
"families": instance_families,
"representations": []
"representations": [],
"thumbnailSource": thumbnail_path
})
for key, value in in_data.items():
if key not in instance.data:
Expand Down