diff --git a/CHANGELOG.md b/CHANGELOG.md index 8af555adf2..c616b70e3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,20 @@ # Changelog -## [3.14.3-nightly.5](https://github.com/pypeclub/OpenPype/tree/HEAD) +## [3.14.4-nightly.1](https://github.com/pypeclub/OpenPype/tree/HEAD) -[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.14.2...HEAD) +[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.14.3...HEAD) + +**🐛 Bug fixes** + +- Publisher: Files Drag n Drop cleanup [\#3888](https://github.com/pypeclub/OpenPype/pull/3888) + +**🔀 Refactored code** + +- General: import 'Logger' from 'openpype.lib' [\#3926](https://github.com/pypeclub/OpenPype/pull/3926) + +## [3.14.3](https://github.com/pypeclub/OpenPype/tree/3.14.3) (2022-10-03) + +[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.14.3-nightly.7...3.14.3) **🚀 Enhancements** @@ -16,13 +28,12 @@ - Flame: make migratable projects after creation [\#3860](https://github.com/pypeclub/OpenPype/pull/3860) - Photoshop: synchronize image version with workfile [\#3854](https://github.com/pypeclub/OpenPype/pull/3854) - General: Transcoding handle float2 attr type [\#3849](https://github.com/pypeclub/OpenPype/pull/3849) -- General: Simple script for getting license information about used packages [\#3843](https://github.com/pypeclub/OpenPype/pull/3843) -- Houdini: Increment current file on workfile publish [\#3840](https://github.com/pypeclub/OpenPype/pull/3840) - General: Workfile template build enhancements [\#3838](https://github.com/pypeclub/OpenPype/pull/3838) - General: lock task workfiles when they are working on [\#3810](https://github.com/pypeclub/OpenPype/pull/3810) **🐛 Bug fixes** +- Maya: Fix Render single camera validator [\#3929](https://github.com/pypeclub/OpenPype/pull/3929) - Flame: loading multilayer exr to batch/reel is working [\#3901](https://github.com/pypeclub/OpenPype/pull/3901) - Hiero: Fix inventory check on launch [\#3895](https://github.com/pypeclub/OpenPype/pull/3895) - WebPublisher: Fix import after refactor [\#3891](https://github.com/pypeclub/OpenPype/pull/3891) @@ -33,19 +44,18 @@ - Tray Publisher: skip plugin if otioTimeline is missing [\#3856](https://github.com/pypeclub/OpenPype/pull/3856) - Flame: retimed attributes are integrated with settings [\#3855](https://github.com/pypeclub/OpenPype/pull/3855) - Maya: Extract Playblast fix textures + labelize viewport show settings [\#3852](https://github.com/pypeclub/OpenPype/pull/3852) -- Maya Deadline: Fix Tile Rendering by forcing integer pixel values [\#3758](https://github.com/pypeclub/OpenPype/pull/3758) +- Maya: Publishing data key change [\#3811](https://github.com/pypeclub/OpenPype/pull/3811) **🔀 Refactored code** +- Maya: Remove unused 'openpype.api' imports in plugins [\#3925](https://github.com/pypeclub/OpenPype/pull/3925) - Resolve: Use new Extractor location [\#3918](https://github.com/pypeclub/OpenPype/pull/3918) - Unreal: Use new Extractor location [\#3917](https://github.com/pypeclub/OpenPype/pull/3917) - Flame: Use new Extractor location [\#3916](https://github.com/pypeclub/OpenPype/pull/3916) - Houdini: Use new Extractor location [\#3894](https://github.com/pypeclub/OpenPype/pull/3894) - Harmony: Use new Extractor location [\#3893](https://github.com/pypeclub/OpenPype/pull/3893) - Hiero: Use new Extractor location [\#3851](https://github.com/pypeclub/OpenPype/pull/3851) -- Maya: Remove old legacy \(ftrack\) plug-ins that are of no use anymore [\#3819](https://github.com/pypeclub/OpenPype/pull/3819) - Nuke: Use new Extractor location [\#3799](https://github.com/pypeclub/OpenPype/pull/3799) -- Maya: Use new Extractor location [\#3775](https://github.com/pypeclub/OpenPype/pull/3775) **Merged pull requests:** @@ -57,10 +67,6 @@ [Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.14.2-nightly.5...3.14.2) -**🆕 New features** - -- Nuke: Build workfile by template [\#3763](https://github.com/pypeclub/OpenPype/pull/3763) - **🚀 Enhancements** - Flame: Adding Creator's retimed shot and handles switch [\#3826](https://github.com/pypeclub/OpenPype/pull/3826) @@ -73,17 +79,14 @@ - General: Fix Pattern access in client code [\#3828](https://github.com/pypeclub/OpenPype/pull/3828) - Launcher: Skip opening last work file works for groups [\#3822](https://github.com/pypeclub/OpenPype/pull/3822) -- Maya: Publishing data key change [\#3811](https://github.com/pypeclub/OpenPype/pull/3811) - Igniter: Fix status handling when version is already installed [\#3804](https://github.com/pypeclub/OpenPype/pull/3804) - Resolve: Addon import is Python 2 compatible [\#3798](https://github.com/pypeclub/OpenPype/pull/3798) -- Hiero: retimed clip publishing is working [\#3792](https://github.com/pypeclub/OpenPype/pull/3792) - nuke: validate write node is not failing due wrong type [\#3780](https://github.com/pypeclub/OpenPype/pull/3780) - Fix - changed format of version string in pyproject.toml [\#3777](https://github.com/pypeclub/OpenPype/pull/3777) -- Ftrack status fix typo prgoress -\> progress [\#3761](https://github.com/pypeclub/OpenPype/pull/3761) -- Fix version resolution [\#3757](https://github.com/pypeclub/OpenPype/pull/3757) **🔀 Refactored code** +- Maya: Remove old legacy \(ftrack\) plug-ins that are of no use anymore [\#3819](https://github.com/pypeclub/OpenPype/pull/3819) - Photoshop: Use new Extractor location [\#3789](https://github.com/pypeclub/OpenPype/pull/3789) - Blender: Use new Extractor location [\#3787](https://github.com/pypeclub/OpenPype/pull/3787) - AfterEffects: Use new Extractor location [\#3784](https://github.com/pypeclub/OpenPype/pull/3784) @@ -92,8 +95,6 @@ - General: Move queries of asset and representation links [\#3770](https://github.com/pypeclub/OpenPype/pull/3770) - General: Move create project folders to pipeline [\#3768](https://github.com/pypeclub/OpenPype/pull/3768) - General: Create project function moved to client code [\#3766](https://github.com/pypeclub/OpenPype/pull/3766) -- Maya: Refactor submit deadline to use AbstractSubmitDeadline [\#3759](https://github.com/pypeclub/OpenPype/pull/3759) -- General: Change publish template settings location [\#3755](https://github.com/pypeclub/OpenPype/pull/3755) **Merged pull requests:** @@ -104,18 +105,6 @@ [Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.14.1-nightly.4...3.14.1) -**🚀 Enhancements** - -- General: Thumbnail can use project roots [\#3750](https://github.com/pypeclub/OpenPype/pull/3750) - -**🐛 Bug fixes** - -- Maya: Fix typo in getPanel argument `with\_focus` -\> `withFocus` [\#3753](https://github.com/pypeclub/OpenPype/pull/3753) - -**🔀 Refactored code** - -- General: Move delivery logic to pipeline [\#3751](https://github.com/pypeclub/OpenPype/pull/3751) - ## [3.14.0](https://github.com/pypeclub/OpenPype/tree/3.14.0) (2022-08-18) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.14.0-nightly.1...3.14.0) diff --git a/openpype/hosts/aftereffects/api/launch_logic.py b/openpype/hosts/aftereffects/api/launch_logic.py index 30a3e1f1c3..9c8513fe8c 100644 --- a/openpype/hosts/aftereffects/api/launch_logic.py +++ b/openpype/hosts/aftereffects/api/launch_logic.py @@ -12,6 +12,7 @@ from wsrpc_aiohttp import ( from Qt import QtCore +from openpype.lib import Logger from openpype.pipeline import legacy_io from openpype.tools.utils import host_tools from openpype.tools.adobe_webserver.app import WebServerTool @@ -84,8 +85,6 @@ class ProcessLauncher(QtCore.QObject): @property def log(self): if self._log is None: - from openpype.api import Logger - self._log = Logger.get_logger("{}-launcher".format( self.route_name)) return self._log diff --git a/openpype/hosts/aftereffects/api/pipeline.py b/openpype/hosts/aftereffects/api/pipeline.py index c13c22ced5..7026fe3f05 100644 --- a/openpype/hosts/aftereffects/api/pipeline.py +++ b/openpype/hosts/aftereffects/api/pipeline.py @@ -4,8 +4,7 @@ from Qt import QtWidgets import pyblish.api -from openpype import lib -from openpype.api import Logger +from openpype.lib import Logger, register_event_callback from openpype.pipeline import ( register_loader_plugin_path, register_creator_plugin_path, @@ -16,9 +15,8 @@ from openpype.pipeline import ( ) from openpype.pipeline.load import any_outdated_containers import openpype.hosts.aftereffects -from openpype.lib import register_event_callback -from .launch_logic import get_stub +from .launch_logic import get_stub, ConnectionNotEstablishedYet log = Logger.get_logger(__name__) @@ -111,7 +109,7 @@ def ls(): """ try: stub = get_stub() # only after AfterEffects is up - except lib.ConnectionNotEstablishedYet: + except ConnectionNotEstablishedYet: print("Not connected yet, ignoring") return @@ -284,7 +282,7 @@ def _get_stub(): """ try: stub = get_stub() # only after Photoshop is up - except lib.ConnectionNotEstablishedYet: + except ConnectionNotEstablishedYet: print("Not connected yet, ignoring") return diff --git a/openpype/hosts/blender/api/lib.py b/openpype/hosts/blender/api/lib.py index 9cd1ace821..05912885f7 100644 --- a/openpype/hosts/blender/api/lib.py +++ b/openpype/hosts/blender/api/lib.py @@ -6,7 +6,7 @@ from typing import Dict, List, Union import bpy import addon_utils -from openpype.api import Logger +from openpype.lib import Logger from . import pipeline diff --git a/openpype/hosts/blender/api/pipeline.py b/openpype/hosts/blender/api/pipeline.py index ea405b028e..c2aee1e653 100644 --- a/openpype/hosts/blender/api/pipeline.py +++ b/openpype/hosts/blender/api/pipeline.py @@ -20,8 +20,8 @@ from openpype.pipeline import ( deregister_creator_plugin_path, AVALON_CONTAINER_ID, ) -from openpype.api import Logger from openpype.lib import ( + Logger, register_event_callback, emit_event ) diff --git a/openpype/hosts/celaction/api/cli.py b/openpype/hosts/celaction/api/cli.py index eb91def090..88fc11cafb 100644 --- a/openpype/hosts/celaction/api/cli.py +++ b/openpype/hosts/celaction/api/cli.py @@ -6,9 +6,8 @@ import argparse import pyblish.api import pyblish.util -from openpype.api import Logger -import openpype import openpype.hosts.celaction +from openpype.lib import Logger from openpype.hosts.celaction import api as celaction from openpype.tools.utils import host_tools from openpype.pipeline import install_openpype_plugins diff --git a/openpype/hosts/flame/api/lib.py b/openpype/hosts/flame/api/lib.py index b7f7b24e51..6aca5c5ce6 100644 --- a/openpype/hosts/flame/api/lib.py +++ b/openpype/hosts/flame/api/lib.py @@ -12,6 +12,9 @@ import xml.etree.cElementTree as cET from copy import deepcopy, copy from xml.etree import ElementTree as ET from pprint import pformat + +from openpype.lib import Logger, run_subprocess + from .constants import ( MARKER_COLOR, MARKER_DURATION, @@ -20,9 +23,7 @@ from .constants import ( MARKER_PUBLISH_DEFAULT ) -import openpype.api as openpype - -log = openpype.Logger.get_logger(__name__) +log = Logger.get_logger(__name__) FRAME_PATTERN = re.compile(r"[\._](\d+)[\.]") @@ -1016,7 +1017,7 @@ class MediaInfoFile(object): try: # execute creation of clip xml template data - openpype.run_subprocess(cmd_args) + run_subprocess(cmd_args) except TypeError as error: raise TypeError( "Error creating `{}` due: {}".format(fpath, error)) diff --git a/openpype/hosts/flame/api/pipeline.py b/openpype/hosts/flame/api/pipeline.py index 324d13bc3f..3a23389961 100644 --- a/openpype/hosts/flame/api/pipeline.py +++ b/openpype/hosts/flame/api/pipeline.py @@ -5,7 +5,7 @@ import os import contextlib from pyblish import api as pyblish -from openpype.api import Logger +from openpype.lib import Logger from openpype.pipeline import ( register_loader_plugin_path, register_creator_plugin_path, diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index 1a26e96c79..4bbdc79621 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -9,13 +9,14 @@ from Qt import QtCore, QtWidgets import openpype.api as openpype import qargparse from openpype import style +from openpype.lib import Logger from openpype.pipeline import LegacyCreator, LoaderPlugin from . import constants from . import lib as flib from . import pipeline as fpipeline -log = openpype.Logger.get_logger(__name__) +log = Logger.get_logger(__name__) class CreatorWidget(QtWidgets.QDialog): diff --git a/openpype/hosts/flame/api/render_utils.py b/openpype/hosts/flame/api/render_utils.py index a29d6be695..7e50c2b23e 100644 --- a/openpype/hosts/flame/api/render_utils.py +++ b/openpype/hosts/flame/api/render_utils.py @@ -1,6 +1,6 @@ import os from xml.etree import ElementTree as ET -from openpype.api import Logger +from openpype.lib import Logger log = Logger.get_logger(__name__) diff --git a/openpype/hosts/flame/api/utils.py b/openpype/hosts/flame/api/utils.py index 2dfdfa8f48..fb8bdee42d 100644 --- a/openpype/hosts/flame/api/utils.py +++ b/openpype/hosts/flame/api/utils.py @@ -4,7 +4,7 @@ Flame utils for syncing scripts import os import shutil -from openpype.api import Logger +from openpype.lib import Logger log = Logger.get_logger(__name__) diff --git a/openpype/hosts/hiero/api/menu.py b/openpype/hosts/hiero/api/menu.py index 541a1f1f92..2a7560c6ba 100644 --- a/openpype/hosts/hiero/api/menu.py +++ b/openpype/hosts/hiero/api/menu.py @@ -4,7 +4,7 @@ import sys import hiero.core from hiero.ui import findMenuAction -from openpype.api import Logger +from openpype.lib import Logger from openpype.pipeline import legacy_io from openpype.tools.utils import host_tools diff --git a/openpype/hosts/hiero/api/tags.py b/openpype/hosts/hiero/api/tags.py index 10df96fa53..fac26da03a 100644 --- a/openpype/hosts/hiero/api/tags.py +++ b/openpype/hosts/hiero/api/tags.py @@ -3,7 +3,7 @@ import os import hiero from openpype.client import get_project, get_assets -from openpype.api import Logger +from openpype.lib import Logger from openpype.pipeline import legacy_io log = Logger.get_logger(__name__) diff --git a/openpype/hosts/hiero/api/workio.py b/openpype/hosts/hiero/api/workio.py index 762e22804f..040fd1435a 100644 --- a/openpype/hosts/hiero/api/workio.py +++ b/openpype/hosts/hiero/api/workio.py @@ -1,7 +1,7 @@ import os import hiero -from openpype.api import Logger +from openpype.lib import Logger log = Logger.get_logger(__name__) diff --git a/openpype/hosts/houdini/api/shelves.py b/openpype/hosts/houdini/api/shelves.py index 248d99105c..3ccab964cd 100644 --- a/openpype/hosts/houdini/api/shelves.py +++ b/openpype/hosts/houdini/api/shelves.py @@ -1,7 +1,6 @@ import os import logging import platform -import six from openpype.settings import get_project_settings @@ -9,16 +8,10 @@ import hou log = logging.getLogger("openpype.hosts.houdini.shelves") -if six.PY2: - FileNotFoundError = IOError - def generate_shelves(): """This function generates complete shelves from shelf set to tools in Houdini from openpype project settings houdini shelf definition. - - Raises: - FileNotFoundError: Raised when the shelf set filepath does not exist """ current_os = platform.system().lower() @@ -27,51 +20,41 @@ def generate_shelves(): shelves_set_config = project_settings["houdini"]["shelves"] if not shelves_set_config: - log.debug( - "No custom shelves found in project settings." - ) + log.debug("No custom shelves found in project settings.") return for shelf_set_config in shelves_set_config: shelf_set_filepath = shelf_set_config.get('shelf_set_source_path') + shelf_set_os_filepath = shelf_set_filepath[current_os] + if shelf_set_os_filepath: + if not os.path.isfile(shelf_set_os_filepath): + log.error("Shelf path doesn't exist - " + "{}".format(shelf_set_os_filepath)) + continue - if shelf_set_filepath[current_os]: - if not os.path.isfile(shelf_set_filepath[current_os]): - raise FileNotFoundError( - "This path doesn't exist - {}".format( - shelf_set_filepath[current_os] - ) - ) - - hou.shelves.newShelfSet(file_path=shelf_set_filepath[current_os]) + hou.shelves.newShelfSet(file_path=shelf_set_os_filepath) continue shelf_set_name = shelf_set_config.get('shelf_set_name') if not shelf_set_name: - log.warning( - "No name found in shelf set definition." - ) - return - - shelf_set = get_or_create_shelf_set(shelf_set_name) + log.warning("No name found in shelf set definition.") + continue shelves_definition = shelf_set_config.get('shelf_definition') - if not shelves_definition: log.debug( "No shelf definition found for shelf set named '{}'".format( shelf_set_name ) ) - return + continue + shelf_set = get_or_create_shelf_set(shelf_set_name) for shelf_definition in shelves_definition: shelf_name = shelf_definition.get('shelf_name') if not shelf_name: - log.warning( - "No name found in shelf definition." - ) - return + log.warning("No name found in shelf definition.") + continue shelf = get_or_create_shelf(shelf_name) @@ -81,7 +64,7 @@ def generate_shelves(): shelf_name ) ) - return + continue mandatory_attributes = {'name', 'script'} for tool_definition in shelf_definition.get('tools_list'): @@ -91,14 +74,14 @@ def generate_shelves(): tool_definition[key] for key in mandatory_attributes ): log.warning( - "You need to specify at least the name and \ -the script path of the tool.") + "You need to specify at least the name and the " + "script path of the tool.") continue tool = get_or_create_tool(tool_definition, shelf) if not tool: - return + continue # Add the tool to the shelf if not already in it if tool not in shelf.tools(): @@ -121,12 +104,10 @@ def get_or_create_shelf_set(shelf_set_label): """ all_shelves_sets = hou.shelves.shelfSets().values() - shelf_sets = [ - shelf for shelf in all_shelves_sets if shelf.label() == shelf_set_label - ] - - if shelf_sets: - return shelf_sets[0] + shelf_set = next((shelf for shelf in all_shelves_sets if + shelf.label() == shelf_set_label), None) + if shelf_set: + return shelf_set shelf_set_name = shelf_set_label.replace(' ', '_').lower() new_shelf_set = hou.shelves.newShelfSet( @@ -148,10 +129,9 @@ def get_or_create_shelf(shelf_label): """ all_shelves = hou.shelves.shelves().values() - shelf = [s for s in all_shelves if s.label() == shelf_label] - + shelf = next((s for s in all_shelves if s.label() == shelf_label), None) if shelf: - return shelf[0] + return shelf shelf_name = shelf_label.replace(' ', '_').lower() new_shelf = hou.shelves.newShelf( @@ -175,23 +155,21 @@ def get_or_create_tool(tool_definition, shelf): existing_tools = shelf.tools() tool_label = tool_definition.get('label') - existing_tool = [ - tool for tool in existing_tools if tool.label() == tool_label - ] - + existing_tool = next( + (tool for tool in existing_tools if tool.label() == tool_label), + None + ) if existing_tool: tool_definition.pop('name', None) tool_definition.pop('label', None) - existing_tool[0].setData(**tool_definition) - return existing_tool[0] + existing_tool.setData(**tool_definition) + return existing_tool tool_name = tool_label.replace(' ', '_').lower() if not os.path.exists(tool_definition['script']): log.warning( - "This path doesn't exist - {}".format( - tool_definition['script'] - ) + "This path doesn't exist - {}".format(tool_definition['script']) ) return diff --git a/openpype/hosts/maya/plugins/publish/collect_render.py b/openpype/hosts/maya/plugins/publish/collect_render.py index 14aac2f206..b1ad3ca58e 100644 --- a/openpype/hosts/maya/plugins/publish/collect_render.py +++ b/openpype/hosts/maya/plugins/publish/collect_render.py @@ -102,23 +102,26 @@ class CollectMayaRender(pyblish.api.ContextPlugin): } for layer in collected_render_layers: - try: - if layer.startswith("LAYER_"): - # this is support for legacy mode where render layers - # started with `LAYER_` prefix. - expected_layer_name = re.search( - r"^LAYER_(.*)", layer).group(1) - else: - # new way is to prefix render layer name with instance - # namespace. - expected_layer_name = re.search( - r"^.+:(.*)", layer).group(1) - except IndexError: + if layer.startswith("LAYER_"): + # this is support for legacy mode where render layers + # started with `LAYER_` prefix. + layer_name_pattern = r"^LAYER_(.*)" + else: + # new way is to prefix render layer name with instance + # namespace. + layer_name_pattern = r"^.+:(.*)" + + # todo: We should have a more explicit way to link the renderlayer + match = re.match(layer_name_pattern, layer) + if not match: msg = "Invalid layer name in set [ {} ]".format(layer) self.log.warning(msg) continue - self.log.info("processing %s" % layer) + expected_layer_name = match.group(1) + self.log.info("Processing '{}' as layer [ {} ]" + "".format(layer, expected_layer_name)) + # check if layer is part of renderSetup if expected_layer_name not in maya_render_layers: msg = "Render layer [ {} ] is not in " "Render Setup".format( diff --git a/openpype/hosts/maya/plugins/publish/validate_rendersettings.py b/openpype/hosts/maya/plugins/publish/validate_rendersettings.py index 2474b2ead6..ca44e98605 100644 --- a/openpype/hosts/maya/plugins/publish/validate_rendersettings.py +++ b/openpype/hosts/maya/plugins/publish/validate_rendersettings.py @@ -268,14 +268,20 @@ class ValidateRenderSettings(pyblish.api.InstancePlugin): # go through definitions and test if such node.attribute exists. # if so, compare its value from the one required. for attr, value in OrderedDict(validation_settings).items(): - # first get node of that type cls.log.debug("{}: {}".format(attr, value)) - node_type = attr.split(".")[0] - attribute_name = ".".join(attr.split(".")[1:]) + if "." not in attr: + cls.log.warning("Skipping invalid attribute defined in " + "validation settings: '{}'".format(attr)) + continue + + node_type, attribute_name = attr.split(".", 1) + + # first get node of that type nodes = cmds.ls(type=node_type) - if not isinstance(nodes, list): - cls.log.warning("No nodes of '{}' found.".format(node_type)) + if not nodes: + cls.log.warning( + "No nodes of type '{}' found.".format(node_type)) continue for node in nodes: diff --git a/openpype/hosts/nuke/api/gizmo_menu.py b/openpype/hosts/nuke/api/gizmo_menu.py index 0f1a3e03fc..9edfc62e3b 100644 --- a/openpype/hosts/nuke/api/gizmo_menu.py +++ b/openpype/hosts/nuke/api/gizmo_menu.py @@ -2,7 +2,7 @@ import os import re import nuke -from openpype.api import Logger +from openpype.lib import Logger log = Logger.get_logger(__name__) diff --git a/openpype/hosts/nuke/api/pipeline.py b/openpype/hosts/nuke/api/pipeline.py index e8fba8ae34..013b5e3ed4 100644 --- a/openpype/hosts/nuke/api/pipeline.py +++ b/openpype/hosts/nuke/api/pipeline.py @@ -14,10 +14,9 @@ from openpype.host import ( INewPublisher ) from openpype.api import ( - Logger, get_current_project_settings ) -from openpype.lib import register_event_callback +from openpype.lib import register_event_callback, Logger from openpype.pipeline import ( register_loader_plugin_path, register_creator_plugin_path, diff --git a/openpype/hosts/nuke/plugins/inventory/repair_old_loaders.py b/openpype/hosts/nuke/plugins/inventory/repair_old_loaders.py index c04c939a8d..764499ff0c 100644 --- a/openpype/hosts/nuke/plugins/inventory/repair_old_loaders.py +++ b/openpype/hosts/nuke/plugins/inventory/repair_old_loaders.py @@ -1,4 +1,4 @@ -from openpype.api import Logger +from openpype.lib import Logger from openpype.pipeline import InventoryAction from openpype.hosts.nuke.api.lib import set_avalon_knob_data diff --git a/openpype/hosts/photoshop/api/launch_logic.py b/openpype/hosts/photoshop/api/launch_logic.py index 0bbb19523d..1f0203dca6 100644 --- a/openpype/hosts/photoshop/api/launch_logic.py +++ b/openpype/hosts/photoshop/api/launch_logic.py @@ -10,7 +10,7 @@ from wsrpc_aiohttp import ( from Qt import QtCore -from openpype.api import Logger +from openpype.lib import Logger from openpype.pipeline import legacy_io from openpype.tools.utils import host_tools from openpype.tools.adobe_webserver.app import WebServerTool diff --git a/openpype/hosts/photoshop/api/pipeline.py b/openpype/hosts/photoshop/api/pipeline.py index f660096630..9f6fc0983c 100644 --- a/openpype/hosts/photoshop/api/pipeline.py +++ b/openpype/hosts/photoshop/api/pipeline.py @@ -3,8 +3,7 @@ from Qt import QtWidgets import pyblish.api -from openpype.api import Logger -from openpype.lib import register_event_callback +from openpype.lib import register_event_callback, Logger from openpype.pipeline import ( legacy_io, register_loader_plugin_path, diff --git a/openpype/hosts/resolve/api/workio.py b/openpype/hosts/resolve/api/workio.py index 5a742ecf7e..5ce73eea53 100644 --- a/openpype/hosts/resolve/api/workio.py +++ b/openpype/hosts/resolve/api/workio.py @@ -1,7 +1,7 @@ """Host API required Work Files tool""" import os -from openpype.api import Logger +from openpype.lib import Logger from .lib import ( get_project_manager, get_current_project, diff --git a/openpype/hosts/traypublisher/plugins/create/create_from_settings.py b/openpype/hosts/traypublisher/plugins/create/create_from_settings.py index 41c1c29bb0..5d80c20309 100644 --- a/openpype/hosts/traypublisher/plugins/create/create_from_settings.py +++ b/openpype/hosts/traypublisher/plugins/create/create_from_settings.py @@ -1,5 +1,6 @@ import os -from openpype.api import get_project_settings, Logger +from openpype.lib import Logger +from openpype.api import get_project_settings log = Logger.get_logger(__name__) diff --git a/openpype/modules/ftrack/scripts/sub_event_status.py b/openpype/modules/ftrack/scripts/sub_event_status.py index 3163642e3f..6c7ecb8351 100644 --- a/openpype/modules/ftrack/scripts/sub_event_status.py +++ b/openpype/modules/ftrack/scripts/sub_event_status.py @@ -15,8 +15,8 @@ from openpype_modules.ftrack.ftrack_server.lib import ( TOPIC_STATUS_SERVER, TOPIC_STATUS_SERVER_RESULT ) -from openpype.api import Logger from openpype.lib import ( + Logger, is_current_version_studio_latest, is_running_from_build, get_expected_version, diff --git a/openpype/modules/ftrack/scripts/sub_event_storer.py b/openpype/modules/ftrack/scripts/sub_event_storer.py index 204cce89e8..a7e77951af 100644 --- a/openpype/modules/ftrack/scripts/sub_event_storer.py +++ b/openpype/modules/ftrack/scripts/sub_event_storer.py @@ -17,10 +17,10 @@ from openpype_modules.ftrack.ftrack_server.lib import ( ) from openpype_modules.ftrack.lib import get_ftrack_event_mongo_info from openpype.lib import ( + Logger, get_openpype_version, get_build_version ) -from openpype.api import Logger log = Logger.get_logger("Event storer") subprocess_started = datetime.datetime.now() diff --git a/openpype/modules/log_viewer/log_view_module.py b/openpype/modules/log_viewer/log_view_module.py index 14be6b392e..da1628b71f 100644 --- a/openpype/modules/log_viewer/log_view_module.py +++ b/openpype/modules/log_viewer/log_view_module.py @@ -1,4 +1,3 @@ -from openpype.api import Logger from openpype.modules import OpenPypeModule from openpype_interfaces import ITrayModule diff --git a/openpype/modules/sync_server/providers/local_drive.py b/openpype/modules/sync_server/providers/local_drive.py index 01bc891d08..8f55dc529b 100644 --- a/openpype/modules/sync_server/providers/local_drive.py +++ b/openpype/modules/sync_server/providers/local_drive.py @@ -4,7 +4,7 @@ import shutil import threading import time -from openpype.api import Logger +from openpype.lib import Logger from openpype.pipeline import Anatomy from .abstract_provider import AbstractProvider diff --git a/openpype/pipeline/create/creator_plugins.py b/openpype/pipeline/create/creator_plugins.py index 5b0532c60a..945a97a99c 100644 --- a/openpype/pipeline/create/creator_plugins.py +++ b/openpype/pipeline/create/creator_plugins.py @@ -9,7 +9,7 @@ from abc import ( import six from openpype.settings import get_system_settings, get_project_settings -from .subset_name import get_subset_name +from openpype.lib import Logger from openpype.pipeline.plugin_discover import ( discover, register_plugin, @@ -18,6 +18,7 @@ from openpype.pipeline.plugin_discover import ( deregister_plugin_path ) +from .subset_name import get_subset_name from .legacy_create import LegacyCreator @@ -143,8 +144,6 @@ class BaseCreator: """ if self._log is None: - from openpype.api import Logger - self._log = Logger.get_logger(self.__class__.__name__) return self._log diff --git a/openpype/pipeline/plugin_discover.py b/openpype/pipeline/plugin_discover.py index 004e530b1c..7edd9ac290 100644 --- a/openpype/pipeline/plugin_discover.py +++ b/openpype/pipeline/plugin_discover.py @@ -2,7 +2,7 @@ import os import inspect import traceback -from openpype.api import Logger +from openpype.lib import Logger from openpype.lib.python_module_tools import ( modules_from_path, classes_from_module, diff --git a/openpype/settings/defaults/project_settings/houdini.json b/openpype/settings/defaults/project_settings/houdini.json index cdf829db57..1517983569 100644 --- a/openpype/settings/defaults/project_settings/houdini.json +++ b/openpype/settings/defaults/project_settings/houdini.json @@ -1,15 +1,5 @@ { - "shelves": [ - { - "shelf_set_name": "OpenPype Shelves", - "shelf_set_source_path": { - "windows": "", - "darwin": "", - "linux": "" - }, - "shelf_definition": [] - } - ], + "shelves": [], "create": { "CreateArnoldAss": { "enabled": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_render_settings.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_render_settings.json index 6ee02ca78f..0cbb684fc6 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_render_settings.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_render_settings.json @@ -140,7 +140,7 @@ }, { "type": "label", - "label": "Add additional options - put attribute and value, like AASamples" + "label": "Add additional options - put attribute and value, like defaultArnoldRenderOptions.AASamples = 4" }, { "type": "dict-modifiable", @@ -276,7 +276,7 @@ }, { "type": "label", - "label": "Add additional options - put attribute and value, like aaFilterSize" + "label": "Add additional options - put attribute and value, like vraySettings.aaFilterSize = 1.5" }, { "type": "dict-modifiable", @@ -405,7 +405,7 @@ }, { "type": "label", - "label": "Add additional options - put attribute and value, like reflectionMaxTraceDepth" + "label": "Add additional options - put attribute and value, like redshiftOptions.reflectionMaxTraceDepth = 3" }, { "type": "dict-modifiable", diff --git a/openpype/tools/launcher/actions.py b/openpype/tools/launcher/actions.py index 546bda1c34..b954110da4 100644 --- a/openpype/tools/launcher/actions.py +++ b/openpype/tools/launcher/actions.py @@ -4,8 +4,9 @@ from Qt import QtWidgets, QtGui from openpype import PLUGINS_DIR from openpype import style -from openpype.api import Logger, resources +from openpype.api import resources from openpype.lib import ( + Logger, ApplictionExecutableNotFound, ApplicationLaunchFailed ) diff --git a/openpype/tools/settings/local_settings/window.py b/openpype/tools/settings/local_settings/window.py index 6a2db3fff5..761b978ab4 100644 --- a/openpype/tools/settings/local_settings/window.py +++ b/openpype/tools/settings/local_settings/window.py @@ -1,4 +1,3 @@ -import logging from Qt import QtWidgets, QtGui from openpype import style @@ -7,10 +6,10 @@ from openpype.settings.lib import ( get_local_settings, save_local_settings ) +from openpype.lib import Logger from openpype.tools.settings import CHILD_OFFSET from openpype.tools.utils import MessageOverlayObject from openpype.api import ( - Logger, SystemSettings, ProjectSettings ) diff --git a/openpype/tools/standalonepublish/widgets/widget_components.py b/openpype/tools/standalonepublish/widgets/widget_components.py index b3280089c3..237e1da583 100644 --- a/openpype/tools/standalonepublish/widgets/widget_components.py +++ b/openpype/tools/standalonepublish/widgets/widget_components.py @@ -6,9 +6,10 @@ import string from Qt import QtWidgets, QtCore -from openpype.api import execute, Logger from openpype.pipeline import legacy_io from openpype.lib import ( + execute, + Logger, get_openpype_execute_args, apply_project_environments_value ) diff --git a/openpype/tools/stdout_broker/app.py b/openpype/tools/stdout_broker/app.py index a42d93dab4..f8dc2111aa 100644 --- a/openpype/tools/stdout_broker/app.py +++ b/openpype/tools/stdout_broker/app.py @@ -6,8 +6,8 @@ import websocket import json from datetime import datetime +from openpype.lib import Logger from openpype_modules.webserver.host_console_listener import MsgAction -from openpype.api import Logger log = Logger.get_logger(__name__) diff --git a/openpype/tools/utils/host_tools.py b/openpype/tools/utils/host_tools.py index d2f05d3302..552ce0d432 100644 --- a/openpype/tools/utils/host_tools.py +++ b/openpype/tools/utils/host_tools.py @@ -7,6 +7,7 @@ import os import pyblish.api from openpype.host import IWorkfileHost, ILoadHost +from openpype.lib import Logger from openpype.pipeline import ( registered_host, legacy_io, @@ -23,6 +24,7 @@ class HostToolsHelper: Class may also contain tools that are available only for one or few hosts. """ + def __init__(self, parent=None): self._log = None # Global parent for all tools (may and may not be set) @@ -42,8 +44,6 @@ class HostToolsHelper: @property def log(self): if self._log is None: - from openpype.api import Logger - self._log = Logger.get_logger(self.__class__.__name__) return self._log diff --git a/openpype/tools/utils/lib.py b/openpype/tools/utils/lib.py index fe7dda454b..31ea26cef9 100644 --- a/openpype/tools/utils/lib.py +++ b/openpype/tools/utils/lib.py @@ -16,11 +16,8 @@ from openpype.style import ( get_objected_colors, ) from openpype.resources import get_image_path -from openpype.lib import filter_profiles -from openpype.api import ( - get_project_settings, - Logger -) +from openpype.lib import filter_profiles, Logger +from openpype.api import get_project_settings from openpype.pipeline import registered_host log = Logger.get_logger(__name__) diff --git a/openpype/version.py b/openpype/version.py index 18ff49ffbf..50864c0f1c 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.14.3-nightly.5" +__version__ = "3.14.4-nightly.1" diff --git a/openpype/widgets/attribute_defs/files_widget.py b/openpype/widgets/attribute_defs/files_widget.py index d29aa1b607..259cb774b0 100644 --- a/openpype/widgets/attribute_defs/files_widget.py +++ b/openpype/widgets/attribute_defs/files_widget.py @@ -138,11 +138,13 @@ class DropEmpty(QtWidgets.QWidget): allowed_items = [item + "s" for item in allowed_items] if not allowed_items: + self._drop_label_widget.setVisible(False) self._items_label_widget.setText( "It is not allowed to add anything here!" ) return + self._drop_label_widget.setVisible(True) items_label = "Multiple " if self._single_item: items_label = "Single " @@ -235,10 +237,41 @@ class FilesModel(QtGui.QStandardItemModel): self._filenames_by_dirpath = collections.defaultdict(set) self._items_by_dirpath = collections.defaultdict(list) + self.rowsAboutToBeRemoved.connect(self._on_about_to_be_removed) + self.rowsInserted.connect(self._on_insert) + @property def id(self): return self._id + def _on_about_to_be_removed(self, parent_index, start, end): + """Make sure that removed items are removed from items mapping. + + Connected with '_on_insert'. When user drag item and drop it to same + view the item is actually removed and creted again but it happens in + inner calls of Qt. + """ + + for row in range(start, end + 1): + index = self.index(row, 0, parent_index) + item_id = index.data(ITEM_ID_ROLE) + if item_id is not None: + self._items_by_id.pop(item_id, None) + + def _on_insert(self, parent_index, start, end): + """Make sure new added items are stored in items mapping. + + Connected to '_on_about_to_be_removed'. Some items are not created + using '_create_item' but are recreated using Qt. So the item is not in + mapping and if it would it would not lead to same item pointer. + """ + + for row in range(start, end + 1): + index = self.index(start, end, parent_index) + item_id = index.data(ITEM_ID_ROLE) + if item_id not in self._items_by_id: + self._items_by_id[item_id] = self.item(row) + def set_multivalue(self, multivalue): """Disable filtering.""" @@ -352,6 +385,10 @@ class FilesModel(QtGui.QStandardItemModel): src_item_id = index.data(ITEM_ID_ROLE) src_item = self._items_by_id.get(src_item_id) + src_row = None + if src_item: + src_row = src_item.row() + # Take out items that should be moved items = [] for item_id in item_ids: @@ -365,10 +402,12 @@ class FilesModel(QtGui.QStandardItemModel): return False # Calculate row where items should be inserted - if src_item: - src_row = src_item.row() - else: - src_row = root.rowCount() + row_count = root.rowCount() + if src_row is None: + src_row = row_count + + if src_row > row_count: + src_row = row_count root.insertRow(src_row, items) return True @@ -592,6 +631,13 @@ class FilesView(QtWidgets.QListView): self._remove_btn.setVisible(not multivalue) + def update_remove_btn_visibility(self): + model = self.model() + visible = False + if model: + visible = model.rowCount() > 0 + self._remove_btn.setVisible(visible) + def has_selected_item_ids(self): """Is any index selected.""" for index in self.selectionModel().selectedIndexes(): @@ -655,6 +701,7 @@ class FilesView(QtWidgets.QListView): def showEvent(self, event): super(FilesView, self).showEvent(event) self._update_remove_btn() + self.update_remove_btn_visibility() class FilesWidget(QtWidgets.QFrame): @@ -673,12 +720,13 @@ class FilesWidget(QtWidgets.QFrame): files_proxy_model.setSourceModel(files_model) files_view = FilesView(self) files_view.setModel(files_proxy_model) - files_view.setVisible(False) - layout = QtWidgets.QHBoxLayout(self) + layout = QtWidgets.QStackedLayout(self) layout.setContentsMargins(0, 0, 0, 0) - layout.addWidget(empty_widget, 1) - layout.addWidget(files_view, 1) + layout.setStackingMode(layout.StackAll) + layout.addWidget(empty_widget) + layout.addWidget(files_view) + layout.setCurrentWidget(empty_widget) files_proxy_model.rowsInserted.connect(self._on_rows_inserted) files_proxy_model.rowsRemoved.connect(self._on_rows_removed) @@ -698,6 +746,8 @@ class FilesWidget(QtWidgets.QFrame): self._widgets_by_id = {} + self._layout = layout + def _set_multivalue(self, multivalue): if self._multivalue == multivalue: return @@ -774,6 +824,8 @@ class FilesWidget(QtWidgets.QFrame): if not self._in_set_value: self.value_changed.emit() + self._update_visibility() + def _on_rows_removed(self, parent_index, start_row, end_row): available_item_ids = set() for row in range(self._files_proxy_model.rowCount()): @@ -793,6 +845,7 @@ class FilesWidget(QtWidgets.QFrame): if not self._in_set_value: self.value_changed.emit() + self._update_visibility() def _on_split_request(self): if self._multivalue: @@ -836,29 +889,6 @@ class FilesWidget(QtWidgets.QFrame): menu.popup(pos) - def sizeHint(self): - # Get size hints of widget and visible widgets - result = super(FilesWidget, self).sizeHint() - if not self._files_view.isVisible(): - not_visible_hint = self._files_view.sizeHint() - else: - not_visible_hint = self._empty_widget.sizeHint() - - # Get margins of this widget - margins = self.layout().contentsMargins() - - # Change size hint based on result of maximum size hint of widgets - result.setWidth(max( - result.width(), - not_visible_hint.width() + margins.left() + margins.right() - )) - result.setHeight(max( - result.height(), - not_visible_hint.height() + margins.top() + margins.bottom() - )) - - return result - def dragEnterEvent(self, event): if self._multivalue: return @@ -890,7 +920,6 @@ class FilesWidget(QtWidgets.QFrame): mime_data = event.mimeData() if mime_data.hasUrls(): event.accept() - # event.setDropAction(QtCore.Qt.CopyAction) filepaths = [] for url in mime_data.urls(): filepath = url.toLocalFile() @@ -956,13 +985,15 @@ class FilesWidget(QtWidgets.QFrame): def _add_filepaths(self, filepaths): self._files_model.add_filepaths(filepaths) - self._update_visibility() def _remove_item_by_ids(self, item_ids): self._files_model.remove_item_by_ids(item_ids) - self._update_visibility() def _update_visibility(self): files_exists = self._files_proxy_model.rowCount() > 0 - self._files_view.setVisible(files_exists) - self._empty_widget.setVisible(not files_exists) + if files_exists: + current_widget = self._files_view + else: + current_widget = self._empty_widget + self._layout.setCurrentWidget(current_widget) + self._files_view.update_remove_btn_visibility() diff --git a/tests/lib/testing_classes.py b/tests/lib/testing_classes.py index 85121f0d73..78a9f81095 100644 --- a/tests/lib/testing_classes.py +++ b/tests/lib/testing_classes.py @@ -10,7 +10,7 @@ import glob import platform from tests.lib.db_handler import DBHandler -from distribution.file_handler import RemoteFileHandler +from common.openpype_common.distribution.file_handler import RemoteFileHandler class BaseTest: