Merge branch 'develop' into bugfix/OP-6356_3dsamx-empty-render-instance

This commit is contained in:
Kayla Man 2023-07-24 16:09:55 +08:00
commit 6264d42409
16 changed files with 202 additions and 74 deletions

View file

@ -35,6 +35,8 @@ body:
label: Version
description: What version are you running? Look to OpenPype Tray
options:
- 3.16.2-nightly.2
- 3.16.2-nightly.1
- 3.16.1
- 3.16.0
- 3.16.0-nightly.2
@ -133,8 +135,6 @@ body:
- 3.14.6
- 3.14.6-nightly.3
- 3.14.6-nightly.2
- 3.14.6-nightly.1
- 3.14.5
validations:
required: true
- type: dropdown

View file

@ -32,12 +32,16 @@ RUN yum -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.n
wget \
gcc \
zlib-devel \
pcre-devel \
perl-core \
bzip2 \
bzip2-devel \
readline-devel \
sqlite sqlite-devel \
openssl-devel \
openssl-libs \
openssl11-devel \
openssl11-libs \
tk-devel libffi-devel \
patchelf \
automake \
@ -71,7 +75,12 @@ RUN echo 'export PATH="$HOME/.pyenv/bin:$PATH"'>> $HOME/.bashrc \
&& echo 'eval "$(pyenv init -)"' >> $HOME/.bashrc \
&& echo 'eval "$(pyenv virtualenv-init -)"' >> $HOME/.bashrc \
&& echo 'eval "$(pyenv init --path)"' >> $HOME/.bashrc
RUN source $HOME/.bashrc && pyenv install ${OPENPYPE_PYTHON_VERSION}
RUN source $HOME/.bashrc \
&& export CPPFLAGS="-I/usr/include/openssl11" \
&& export LDFLAGS="-L/usr/lib64/openssl11 -lssl -lcrypto" \
&& export PATH=/usr/local/openssl/bin:$PATH \
&& export LD_LIBRARY_PATH=/usr/local/openssl/lib:$LD_LIBRARY_PATH \
&& pyenv install ${OPENPYPE_PYTHON_VERSION}
COPY . /opt/openpype/
RUN rm -rf /openpype/.poetry || echo "No Poetry installed yet."
@ -93,12 +102,13 @@ RUN source $HOME/.bashrc \
RUN source $HOME/.bashrc \
&& ./tools/fetch_thirdparty_libs.sh
RUN echo 'export PYTHONPATH="/opt/openpype/vendor/python:$PYTHONPATH"'>> $HOME/.bashrc
RUN source $HOME/.bashrc \
&& bash ./tools/build.sh
RUN cp /usr/lib64/libffi* ./build/exe.linux-x86_64-3.9/lib \
&& cp /usr/lib64/libssl* ./build/exe.linux-x86_64-3.9/lib \
&& cp /usr/lib64/libcrypto* ./build/exe.linux-x86_64-3.9/lib \
&& cp /usr/lib64/openssl11/libssl* ./build/exe.linux-x86_64-3.9/lib \
&& cp /usr/lib64/openssl11/libcrypto* ./build/exe.linux-x86_64-3.9/lib \
&& cp /root/.pyenv/versions/${OPENPYPE_PYTHON_VERSION}/lib/libpython* ./build/exe.linux-x86_64-3.9/lib \
&& cp /usr/lib64/libxcb* ./build/exe.linux-x86_64-3.9/vendor/python/PySide2/Qt/lib

View file

