[Automated] Merged develop into main

This commit is contained in:
pypebot 2022-09-21 06:11:59 +02:00 committed by GitHub
commit 519fe2b4d0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 311 additions and 57 deletions

View file

@ -5,6 +5,7 @@ import maya.mel as mel
import six
import sys
from openpype.lib import Logger
from openpype.api import (
get_project_settings,
get_current_project_settings
@ -38,6 +39,8 @@ class RenderSettings(object):
"underscore": "_"
}
log = Logger.get_logger("RenderSettings")
@classmethod
def get_image_prefix_attr(cls, renderer):
return cls._image_prefix_nodes[renderer]
@ -133,20 +136,7 @@ class RenderSettings(object):
cmds.setAttr(
"defaultArnoldDriver.mergeAOVs", multi_exr)
# Passes additional options in from the schema as a list
# but converts it to a dictionary because ftrack doesn't
# allow fullstops in custom attributes. Then checks for
# type of MtoA attribute passed to adjust the `setAttr`
# command accordingly.
self._additional_attribs_setter(additional_options)
for item in additional_options:
attribute, value = item
if (cmds.getAttr(str(attribute), type=True)) == "long":
cmds.setAttr(str(attribute), int(value))
elif (cmds.getAttr(str(attribute), type=True)) == "bool":
cmds.setAttr(str(attribute), int(value), type = "Boolean") # noqa
elif (cmds.getAttr(str(attribute), type=True)) == "string":
cmds.setAttr(str(attribute), str(value), type = "string") # noqa
reset_frame_range()
def _set_redshift_settings(self, width, height):
@ -230,12 +220,20 @@ class RenderSettings(object):
cmds.setAttr("defaultRenderGlobals.extensionPadding", 4)
def _additional_attribs_setter(self, additional_attribs):
print(additional_attribs)
for item in additional_attribs:
attribute, value = item
if (cmds.getAttr(str(attribute), type=True)) == "long":
cmds.setAttr(str(attribute), int(value))
elif (cmds.getAttr(str(attribute), type=True)) == "bool":
cmds.setAttr(str(attribute), int(value)) # noqa
elif (cmds.getAttr(str(attribute), type=True)) == "string":
cmds.setAttr(str(attribute), str(value), type = "string") # noqa
attribute = str(attribute) # ensure str conversion from settings
attribute_type = cmds.getAttr(attribute, type=True)
if attribute_type in {"long", "bool"}:
cmds.setAttr(attribute, int(value))
elif attribute_type == "string":
cmds.setAttr(attribute, str(value), type="string")
elif attribute_type in {"double", "doubleAngle", "doubleLinear"}:
cmds.setAttr(attribute, float(value))
else:
self.log.error(
"Attribute {attribute} can not be set due to unsupported "
"type: {attribute_type}".format(
attribute=attribute,
attribute_type=attribute_type)
)

View file

