diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 31d1ec74f5..2d9915609a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -35,6 +35,7 @@ body: label: Version description: What version are you running? Look to OpenPype Tray options: + - 3.16.2-nightly.1 - 3.16.1 - 3.16.0 - 3.16.0-nightly.2 @@ -134,7 +135,6 @@ body: - 3.14.6-nightly.3 - 3.14.6-nightly.2 - 3.14.6-nightly.1 - - 3.14.5 validations: required: true - type: dropdown diff --git a/Dockerfile.centos7 b/Dockerfile.centos7 index ce1a624a4f..9217140f20 100644 --- a/Dockerfile.centos7 +++ b/Dockerfile.centos7 @@ -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 diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py index 4cf00375bf..408764e1a8 100644 --- a/igniter/bootstrap_repos.py +++ b/igniter/bootstrap_repos.py @@ -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) diff --git a/openpype/hosts/houdini/plugins/create/create_redshift_proxy.py b/openpype/hosts/houdini/plugins/create/create_redshift_proxy.py index 8b6a68437b..b814dd9d57 100644 --- a/openpype/hosts/houdini/plugins/create/create_redshift_proxy.py +++ b/openpype/hosts/houdini/plugins/create/create_redshift_proxy.py @@ -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: diff --git a/openpype/hosts/houdini/plugins/publish/collect_pointcache_type.py b/openpype/hosts/houdini/plugins/publish/collect_pointcache_type.py index 6c527377e0..3323e97c20 100644 --- a/openpype/hosts/houdini/plugins/publish/collect_pointcache_type.py +++ b/openpype/hosts/houdini/plugins/publish/collect_pointcache_type.py @@ -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"] diff --git a/openpype/modules/deadline/plugins/publish/collect_pools.py b/openpype/modules/deadline/plugins/publish/collect_pools.py index 706374d972..a25b149f11 100644 --- a/openpype/modules/deadline/plugins/publish/collect_pools.py +++ b/openpype/modules/deadline/plugins/publish/collect_pools.py @@ -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 diff --git a/openpype/tools/project_manager/project_manager/model.py b/openpype/tools/project_manager/project_manager/model.py index 29a26f700f..f6c98d6f6c 100644 --- a/openpype/tools/project_manager/project_manager/model.py +++ b/openpype/tools/project_manager/project_manager/model.py @@ -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: diff --git a/openpype/tools/project_manager/project_manager/multiselection_combobox.py b/openpype/tools/project_manager/project_manager/multiselection_combobox.py index 4b5d468982..4100ada221 100644 --- a/openpype/tools/project_manager/project_manager/multiselection_combobox.py +++ b/openpype/tools/project_manager/project_manager/multiselection_combobox.py @@ -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): diff --git a/openpype/tools/settings/settings/multiselection_combobox.py b/openpype/tools/settings/settings/multiselection_combobox.py index 896be3c06c..d64fc83745 100644 --- a/openpype/tools/settings/settings/multiselection_combobox.py +++ b/openpype/tools/settings/settings/multiselection_combobox.py @@ -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() diff --git a/openpype/tools/utils/constants.py b/openpype/tools/utils/constants.py index 99f2602ee3..77324762b3 100644 --- a/openpype/tools/utils/constants.py +++ b/openpype/tools/utils/constants.py @@ -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 diff --git a/openpype/version.py b/openpype/version.py index b2dfc857a3..40375bef43 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.16.1" +__version__ = "3.16.2-nightly.1"