@ -35,6 +35,29 @@ LOG_WARNING = 1
LOG_ERROR = 3
def sanitize_long_path(path):
"""Sanitize long paths (260 characters) when on Windows.
Long paths are not capatible with ZipFile or reading a file, so we can
shorten the path to use.
Args:
path (str): path to either directory or file.
Returns:
str: sanitized path
"""
if platform.system().lower() != "windows":
return path
path = os.path.abspath(path)
if path.startswith("\\\\"):
path = "\\\\?\\UNC\\" + path[2:]
else:
path = "\\\\?\\" + path
return path
def sha256sum(filename):
"""Calculate sha256 for content of the file.
@ -54,6 +77,13 @@ def sha256sum(filename):
return h.hexdigest()
class ZipFileLongPaths(ZipFile):
def _extract_member(self, member, targetpath, pwd):
return ZipFile._extract_member(
self, member, sanitize_long_path(targetpath), pwd
)
class OpenPypeVersion(semver.VersionInfo):
"""Class for storing information about OpenPype version.
@ -780,7 +810,7 @@ class BootstrapRepos:
def _create_openpype_zip(self, zip_path: Path, openpype_path: Path) -> None:
"""Pack repositories and OpenPype into zip.
We are using :mod:`zipfile` instead :meth:`shutil.make_archive`
We are using :mod:`ZipFile` instead :meth:`shutil.make_archive`
because we need to decide what file and directories to include in zip
and what not. They are determined by :attr:`zip_filter` on file level
and :attr:`openpype_filter` on top level directory in OpenPype
@ -834,7 +864,7 @@ class BootstrapRepos:
checksums.append(
(
sha256sum(file.as_posix()),
sha256sum(sanitize_long_path(file.as_posix())),
file.resolve().relative_to(openpype_root)
)
)
@ -958,7 +988,9 @@ class BootstrapRepos:
if platform.system().lower() == "windows":
file_name = file_name.replace("/", "\\")
try:
current = sha256sum((path / file_name).as_posix())
current = sha256sum(
sanitize_long_path((path / file_name).as_posix())
)
except FileNotFoundError:
return False, f"Missing file [ {file_name} ]"
@ -1270,7 +1302,7 @@ class BootstrapRepos:
# extract zip there
self._print("Extracting zip to destination ...")
with ZipFile(version.path, "r") as zip_ref:
with ZipFileLongPaths(version.path, "r") as zip_ref:
zip_ref.extractall(destination)
self._print(f"Installed as {version.path.stem}")
@ -1386,7 +1418,7 @@ class BootstrapRepos:
# extract zip there
self._print("extracting zip to destination ...")
with ZipFile(openpype_version.path, "r") as zip_ref:
with ZipFileLongPaths(openpype_version.path, "r") as zip_ref:
self._progress_callback(75)
zip_ref.extractall(destination)
self._progress_callback(100)

View file

@ -18,8 +18,10 @@ class SelectInvalidAction(pyblish.api.Action):
icon = "search" # Icon from Awesome Icon
def process(self, context, plugin):
errored_instances = get_errored_instances_from_context(context,
plugin=plugin)
errored_instances = get_errored_instances_from_context(
context,
plugin=plugin,
)
# Get the invalid nodes for the plug-ins
self.log.info("Finding invalid nodes..")
@ -51,6 +53,7 @@ class SelectInvalidAction(pyblish.api.Action):
names = set()
for tool in invalid:
flow.Select(tool, True)
comp.SetActiveTool(tool)
names.add(tool.Name)
self.log.info(
"Selecting invalid tools: %s" % ", ".join(sorted(names))

View file

@ -33,7 +33,7 @@ class CreateRedshiftProxy(plugin.HoudiniCreator):
instance_node = hou.node(instance.get("instance_node"))
parms = {
"RS_archive_file": '$HIP/pyblish/`{}.$F4.rs'.format(subset_name),
"RS_archive_file": '$HIP/pyblish/{}.$F4.rs'.format(subset_name),
}
if self.selected_nodes:

View file

@ -17,5 +17,5 @@ class CollectPointcacheType(pyblish.api.InstancePlugin):
def process(self, instance):
if instance.data["creator_identifier"] == "io.openpype.creators.houdini.bgeo": # noqa: E501
instance.data["families"] += ["bgeo"]
elif instance.data["creator_identifier"] == "io.openpype.creators.houdini.alembic": # noqa: E501
elif instance.data["creator_identifier"] == "io.openpype.creators.houdini.pointcache": # noqa: E501
instance.data["families"] += ["abc"]

View file

@ -8,13 +8,24 @@ from maya import cmds
from maya.app.renderSetup.model import renderSetup
from openpype.lib import BoolDef, Logger
from openpype.pipeline import AVALON_CONTAINER_ID, Anatomy, CreatedInstance
from openpype.pipeline import Creator as NewCreator
from openpype.pipeline import (
CreatorError, LegacyCreator, LoaderPlugin, get_representation_path,
legacy_io)
from openpype.pipeline.load import LoadError
from openpype.settings import get_project_settings
from openpype.pipeline import (
AVALON_CONTAINER_ID,
Anatomy,
CreatedInstance,
Creator as NewCreator,
AutoCreator,
HiddenCreator,
CreatorError,
LegacyCreator,
LoaderPlugin,
get_representation_path,
legacy_io,
)
from openpype.pipeline.load import LoadError
from . import lib
from .lib import imprint, read
@ -177,6 +188,36 @@ class MayaCreatorBase(object):
return node_data
def _default_collect_instances(self):
self.cache_subsets(self.collection_shared_data)
cached_subsets = self.collection_shared_data["maya_cached_subsets"]
for node in cached_subsets.get(self.identifier, []):
node_data = self.read_instance_node(node)
created_instance = CreatedInstance.from_existing(node_data, self)
self._add_instance_to_context(created_instance)
def _default_update_instances(self, update_list):
for created_inst, _changes in update_list:
data = created_inst.data_to_store()
node = data.get("instance_node")
self.imprint_instance_node(node, data)
def _default_remove_instances(self, instances):
"""Remove specified instance from the scene.
This is only removing `id` parameter so instance is no longer
instance, because it might contain valuable data for artist.
"""
for instance in instances:
node = instance.data.get("instance_node")
if node:
cmds.delete(node)
self._remove_instance_from_context(instance)
@six.add_metaclass(ABCMeta)
class MayaCreator(NewCreator, MayaCreatorBase):
@ -202,34 +243,13 @@ class MayaCreator(NewCreator, MayaCreatorBase):
return instance
def collect_instances(self):
self.cache_subsets(self.collection_shared_data)
cached_subsets = self.collection_shared_data["maya_cached_subsets"]
for node in cached_subsets.get(self.identifier, []):
node_data = self.read_instance_node(node)
created_instance = CreatedInstance.from_existing(node_data, self)
self._add_instance_to_context(created_instance)
return self._default_collect_instances()
def update_instances(self, update_list):
for created_inst, _changes in update_list:
data = created_inst.data_to_store()
node = data.get("instance_node")
self.imprint_instance_node(node, data)
return self._default_update_instances(update_list)
def remove_instances(self, instances):
"""Remove specified instance from the scene.
This is only removing `id` parameter so instance is no longer
instance, because it might contain valuable data for artist.
"""
for instance in instances:
node = instance.data.get("instance_node")
if node:
cmds.delete(node)
self._remove_instance_from_context(instance)
return self._default_remove_instances(instances)
def get_pre_create_attr_defs(self):
return [
@ -239,6 +259,40 @@ class MayaCreator(NewCreator, MayaCreatorBase):
]
class MayaAutoCreator(AutoCreator, MayaCreatorBase):
"""Automatically triggered creator for Maya.
The plugin is not visible in UI, and 'create' method does not expect
any arguments.
"""
def collect_instances(self):
return self._default_collect_instances()
def update_instances(self, update_list):
return self._default_update_instances(update_list)
def remove_instances(self, instances):
return self._default_remove_instances(instances)
class MayaHiddenCreator(HiddenCreator, MayaCreatorBase):
"""Hidden creator for Maya.
The plugin is not visible in UI, and it does not have strictly defined
arguments for 'create' method.
"""
def collect_instances(self):
return self._default_collect_instances()
def update_instances(self, update_list):
return self._default_update_instances(update_list)
def remove_instances(self, instances):
return self._default_remove_instances(instances)
def ensure_namespace(namespace):
"""Make sure the namespace exists.