@ -13,7 +13,7 @@ from maya import cmds # noqa
import pyblish.api
from openpype.lib import source_hash
from openpype.lib import source_hash, run_subprocess
from openpype.pipeline import legacy_io, publish
from openpype.hosts.maya.api import lib
@ -68,7 +68,7 @@ def find_paths_by_hash(texture_hash):
return legacy_io.distinct(key, {"type": "version"})
def maketx(source, destination, *args):
def maketx(source, destination, args, logger):
"""Make `.tx` using `maketx` with some default settings.
The settings are based on default as used in Arnold's
@ -79,7 +79,8 @@ def maketx(source, destination, *args):
Args:
source (str): Path to source file.
destination (str): Writing destination path.
*args: Additional arguments for `maketx`.
args (list): Additional arguments for `maketx`.
logger (logging.Logger): Logger to log messages to.
Returns:
str: Output of `maketx` command.
@ -94,7 +95,7 @@ def maketx(source, destination, *args):
"OIIO tool not found in {}".format(maketx_path))
raise AssertionError("OIIO tool not found")
cmd = [
subprocess_args = [
maketx_path,
"-v", # verbose
"-u", # update mode
@ -103,27 +104,20 @@ def maketx(source, destination, *args):
"--checknan",
# use oiio-optimized settings for tile-size, planarconfig, metadata
"--oiio",
"--filter lanczos3",
escape_space(source)
"--filter", "lanczos3",
source
]
cmd.extend(args)
cmd.extend(["-o", escape_space(destination)])
subprocess_args.extend(args)
subprocess_args.extend(["-o", destination])
cmd = " ".join(cmd)
cmd = " ".join(subprocess_args)
logger.debug(cmd)
CREATE_NO_WINDOW = 0x08000000 # noqa
kwargs = dict(args=cmd, stderr=subprocess.STDOUT)
if sys.platform == "win32":
kwargs["creationflags"] = CREATE_NO_WINDOW
try:
out = subprocess.check_output(**kwargs)
except subprocess.CalledProcessError as exc:
print(exc)
import traceback
traceback.print_exc()
out = run_subprocess(subprocess_args)
except Exception:
logger.error("Maketx converion failed", exc_info=True)
raise
return out
@ -524,15 +518,17 @@ class ExtractLook(publish.Extractor):
if do_maketx and ext != ".tx":
# Produce .tx file in staging if source file is not .tx
converted = os.path.join(staging, "resources", fname + ".tx")
additional_args = [
"--sattrib",
"sourceHash",
texture_hash
]
if linearize:
self.log.info("tx: converting sRGB -> linear")
colorconvert = "--colorconvert sRGB linear"
else:
colorconvert = ""
additional_args.extend(["--colorconvert", "sRGB", "linear"])
config_path = get_ocio_config_path("nuke-default")
color_config = "--colorconfig {0}".format(config_path)
additional_args.extend(["--colorconfig", config_path])
# Ensure folder exists
if not os.path.exists(os.path.dirname(converted)):
os.makedirs(os.path.dirname(converted))
@ -541,12 +537,8 @@ class ExtractLook(publish.Extractor):
maketx(
filepath,
converted,
# Include `source-hash` as string metadata
"--sattrib",
"sourceHash",
escape_space(texture_hash),
colorconvert,
color_config
additional_args,
self.log
)
return converted, COPY, texture_hash

View file

@ -0,0 +1,55 @@
"""Collects published version of workfile and increments it.
For synchronization of published image and workfile version it is required
to store workfile version from workfile file name in context.data["version"].
In remote publishing this name is unreliable (artist might not follow naming
convention etc.), last published workfile version for particular workfile
subset is used instead.
This plugin runs only in remote publishing (eg. Webpublisher).
Requires:
context.data["assetEntity"]
Provides:
context["version"] - incremented latest published workfile version
"""
import pyblish.api
from openpype.client import get_last_version_by_subset_name
class CollectPublishedVersion(pyblish.api.ContextPlugin):
"""Collects published version of workfile and increments it."""
order = pyblish.api.CollectorOrder + 0.190
label = "Collect published version"
hosts = ["photoshop"]
targets = ["remotepublish"]
def process(self, context):
workfile_subset_name = None
for instance in context:
if instance.data["family"] == "workfile":
workfile_subset_name = instance.data["subset"]
break
if not workfile_subset_name:
self.log.warning("No workfile instance found, "
"synchronization of version will not work.")
return
project_name = context.data["projectName"]
asset_doc = context.data["assetEntity"]
asset_id = asset_doc["_id"]
version_doc = get_last_version_by_subset_name(project_name,
workfile_subset_name,
asset_id)
version_int = 1
if version_doc:
version_int += int(version_doc["name"])
self.log.debug(f"Setting {version_int} to context.")
context.data["version"] = version_int

View file

@ -0,0 +1,24 @@
import pyblish.api
class CollectVersion(pyblish.api.InstancePlugin):
"""Collect version for publishable instances.
Used to synchronize version from workfile to all publishable instances:
- image (manually created or color coded)
- review
Dev comment:
Explicit collector created to control this from single place and not from
3 different.
"""
order = pyblish.api.CollectorOrder + 0.200
label = 'Collect Version'
hosts = ["photoshop"]
families = ["image", "review"]
def process(self, instance):
workfile_version = instance.context.data["version"]
self.log.debug(f"Applying version {workfile_version}")
instance.data["version"] = workfile_version

View file

@ -577,8 +577,11 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin):
return
except OSError as exc:
# re-raise exception if different than cross drive path
if exc.errno != errno.EXDEV:
# re-raise exception if different than
# EXDEV - cross drive path
# EINVAL - wrong format, must be NTFS
self.log.debug("Hardlink failed with errno:'{}'".format(exc.errno))
if exc.errno not in [errno.EXDEV, errno.EINVAL]:
raise
shutil.copy(src_path, dst_path)

View file

@ -187,7 +187,7 @@ class PypeCommands:
(to choose validator for example)
"""
from openpype.hosts.webpublisher.cli_functions import (
from openpype.hosts.webpublisher.publish_functions import (
cli_publish_from_app
)

View file

@ -49,7 +49,7 @@
"vray_renderer": {
"image_prefix": "maya/<scene>/<Layer>/<Layer>",
"engine": "1",
"image_format": "png",
"image_format": "exr",
"aov_list": [],
"additional_options": []
},
@ -57,7 +57,7 @@
"image_prefix": "maya/<Scene>/<RenderLayer>/<RenderLayer>",
"primary_gi_engine": "0",
"secondary_gi_engine": "0",
"image_format": "iff",
"image_format": "exr",
"multilayer_exr": true,
"force_combine": true,
"aov_list": [],

View file

@ -15,6 +15,9 @@
"CollectInstances": {
"flatten_subset_template": ""
},
"CollectVersion": {
"enabled": false
},
"ValidateContainers": {
"enabled": true,
"optional": true,

View file

@ -131,6 +131,23 @@
}
]
},
{
"type": "dict",
"collapsible": true,
"key": "CollectVersion",
"label": "Collect Version",
"children": [
{
"type": "label",
"label": "Synchronize version for image and review instances by workfile version."
},
{
"type": "boolean",
"key": "enabled",
"label": "Enabled"
}
]
},
{
"type": "schema_template",
"name": "template_publish_plugin",

View file

@ -7,6 +7,7 @@ publishing plugins.
"""
from Qt import QtWidgets, QtCore
import qtawesome
from openpype.pipeline import (
install_host,
@ -20,6 +21,27 @@ from openpype.tools.utils.models import (
ProjectSortFilterProxy
)
from openpype.tools.utils import PlaceholderLineEdit
import appdirs
from openpype.lib import JSONSettingRegistry
class TrayPublisherRegistry(JSONSettingRegistry):
"""Class handling OpenPype general settings registry.
Attributes:
vendor (str): Name used for path construction.
product (str): Additional name used for path construction.
"""
def __init__(self):
self.vendor = "pypeclub"
self.product = "openpype"
name = "tray_publisher"
path = appdirs.user_data_dir(self.product, self.vendor)
super(TrayPublisherRegistry, self).__init__(name, path)
class StandaloneOverlayWidget(QtWidgets.QFrame):
project_selected = QtCore.Signal(str)
@ -43,6 +65,7 @@ class StandaloneOverlayWidget(QtWidgets.QFrame):
projects_model = ProjectModel(dbcon)
projects_proxy = ProjectSortFilterProxy()
projects_proxy.setSourceModel(projects_model)
projects_proxy.setFilterKeyColumn(0)
projects_view = QtWidgets.QListView(content_widget)
projects_view.setObjectName("ChooseProjectView")
@ -59,10 +82,17 @@ class StandaloneOverlayWidget(QtWidgets.QFrame):
btns_layout.addWidget(cancel_btn, 0)
btns_layout.addWidget(confirm_btn, 0)
txt_filter = PlaceholderLineEdit(content_widget)
txt_filter.setPlaceholderText("Quick filter projects..")
txt_filter.setClearButtonEnabled(True)
txt_filter.addAction(qtawesome.icon("fa.filter", color="gray"),
QtWidgets.QLineEdit.LeadingPosition)
content_layout = QtWidgets.QVBoxLayout(content_widget)
content_layout.setContentsMargins(0, 0, 0, 0)
content_layout.setSpacing(20)
content_layout.addWidget(header_label, 0)
content_layout.addWidget(txt_filter, 0)
content_layout.addWidget(projects_view, 1)
content_layout.addLayout(btns_layout, 0)
@ -79,17 +109,40 @@ class StandaloneOverlayWidget(QtWidgets.QFrame):
projects_view.doubleClicked.connect(self._on_double_click)
confirm_btn.clicked.connect(self._on_confirm_click)
cancel_btn.clicked.connect(self._on_cancel_click)
txt_filter.textChanged.connect(self._on_text_changed)
self._projects_view = projects_view
self._projects_model = projects_model
self._projects_proxy = projects_proxy
self._cancel_btn = cancel_btn
self._confirm_btn = confirm_btn
self._txt_filter = txt_filter
self._publisher_window = publisher_window
self._project_name = None
def showEvent(self, event):
self._projects_model.refresh()
# Sort projects after refresh
self._projects_proxy.sort(0)
setting_registry = TrayPublisherRegistry()
try:
project_name = setting_registry.get_item("project_name")
except ValueError:
project_name = None
if project_name:
index = None
src_index = self._projects_model.find_project(project_name)
if src_index is not None:
index = self._projects_proxy.mapFromSource(src_index)
if index:
mode = (
QtCore.QItemSelectionModel.Select
| QtCore.QItemSelectionModel.Rows)
self._projects_view.selectionModel().select(index, mode)
self._cancel_btn.setVisible(self._project_name is not None)
super(StandaloneOverlayWidget, self).showEvent(event)
@ -102,6 +155,10 @@ class StandaloneOverlayWidget(QtWidgets.QFrame):
def _on_cancel_click(self):
self._set_project(self._project_name)
def _on_text_changed(self):
self._projects_proxy.setFilterRegularExpression(
self._txt_filter.text())
def set_selected_project(self):
index = self._projects_view.currentIndex()
@ -119,6 +176,9 @@ class StandaloneOverlayWidget(QtWidgets.QFrame):
self.setVisible(False)
self.project_selected.emit(project_name)
setting_registry = TrayPublisherRegistry()
setting_registry.set_item("project_name", project_name)
class TrayPublishWindow(PublisherWindow):
def __init__(self, *args, **kwargs):

View file

@ -330,11 +330,26 @@ class ProjectModel(QtGui.QStandardItemModel):
if new_items:
root_item.appendRows(new_items)
def find_project(self, project_name):
"""
Get index of 'project_name' value.
Args:
project_name (str):
Returns:
(QModelIndex)
"""
val = self._items_by_name.get(project_name)
if val:
return self.indexFromItem(val)
class ProjectSortFilterProxy(QtCore.QSortFilterProxyModel):
def __init__(self, *args, **kwargs):
super(ProjectSortFilterProxy, self).__init__(*args, **kwargs)
self._filter_enabled = True
# Disable case sensitivity
self.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive)
def lessThan(self, left_index, right_index):
if left_index.data(PROJECT_NAME_ROLE) is None:
@ -356,10 +371,14 @@ class ProjectSortFilterProxy(QtCore.QSortFilterProxyModel):
def filterAcceptsRow(self, source_row, source_parent):
index = self.sourceModel().index(source_row, 0, source_parent)
string_pattern = self.filterRegularExpression().pattern()
if self._filter_enabled:
result = self._custom_index_filter(index)
if result is not None:
return result
project_name = index.data(PROJECT_NAME_ROLE)
if project_name is None:
return result
return string_pattern.lower() in project_name.lower()
return super(ProjectSortFilterProxy, self).filterAcceptsRow(
source_row, source_parent

View file

@ -0,0 +1,83 @@
# -*- coding: utf-8 -*-
"""Get version and license information on used Python packages.
This is getting over all packages installed with Poetry and printing out
their name, version and available license information from PyPi in Markdown
table format.
Usage:
./.poetry/bin/poetry run python ./tools/get_python_packages_info.py
"""
import toml
import requests
packages = []
# define column headers
package_header = "Package"
version_header = "Version"
license_header = "License"
name_col_width = len(package_header)
version_col_width = len(version_header)
license_col_width = len(license_header)
# read lock file to get packages
with open("poetry.lock", "r") as fb:
lock_content = toml.load(fb)
for package in lock_content["package"]:
# query pypi for license information
url = f"https://pypi.org/pypi/{package['name']}/json"
response = requests.get(
f"https://pypi.org/pypi/{package['name']}/json")
package_data = response.json()
version = package.get("version") or "N/A"
try:
package_license = package_data["info"].get("license") or "N/A"
except KeyError:
package_license = "N/A"
if len(package_license) > 64:
package_license = f"{package_license[:32]}..."
packages.append(
(
package["name"],
version,
package_license
)
)
# update column width based on max string length
if len(package["name"]) > name_col_width:
name_col_width = len(package["name"])
if len(version) > version_col_width:
version_col_width = len(version)
if len(package_license) > license_col_width:
license_col_width = len(package_license)
# pad columns
name_col_width += 2
version_col_width += 2
license_col_width += 2
# print table header
print((f"|{package_header.center(name_col_width)}"
f"|{version_header.center(version_col_width)}"
f"|{license_header.center(license_col_width)}|"))
print(
"|" + ("-" * len(package_header.center(name_col_width))) +
"|" + ("-" * len(version_header.center(version_col_width))) +
"|" + ("-" * len(license_header.center(license_col_width))) + "|")
# print rest of the table
for package in packages:
print((
f"|{package[0].center(name_col_width)}"
f"|{package[1].center(version_col_width)}"
f"|{package[2].center(license_col_width)}|"
))