View file

@ -3,7 +3,9 @@
from __future__ import absolute_import
import pyblish.api
from openpype.pipeline.publish import ValidateContentsOrder
from openpype.pipeline.publish import (
ValidateContentsOrder, PublishValidationError
)
from maya import cmds
@ -108,4 +110,5 @@ class ValidateInstanceInContext(pyblish.api.InstancePlugin):
asset = instance.data.get("asset")
context_asset = instance.context.data["assetEntity"]["name"]
msg = "{} has asset {}".format(instance.name, asset)
assert asset == context_asset, msg
if asset != context_asset:
raise PublishValidationError(msg)

View file

@ -47,10 +47,6 @@ class CollectDeadlinePools(pyblish.api.InstancePlugin,
if instance.data["secondaryPool"] == "-":
instance.data["secondaryPool"] = None
self.log.info("prima::{}".format(instance.data["primaryPool"]))
self.log.info(
"secondaryPool::{}".format(instance.data["secondaryPool"]))
@classmethod
def get_attribute_defs(cls):
# TODO: Preferably this would be an enum for the user

View file

@ -13,6 +13,7 @@ from .create import (
BaseCreator,
Creator,
AutoCreator,
HiddenCreator,
CreatedInstance,
CreatorError,
@ -114,6 +115,7 @@ __all__ = (
"BaseCreator",
"Creator",
"AutoCreator",
"HiddenCreator",
"CreatedInstance",
"CreatorError",

View file

@ -1165,8 +1165,8 @@ class CreatedInstance:
Args:
instance_data (Dict[str, Any]): Data in a structure ready for
'CreatedInstance' object.
creator (Creator): Creator plugin which is creating the instance
of for which the instance belong.
creator (BaseCreator): Creator plugin which is creating the
instance of for which the instance belong.
"""
instance_data = copy.deepcopy(instance_data)

View file

@ -84,6 +84,13 @@ class ProjectProxyFilter(QtCore.QSortFilterProxyModel):
super(ProjectProxyFilter, self).__init__(*args, **kwargs)
self._filter_default = False
def lessThan(self, left, right):
if left.data(PROJECT_NAME_ROLE) is None:
return True
if right.data(PROJECT_NAME_ROLE) is None:
return False
return super(ProjectProxyFilter, self).lessThan(left, right)
def set_filter_default(self, enabled=True):
"""Set if filtering of default item is enabled."""
if enabled == self._filter_default:

View file

@ -1,6 +1,14 @@
from qtpy import QtCore, QtWidgets
from openpype.tools.utils.lib import checkstate_int_to_enum
from openpype.tools.utils.lib import (
checkstate_int_to_enum,
checkstate_enum_to_int,
)
from openpype.tools.utils.constants import (
CHECKED_INT,
UNCHECKED_INT,
ITEM_IS_USER_TRISTATE,
)
class ComboItemDelegate(QtWidgets.QStyledItemDelegate):
@ -107,9 +115,9 @@ class MultiSelectionComboBox(QtWidgets.QComboBox):
return
if state == QtCore.Qt.Unchecked:
new_state = QtCore.Qt.Checked
new_state = CHECKED_INT
else:
new_state = QtCore.Qt.Unchecked
new_state = UNCHECKED_INT
elif event.type() == QtCore.QEvent.KeyPress:
# TODO: handle QtCore.Qt.Key_Enter, Key_Return?
@ -117,15 +125,15 @@ class MultiSelectionComboBox(QtWidgets.QComboBox):
# toggle the current items check state
if (
index_flags & QtCore.Qt.ItemIsUserCheckable
and index_flags & QtCore.Qt.ItemIsTristate
and index_flags & ITEM_IS_USER_TRISTATE
):
new_state = QtCore.Qt.CheckState((int(state) + 1) % 3)
new_state = (checkstate_enum_to_int(state) + 1) % 3
elif index_flags & QtCore.Qt.ItemIsUserCheckable:
if state != QtCore.Qt.Checked:
new_state = QtCore.Qt.Checked
new_state = CHECKED_INT
else:
new_state = QtCore.Qt.Unchecked
new_state = UNCHECKED_INT
if new_state is not None:
model.setData(current_index, new_state, QtCore.Qt.CheckStateRole)
@ -180,9 +188,9 @@ class MultiSelectionComboBox(QtWidgets.QComboBox):
for idx in range(self.count()):
value = self.itemData(idx, role=QtCore.Qt.UserRole)
if value in values:
check_state = QtCore.Qt.Checked
check_state = CHECKED_INT
else:
check_state = QtCore.Qt.Unchecked
check_state = UNCHECKED_INT
self.setItemData(idx, check_state, QtCore.Qt.CheckStateRole)
def value(self):

View file

@ -1,5 +1,13 @@
from qtpy import QtCore, QtGui, QtWidgets
from openpype.tools.utils.lib import checkstate_int_to_enum
from openpype.tools.utils.lib import (
checkstate_int_to_enum,
checkstate_enum_to_int,
)
from openpype.tools.utils.constants import (
CHECKED_INT,
UNCHECKED_INT,
ITEM_IS_USER_TRISTATE,
)
class ComboItemDelegate(QtWidgets.QStyledItemDelegate):
@ -30,7 +38,7 @@ class MultiSelectionComboBox(QtWidgets.QComboBox):
QtCore.Qt.Key_PageDown,
QtCore.Qt.Key_PageUp,
QtCore.Qt.Key_Home,
QtCore.Qt.Key_End
QtCore.Qt.Key_End,
}
top_bottom_padding = 2
@ -127,25 +135,25 @@ class MultiSelectionComboBox(QtWidgets.QComboBox):
return
if state == QtCore.Qt.Unchecked:
new_state = QtCore.Qt.Checked
new_state = CHECKED_INT
else:
new_state = QtCore.Qt.Unchecked
new_state = UNCHECKED_INT
elif event.type() == QtCore.QEvent.KeyPress:
# TODO: handle QtCore.Qt.Key_Enter, Key_Return?
if event.key() == QtCore.Qt.Key_Space:
# toggle the current items check state
if (
index_flags & QtCore.Qt.ItemIsUserCheckable
and index_flags & QtCore.Qt.ItemIsTristate
and index_flags & ITEM_IS_USER_TRISTATE
):
new_state = QtCore.Qt.CheckState((int(state) + 1) % 3)
new_state = (checkstate_enum_to_int(state) + 1) % 3
elif index_flags & QtCore.Qt.ItemIsUserCheckable:
# toggle the current items check state
if state != QtCore.Qt.Checked:
new_state = QtCore.Qt.Checked
new_state = CHECKED_INT
else:
new_state = QtCore.Qt.Unchecked
new_state = UNCHECKED_INT
if new_state is not None:
model.setData(current_index, new_state, QtCore.Qt.CheckStateRole)
@ -249,7 +257,6 @@ class MultiSelectionComboBox(QtWidgets.QComboBox):
QtWidgets.QStyle.SC_ComboBoxArrow
)
total_width = option.rect.width() - btn_rect.width()
font_metricts = self.fontMetrics()
line = 0
self.lines = {line: []}
@ -305,9 +312,9 @@ class MultiSelectionComboBox(QtWidgets.QComboBox):
for idx in range(self.count()):
value = self.itemData(idx, role=QtCore.Qt.UserRole)
if value in values:
check_state = QtCore.Qt.Checked
check_state = CHECKED_INT
else:
check_state = QtCore.Qt.Unchecked
check_state = UNCHECKED_INT
self.setItemData(idx, check_state, QtCore.Qt.CheckStateRole)
self.update_size_hint()

View file

@ -5,6 +5,12 @@ UNCHECKED_INT = getattr(QtCore.Qt.Unchecked, "value", 0)
PARTIALLY_CHECKED_INT = getattr(QtCore.Qt.PartiallyChecked, "value", 1)
CHECKED_INT = getattr(QtCore.Qt.Checked, "value", 2)
# Checkbox state
try:
ITEM_IS_USER_TRISTATE = QtCore.Qt.ItemIsUserTristate
except AttributeError:
ITEM_IS_USER_TRISTATE = QtCore.Qt.ItemIsTristate
DEFAULT_PROJECT_LABEL = "< Default >"
PROJECT_NAME_ROLE = QtCore.Qt.UserRole + 101
PROJECT_IS_ACTIVE_ROLE = QtCore.Qt.UserRole + 102

View file

@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
"""Package declaring Pype version."""
__version__ = "3.16.1"
__version__ = "3.16.2-nightly.2"