From 893a84526c8308c12cdbc7f915470735a1d8faa0 Mon Sep 17 00:00:00 2001 From: Bo Zhou Date: Fri, 18 Mar 2022 19:15:07 +0900 Subject: [PATCH 001/213] add new render product for 3delight --- openpype/hosts/maya/api/lib_renderproducts.py | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/openpype/hosts/maya/api/lib_renderproducts.py b/openpype/hosts/maya/api/lib_renderproducts.py index 0c349988748..69c4eae18e6 100644 --- a/openpype/hosts/maya/api/lib_renderproducts.py +++ b/openpype/hosts/maya/api/lib_renderproducts.py @@ -1107,6 +1107,81 @@ def get_files(self, product, camera): return new_files +class RenderProducts3Delight(ARenderProducts): + """Expected files for Renderman renderer. + + Warning: + This is very rudimentary and needs more love and testing. + """ + + renderer = "_3delight" + + def get_render_products(self): + """Get all AOVs. + + See Also: + :func:`ARenderProducts.get_render_products()` + + """ + cameras = [ + self.sanitize_camera_name(c) + for c in self.get_renderable_cameras() + ] + + if not cameras: + cameras = [ + self.sanitize_camera_name( + self.get_renderable_cameras()[0]) + ] + products = [] + + default_ext = "exr" + + nodes = cmds.listConnections('dlRenderGlobals1') + assert len(nodes) == 1 + node = nodes[0] + + num_layers = cmds.getAttr( + '{}.layerOutput'.format(node), + size=True) + for i in range(num_layers): + output = cmds.getAttr( + '{}.layerOutput[{}]'.format(node, i)) + if not output: + continue + + output_var = cmds.getAttr( + '{}.layerOutputVariables[{}]'.format(node, i)) + output_var_tokens = layerOutputVariable.split('|') + name = output_var_tokens[4] + + for camera in cameras: + product = RenderProduct(productName=name, + ext=default_ext, + camera=camera) + products.append(product) + + return products + + def get_files(self, product, camera): + """Get expected files. + + See Also: + :func:`ARenderProducts.get_files()` + """ + files = super(RenderProducts3Delight, self).get_files(product, camera) + + layer_data = self.layer_data + new_files = [] + for file in files: + new_file = "{}/{}/{}".format( + layer_data["sceneName"], layer_data["layerName"], file + ) + new_files.append(new_file) + + return new_files + + class AOVError(Exception): """Custom exception for determining AOVs.""" From e85ef95b443825badf6b30e598712ba24cd6aeb0 Mon Sep 17 00:00:00 2001 From: Bo Zhou Date: Fri, 18 Mar 2022 20:00:57 +0900 Subject: [PATCH 002/213] improve 3delight render product class and return it --- openpype/hosts/maya/api/lib_renderproducts.py | 35 ++++++------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/openpype/hosts/maya/api/lib_renderproducts.py b/openpype/hosts/maya/api/lib_renderproducts.py index 69c4eae18e6..97aa8e89573 100644 --- a/openpype/hosts/maya/api/lib_renderproducts.py +++ b/openpype/hosts/maya/api/lib_renderproducts.py @@ -77,6 +77,7 @@ "arnold": "defaultRenderGlobals.imageFilePrefix", "renderman": "rmanGlobals.imageFileFormat", "redshift": "defaultRenderGlobals.imageFilePrefix", + "_3delight": "defaultRenderGlobals.imageFilePrefix", } @@ -154,7 +155,8 @@ class Instance(object): "arnold": RenderProductsArnold, "vray": RenderProductsVray, "redshift": RenderProductsRedshift, - "renderman": RenderProductsRenderman + "renderman": RenderProductsRenderman, + "_3delight": RenderProducts3Delight }.get(renderer_name.lower(), None) if renderer is None: raise UnsupportedRendererException( @@ -1137,13 +1139,16 @@ def get_render_products(self): default_ext = "exr" - nodes = cmds.listConnections('dlRenderGlobals1') + nodes = cmds.listConnections( + 'dlRenderGlobals1', + type='dlRenderSettings') assert len(nodes) == 1 node = nodes[0] num_layers = cmds.getAttr( - '{}.layerOutput'.format(node), + '{}.layerOutputVariables'.format(node), size=True) + assert num_layers > 0 for i in range(num_layers): output = cmds.getAttr( '{}.layerOutput[{}]'.format(node, i)) @@ -1152,35 +1157,17 @@ def get_render_products(self): output_var = cmds.getAttr( '{}.layerOutputVariables[{}]'.format(node, i)) - output_var_tokens = layerOutputVariable.split('|') - name = output_var_tokens[4] + output_var_tokens = output_var.split('|') + aov_name = output_var_tokens[4] for camera in cameras: - product = RenderProduct(productName=name, + product = RenderProduct(productName=aov_name, ext=default_ext, camera=camera) products.append(product) return products - def get_files(self, product, camera): - """Get expected files. - - See Also: - :func:`ARenderProducts.get_files()` - """ - files = super(RenderProducts3Delight, self).get_files(product, camera) - - layer_data = self.layer_data - new_files = [] - for file in files: - new_file = "{}/{}/{}".format( - layer_data["sceneName"], layer_data["layerName"], file - ) - new_files.append(new_file) - - return new_files - class AOVError(Exception): """Custom exception for determining AOVs.""" From 3ec621a8efa97bc33c63f33fd87a62a76fa7d0c2 Mon Sep 17 00:00:00 2001 From: Bo Zhou Date: Fri, 18 Mar 2022 20:02:29 +0900 Subject: [PATCH 003/213] add method _set_3delight_settings to render creator --- .../maya/plugins/create/create_render.py | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_render.py b/openpype/hosts/maya/plugins/create/create_render.py index 9002ae38763..c06fe8a76dd 100644 --- a/openpype/hosts/maya/plugins/create/create_render.py +++ b/openpype/hosts/maya/plugins/create/create_render.py @@ -77,7 +77,8 @@ class CreateRender(plugin.Creator): 'vray': 'vraySettings.fileNamePrefix', 'arnold': 'defaultRenderGlobals.imageFilePrefix', 'renderman': 'defaultRenderGlobals.imageFilePrefix', - 'redshift': 'defaultRenderGlobals.imageFilePrefix' + 'redshift': 'defaultRenderGlobals.imageFilePrefix', + '_3delight': 'defaultRenderGlobals.imageFilePrefix' } _image_prefixes = { @@ -85,7 +86,8 @@ class CreateRender(plugin.Creator): 'vray': 'maya///', 'arnold': 'maya///{aov_separator}', # noqa 'renderman': 'maya///{aov_separator}', - 'redshift': 'maya///' # noqa + 'redshift': 'maya///', # noqa + '_3delight': 'maya///' # noqa } _aov_chars = { @@ -462,6 +464,8 @@ def _set_default_renderer_settings(self, renderer): asset["data"].get("resolutionHeight")) self._set_global_output_settings() + if renderer == "_3delight": + self._set_3delight_settings(asset) def _set_vray_settings(self, asset): # type: (dict) -> None @@ -507,6 +511,38 @@ def _set_vray_settings(self, asset): "{}.height".format(node), asset["data"].get("resolutionHeight")) + def _set_3delight_settings(self, asset): + # type: (dict) -> None + """Sets important settings for 3Delight.""" + nodes = cmds.listConnections( + 'dlRenderGlobals1', + type='dlRenderSettings') + assert len(nodes) == 1 + node = nodes[0] + + # frame range + start_frame = int(cmds.playbackOptions(query=True, + animationStartTime=True)) + end_frame = int(cmds.playbackOptions(query=True, + animationEndTime=True)) + + cmds.setAttr( + "{}.startFrame".format(node), start_frame) + cmds.setAttr( + "{}.endFrame".format(node), end_frame) + + # outputOptionsDefault + cmds.setAttr( + "{}.outputOptionsDefault".format(node), 2) + + # resolution + cmds.setAttr( + "defaultResolution.width", + asset["data"].get("resolutionWidth")) + cmds.setAttr( + "defaultResolution.height", + asset["data"].get("resolutionHeight")) + @staticmethod def _set_global_output_settings(): # enable animation From 87c697b51bd42655ef3936187240b4a2681efaac Mon Sep 17 00:00:00 2001 From: DMO Date: Wed, 22 Jun 2022 13:52:02 +0900 Subject: [PATCH 004/213] Rewrote large portions of the file to handle different nodes that may contain texture files. Currently supported are: - file (maya) - aiImage (Arnold) - RedshiftNormalMap (Redshift) - dlTexture (3Delight) - dlTriplanar (3Delight) --- .../publish/collect_multiverse_look.py | 260 +++++++++++------- 1 file changed, 153 insertions(+), 107 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py b/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py index edf40a27a6f..4bd2476feb9 100644 --- a/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py +++ b/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py @@ -21,37 +21,68 @@ MIPMAP_EXTENSIONS = ['tdl'] -def get_look_attrs(node): - """Returns attributes of a node that are important for the look. +class _NodeTypeAttrib(object): + """docstring for _NodeType""" - These are the "changed" attributes (those that have edits applied - in the current scene). + def __init__(self, name, fname, computed_fname=None, colour_space=None): + self.name = name + self.fname = fname + self.computed_fname = computed_fname or fname + self.colour_space = colour_space or "colorSpace" - Returns: - list: Attribute names to extract + def get_fname(self, node): + return "{}.{}".format(node, self.fname) - """ - # When referenced get only attributes that are "changed since file open" - # which includes any reference edits, otherwise take *all* user defined - # attributes - is_referenced = cmds.referenceQuery(node, isNodeReferenced=True) - result = cmds.listAttr(node, userDefined=True, - changedSinceFileOpen=is_referenced) or [] + def get_computed_fname(self, node): + return "{}.{}".format(node, self.computed_fname) + + def get_colour_space(self, node): + return "{}.{}".format(node, self.colour_space) + + def __str__(self): + return "_NodeTypeAttrib(name={}, fname={}, " + "computed_fname={}, colour_space={})".format( + self.name, self.fname, self.computed_fname, self.colour_space) + + +NODETYPES = { + "file": [_NodeTypeAttrib("file", "fileTextureName", + "computedFileTextureNamePattern")], + "aiImage": [_NodeTypeAttrib("aiImage", "filename")], + "RedshiftNormalMap": [_NodeTypeAttrib("RedshiftNormalMap", "tex0")], + "dlTexture": [_NodeTypeAttrib("dlTexture", "textureFile", + None, "textureFile_meta_colorspace")], + "dlTriplanar": [_NodeTypeAttrib("dlTriplanar", "colorTexture", + None, "colorTexture_meta_colorspace"), + _NodeTypeAttrib("dlTriplanar", "floatTexture", + None, "floatTexture_meta_colorspace"), + _NodeTypeAttrib("dlTriplanar", "heightTexture", + None, "heightTexture_meta_colorspace")] +} + + +def get_file_paths_for_node(node): + """Gets all the file paths in this node. + + Returns all filepaths that this node references. Some node types only + reference one, but others, like dlTriplanar, can reference 3. + + Args: + node (str): Name of the Maya node - # `cbId` is added when a scene is saved, ignore by default - if "cbId" in result: - result.remove("cbId") + Returns + list(str): A list with all evaluated maya attributes for filepaths. + """ - # For shapes allow render stat changes - if cmds.objectType(node, isAType="shape"): - attrs = cmds.listAttr(node, changedSinceFileOpen=True) or [] - for attr in attrs: - if attr in SHAPE_ATTRS: - result.append(attr) - elif attr.startswith('ai'): - result.append(attr) + node_type = cmds.nodeType(node) + if node_type not in NODETYPES: + return [] - return result + paths = [] + for node_type_attr in NODETYPES[node_type]: + fname = cmds.getAttr("{}.{}".format(node, node_type_attr.fname)) + paths.append(fname) + return paths def node_uses_image_sequence(node): @@ -69,13 +100,29 @@ def node_uses_image_sequence(node): """ # useFrameExtension indicates an explicit image sequence - node_path = get_file_node_path(node).lower() + paths = get_file_node_paths(node) + paths = [path.lower() for path in paths] # The following tokens imply a sequence patterns = ["", "", "", "u_v", ""] lower = texture_pattern.lower() if any(pattern in lower for pattern in patterns): - return texture_pattern + return [texture_pattern] - if cmds.nodeType(node) == 'aiImage': - return cmds.getAttr('{0}.filename'.format(node)) - if cmds.nodeType(node) == 'RedshiftNormalMap': - return cmds.getAttr('{}.tex0'.format(node)) - - # otherwise use fileTextureName - return cmds.getAttr('{0}.fileTextureName'.format(node)) + return get_file_paths_for_node(node) def get_file_node_files(node): @@ -181,15 +222,13 @@ def get_file_node_files(node): """ - path = get_file_node_path(node) - path = cmds.workspace(expandName=path) + paths = get_file_node_paths(node) + paths = [cmds.workspace(expandName=path) for path in paths] if node_uses_image_sequence(node): - glob_pattern = seq_to_glob(path) - return glob.glob(glob_pattern) - elif os.path.exists(path): - return [path] + globs = [glob.glob(seq_to_glob(path)) for path in paths] + return globs else: - return [] + return list(filter(lambda x: os.path.exists(x), paths)) def get_mipmap(fname): @@ -211,6 +250,11 @@ def is_mipmap(fname): class CollectMultiverseLookData(pyblish.api.InstancePlugin): """Collect Multiverse Look + Searches through the overrides finding all material overrides. From there + it extracts the shading group and then finds all texture files in the + shading group network. It also checks for mipmap versions of texture files + and adds them to the resouces to get published. + """ order = pyblish.api.CollectorOrder + 0.2 @@ -258,12 +302,20 @@ def process(self, instance): shadingGroup), "members": list()} # The SG may reference files, add those too! - history = cmds.listHistory(shadingGroup) - files = cmds.ls(history, type="file", long=True) + history = cmds.listHistory( + shadingGroup, allConnections=True) + + # We need to iterate over node_types since `cmds.ls` may + # error out if we don't have the appropriate plugin loaded. + files = [] + for node_type in NODETYPES.keys(): + files += cmds.ls(history, + type=node_type, + long=True) for f in files: resources = self.collect_resource(f, publishMipMap) - instance.data["resources"].append(resources) + instance.data["resources"] += resources elif isinstance(matOver, multiverse.MaterialSourceUsdPath): # TODO: Handle this later. @@ -284,69 +336,63 @@ def collect_resource(self, node, publishMipMap): dict """ - self.log.debug("processing: {}".format(node)) - if cmds.nodeType(node) not in ["file", "aiImage", "RedshiftNormalMap"]: - self.log.error( - "Unsupported file node: {}".format(cmds.nodeType(node))) + node_type = cmds.nodeType(node) + self.log.debug("processing: {}/{}".format(node, node_type)) + + if not node_type in NODETYPES: + self.log.error("Unsupported file node: {}".format(node_type)) raise AssertionError("Unsupported file node") - if cmds.nodeType(node) == 'file': - self.log.debug(" - file node") - attribute = "{}.fileTextureName".format(node) - computed_attribute = "{}.computedFileTextureNamePattern".format( - node) - elif cmds.nodeType(node) == 'aiImage': - self.log.debug("aiImage node") - attribute = "{}.filename".format(node) - computed_attribute = attribute - elif cmds.nodeType(node) == 'RedshiftNormalMap': - self.log.debug("RedshiftNormalMap node") - attribute = "{}.tex0".format(node) - computed_attribute = attribute - - source = cmds.getAttr(attribute) - self.log.info(" - file source: {}".format(source)) - color_space_attr = "{}.colorSpace".format(node) - try: - color_space = cmds.getAttr(color_space_attr) - except ValueError: - # node doesn't have colorspace attribute + resources = [] + for node_type_attr in NODETYPES[node_type]: + fname_attrib = node_type_attr.get_fname(node) + computed_fname_attrib = node_type_attr.get_computed_fname(node) + colour_space_attrib = node_type_attr.get_colour_space(node) + + source = cmds.getAttr(fname_attrib) color_space = "Raw" - # Compare with the computed file path, e.g. the one with the - # pattern in it, to generate some logging information about this - # difference - # computed_attribute = "{}.computedFileTextureNamePattern".format(node) - computed_source = cmds.getAttr(computed_attribute) - if source != computed_source: - self.log.debug("Detected computed file pattern difference " - "from original pattern: {0} " - "({1} -> {2})".format(node, - source, - computed_source)) - - # We replace backslashes with forward slashes because V-Ray - # can't handle the UDIM files with the backslashes in the - # paths as the computed patterns - source = source.replace("\\", "/") - - files = get_file_node_files(node) - files = self.handle_files(files, publishMipMap) - if len(files) == 0: - self.log.error("No valid files found from node `%s`" % node) - - self.log.info("collection of resource done:") - self.log.info(" - node: {}".format(node)) - self.log.info(" - attribute: {}".format(attribute)) - self.log.info(" - source: {}".format(source)) - self.log.info(" - file: {}".format(files)) - self.log.info(" - color space: {}".format(color_space)) - - # Define the resource - return {"node": node, - "attribute": attribute, - "source": source, # required for resources - "files": files, - "color_space": color_space} # required for resources + try: + color_space = cmds.getAttr(colour_space_attrib) + except ValueError: + # node doesn't have colorspace attribute, use "Raw" from before + pass + # Compare with the computed file path, e.g. the one with the + # pattern in it, to generate some logging information about this + # difference + # computed_attribute = "{}.computedFileTextureNamePattern".format(node) + computed_source = cmds.getAttr(computed_fname_attrib) + if source != computed_source: + self.log.debug("Detected computed file pattern difference " + "from original pattern: {0} " + "({1} -> {2})".format(node, + source, + computed_source)) + + # We replace backslashes with forward slashes because V-Ray + # can't handle the UDIM files with the backslashes in the + # paths as the computed patterns + source = source.replace("\\", "/") + + files = get_file_node_files(node) + files = self.handle_files(files, publishMipMap) + if len(files) == 0: + self.log.error("No valid files found from node `%s`" % node) + + self.log.info("collection of resource done:") + self.log.info(" - node: {}".format(node)) + self.log.info(" - attribute: {}".format(fname_attrib)) + self.log.info(" - source: {}".format(source)) + self.log.info(" - file: {}".format(files)) + self.log.info(" - color space: {}".format(color_space)) + + # Define the resource + resource = {"node": node, + "attribute": fname_attrib, + "source": source, # required for resources + "files": files, + "color_space": color_space} # required for resources + resources.append(resource) + return resources def handle_files(self, files, publishMipMap): """This will go through all the files and make sure that they are From 2007c759478b08b002a0d0a91816c5cf0e769c3f Mon Sep 17 00:00:00 2001 From: DMO Date: Fri, 24 Jun 2022 15:22:31 +0900 Subject: [PATCH 005/213] Reverting `mvUsd` family back to `usd` so that other software can create standard Usd files and still be imported directly by Multiverse. --- openpype/hosts/maya/plugins/create/create_multiverse_usd.py | 2 +- openpype/hosts/maya/plugins/load/load_multiverse_usd.py | 2 +- openpype/hosts/maya/plugins/load/load_reference.py | 3 ++- openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py | 2 +- openpype/plugins/publish/integrate_new.py | 1 - 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_multiverse_usd.py b/openpype/hosts/maya/plugins/create/create_multiverse_usd.py index 5290d5143fc..8cd76b5f406 100644 --- a/openpype/hosts/maya/plugins/create/create_multiverse_usd.py +++ b/openpype/hosts/maya/plugins/create/create_multiverse_usd.py @@ -6,7 +6,7 @@ class CreateMultiverseUsd(plugin.Creator): name = "mvUsdMain" label = "Multiverse USD Asset" - family = "mvUsd" + family = "usd" icon = "cubes" def __init__(self, *args, **kwargs): diff --git a/openpype/hosts/maya/plugins/load/load_multiverse_usd.py b/openpype/hosts/maya/plugins/load/load_multiverse_usd.py index 3350dc6ac9d..76d7c306a08 100644 --- a/openpype/hosts/maya/plugins/load/load_multiverse_usd.py +++ b/openpype/hosts/maya/plugins/load/load_multiverse_usd.py @@ -16,7 +16,7 @@ class MultiverseUsdLoader(load.LoaderPlugin): """Read USD data in a Multiverse Compound""" - families = ["model", "mvUsd", "mvUsdComposition", "mvUsdOverride", + families = ["model", "usd", "mvUsdComposition", "mvUsdOverride", "pointcache", "animation"] representations = ["usd", "usda", "usdc", "usdz", "abc"] diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index e4355ed3d4e..0a2640014ce 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -25,7 +25,8 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): "rig", "camerarig", "xgen", - "staticMesh"] + "staticMesh", + "mvLook"] representations = ["ma", "abc", "fbx", "mb"] label = "Reference" diff --git a/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py b/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py index 3654be7b34c..b1aaf9d9ba0 100644 --- a/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py +++ b/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py @@ -26,7 +26,7 @@ class ExtractMultiverseUsd(openpype.api.Extractor): label = "Extract Multiverse USD Asset" hosts = ["maya"] - families = ["mvUsd"] + families = ["usd"] scene_type = "usd" file_formats = ["usd", "usda", "usdz"] diff --git a/openpype/plugins/publish/integrate_new.py b/openpype/plugins/publish/integrate_new.py index 24711052509..f5ca1251896 100644 --- a/openpype/plugins/publish/integrate_new.py +++ b/openpype/plugins/publish/integrate_new.py @@ -110,7 +110,6 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): "staticMesh", "skeletalMesh", "mvLook", - "mvUsd", "mvUsdComposition", "mvUsdOverride", "simpleUnrealTexture" From ab1c86a96dda5eb92281e810027d8952c91b0a31 Mon Sep 17 00:00:00 2001 From: DMO Date: Tue, 28 Jun 2022 15:40:45 +0900 Subject: [PATCH 006/213] Do not lock nodes, it's not needed and it makes things much harder. --- openpype/hosts/maya/plugins/load/load_multiverse_usd.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_multiverse_usd.py b/openpype/hosts/maya/plugins/load/load_multiverse_usd.py index 76d7c306a08..24b97db365b 100644 --- a/openpype/hosts/maya/plugins/load/load_multiverse_usd.py +++ b/openpype/hosts/maya/plugins/load/load_multiverse_usd.py @@ -47,9 +47,6 @@ def load(self, context, name=None, namespace=None, options=None): transform = cmds.listRelatives( shape, parent=True, fullPath=True)[0] - # Lock the shape node so the user cannot delete it. - cmds.lockNode(shape, lock=True) - nodes = [transform, shape] self[:] = nodes From c5fd2a970eab972b87468d5c3f7e796b3c11c2e1 Mon Sep 17 00:00:00 2001 From: DMO Date: Tue, 28 Jun 2022 17:13:18 +0900 Subject: [PATCH 007/213] I was incorrectly globing into an array of arrays instead of single long array. --- .../hosts/maya/plugins/publish/collect_multiverse_look.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py b/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py index 4bd2476feb9..b11dbaeba60 100644 --- a/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py +++ b/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py @@ -225,7 +225,9 @@ def get_file_node_files(node): paths = get_file_node_paths(node) paths = [cmds.workspace(expandName=path) for path in paths] if node_uses_image_sequence(node): - globs = [glob.glob(seq_to_glob(path)) for path in paths] + globs = [] + for path in paths: + globs += glob.glob(seq_to_glob(path)) return globs else: return list(filter(lambda x: os.path.exists(x), paths)) From 044946e8c5f65a8a4ae308862b2ed5d57494e501 Mon Sep 17 00:00:00 2001 From: DMO Date: Tue, 28 Jun 2022 20:47:28 +0900 Subject: [PATCH 008/213] Strip UsdComp namespaces. Write attributes from mvLook. Formatting. --- .../hosts/maya/plugins/create/create_multiverse_usd_comp.py | 2 +- openpype/hosts/maya/plugins/publish/collect_look.py | 3 ++- .../hosts/maya/plugins/publish/collect_multiverse_look.py | 2 +- .../hosts/maya/plugins/publish/extract_multiverse_look.py | 2 +- .../hosts/maya/plugins/publish/validate_mvlook_contents.py | 5 +++-- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_multiverse_usd_comp.py b/openpype/hosts/maya/plugins/create/create_multiverse_usd_comp.py index ed466a80688..a92969eb9a9 100644 --- a/openpype/hosts/maya/plugins/create/create_multiverse_usd_comp.py +++ b/openpype/hosts/maya/plugins/create/create_multiverse_usd_comp.py @@ -17,7 +17,7 @@ def __init__(self, *args, **kwargs): # Order of `fileFormat` must match extract_multiverse_usd_comp.py self.data["fileFormat"] = ["usda", "usd"] - self.data["stripNamespaces"] = False + self.data["stripNamespaces"] = True self.data["mergeTransformAndShape"] = False self.data["flattenContent"] = False self.data["writeAsCompoundLayers"] = False diff --git a/openpype/hosts/maya/plugins/publish/collect_look.py b/openpype/hosts/maya/plugins/publish/collect_look.py index e8ada57f8f0..28c57e04b5c 100644 --- a/openpype/hosts/maya/plugins/publish/collect_look.py +++ b/openpype/hosts/maya/plugins/publish/collect_look.py @@ -440,7 +440,8 @@ def collect(self, instance): for res in self.collect_resources(n): instance.data["resources"].append(res) - self.log.info("Collected resources: {}".format(instance.data["resources"])) + self.log.info("Collected resources: {}".format( + instance.data["resources"])) # Log warning when no relevant sets were retrieved for the look. if ( diff --git a/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py b/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py index b11dbaeba60..4c50e4df27b 100644 --- a/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py +++ b/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py @@ -256,7 +256,7 @@ class CollectMultiverseLookData(pyblish.api.InstancePlugin): it extracts the shading group and then finds all texture files in the shading group network. It also checks for mipmap versions of texture files and adds them to the resouces to get published. - + """ order = pyblish.api.CollectorOrder + 0.2 diff --git a/openpype/hosts/maya/plugins/publish/extract_multiverse_look.py b/openpype/hosts/maya/plugins/publish/extract_multiverse_look.py index 82e2b41929c..b97314d5a11 100644 --- a/openpype/hosts/maya/plugins/publish/extract_multiverse_look.py +++ b/openpype/hosts/maya/plugins/publish/extract_multiverse_look.py @@ -73,7 +73,7 @@ def default_options(self): "writeAll": False, "writeTransforms": False, "writeVisibility": False, - "writeAttributes": False, + "writeAttributes": True, "writeMaterials": True, "writeVariants": False, "writeVariantsDefinition": False, diff --git a/openpype/hosts/maya/plugins/publish/validate_mvlook_contents.py b/openpype/hosts/maya/plugins/publish/validate_mvlook_contents.py index bac2c030c80..a755ec1da9e 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mvlook_contents.py +++ b/openpype/hosts/maya/plugins/publish/validate_mvlook_contents.py @@ -80,13 +80,14 @@ def valid_file(self, fname): def is_or_has_mipmap(self, fname, files): ext = os.path.splitext(fname)[1][1:] if ext in MIPMAP_EXTENSIONS: - self.log.debug("Is a mipmap '{}'".format(fname)) + self.log.debug(" - Is a mipmap '{}'".format(fname)) return True for colour_space in COLOUR_SPACES: for mipmap_ext in MIPMAP_EXTENSIONS: mipmap_fname = '.'.join([fname, colour_space, mipmap_ext]) if mipmap_fname in files: - self.log.debug("Has a mipmap '{}'".format(fname)) + self.log.debug( + " - Has a mipmap '{}'".format(mipmap_fname)) return True return False From ef2f4f3e4551e38a0c92461794a8fc1fed45717b Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 29 Jun 2022 16:45:26 +0200 Subject: [PATCH 009/213] Remove fallback to context `handleStart` and `handleEnd` if instance `frameStart` and `frameEnd` matches context --- .../maya/plugins/publish/collect_instances.py | 43 +++++-------------- 1 file changed, 11 insertions(+), 32 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_instances.py b/openpype/hosts/maya/plugins/publish/collect_instances.py index ad1f794680d..6c6819f0a24 100644 --- a/openpype/hosts/maya/plugins/publish/collect_instances.py +++ b/openpype/hosts/maya/plugins/publish/collect_instances.py @@ -74,13 +74,6 @@ def process(self, context): objectset = cmds.ls("*.id", long=True, type="objectSet", recursive=True, objectsOnly=True) - ctx_frame_start = context.data['frameStart'] - ctx_frame_end = context.data['frameEnd'] - ctx_handle_start = context.data['handleStart'] - ctx_handle_end = context.data['handleEnd'] - ctx_frame_start_handle = context.data['frameStartHandle'] - ctx_frame_end_handle = context.data['frameEndHandle'] - context.data['objectsets'] = objectset for objset in objectset: @@ -156,34 +149,20 @@ def process(self, context): # Append start frame and end frame to label if present if "frameStart" and "frameEnd" in data: - # if frame range on maya set is the same as full shot range - # adjust the values to match the asset data - if (ctx_frame_start_handle == data["frameStart"] - and ctx_frame_end_handle == data["frameEnd"]): # noqa: W503, E501 - data["frameStartHandle"] = ctx_frame_start_handle - data["frameEndHandle"] = ctx_frame_end_handle - data["frameStart"] = ctx_frame_start - data["frameEnd"] = ctx_frame_end - data["handleStart"] = ctx_handle_start - data["handleEnd"] = ctx_handle_end - - # if there are user values on start and end frame not matching - # the asset, use them - - else: - if "handles" in data: - data["handleStart"] = data["handles"] - data["handleEnd"] = data["handles"] - else: - data["handleStart"] = 0 - data["handleEnd"] = 0 - - data["frameStartHandle"] = data["frameStart"] - data["handleStart"] # noqa: E501 - data["frameEndHandle"] = data["frameEnd"] + data["handleEnd"] # noqa: E501 - + # Backwards compatibility for 'handles' data if "handles" in data: + data["handleStart"] = data["handles"] + data["handleEnd"] = data["handles"] data.pop('handles') + # Take handles from context if not set locally on the instance + for key in ["handleStart", "handleEnd"]: + if key not in data: + data[key] = context.data[key] + + data["frameStartHandle"] = data["frameStart"] - data["handleStart"] # noqa: E501 + data["frameEndHandle"] = data["frameEnd"] + data["handleEnd"] # noqa: E501 + label += " [{0}-{1}]".format(int(data["frameStartHandle"]), int(data["frameEndHandle"])) From cb82e66d01c506bd9038acec6904a0a7b39cbb37 Mon Sep 17 00:00:00 2001 From: DMO Date: Wed, 6 Jul 2022 11:30:47 +0900 Subject: [PATCH 010/213] Naming suffixes can be overridden in project settings, lets print out what they *actually* are. --- .../publish/validate_transform_naming_suffix.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py b/openpype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py index 6f5ff24b9c0..51561b35e14 100644 --- a/openpype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py +++ b/openpype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py @@ -20,6 +20,7 @@ class ValidateTransformNamingSuffix(pyblish.api.InstancePlugin): - nurbsSurface: _NRB - locator: _LOC - null/group: _GRP + Suffices can also be overriden by project settings. .. warning:: This grabs the first child shape as a reference and doesn't use the @@ -43,6 +44,13 @@ class ValidateTransformNamingSuffix(pyblish.api.InstancePlugin): ALLOW_IF_NOT_IN_SUFFIX_TABLE = True + @classmethod + def get_table_for_invalid(cls): + ss = [] + for k,v in cls.SUFFIX_NAMING_TABLE.items(): + ss.append(" - {}: {}".format(k,", ".join(v))) + return "\n".join(ss) + @staticmethod def is_valid_name(node_name, shape_type, SUFFIX_NAMING_TABLE, ALLOW_IF_NOT_IN_SUFFIX_TABLE): @@ -105,5 +113,7 @@ def process(self, instance): """ invalid = self.get_invalid(instance) if invalid: + valid = self.get_table_for_invalid() raise ValueError("Incorrectly named geometry " - "transforms: {0}".format(invalid)) + "transforms: {0}, accepted suffixes are: " + "\n{1}".format(invalid, valid)) From 56f13bf388ad42a4a34e533fe5ea529ffd7f6668 Mon Sep 17 00:00:00 2001 From: DMO Date: Wed, 6 Jul 2022 12:39:40 +0900 Subject: [PATCH 011/213] Move alembic publishing options around. Adding more publishing options to the schema and defaults. --- .../defaults/project_settings/maya.json | 22 +++--- .../schemas/schema_maya_publish.json | 72 ++++++++++++------- 2 files changed, 62 insertions(+), 32 deletions(-) diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index cdd3a62d004..82d5ab20cb0 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -401,14 +401,6 @@ "optional": true, "active": true }, - "ExtractAlembic": { - "enabled": true, - "families": [ - "pointcache", - "model", - "vrayproxy" - ] - }, "ValidateRigContents": { "enabled": false, "optional": true, @@ -561,6 +553,20 @@ "optional": true, "active": true, "bake_attributes": [] + }, + "ExtractAlembic": { + "enabled": true, + "families": [ + "pointcache", + "model", + "vrayproxy" + ] + }, + "ExtractAnimation": { + "enabled": true + }, + "ExtractMultiverseUsdAnim": { + "enabled": true } }, "load": { diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json index 41b681d893f..e77bb1a6f87 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json @@ -504,30 +504,6 @@ "label": "ValidateUniqueNames" } ] - }, - { - "type": "label", - "label": "Extractors" - }, - { - "type": "dict", - "collapsible": true, - "key": "ExtractAlembic", - "label": "Extract Alembic", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "key": "families", - "label": "Families", - "type": "list", - "object_type": "text" - } - ] } ] }, @@ -686,6 +662,54 @@ "is_list": true } ] + }, + { + "type": "dict", + "collapsible": true, + "key": "ExtractAlembic", + "label": "Extract Alembic", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "ExtractAnimation", + "label": "Extract Alembic Animation", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + } + ] + }, + { + "type": "dict", + "collapsible": true, + "key": "ExtractMultiverseUsdAnim", + "label": "Extract USD Animation", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + } + ] } ] } From 53f56853d9519b96115ab92e10aa610d776cd5be Mon Sep 17 00:00:00 2001 From: DMO Date: Wed, 6 Jul 2022 12:43:24 +0900 Subject: [PATCH 012/213] Subclass ExtractMultiverseUsd as ExtractMultiverseUsdAnim to enable animation-specific USD extraction. Adding noNormals to settings to allow skipping normals for meshes that will be sub-divided later. --- .../maya/plugins/create/create_animation.py | 3 + .../maya/plugins/publish/extract_animation.py | 4 +- .../plugins/publish/extract_multiverse_usd.py | 70 ++++++++++++++++--- 3 files changed, 65 insertions(+), 12 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_animation.py b/openpype/hosts/maya/plugins/create/create_animation.py index 5cd1f7090a0..b9e1f3b7a2f 100644 --- a/openpype/hosts/maya/plugins/create/create_animation.py +++ b/openpype/hosts/maya/plugins/create/create_animation.py @@ -42,3 +42,6 @@ def __init__(self, *args, **kwargs): # Default to not send to farm. self.data["farm"] = False self.data["priority"] = 50 + + # Default to write normals. + self.data["writeNormals"] = True diff --git a/openpype/hosts/maya/plugins/publish/extract_animation.py b/openpype/hosts/maya/plugins/publish/extract_animation.py index abe5ed3bf50..7ce80b4679a 100644 --- a/openpype/hosts/maya/plugins/publish/extract_animation.py +++ b/openpype/hosts/maya/plugins/publish/extract_animation.py @@ -63,7 +63,9 @@ def process(self, instance): "selection": True, "worldSpace": instance.data.get("worldSpace", True), "writeColorSets": instance.data.get("writeColorSets", False), - "writeFaceSets": instance.data.get("writeFaceSets", False) + "writeFaceSets": instance.data.get("writeFaceSets", False), + # 'noNormals' is the standard alembic option name. + "noNormals": not instance.data.get("writeNormals", True) } if not instance.data.get("includeParentHierarchy", True): diff --git a/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py b/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py index b1aaf9d9ba0..2a99dffa8dc 100644 --- a/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py +++ b/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py @@ -2,6 +2,7 @@ import six from maya import cmds +from maya import mel import openpype.api from openpype.hosts.maya.api.lib import maintained_selection @@ -87,7 +88,7 @@ def default_options(self): return { "stripNamespaces": False, "mergeTransformAndShape": False, - "writeAncestors": True, + "writeAncestors": False, "flattenParentXforms": False, "writeSparseOverrides": False, "useMetaPrimPath": False, @@ -147,6 +148,13 @@ def parse_overrides(self, instance, options): return options + def get_default_options(self): + self.log.info("ExtractMultiverseUsd get_default_options") + return self.default_options + + def filter_members(self, members): + return members + def process(self, instance): # Load plugin first cmds.loadPlugin("MultiverseForMaya", quiet=True) @@ -161,7 +169,7 @@ def process(self, instance): file_path = file_path.replace('\\', '/') # Parse export options - options = self.default_options + options = self.get_default_options() options = self.parse_overrides(instance, options) self.log.info("Export options: {0}".format(options)) @@ -170,27 +178,35 @@ def process(self, instance): with maintained_selection(): members = instance.data("setMembers") - self.log.info('Collected object {}'.format(members)) + self.log.info('Collected objects: {}'.format(members)) + members = self.filter_members(members) + if not members: + self.log.error('No members!') + return + self.log.info(' - filtered: {}'.format(members)) import multiverse time_opts = None frame_start = instance.data['frameStart'] frame_end = instance.data['frameEnd'] - handle_start = instance.data['handleStart'] - handle_end = instance.data['handleEnd'] - step = instance.data['step'] - fps = instance.data['fps'] if frame_end != frame_start: time_opts = multiverse.TimeOptions() time_opts.writeTimeRange = True + + handle_start = instance.data['handleStart'] + handle_end = instance.data['handleEnd'] + time_opts.frameRange = ( frame_start - handle_start, frame_end + handle_end) - time_opts.frameIncrement = step - time_opts.numTimeSamples = instance.data["numTimeSamples"] - time_opts.timeSamplesSpan = instance.data["timeSamplesSpan"] - time_opts.framePerSecond = fps + time_opts.frameIncrement = instance.data['step'] + time_opts.numTimeSamples = instance.data.get( + 'numTimeSamples', options['numTimeSamples']) + time_opts.timeSamplesSpan = instance.data.get( + 'timeSamplesSpan', options['timeSamplesSpan']) + time_opts.framePerSecond = instance.data.get( + 'fps', mel.eval('currentTimeUnitToFPS()')) asset_write_opts = multiverse.AssetWriteOptions(time_opts) options_discard_keys = { @@ -203,11 +219,15 @@ def process(self, instance): 'step', 'fps' } + self.log.debug("Write Options:") for key, value in options.items(): if key in options_discard_keys: continue + + self.log.debug(" - {}={}".format(key, value)) setattr(asset_write_opts, key, value) + self.log.info('WriteAsset: {} / {}'.format(file_path, members)) multiverse.WriteAsset(file_path, members, asset_write_opts) if "representations" not in instance.data: @@ -223,3 +243,31 @@ def process(self, instance): self.log.info("Extracted instance {} to {}".format( instance.name, file_path)) + + +class ExtractMultiverseUsdAnim(ExtractMultiverseUsd): + """Extractor for Multiverse USD Animation Sparse Cache data. + + This will extract the sparse cache data from the scene and generate a + USD file with all the animation data. + + Upon publish a .usd sparse cache will be written. + """ + label = "Extract Multiverse USD Animation Sparse Cache" + families = ["animation"] + + def get_default_options(self): + anim_options = self.default_options + anim_options["writeSparseOverrides"] = True + anim_options["stripNamespaces"] = True + return anim_options + + def filter_members(self, members): + out_set = next((i for i in members if i.endswith("out_SET")), None) + + if out_set is None: + self.log.warning("Expecting out_SET") + return None + + members = cmds.ls(cmds.sets(out_set, query=True), long=True) + return members From cdf165ed79c7b9f3939d3b199d0757f9bac74e5b Mon Sep 17 00:00:00 2001 From: DMO Date: Wed, 6 Jul 2022 12:43:46 +0900 Subject: [PATCH 013/213] The mvLook should skip namespaces. --- openpype/hosts/maya/plugins/publish/extract_multiverse_look.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_multiverse_look.py b/openpype/hosts/maya/plugins/publish/extract_multiverse_look.py index b97314d5a11..8a5d7e4e531 100644 --- a/openpype/hosts/maya/plugins/publish/extract_multiverse_look.py +++ b/openpype/hosts/maya/plugins/publish/extract_multiverse_look.py @@ -78,7 +78,7 @@ def default_options(self): "writeVariants": False, "writeVariantsDefinition": False, "writeActiveState": False, - "writeNamespaces": False, + "writeNamespaces": True, "numTimeSamples": 1, "timeSamplesSpan": 0.0 } From 559210ffb6ffacd68be8227d56b90c036a70c184 Mon Sep 17 00:00:00 2001 From: DMO Date: Wed, 6 Jul 2022 12:44:24 +0900 Subject: [PATCH 014/213] If there's a mixed-attribute on the node, this will fail, just skip it with a warning. --- openpype/hosts/maya/plugins/publish/collect_look.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openpype/hosts/maya/plugins/publish/collect_look.py b/openpype/hosts/maya/plugins/publish/collect_look.py index 28c57e04b5c..40edd3b2f45 100644 --- a/openpype/hosts/maya/plugins/publish/collect_look.py +++ b/openpype/hosts/maya/plugins/publish/collect_look.py @@ -549,6 +549,11 @@ def collect_attributes_changed(self, instance): if not cmds.attributeQuery(attr, node=node, exists=True): continue attribute = "{}.{}".format(node, attr) + # We don't support mixed-type attributes yet. + if cmds.attributeQuery(attr, node=node, multi=True): + self.log.warning("Attribute '{}' is mixed-type and is " + "not supported yet.".format(attribute)) + continue if cmds.getAttr(attribute, type=True) == "message": continue node_attributes[attr] = cmds.getAttr(attribute) From 77c45e2b7f110e6939b685b03c613f7feef7f718 Mon Sep 17 00:00:00 2001 From: DMO Date: Wed, 20 Jul 2022 11:04:17 +0900 Subject: [PATCH 015/213] Animation should also write out UsdAttributes. --- openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py b/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py index 2a99dffa8dc..40dd5dfe505 100644 --- a/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py +++ b/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py @@ -259,6 +259,7 @@ class ExtractMultiverseUsdAnim(ExtractMultiverseUsd): def get_default_options(self): anim_options = self.default_options anim_options["writeSparseOverrides"] = True + anim_options["writeUsdAttributes"] = True anim_options["stripNamespaces"] = True return anim_options From 8ae92351a7e3e1758cca4a62e4170749ed93a914 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 16 Sep 2022 21:27:00 +0800 Subject: [PATCH 016/213] Import Reference during Publish --- openpype/hosts/maya/plugins/create/create_look.py | 3 +++ openpype/hosts/maya/plugins/publish/extract_look.py | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/openpype/hosts/maya/plugins/create/create_look.py b/openpype/hosts/maya/plugins/create/create_look.py index 44e439fe1f9..cecdf9f54d4 100644 --- a/openpype/hosts/maya/plugins/create/create_look.py +++ b/openpype/hosts/maya/plugins/create/create_look.py @@ -21,6 +21,9 @@ def __init__(self, *args, **kwargs): # Whether to automatically convert the textures to .tx upon publish. self.data["maketx"] = self.make_tx + # Enable users to import reference + self.data["importReference"] = False + # Enable users to force a copy. # - on Windows is "forceCopy" always changed to `True` because of # windows implementation of hardlinks diff --git a/openpype/hosts/maya/plugins/publish/extract_look.py b/openpype/hosts/maya/plugins/publish/extract_look.py index 91b0da75c6e..845af0d32d4 100644 --- a/openpype/hosts/maya/plugins/publish/extract_look.py +++ b/openpype/hosts/maya/plugins/publish/extract_look.py @@ -255,6 +255,14 @@ def process(self, instance): hashes = results["fileHashes"] remap = results["attrRemap"] + # Import Reference if the option is enabled + ref_import = instance.data.get("importReference", True) + if ref_import: + reference_node = cmds.ls(type="reference") + for r in reference_node: + rFile = cmds.referenceQuery(r, f=True) + cmds.file(rFile, importReference=True) + # Extract in correct render layer layer = instance.data.get("renderlayer", "defaultRenderLayer") with lib.renderlayer(layer): From 25279d276d8ef1035aea327c67b49a44ab29d647 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Sep 2022 17:59:39 +0800 Subject: [PATCH 017/213] Import Reference during Publish --- .../hosts/maya/plugins/create/create_look.py | 3 - .../publish/extract_import_reference.py | 122 ++++++++++++++++++ .../maya/plugins/publish/extract_look.py | 8 -- .../defaults/project_settings/maya.json | 3 + .../schemas/schema_maya_publish.json | 16 ++- 5 files changed, 140 insertions(+), 12 deletions(-) create mode 100644 openpype/hosts/maya/plugins/publish/extract_import_reference.py diff --git a/openpype/hosts/maya/plugins/create/create_look.py b/openpype/hosts/maya/plugins/create/create_look.py index cecdf9f54d4..44e439fe1f9 100644 --- a/openpype/hosts/maya/plugins/create/create_look.py +++ b/openpype/hosts/maya/plugins/create/create_look.py @@ -21,9 +21,6 @@ def __init__(self, *args, **kwargs): # Whether to automatically convert the textures to .tx upon publish. self.data["maketx"] = self.make_tx - # Enable users to import reference - self.data["importReference"] = False - # Enable users to force a copy. # - on Windows is "forceCopy" always changed to `True` because of # windows implementation of hardlinks diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py new file mode 100644 index 00000000000..3e44addf6c6 --- /dev/null +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -0,0 +1,122 @@ +import os +import pyblish.api +from openpype.pipeline import publish, legacy_io +from openpype.settings import get_project_settings + + +def _get_project_setting(): + project_name = legacy_io.active_project() + project_setting = get_project_settings(project_name) + maya_enabled = ( + project_setting["maya"]["publish"]["ImportReference"]["enabled"] + ) + use_published = ( + project_setting["deadline"]["publish"]["MayaSubmitDeadline"]["use_published"] + ) + if maya_enabled != use_published: + return False + else: + return use_published + + +class ImportReference(publish.Extractor): + """ + + Extract the scene with imported reference. + The temp scene with imported reference is + published for rendering if this extractor is activated + + """ + + label = "Import Reference" + order = pyblish.api.ExtractorOrder - 0.48 + hosts = ["maya"] + families = ["renderlayer", "workfile"] + active = _get_project_setting() + optional = True + tmp_format = "_tmp" + + def process(self, instance): + from maya import cmds + + ext_mapping = ( + instance.context.data["project_settings"]["maya"]["ext_mapping"] + ) + if ext_mapping: + self.log.info("Looking in settings for scene type ...") + # use extension mapping for first family found + for family in self.families: + try: + self.scene_type = ext_mapping[family] + self.log.info( + "Using {} as scene type".format(self.scene_type)) + break + except KeyError: + # no preset found + pass + + _scene_type = ("mayaAscii" + if self.scene_type == "ma" + else "mayaBinary") + + dir_path = self.staging_dir(instance) + # named the file with imported reference + tmp_name = instance.name + self.tmp_format + m_ref_fname = "{0}.{1}".format(tmp_name, self.scene_type) + + m_ref_path = os.path.join(dir_path, m_ref_fname) + + self.log.info("Performing extraction..") + current = cmds.file(query=True, sceneName=True) + cmds.file(save=True, force=True + + self.log.info("Performing extraction..") + + # create temp scene with imported + # reference for rendering + reference_node = cmds.ls(type="reference") + for r in reference_node: + rFile = cmds.referenceQuery(r, f=True) + if r == "sharedReferenceNode": + cmds.file(rFile, removeReference = True, referenceNode=r) + cmds.file(rFile, importReference=True) + + if current.endswith(self.scene_type): + current_path = os.path.dirname(current) + tmp_path_name = os.path.join(current_path, tmp_name) + cmds.file(rename=tmp_path_name) + cmds.file(save=True, force=True) + + with lib.maintained_selection(): + cmds.select(all=True, noExpand=True) + cmds.file(m_ref_path, + force=True, + typ = _scene_type, + exportSelected=True, + channels=True, + constraints=True, + shader=True, + expressions=True, + constructionHistory=True + ) + + if "files" not in instance.data: + instance.data["files"] = [] + + instance.data["files"].append(m_ref_path) + + if instance.data.get("representations") is None: + instance.data["representations"] = [] + + ref_representation = { + "name": self.scene_type, + "ext": self.scene_type, + "files": os.path.basename(m_ref_fname), + "stagingDir": dir_path + } + instance.data["representations"].append(ref_representation) + + self.log.info("Extracted instance '%s' to : '%s'" % (tmp_name, + m_ref_path)) + + cmds.file(current, open=True) diff --git a/openpype/hosts/maya/plugins/publish/extract_look.py b/openpype/hosts/maya/plugins/publish/extract_look.py index 845af0d32d4..91b0da75c6e 100644 --- a/openpype/hosts/maya/plugins/publish/extract_look.py +++ b/openpype/hosts/maya/plugins/publish/extract_look.py @@ -255,14 +255,6 @@ def process(self, instance): hashes = results["fileHashes"] remap = results["attrRemap"] - # Import Reference if the option is enabled - ref_import = instance.data.get("importReference", True) - if ref_import: - reference_node = cmds.ls(type="reference") - for r in reference_node: - rFile = cmds.referenceQuery(r, f=True) - cmds.file(rFile, importReference=True) - # Extract in correct render layer layer = instance.data.get("renderlayer", "defaultRenderLayer") with lib.renderlayer(layer): diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 8643297f021..76be3c393ea 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -762,6 +762,9 @@ } } }, + "ImportReference": { + "enabled": false + }, "ExtractMayaSceneRaw": { "enabled": true, "add_for_families": [ diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json index 53247f6bd4b..4cf84795b5a 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json @@ -62,7 +62,7 @@ } ] }, - { + { "type": "dict", "collapsible": true, "key": "ValidateFrameRange", @@ -807,6 +807,20 @@ "type": "schema", "name": "schema_maya_capture" }, + { + "type": "dict", + "collapsible": true, + "key": "ImportReference", + "label": "Extract Scenes with Imported Reference", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + } + ] + }, { "type": "dict", "collapsible": true, From e5cf620575d535eb5415f8f0b22858c954fb5eb2 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Sep 2022 18:06:50 +0800 Subject: [PATCH 018/213] Import Reference during Publish --- .../hosts/maya/plugins/publish/extract_import_reference.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 3e44addf6c6..ac425ee083a 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -1,4 +1,5 @@ import os + import pyblish.api from openpype.pipeline import publish, legacy_io from openpype.settings import get_project_settings @@ -11,8 +12,8 @@ def _get_project_setting(): project_setting["maya"]["publish"]["ImportReference"]["enabled"] ) use_published = ( - project_setting["deadline"]["publish"]["MayaSubmitDeadline"]["use_published"] - ) + project_setting["deadline"]["publish"]["MayaSubmitDeadline"]["use_published"] # noqa + ) if maya_enabled != use_published: return False else: From 06b982fd435c2549266cf400e47cbc3c3bb404f0 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Sep 2022 18:09:21 +0800 Subject: [PATCH 019/213] Import Reference during Publish --- .../hosts/maya/plugins/publish/extract_import_reference.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index ac425ee083a..feacf072c3a 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -1,5 +1,5 @@ -import os +import os import pyblish.api from openpype.pipeline import publish, legacy_io from openpype.settings import get_project_settings @@ -10,7 +10,7 @@ def _get_project_setting(): project_setting = get_project_settings(project_name) maya_enabled = ( project_setting["maya"]["publish"]["ImportReference"]["enabled"] - ) + ) use_published = ( project_setting["deadline"]["publish"]["MayaSubmitDeadline"]["use_published"] # noqa ) From 89b7699c21f9c0b55e5a4cdc2755bc242b7e12e9 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Sep 2022 18:10:48 +0800 Subject: [PATCH 020/213] Import Reference during Publish --- .../hosts/maya/plugins/publish/extract_import_reference.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index feacf072c3a..eb225127f00 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -1,6 +1,7 @@ - import os + import pyblish.api + from openpype.pipeline import publish, legacy_io from openpype.settings import get_project_settings From 2dc186f1911c2b5eebfa98e513a89831a51389ec Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Sep 2022 18:16:05 +0800 Subject: [PATCH 021/213] Import Reference during Publish --- .../hosts/maya/plugins/publish/extract_import_reference.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index eb225127f00..56b757f2b4e 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -1,5 +1,3 @@ -import os - import pyblish.api from openpype.pipeline import publish, legacy_io @@ -39,6 +37,7 @@ class ImportReference(publish.Extractor): tmp_format = "_tmp" def process(self, instance): + import os from maya import cmds ext_mapping = ( From bf7cca8586f485fb4507eef0cd050f6cd4cf481e Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Sep 2022 18:17:25 +0800 Subject: [PATCH 022/213] Import Reference during Publish --- .../hosts/maya/plugins/publish/extract_import_reference.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 56b757f2b4e..eb225127f00 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -1,3 +1,5 @@ +import os + import pyblish.api from openpype.pipeline import publish, legacy_io @@ -37,7 +39,6 @@ class ImportReference(publish.Extractor): tmp_format = "_tmp" def process(self, instance): - import os from maya import cmds ext_mapping = ( From 93c9ec8d16404bd17fd8de7c25c3bc5829402b20 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Sep 2022 18:22:52 +0800 Subject: [PATCH 023/213] Import Reference during Publish --- .../hosts/maya/plugins/publish/extract_import_reference.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index eb225127f00..8be37c91e3e 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -1,5 +1,7 @@ import os +from maya import cmds + import pyblish.api from openpype.pipeline import publish, legacy_io @@ -39,8 +41,6 @@ class ImportReference(publish.Extractor): tmp_format = "_tmp" def process(self, instance): - from maya import cmds - ext_mapping = ( instance.context.data["project_settings"]["maya"]["ext_mapping"] ) From 3ce41cbae8c7e0bb5b00951f8e7a9927df472da3 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Sep 2022 18:33:57 +0800 Subject: [PATCH 024/213] Import Reference during Publish --- openpype/hosts/maya/plugins/publish/extract_import_reference.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 8be37c91e3e..2f9e8516d71 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -70,7 +70,7 @@ def process(self, instance): self.log.info("Performing extraction..") current = cmds.file(query=True, sceneName=True) - cmds.file(save=True, force=True + cmds.file(save=True, force=True) self.log.info("Performing extraction..") From 79e78beb61ef1c5f5bae9042a6b07db419fb79f8 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Sep 2022 18:36:24 +0800 Subject: [PATCH 025/213] Import Reference during Publish --- .../hosts/maya/plugins/publish/extract_import_reference.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 2f9e8516d71..1ed109d7209 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -6,6 +6,7 @@ from openpype.pipeline import publish, legacy_io from openpype.settings import get_project_settings +from openpype.hosts.maya.api import lib def _get_project_setting(): @@ -80,7 +81,7 @@ def process(self, instance): for r in reference_node: rFile = cmds.referenceQuery(r, f=True) if r == "sharedReferenceNode": - cmds.file(rFile, removeReference = True, referenceNode=r) + cmds.file(rFile, removeReference=True, referenceNode=r) cmds.file(rFile, importReference=True) if current.endswith(self.scene_type): @@ -93,14 +94,14 @@ def process(self, instance): cmds.select(all=True, noExpand=True) cmds.file(m_ref_path, force=True, - typ = _scene_type, + typ=_scene_type, exportSelected=True, channels=True, constraints=True, shader=True, expressions=True, constructionHistory=True - ) + ) if "files" not in instance.data: instance.data["files"] = [] From 92a80eb24a4e9a849e58ef5ca57d885f9efa396a Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Sep 2022 18:38:08 +0800 Subject: [PATCH 026/213] Import Reference during Publish --- openpype/hosts/maya/plugins/publish/extract_import_reference.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 1ed109d7209..4035e8a9e6c 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -101,7 +101,7 @@ def process(self, instance): shader=True, expressions=True, constructionHistory=True - ) + ) if "files" not in instance.data: instance.data["files"] = [] From ab7ed6becbd100c6e9f8960065bdeffc8f609b5b Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 28 Sep 2022 18:39:22 +0800 Subject: [PATCH 027/213] Import Reference during Publish --- .../hosts/maya/plugins/publish/extract_import_reference.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 4035e8a9e6c..cde8f677897 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -100,8 +100,7 @@ def process(self, instance): constraints=True, shader=True, expressions=True, - constructionHistory=True - ) + constructionHistory=True) if "files" not in instance.data: instance.data["files"] = [] From ee78e9e67036308e651a9a0005b72a4d16ecce17 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 4 Oct 2022 15:45:17 +0800 Subject: [PATCH 028/213] Import Reference during Publish --- .../publish/extract_import_reference.py | 58 +++++++++---------- .../deadline/abstract_submit_deadline.py | 7 ++- .../defaults/project_settings/deadline.json | 1 + .../defaults/project_settings/maya.json | 3 - .../schema_project_deadline.json | 5 ++ .../schemas/schema_maya_publish.json | 14 ----- 6 files changed, 37 insertions(+), 51 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index cde8f677897..4cbc963cf33 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -9,22 +9,16 @@ from openpype.hosts.maya.api import lib -def _get_project_setting(): +def _import_reference(): project_name = legacy_io.active_project() project_setting = get_project_settings(project_name) - maya_enabled = ( - project_setting["maya"]["publish"]["ImportReference"]["enabled"] + import_reference = ( + project_setting["deadline"]["publish"]["MayaSubmitDeadline"]["import_reference"] # noqa ) - use_published = ( - project_setting["deadline"]["publish"]["MayaSubmitDeadline"]["use_published"] # noqa - ) - if maya_enabled != use_published: - return False - else: - return use_published + return import_reference -class ImportReference(publish.Extractor): +class ExtractImportReference(publish.Extractor): """ Extract the scene with imported reference. @@ -33,12 +27,11 @@ class ImportReference(publish.Extractor): """ - label = "Import Reference" + label = "Extract Import Reference" order = pyblish.api.ExtractorOrder - 0.48 hosts = ["maya"] families = ["renderlayer", "workfile"] - active = _get_project_setting() - optional = True + active = _import_reference() tmp_format = "_tmp" def process(self, instance): @@ -54,9 +47,10 @@ def process(self, instance): self.log.info( "Using {} as scene type".format(self.scene_type)) break + except KeyError: - # no preset found - pass + # set scene type to ma + self.scene_type = "ma" _scene_type = ("mayaAscii" if self.scene_type == "ma" @@ -64,6 +58,8 @@ def process(self, instance): dir_path = self.staging_dir(instance) # named the file with imported reference + if instance.name == "Main": + return tmp_name = instance.name + self.tmp_format m_ref_fname = "{0}.{1}".format(tmp_name, self.scene_type) @@ -72,23 +68,20 @@ def process(self, instance): self.log.info("Performing extraction..") current = cmds.file(query=True, sceneName=True) cmds.file(save=True, force=True) - - self.log.info("Performing extraction..") - # create temp scene with imported # reference for rendering reference_node = cmds.ls(type="reference") for r in reference_node: - rFile = cmds.referenceQuery(r, f=True) + ref_file = cmds.referenceQuery(r, f=True) if r == "sharedReferenceNode": - cmds.file(rFile, removeReference=True, referenceNode=r) - cmds.file(rFile, importReference=True) + cmds.file(ref_file, removeReference=True, referenceNode=r) + return + cmds.file(ref_file, importReference=True) - if current.endswith(self.scene_type): - current_path = os.path.dirname(current) - tmp_path_name = os.path.join(current_path, tmp_name) - cmds.file(rename=tmp_path_name) - cmds.file(save=True, force=True) + cmds.file(rename=m_ref_fname) + cmds.file(save=True, force=True) + tmp_filepath = cmds.file(query=True, sceneName=True) + instance.context.data["currentFile"] = tmp_filepath with lib.maintained_selection(): cmds.select(all=True, noExpand=True) @@ -104,8 +97,7 @@ def process(self, instance): if "files" not in instance.data: instance.data["files"] = [] - - instance.data["files"].append(m_ref_path) + instance.data["files"].append(m_ref_fname) if instance.data.get("representations") is None: instance.data["representations"] = [] @@ -113,12 +105,14 @@ def process(self, instance): ref_representation = { "name": self.scene_type, "ext": self.scene_type, - "files": os.path.basename(m_ref_fname), - "stagingDir": dir_path + "files": m_ref_fname, + "stagingDir": os.path.dirname(tmp_filepath) } + instance.data["representations"].append(ref_representation) - self.log.info("Extracted instance '%s' to : '%s'" % (tmp_name, + self.log.info("Extracted instance '%s' to : '%s'" % (m_ref_fname, m_ref_path)) + #re-open the previous scene cmds.file(current, open=True) diff --git a/openpype/modules/deadline/abstract_submit_deadline.py b/openpype/modules/deadline/abstract_submit_deadline.py index 512ff800ee6..909a5871e3e 100644 --- a/openpype/modules/deadline/abstract_submit_deadline.py +++ b/openpype/modules/deadline/abstract_submit_deadline.py @@ -400,6 +400,7 @@ class AbstractSubmitDeadline(pyblish.api.InstancePlugin): label = "Submit to Deadline" order = pyblish.api.IntegratorOrder + 0.1 + import_reference = False use_published = True asset_dependencies = False @@ -516,7 +517,6 @@ def from_published_scene(self, replace_in_path=True): published. """ - instance = self._instance workfile_instance = self._get_workfile_instance(instance.context) if workfile_instance is None: @@ -524,7 +524,10 @@ def from_published_scene(self, replace_in_path=True): # determine published path from Anatomy. template_data = workfile_instance.data.get("anatomyData") - rep = workfile_instance.data.get("representations")[0] + if self.import_reference: + rep = workfile_instance.data.get("representations")[1] + else: + rep = workfile_instance.data.get("representations")[0] template_data["representation"] = rep.get("name") template_data["ext"] = rep.get("ext") template_data["comment"] = None diff --git a/openpype/settings/defaults/project_settings/deadline.json b/openpype/settings/defaults/project_settings/deadline.json index a6e7b4a94a5..5f0731fb0c8 100644 --- a/openpype/settings/defaults/project_settings/deadline.json +++ b/openpype/settings/defaults/project_settings/deadline.json @@ -25,6 +25,7 @@ "active": true, "tile_assembler_plugin": "OpenPypeTileAssembler", "use_published": true, + "import_reference": false, "asset_dependencies": true, "priority": 50, "tile_priority": 50, diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 76be3c393ea..8643297f021 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -762,9 +762,6 @@ } } }, - "ImportReference": { - "enabled": false - }, "ExtractMayaSceneRaw": { "enabled": true, "add_for_families": [ diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json b/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json index cd1741ba8b3..be95c682c48 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json @@ -130,6 +130,11 @@ "key": "use_published", "label": "Use Published scene" }, + { + "type": "boolean", + "key": "import_reference", + "label": "Use Scene with Imported Reference" + }, { "type": "boolean", "key": "asset_dependencies", diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json index 4cf84795b5a..07d8f6aea03 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json @@ -807,20 +807,6 @@ "type": "schema", "name": "schema_maya_capture" }, - { - "type": "dict", - "collapsible": true, - "key": "ImportReference", - "label": "Extract Scenes with Imported Reference", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - } - ] - }, { "type": "dict", "collapsible": true, From 6f914a001de08931f84bf1753f0d3ad3bf903593 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 4 Oct 2022 15:46:12 +0800 Subject: [PATCH 029/213] Import Reference during Publish --- openpype/hosts/maya/plugins/publish/extract_import_reference.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 4cbc963cf33..fd2687995b1 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -114,5 +114,5 @@ def process(self, instance): self.log.info("Extracted instance '%s' to : '%s'" % (m_ref_fname, m_ref_path)) - #re-open the previous scene + # re-open the previous scene cmds.file(current, open=True) From df937a8e9ee028142ce4f56ea17c58e166b800ed Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 4 Oct 2022 16:11:26 +0800 Subject: [PATCH 030/213] Import Reference during Publish --- openpype/hosts/maya/plugins/publish/extract_import_reference.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index fd2687995b1..92426e97dd2 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -32,6 +32,7 @@ class ExtractImportReference(publish.Extractor): hosts = ["maya"] families = ["renderlayer", "workfile"] active = _import_reference() + optional= True tmp_format = "_tmp" def process(self, instance): From dfd30d54601939ac31f72fa417a8bb00f67c5881 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 4 Oct 2022 16:13:21 +0800 Subject: [PATCH 031/213] Import Reference during Publish --- openpype/hosts/maya/plugins/publish/extract_import_reference.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 92426e97dd2..193048e9dcf 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -32,7 +32,7 @@ class ExtractImportReference(publish.Extractor): hosts = ["maya"] families = ["renderlayer", "workfile"] active = _import_reference() - optional= True + optional = True tmp_format = "_tmp" def process(self, instance): From 590496814df787ba33a7bb366654fe14d8da76e3 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 7 Oct 2022 22:07:15 +0800 Subject: [PATCH 032/213] Import Reference during Publish --- .../publish/extract_import_reference.py | 72 +++++++++++-------- 1 file changed, 44 insertions(+), 28 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 193048e9dcf..7448cf9966d 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -1,9 +1,11 @@ import os +import sys from maya import cmds import pyblish.api +from openpype.lib import run_subprocess from openpype.pipeline import publish, legacy_io from openpype.settings import get_project_settings from openpype.hosts.maya.api import lib @@ -31,7 +33,7 @@ class ExtractImportReference(publish.Extractor): order = pyblish.api.ExtractorOrder - 0.48 hosts = ["maya"] families = ["renderlayer", "workfile"] - active = _import_reference() + active = True optional = True tmp_format = "_tmp" @@ -62,31 +64,48 @@ def process(self, instance): if instance.name == "Main": return tmp_name = instance.name + self.tmp_format - m_ref_fname = "{0}.{1}".format(tmp_name, self.scene_type) + current_name = cmds.file(query=True, sceneName=True) + ref_scene_name = "{0}.{1}".format(tmp_name, self.scene_type) - m_ref_path = os.path.join(dir_path, m_ref_fname) + reference_path = os.path.join(dir_path, ref_scene_name) self.log.info("Performing extraction..") - current = cmds.file(query=True, sceneName=True) - cmds.file(save=True, force=True) - # create temp scene with imported - # reference for rendering - reference_node = cmds.ls(type="reference") - for r in reference_node: - ref_file = cmds.referenceQuery(r, f=True) - if r == "sharedReferenceNode": - cmds.file(ref_file, removeReference=True, referenceNode=r) - return - cmds.file(ref_file, importReference=True) - - cmds.file(rename=m_ref_fname) - cmds.file(save=True, force=True) - tmp_filepath = cmds.file(query=True, sceneName=True) + script = ("import maya.standalone\nmaya.standalone.initialize()\n" + "cmds.file('{current_name}', open=True, force=True)\n" + "reference_node = cmds.ls(type='reference')\n" + "for ref in reference_node:\n" + "\tref_file = cmds.referenceQuery(ref, f=True)\n" + "\tif ref == 'sharedReferenceNode':\n" + "\t\tcmds.file(ref_file, removeReference=True, referenceNode=ref)\n" + "\telse:\n" + "\t\tcmds.file(ref_file, importReference=True)\n" + "try:\n" + "\tcmds.file(rename='{ref_scene_name}')\n" + "except SyntaxError:\n" + "\tcmds.file(rename='{ref_scene_name}')\n" + "cmds.file(save=True, force=True)\n") + + mayapy_exe = os.path.join(os.getenv("MAYA_LOCATION"), "bin", "mayapy") + if sys.platform == "windows": + mayapy_exe = mayapy_exe + ".exe" + + subprocess_args = [ + mayapy_exe, + "-c", + script.replace("\n", ";") + ] + try: + out = run_subprocess(subprocess_args) + except Exception: + self.log.error("Import reference failed", exc_info=True) + raise + + proj_file_dir = os.path.dirname(current_name) + tmp_filepath = os.path.join(proj_file_dir, ref_scene_name) instance.context.data["currentFile"] = tmp_filepath - with lib.maintained_selection(): cmds.select(all=True, noExpand=True) - cmds.file(m_ref_path, + cmds.file(reference_path, force=True, typ=_scene_type, exportSelected=True, @@ -98,7 +117,7 @@ def process(self, instance): if "files" not in instance.data: instance.data["files"] = [] - instance.data["files"].append(m_ref_fname) + instance.data["files"].append(ref_scene_name) if instance.data.get("representations") is None: instance.data["representations"] = [] @@ -106,14 +125,11 @@ def process(self, instance): ref_representation = { "name": self.scene_type, "ext": self.scene_type, - "files": m_ref_fname, - "stagingDir": os.path.dirname(tmp_filepath) + "files": ref_scene_name, + "stagingDir": proj_file_dir } instance.data["representations"].append(ref_representation) - self.log.info("Extracted instance '%s' to : '%s'" % (m_ref_fname, - m_ref_path)) - - # re-open the previous scene - cmds.file(current, open=True) + self.log.info("Extracted instance '%s' to : '%s'" % (ref_scene_name, + reference_path)) From 34b7ba9d3abead3437f7c53ea5d4d3abe3cd5720 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 12 Oct 2022 15:14:14 +0800 Subject: [PATCH 033/213] Import Reference during Publish --- .../publish/extract_import_reference.py | 38 ++++++++++--------- .../deadline/abstract_submit_deadline.py | 2 + .../defaults/project_anatomy/templates.json | 2 +- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 7448cf9966d..4ad3c7756dd 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -70,20 +70,24 @@ def process(self, instance): reference_path = os.path.join(dir_path, ref_scene_name) self.log.info("Performing extraction..") - script = ("import maya.standalone\nmaya.standalone.initialize()\n" - "cmds.file('{current_name}', open=True, force=True)\n" - "reference_node = cmds.ls(type='reference')\n" - "for ref in reference_node:\n" - "\tref_file = cmds.referenceQuery(ref, f=True)\n" - "\tif ref == 'sharedReferenceNode':\n" - "\t\tcmds.file(ref_file, removeReference=True, referenceNode=ref)\n" - "\telse:\n" - "\t\tcmds.file(ref_file, importReference=True)\n" - "try:\n" - "\tcmds.file(rename='{ref_scene_name}')\n" - "except SyntaxError:\n" - "\tcmds.file(rename='{ref_scene_name}')\n" - "cmds.file(save=True, force=True)\n") + script = f""" + import maya.standalone + maya.standalone.initialize() + cmds.file('{current_name}', open=True, force=True) + reference_node = cmds.ls(type='reference') + for ref in reference_node: + ref_file = cmds.referenceQuery(ref, f=True) + if ref == 'sharedReferenceNode': + cmds.file(ref_file, removeReference=True, referenceNode=ref) + else: + cmds.file(ref_file, importReference=True) + try: + cmds.file(rename='{ref_scene_name}') + except SyntaxError: + cmds.file(rename='{ref_scene_name}') + + cmds.file(save=True, force=True) + """ mayapy_exe = os.path.join(os.getenv("MAYA_LOCATION"), "bin", "mayapy") if sys.platform == "windows": @@ -100,9 +104,7 @@ def process(self, instance): self.log.error("Import reference failed", exc_info=True) raise - proj_file_dir = os.path.dirname(current_name) - tmp_filepath = os.path.join(proj_file_dir, ref_scene_name) - instance.context.data["currentFile"] = tmp_filepath + instance.context.data["currentFile"] = ref_scene_name with lib.maintained_selection(): cmds.select(all=True, noExpand=True) cmds.file(reference_path, @@ -126,7 +128,7 @@ def process(self, instance): "name": self.scene_type, "ext": self.scene_type, "files": ref_scene_name, - "stagingDir": proj_file_dir + "stagingDir": os.path.dirname(current_name) } instance.data["representations"].append(ref_representation) diff --git a/openpype/modules/deadline/abstract_submit_deadline.py b/openpype/modules/deadline/abstract_submit_deadline.py index 909a5871e3e..1f74b9b19bc 100644 --- a/openpype/modules/deadline/abstract_submit_deadline.py +++ b/openpype/modules/deadline/abstract_submit_deadline.py @@ -526,6 +526,7 @@ def from_published_scene(self, replace_in_path=True): template_data = workfile_instance.data.get("anatomyData") if self.import_reference: rep = workfile_instance.data.get("representations")[1] + # template_data["workfiletype"] = rep.get("workfiletype") else: rep = workfile_instance.data.get("representations")[0] template_data["representation"] = rep.get("name") @@ -535,6 +536,7 @@ def from_published_scene(self, replace_in_path=True): anatomy = instance.context.data['anatomy'] anatomy_filled = anatomy.format(template_data) template_filled = anatomy_filled["publish"]["path"] + # template_filled = anatomy_filled["others"]["mayaWorkfile"]["path"] file_path = os.path.normpath(template_filled) self.log.info("Using published scene for render {}".format(file_path)) diff --git a/openpype/settings/defaults/project_anatomy/templates.json b/openpype/settings/defaults/project_anatomy/templates.json index caf399a9038..72d387335d4 100644 --- a/openpype/settings/defaults/project_anatomy/templates.json +++ b/openpype/settings/defaults/project_anatomy/templates.json @@ -17,7 +17,7 @@ }, "publish": { "folder": "{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/{@version}", - "file": "{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}><_{udim}>.{ext}", + "file": "{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}><_{udim}><_{workfiletype}>.{ext}", "path": "{@folder}/{@file}", "thumbnail": "{thumbnail_root}/{project[name]}/{_id}_{thumbnail_type}.{ext}" }, From de73dd7de6368dbc65e825d325d2058af14633d0 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 14 Oct 2022 21:27:10 +0800 Subject: [PATCH 034/213] Import Reference during Publish --- .../publish/extract_import_reference.py | 92 ++++++++++++------- .../deadline/abstract_submit_deadline.py | 2 - .../defaults/project_anatomy/templates.json | 2 +- 3 files changed, 59 insertions(+), 37 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 4ad3c7756dd..6b935ffb73b 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -4,6 +4,7 @@ from maya import cmds import pyblish.api +import tempfile from openpype.lib import run_subprocess from openpype.pipeline import publish, legacy_io @@ -33,7 +34,7 @@ class ExtractImportReference(publish.Extractor): order = pyblish.api.ExtractorOrder - 0.48 hosts = ["maya"] families = ["renderlayer", "workfile"] - active = True + active = _import_reference() optional = True tmp_format = "_tmp" @@ -68,43 +69,62 @@ def process(self, instance): ref_scene_name = "{0}.{1}".format(tmp_name, self.scene_type) reference_path = os.path.join(dir_path, ref_scene_name) + tmp_path = os.path.dirname(current_name) + "/" + ref_scene_name self.log.info("Performing extraction..") - script = f""" - import maya.standalone - maya.standalone.initialize() - cmds.file('{current_name}', open=True, force=True) - reference_node = cmds.ls(type='reference') - for ref in reference_node: - ref_file = cmds.referenceQuery(ref, f=True) - if ref == 'sharedReferenceNode': - cmds.file(ref_file, removeReference=True, referenceNode=ref) - else: - cmds.file(ref_file, importReference=True) - try: - cmds.file(rename='{ref_scene_name}') - except SyntaxError: - cmds.file(rename='{ref_scene_name}') - - cmds.file(save=True, force=True) - """ + # This generates script for mayapy to take care of reference + # importing outside current session. It is passing current scene + # name and destination scene name. + script = (""" +# -*- coding: utf-8 -*- +'''Script to import references to given scene.''' +import maya.standalone +maya.standalone.initialize() +# scene names filled by caller +current_name = "{current_name}" +ref_scene_name = "{ref_scene_name}" +print(">>> Opening {{}} ...".format(current_name)) +cmds.file(current_name, open=True, force=True) +reference_node = cmds.ls(type='reference') +print(">>> Processing references") +for ref in reference_node: + ref_file = cmds.referenceQuery(ref, f=True) + print("--- {{}}".format(ref)) + print("--> {{}}".format(ref_file)) + if ref == 'sharedReferenceNode': + cmds.file(ref_file, removeReference=True, referenceNode=ref) + else: + cmds.file(ref_file, importReference=True) +print(">>> Saving scene as {{}}".format(ref_scene_name)) + +cmds.file(rename=ref_scene_name) +cmds.file(save=True, force=True) +print("*** Done") + """).format(current_name=current_name, ref_scene_name=tmp_path) mayapy_exe = os.path.join(os.getenv("MAYA_LOCATION"), "bin", "mayapy") if sys.platform == "windows": - mayapy_exe = mayapy_exe + ".exe" - - subprocess_args = [ - mayapy_exe, - "-c", - script.replace("\n", ";") - ] - try: - out = run_subprocess(subprocess_args) - except Exception: - self.log.error("Import reference failed", exc_info=True) - raise - - instance.context.data["currentFile"] = ref_scene_name + mayapy_exe += ".exe" + mayapy_exe = os.path.normpath(mayapy_exe) + # can't use TemporaryNamedFile as that can't be opened in another + # process until handles are closed by context manager. + with tempfile.TemporaryDirectory() as tmp_dir_name: + tmp_file_name = os.path.join(tmp_dir_name, "import_ref.py") + tmp = open(tmp_file_name, "w+t") + subprocess_args = [ + mayapy_exe, + tmp_file_name + ] + self.log.info("Using temp file: {}".format(tmp.name)) + try: + tmp.write(script) + tmp.close() + run_subprocess(subprocess_args) + except Exception: + self.log.error("Import reference failed", exc_info=True) + raise + + with lib.maintained_selection(): cmds.select(all=True, noExpand=True) cmds.file(reference_path, @@ -117,6 +137,8 @@ def process(self, instance): expressions=True, constructionHistory=True) + instance.context.data["currentFile"] = tmp_path + if "files" not in instance.data: instance.data["files"] = [] instance.data["files"].append(ref_scene_name) @@ -128,8 +150,10 @@ def process(self, instance): "name": self.scene_type, "ext": self.scene_type, "files": ref_scene_name, - "stagingDir": os.path.dirname(current_name) + "stagingDir": os.path.dirname(current_name), + "outputName": "imported" } + self.log.info("%s" % ref_representation) instance.data["representations"].append(ref_representation) diff --git a/openpype/modules/deadline/abstract_submit_deadline.py b/openpype/modules/deadline/abstract_submit_deadline.py index 1f74b9b19bc..909a5871e3e 100644 --- a/openpype/modules/deadline/abstract_submit_deadline.py +++ b/openpype/modules/deadline/abstract_submit_deadline.py @@ -526,7 +526,6 @@ def from_published_scene(self, replace_in_path=True): template_data = workfile_instance.data.get("anatomyData") if self.import_reference: rep = workfile_instance.data.get("representations")[1] - # template_data["workfiletype"] = rep.get("workfiletype") else: rep = workfile_instance.data.get("representations")[0] template_data["representation"] = rep.get("name") @@ -536,7 +535,6 @@ def from_published_scene(self, replace_in_path=True): anatomy = instance.context.data['anatomy'] anatomy_filled = anatomy.format(template_data) template_filled = anatomy_filled["publish"]["path"] - # template_filled = anatomy_filled["others"]["mayaWorkfile"]["path"] file_path = os.path.normpath(template_filled) self.log.info("Using published scene for render {}".format(file_path)) diff --git a/openpype/settings/defaults/project_anatomy/templates.json b/openpype/settings/defaults/project_anatomy/templates.json index 72d387335d4..caf399a9038 100644 --- a/openpype/settings/defaults/project_anatomy/templates.json +++ b/openpype/settings/defaults/project_anatomy/templates.json @@ -17,7 +17,7 @@ }, "publish": { "folder": "{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/{@version}", - "file": "{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}><_{udim}><_{workfiletype}>.{ext}", + "file": "{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}><_{udim}>.{ext}", "path": "{@folder}/{@file}", "thumbnail": "{thumbnail_root}/{project[name]}/{_id}_{thumbnail_type}.{ext}" }, From 76a70b70cf75c83c2be3beae8897c17c9ad80cb0 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 14 Oct 2022 21:28:30 +0800 Subject: [PATCH 035/213] Import Reference during Publish --- openpype/hosts/maya/plugins/publish/extract_import_reference.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 6b935ffb73b..e70a27a6f63 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -124,7 +124,6 @@ def process(self, instance): self.log.error("Import reference failed", exc_info=True) raise - with lib.maintained_selection(): cmds.select(all=True, noExpand=True) cmds.file(reference_path, From b82c3ac7f97d74272c4d991e7ec9a230cda6a5b4 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 14 Oct 2022 21:29:51 +0800 Subject: [PATCH 036/213] Import Reference during Publish --- openpype/hosts/maya/plugins/publish/extract_import_reference.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index e70a27a6f63..3ec2f3bba47 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -66,7 +66,7 @@ def process(self, instance): return tmp_name = instance.name + self.tmp_format current_name = cmds.file(query=True, sceneName=True) - ref_scene_name = "{0}.{1}".format(tmp_name, self.scene_type) + ref_scene_name = "{0}.{1}".format(tmp_name, self.scene_type) reference_path = os.path.join(dir_path, ref_scene_name) tmp_path = os.path.dirname(current_name) + "/" + ref_scene_name From ab93c766ea425e6ef7b017465c6905cca014f712 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 25 Oct 2022 20:09:04 +0800 Subject: [PATCH 037/213] Import Reference during Publish --- .../plugins/publish/extract_import_reference.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 3ec2f3bba47..fa9e612dad8 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -7,20 +7,10 @@ import tempfile from openpype.lib import run_subprocess -from openpype.pipeline import publish, legacy_io -from openpype.settings import get_project_settings +from openpype.pipeline import publish from openpype.hosts.maya.api import lib -def _import_reference(): - project_name = legacy_io.active_project() - project_setting = get_project_settings(project_name) - import_reference = ( - project_setting["deadline"]["publish"]["MayaSubmitDeadline"]["import_reference"] # noqa - ) - return import_reference - - class ExtractImportReference(publish.Extractor): """ @@ -34,10 +24,13 @@ class ExtractImportReference(publish.Extractor): order = pyblish.api.ExtractorOrder - 0.48 hosts = ["maya"] families = ["renderlayer", "workfile"] - active = _import_reference() optional = True tmp_format = "_tmp" + @classmethod + def apply_settings(cls, project_setting, system_settings): #noqa + cls.active = project_setting["deadline"]["publish"]["MayaSubmitDeadline"]["import_reference"] # noqa + def process(self, instance): ext_mapping = ( instance.context.data["project_settings"]["maya"]["ext_mapping"] From fbfdefd8d4b487a161db7843e6f6995c1c45b88d Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 25 Oct 2022 20:09:38 +0800 Subject: [PATCH 038/213] Import Reference during Publish --- openpype/hosts/maya/plugins/publish/extract_import_reference.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index fa9e612dad8..b0b69304ef8 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -28,7 +28,7 @@ class ExtractImportReference(publish.Extractor): tmp_format = "_tmp" @classmethod - def apply_settings(cls, project_setting, system_settings): #noqa + def apply_settings(cls, project_setting, system_settings): cls.active = project_setting["deadline"]["publish"]["MayaSubmitDeadline"]["import_reference"] # noqa def process(self, instance): From aeee2a491821133549672a98a0939e402fb7ce12 Mon Sep 17 00:00:00 2001 From: DMO Date: Mon, 31 Oct 2022 14:51:44 +0900 Subject: [PATCH 039/213] Fixed commit for adding proper layered support for USD overrides. --- .../create/create_multiverse_usd_comp.py | 2 +- .../maya/plugins/load/load_multiverse_usd.py | 32 ++++- .../plugins/load/load_multiverse_usd_over.py | 136 ++++++++++++++++++ 3 files changed, 164 insertions(+), 6 deletions(-) create mode 100644 openpype/hosts/maya/plugins/load/load_multiverse_usd_over.py diff --git a/openpype/hosts/maya/plugins/create/create_multiverse_usd_comp.py b/openpype/hosts/maya/plugins/create/create_multiverse_usd_comp.py index a92969eb9a9..ed466a80688 100644 --- a/openpype/hosts/maya/plugins/create/create_multiverse_usd_comp.py +++ b/openpype/hosts/maya/plugins/create/create_multiverse_usd_comp.py @@ -17,7 +17,7 @@ def __init__(self, *args, **kwargs): # Order of `fileFormat` must match extract_multiverse_usd_comp.py self.data["fileFormat"] = ["usda", "usd"] - self.data["stripNamespaces"] = True + self.data["stripNamespaces"] = False self.data["mergeTransformAndShape"] = False self.data["flattenContent"] = False self.data["writeAsCompoundLayers"] = False diff --git a/openpype/hosts/maya/plugins/load/load_multiverse_usd.py b/openpype/hosts/maya/plugins/load/load_multiverse_usd.py index 24b97db365b..13915aa2a92 100644 --- a/openpype/hosts/maya/plugins/load/load_multiverse_usd.py +++ b/openpype/hosts/maya/plugins/load/load_multiverse_usd.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- import maya.cmds as cmds +from maya import mel +import os from openpype.pipeline import ( load, @@ -11,6 +13,7 @@ unique_namespace ) from openpype.hosts.maya.api.pipeline import containerise +from openpype.client import get_representations, get_representation_by_id class MultiverseUsdLoader(load.LoaderPlugin): @@ -26,7 +29,6 @@ class MultiverseUsdLoader(load.LoaderPlugin): color = "orange" def load(self, context, name=None, namespace=None, options=None): - asset = context['asset']['name'] namespace = namespace or unique_namespace( asset + "_", @@ -34,15 +36,16 @@ def load(self, context, name=None, namespace=None, options=None): suffix="_", ) - # Create the shape + # Make sure we can load the plugin cmds.loadPlugin("MultiverseForMaya", quiet=True) + import multiverse + # Create the shape shape = None transform = None with maintained_selection(): cmds.namespace(addNamespace=namespace) with namespaced(namespace, new=False): - import multiverse shape = multiverse.CreateUsdCompound(self.fname) transform = cmds.listRelatives( shape, parent=True, fullPath=True)[0] @@ -67,15 +70,34 @@ def update(self, container, representation): shapes = cmds.ls(members, type="mvUsdCompoundShape") assert shapes, "Cannot find mvUsdCompoundShape in container" - path = get_representation_path(representation) + project_name = representation["context"]["project"]["name"] + prev_representation_id = cmds.getAttr("{}.representation".format(node)) + prev_representation = get_representation_by_id(project_name, + prev_representation_id) + prev_path = os.path.normpath(prev_representation["data"]["path"]) + # Make sure we can load the plugin + cmds.loadPlugin("MultiverseForMaya", quiet=True) import multiverse + for shape in shapes: - multiverse.SetUsdCompoundAssetPaths(shape, [path]) + + asset_paths = multiverse.GetUsdCompoundAssetPaths(shape) + asset_paths = [os.path.normpath(p) for p in asset_paths] + + assert asset_paths.count(prev_path) == 1, \ + "Couldn't find matching path (or too many)" + prev_path_idx = asset_paths.index(prev_path) + + path = get_representation_path(representation) + asset_paths[prev_path_idx] = path + + multiverse.SetUsdCompoundAssetPaths(shape, asset_paths) cmds.setAttr("{}.representation".format(node), str(representation["_id"]), type="string") + mel.eval('refreshEditorTemplates;') def switch(self, container, representation): self.update(container, representation) diff --git a/openpype/hosts/maya/plugins/load/load_multiverse_usd_over.py b/openpype/hosts/maya/plugins/load/load_multiverse_usd_over.py new file mode 100644 index 00000000000..080475461b2 --- /dev/null +++ b/openpype/hosts/maya/plugins/load/load_multiverse_usd_over.py @@ -0,0 +1,136 @@ +# -*- coding: utf-8 -*- +import maya.cmds as cmds +from maya import mel +import os + +import qargparse + +from openpype.pipeline import ( + load, + get_representation_path +) +from openpype.hosts.maya.api.lib import ( + maintained_selection, + namespaced, + unique_namespace +) +from openpype.hosts.maya.api.pipeline import containerise +from openpype.client import get_representations, get_representation_by_id + + +class MultiverseUsdOverLoader(load.LoaderPlugin): + """Reference file""" + + families = ["mvUsdOverride"] + representations = ["usda", "usd", "udsz"] + + label = "Load Usd Override into Compound" + order = -10 + icon = "code-fork" + color = "orange" + + options = [ + qargparse.String( + "Which Compound", + label="Compound", + help="Select which compound to add this as a layer to." + ) + ] + + def load(self, context, name=None, namespace=None, options=None): + asset = context['asset']['name'] + + current_usd = cmds.ls(selection=True, + type="mvUsdCompoundShape", + dag=True, + long=True) + if len(current_usd) != 1: + self.log.error("Current selection invalid: '{}', " + "must contain exactly 1 mvUsdCompoundShape." + "".format(current_usd)) + return + + # Make sure we can load the plugin + cmds.loadPlugin("MultiverseForMaya", quiet=True) + import multiverse + + nodes = current_usd + with maintained_selection(): + multiverse.AddUsdCompoundAssetPath(current_usd[0], self.fname) + + namespace = current_usd[0].split("|")[1].split(":")[0] + + container = containerise( + name=name, + namespace=namespace, + nodes=nodes, + context=context, + loader=self.__class__.__name__) + + cmds.addAttr(container, longName="mvUsdCompoundShape", + niceName="mvUsdCompoundShape", dataType="string") + cmds.setAttr(container + ".mvUsdCompoundShape", + current_usd[0], type="string") + + return container + + def update(self, container, representation): + # type: (dict, dict) -> None + """Update container with specified representation.""" + + cmds.loadPlugin("MultiverseForMaya", quiet=True) + import multiverse + + node = container['objectName'] + assert cmds.objExists(node), "Missing container" + + members = cmds.sets(node, query=True) or [] + shapes = cmds.ls(members, type="mvUsdCompoundShape") + assert shapes, "Cannot find mvUsdCompoundShape in container" + + mvShape = container['mvUsdCompoundShape'] + assert mvShape, "Missing mv source" + + project_name = representation["context"]["project"]["name"] + prev_representation_id = cmds.getAttr("{}.representation".format(node)) + prev_representation = get_representation_by_id(project_name, + prev_representation_id) + prev_path = os.path.normpath(prev_representation["data"]["path"]) + + path = get_representation_path(representation) + + for shape in shapes: + asset_paths = multiverse.GetUsdCompoundAssetPaths(shape) + asset_paths = [os.path.normpath(p) for p in asset_paths] + + assert asset_paths.count(prev_path) == 1, \ + "Couldn't find matching path (or too many)" + prev_path_idx = asset_paths.index(prev_path) + asset_paths[prev_path_idx] = path + multiverse.SetUsdCompoundAssetPaths(shape, asset_paths) + + cmds.setAttr("{}.representation".format(node), + str(representation["_id"]), + type="string") + mel.eval('refreshEditorTemplates;') + + def switch(self, container, representation): + self.update(container, representation) + + def remove(self, container): + # type: (dict) -> None + """Remove loaded container.""" + # Delete container and its contents + if cmds.objExists(container['objectName']): + members = cmds.sets(container['objectName'], query=True) or [] + cmds.delete([container['objectName']] + members) + + # Remove the namespace, if empty + namespace = container['namespace'] + if cmds.namespace(exists=namespace): + members = cmds.namespaceInfo(namespace, listNamespace=True) + if not members: + cmds.namespace(removeNamespace=namespace) + else: + self.log.warning("Namespace not deleted because it " + "still has members: %s", namespace) From c62527a678d97f9bf0e6be6f4ecec3d30433104b Mon Sep 17 00:00:00 2001 From: DMO Date: Mon, 31 Oct 2022 15:15:46 +0900 Subject: [PATCH 040/213] Removing bad merge. --- openpype/hosts/maya/api/lib_renderproducts.py | 58 ------------------- 1 file changed, 58 deletions(-) diff --git a/openpype/hosts/maya/api/lib_renderproducts.py b/openpype/hosts/maya/api/lib_renderproducts.py index 7b9601cda81..cd204445b7b 100644 --- a/openpype/hosts/maya/api/lib_renderproducts.py +++ b/openpype/hosts/maya/api/lib_renderproducts.py @@ -77,7 +77,6 @@ "arnold": "defaultRenderGlobals.imageFilePrefix", "renderman": "rmanGlobals.imageFileFormat", "redshift": "defaultRenderGlobals.imageFilePrefix", - "_3delight": "defaultRenderGlobals.imageFilePrefix", "mayahardware2": "defaultRenderGlobals.imageFilePrefix" } @@ -173,7 +172,6 @@ class Instance(object): "redshift": RenderProductsRedshift, "renderman": RenderProductsRenderman, "mayahardware2": RenderProductsMayaHardware - "_3delight": RenderProducts3Delight }.get(renderer_name.lower(), None) if renderer is None: raise UnsupportedRendererException( @@ -1288,62 +1286,6 @@ def get_render_products(self): for cam in self.get_renderable_cameras(): product = RenderProduct(productName="beauty", ext=ext, camera=cam) products.append(product) -class RenderProducts3Delight(ARenderProducts): - """Expected files for Renderman renderer. - - Warning: - This is very rudimentary and needs more love and testing. - """ - - renderer = "_3delight" - - def get_render_products(self): - """Get all AOVs. - - See Also: - :func:`ARenderProducts.get_render_products()` - - """ - cameras = [ - self.sanitize_camera_name(c) - for c in self.get_renderable_cameras() - ] - - if not cameras: - cameras = [ - self.sanitize_camera_name( - self.get_renderable_cameras()[0]) - ] - products = [] - - default_ext = "exr" - - nodes = cmds.listConnections( - 'dlRenderGlobals1', - type='dlRenderSettings') - assert len(nodes) == 1 - node = nodes[0] - - num_layers = cmds.getAttr( - '{}.layerOutputVariables'.format(node), - size=True) - assert num_layers > 0 - for i in range(num_layers): - output = cmds.getAttr( - '{}.layerOutput[{}]'.format(node, i)) - if not output: - continue - - output_var = cmds.getAttr( - '{}.layerOutputVariables[{}]'.format(node, i)) - output_var_tokens = output_var.split('|') - aov_name = output_var_tokens[4] - - for camera in cameras: - product = RenderProduct(productName=aov_name, - ext=default_ext, - camera=camera) - products.append(product) return products From 422a3c4f8ad9e7cbdb590d4c9f5aeb694cbcc698 Mon Sep 17 00:00:00 2001 From: DMO Date: Mon, 31 Oct 2022 15:33:50 +0900 Subject: [PATCH 041/213] Removed wrongly-staged `extract_animation.py` --- .../maya/plugins/publish/extract_animation.py | 113 ------------------ 1 file changed, 113 deletions(-) delete mode 100644 openpype/hosts/maya/plugins/publish/extract_animation.py diff --git a/openpype/hosts/maya/plugins/publish/extract_animation.py b/openpype/hosts/maya/plugins/publish/extract_animation.py deleted file mode 100644 index 3d1f30b640b..00000000000 --- a/openpype/hosts/maya/plugins/publish/extract_animation.py +++ /dev/null @@ -1,113 +0,0 @@ -import os - -from maya import cmds - -import openpype.api -from openpype.hosts.maya.api.lib import ( - extract_alembic, - suspended_refresh, - maintained_selection, - iter_visible_nodes_in_range -) - - -class ExtractAnimation(openpype.api.Extractor): - """Produce an alembic of just point positions and normals. - - Positions and normals, uvs, creases are preserved, but nothing more, - for plain and predictable point caches. - - Plugin can run locally or remotely (on a farm - if instance is marked with - "farm" it will be skipped in local processing, but processed on farm) - """ - - label = "Extract Animation" - hosts = ["maya"] - families = ["animation"] - targets = ["local", "remote"] - - def process(self, instance): - if instance.data.get("farm"): - self.log.debug("Should be processed on farm, skipping.") - return - - # Collect the out set nodes - out_sets = [node for node in instance if node.endswith("out_SET")] - if len(out_sets) != 1: - raise RuntimeError("Couldn't find exactly one out_SET: " - "{0}".format(out_sets)) - out_set = out_sets[0] - roots = cmds.sets(out_set, query=True) - - # Include all descendants - nodes = roots + cmds.listRelatives(roots, - allDescendents=True, - fullPath=True) or [] - - # Collect the start and end including handles - start = instance.data["frameStartHandle"] - end = instance.data["frameEndHandle"] - - self.log.info("Extracting animation..") - dirname = self.staging_dir(instance) - - parent_dir = self.staging_dir(instance) - filename = "{name}.abc".format(**instance.data) - path = os.path.join(parent_dir, filename) - - options = { - "step": instance.data.get("step", 1.0) or 1.0, - "attr": ["cbId"], - "writeVisibility": True, - "writeCreases": True, - "uvWrite": True, - "selection": True, - "worldSpace": instance.data.get("worldSpace", True), - "writeColorSets": instance.data.get("writeColorSets", False), - "writeFaceSets": instance.data.get("writeFaceSets", False), - # 'noNormals' is the standard alembic option name. - "noNormals": not instance.data.get("writeNormals", True) - } - - if not instance.data.get("includeParentHierarchy", True): - # Set the root nodes if we don't want to include parents - # The roots are to be considered the ones that are the actual - # direct members of the set - options["root"] = roots - - if int(cmds.about(version=True)) >= 2017: - # Since Maya 2017 alembic supports multiple uv sets - write them. - options["writeUVSets"] = True - - if instance.data.get("visibleOnly", False): - # If we only want to include nodes that are visible in the frame - # range then we need to do our own check. Alembic's `visibleOnly` - # flag does not filter out those that are only hidden on some - # frames as it counts "animated" or "connected" visibilities as - # if it's always visible. - nodes = list(iter_visible_nodes_in_range(nodes, - start=start, - end=end)) - - with suspended_refresh(): - with maintained_selection(): - cmds.select(nodes, noExpand=True) - extract_alembic(file=path, - startFrame=float(start), - endFrame=float(end), - **options) - - if "representations" not in instance.data: - instance.data["representations"] = [] - - representation = { - 'name': 'abc', - 'ext': 'abc', - 'files': filename, - "stagingDir": dirname, - } - instance.data["representations"].append(representation) - - instance.context.data["cleanupFullPaths"].append(path) - - self.log.info("Extracted {} to {}".format(instance, dirname)) From 046ed724c802101ad7a4190ae7fb9f3765b907d3 Mon Sep 17 00:00:00 2001 From: DMO Date: Mon, 31 Oct 2022 15:40:25 +0900 Subject: [PATCH 042/213] Addressing valid hound concerns. --- openpype/hosts/maya/plugins/load/load_multiverse_usd.py | 2 +- .../hosts/maya/plugins/load/load_multiverse_usd_over.py | 8 ++------ .../hosts/maya/plugins/publish/collect_multiverse_look.py | 6 +++--- .../plugins/publish/validate_transform_naming_suffix.py | 4 ++-- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_multiverse_usd.py b/openpype/hosts/maya/plugins/load/load_multiverse_usd.py index 13915aa2a92..9e0d38df469 100644 --- a/openpype/hosts/maya/plugins/load/load_multiverse_usd.py +++ b/openpype/hosts/maya/plugins/load/load_multiverse_usd.py @@ -13,7 +13,7 @@ unique_namespace ) from openpype.hosts.maya.api.pipeline import containerise -from openpype.client import get_representations, get_representation_by_id +from openpype.client import get_representation_by_id class MultiverseUsdLoader(load.LoaderPlugin): diff --git a/openpype/hosts/maya/plugins/load/load_multiverse_usd_over.py b/openpype/hosts/maya/plugins/load/load_multiverse_usd_over.py index 080475461b2..8a25508ac21 100644 --- a/openpype/hosts/maya/plugins/load/load_multiverse_usd_over.py +++ b/openpype/hosts/maya/plugins/load/load_multiverse_usd_over.py @@ -10,12 +10,10 @@ get_representation_path ) from openpype.hosts.maya.api.lib import ( - maintained_selection, - namespaced, - unique_namespace + maintained_selection ) from openpype.hosts.maya.api.pipeline import containerise -from openpype.client import get_representations, get_representation_by_id +from openpype.client import get_representation_by_id class MultiverseUsdOverLoader(load.LoaderPlugin): @@ -38,8 +36,6 @@ class MultiverseUsdOverLoader(load.LoaderPlugin): ] def load(self, context, name=None, namespace=None, options=None): - asset = context['asset']['name'] - current_usd = cmds.ls(selection=True, type="mvUsdCompoundShape", dag=True, diff --git a/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py b/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py index 4c50e4df27b..a7cb14855b2 100644 --- a/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py +++ b/openpype/hosts/maya/plugins/publish/collect_multiverse_look.py @@ -253,7 +253,7 @@ class CollectMultiverseLookData(pyblish.api.InstancePlugin): """Collect Multiverse Look Searches through the overrides finding all material overrides. From there - it extracts the shading group and then finds all texture files in the + it extracts the shading group and then finds all texture files in the shading group network. It also checks for mipmap versions of texture files and adds them to the resouces to get published. @@ -341,7 +341,7 @@ def collect_resource(self, node, publishMipMap): node_type = cmds.nodeType(node) self.log.debug("processing: {}/{}".format(node, node_type)) - if not node_type in NODETYPES: + if node_type not in NODETYPES: self.log.error("Unsupported file node: {}".format(node_type)) raise AssertionError("Unsupported file node") @@ -361,7 +361,7 @@ def collect_resource(self, node, publishMipMap): # Compare with the computed file path, e.g. the one with the # pattern in it, to generate some logging information about this # difference - # computed_attribute = "{}.computedFileTextureNamePattern".format(node) + # computed_attribute = "{}.computedFileTextureNamePattern".format(node) # noqa computed_source = cmds.getAttr(computed_fname_attrib) if source != computed_source: self.log.debug("Detected computed file pattern difference " diff --git a/openpype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py b/openpype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py index 4f2a400d91f..65551c8d5e6 100644 --- a/openpype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py +++ b/openpype/hosts/maya/plugins/publish/validate_transform_naming_suffix.py @@ -48,8 +48,8 @@ class ValidateTransformNamingSuffix(pyblish.api.InstancePlugin): @classmethod def get_table_for_invalid(cls): ss = [] - for k,v in cls.SUFFIX_NAMING_TABLE.items(): - ss.append(" - {}: {}".format(k,", ".join(v))) + for k, v in cls.SUFFIX_NAMING_TABLE.items(): + ss.append(" - {}: {}".format(k, ", ".join(v))) return "\n".join(ss) @staticmethod From ef924a2358b883c8118fca413770881e544cf8d2 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 16 Nov 2022 21:00:17 +0800 Subject: [PATCH 043/213] Import Reference during Publish --- .../publish/extract_import_reference.py | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index b0b69304ef8..8e0257dafb1 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -79,16 +79,19 @@ def process(self, instance): ref_scene_name = "{ref_scene_name}" print(">>> Opening {{}} ...".format(current_name)) cmds.file(current_name, open=True, force=True) -reference_node = cmds.ls(type='reference') print(">>> Processing references") -for ref in reference_node: - ref_file = cmds.referenceQuery(ref, f=True) - print("--- {{}}".format(ref)) - print("--> {{}}".format(ref_file)) - if ref == 'sharedReferenceNode': - cmds.file(ref_file, removeReference=True, referenceNode=ref) - else: - cmds.file(ref_file, importReference=True) +all_reference = cmds.file(q=True, reference=True) or [] +for ref in all_reference: + if cmds.referenceQuery(ref, f=True): + cmds.file(ref, importReference=True) + + nested_ref = cmds.file(q=True, reference=True) + if nested_ref: + for new_ref in nested_ref: + if new_ref not in all_reference: + all_reference.append(new_ref) + +print(">>> Finish importing references") print(">>> Saving scene as {{}}".format(ref_scene_name)) cmds.file(rename=ref_scene_name) From d068ff481401e4261f3447ddbd52a7bd0cdd06a6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 24 Nov 2022 10:54:46 +0100 Subject: [PATCH 044/213] Don't use legacy_io session in global plugins --- openpype/plugins/publish/cleanup_farm.py | 6 ++---- .../plugins/publish/collect_anatomy_instance_data.py | 3 +-- .../plugins/publish/collect_scene_loaded_versions.py | 7 ++----- openpype/plugins/publish/integrate.py | 11 +++++------ .../plugins/publish/validate_editorial_asset_name.py | 6 +----- 5 files changed, 11 insertions(+), 22 deletions(-) diff --git a/openpype/plugins/publish/cleanup_farm.py b/openpype/plugins/publish/cleanup_farm.py index 2c6c1625bb8..b87d4698a27 100644 --- a/openpype/plugins/publish/cleanup_farm.py +++ b/openpype/plugins/publish/cleanup_farm.py @@ -4,8 +4,6 @@ import shutil import pyblish.api -from openpype.pipeline import legacy_io - class CleanUpFarm(pyblish.api.ContextPlugin): """Cleans up the staging directory after a successful publish. @@ -23,8 +21,8 @@ class CleanUpFarm(pyblish.api.ContextPlugin): def process(self, context): # Get source host from which farm publishing was started - src_host_name = legacy_io.Session.get("AVALON_APP") - self.log.debug("Host name from session is {}".format(src_host_name)) + src_host_name = context.data["hostName"] + self.log.debug("Host name from context is {}".format(src_host_name)) # Skip process if is not in list of source hosts in which this # plugin should run if src_host_name not in self.allowed_hosts: diff --git a/openpype/plugins/publish/collect_anatomy_instance_data.py b/openpype/plugins/publish/collect_anatomy_instance_data.py index 909b49a07d4..3858b4725ec 100644 --- a/openpype/plugins/publish/collect_anatomy_instance_data.py +++ b/openpype/plugins/publish/collect_anatomy_instance_data.py @@ -32,7 +32,6 @@ get_subsets, get_last_versions ) -from openpype.pipeline import legacy_io class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): @@ -49,7 +48,7 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin): def process(self, context): self.log.info("Collecting anatomy data for all instances.") - project_name = legacy_io.active_project() + project_name = context.data["projectName"] self.fill_missing_asset_docs(context, project_name) self.fill_instance_data_from_asset(context) self.fill_latest_versions(context, project_name) diff --git a/openpype/plugins/publish/collect_scene_loaded_versions.py b/openpype/plugins/publish/collect_scene_loaded_versions.py index 5ff2b46e3bb..627d451f582 100644 --- a/openpype/plugins/publish/collect_scene_loaded_versions.py +++ b/openpype/plugins/publish/collect_scene_loaded_versions.py @@ -1,10 +1,7 @@ import pyblish.api from openpype.client import get_representations -from openpype.pipeline import ( - registered_host, - legacy_io, -) +from openpype.pipeline import registered_host class CollectSceneLoadedVersions(pyblish.api.ContextPlugin): @@ -44,7 +41,7 @@ def process(self, context): for container in containers } - project_name = legacy_io.active_project() + project_name = context.data["projectName"] repre_docs = get_representations( project_name, representation_ids=repre_ids, diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 401270a788d..e19c3eee7cc 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -25,7 +25,6 @@ ) from openpype.lib import source_hash from openpype.lib.file_transaction import FileTransaction -from openpype.pipeline import legacy_io from openpype.pipeline.publish import ( KnownPublishError, get_publish_template_name, @@ -242,7 +241,7 @@ def filter_representations(self, instance): return filtered_repres def register(self, instance, file_transactions, filtered_repres): - project_name = legacy_io.active_project() + project_name = instance.context["projectName"] instance_stagingdir = instance.data.get("stagingDir") if not instance_stagingdir: @@ -803,11 +802,11 @@ def get_template_name(self, instance): """Return anatomy template name to use for integration""" # Anatomy data is pre-filled by Collectors - - project_name = legacy_io.active_project() + context = instance.context + project_name = context.data["projectName"] # Task can be optional in anatomy data - host_name = instance.context.data["hostName"] + host_name = context.data["hostName"] anatomy_data = instance.data["anatomyData"] family = anatomy_data["family"] task_info = anatomy_data.get("task") or {} @@ -818,7 +817,7 @@ def get_template_name(self, instance): family, task_name=task_info.get("name"), task_type=task_info.get("type"), - project_settings=instance.context.data["project_settings"], + project_settings=context.data["project_settings"], logger=self.log ) diff --git a/openpype/plugins/publish/validate_editorial_asset_name.py b/openpype/plugins/publish/validate_editorial_asset_name.py index 694788c4146..4f8a1abf2e2 100644 --- a/openpype/plugins/publish/validate_editorial_asset_name.py +++ b/openpype/plugins/publish/validate_editorial_asset_name.py @@ -2,7 +2,6 @@ import pyblish.api -from openpype.pipeline import legacy_io from openpype.client import get_assets @@ -28,10 +27,7 @@ def process(self, context): asset_and_parents = self.get_parents(context) self.log.debug("__ asset_and_parents: {}".format(asset_and_parents)) - if not legacy_io.Session: - legacy_io.install() - - project_name = legacy_io.active_project() + project_name = context.data["projectName"] db_assets = list(get_assets( project_name, fields=["name", "data.parents"] )) From 59b4f5823719e168bdf4d6f0ccb33a380e83405b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 1 Dec 2022 12:59:49 +0100 Subject: [PATCH 045/213] OP-4504 - added originalBasename and originalDirname to instance Will be used to fill 'source' (and 'online') template. Source template is used to publish in-situ, eg. without copying possibly massive files (as pointcaches etc.) --- .../hosts/traypublisher/plugins/publish/collect_source.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openpype/hosts/traypublisher/plugins/publish/collect_source.py b/openpype/hosts/traypublisher/plugins/publish/collect_source.py index 6ff22be13a3..3d983a89eee 100644 --- a/openpype/hosts/traypublisher/plugins/publish/collect_source.py +++ b/openpype/hosts/traypublisher/plugins/publish/collect_source.py @@ -1,3 +1,5 @@ +import os.path + import pyblish.api @@ -22,3 +24,9 @@ def process(self, context): self.log.info(( "Source of instance \"{}\" was already set to \"{}\"" ).format(instance.data["name"], source)) + + if not instance.data.get("originalBasename"): + instance.data["originalBasename"] = os.path.basename(source) + + if not instance.data.get("originalDirname"): + instance.data["originalDirname"] = os.path.dirname(source) From 5aed3f9bd817b722f529d9289885a7af1cd155e4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 1 Dec 2022 13:01:54 +0100 Subject: [PATCH 046/213] OP-4504 - added originalDirname to data filling template Will be used to fill 'source' (and 'online') template. --- openpype/plugins/publish/integrate.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 401270a788d..a5df6783324 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -535,7 +535,8 @@ def prepare_representation(self, repre, "resolutionHeight": "resolution_height", "fps": "fps", "outputName": "output", - "originalBasename": "originalBasename" + "originalBasename": "originalBasename", + "originalDirname": "originalDirname" }.items(): # Allow to take value from representation # if not found also consider instance.data From 07ee68eea1d13960450a170cc86e8285638d1552 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 1 Dec 2022 13:02:49 +0100 Subject: [PATCH 047/213] OP-4504 - added checks for not overwriting same file Added check for publishing to project folder --- openpype/plugins/publish/integrate.py | 35 ++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index a5df6783324..08b59a35742 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -268,6 +268,8 @@ def register(self, instance, file_transactions, filtered_repres): ) instance.data["versionEntity"] = version + anatomy = instance.context.data["anatomy"] + # Get existing representations (if any) existing_repres_by_name = { repre_doc["name"].lower(): repre_doc @@ -291,6 +293,19 @@ def register(self, instance, file_transactions, filtered_repres): instance) for src, dst in prepared["transfers"]: + if src == dst: + self.log.info( + "Source '{}' same as destination '{}'. Skipping." + .format(src, dst)) + continue + + if not self._is_path_in_project_roots(anatomy.all_root_paths, + dst): + self.log.warning( + "Destination '{}' is not in project folder. Skipping" + .format(dst)) + continue + # todo: add support for hardlink transfers file_transactions.add(src, dst) @@ -340,7 +355,6 @@ def register(self, instance, file_transactions, filtered_repres): # Compute the resource file infos once (files belonging to the # version instance instead of an individual representation) so # we can re-use those file infos per representation - anatomy = instance.context.data["anatomy"] resource_file_infos = self.get_files_info(resource_destinations, sites=sites, anatomy=anatomy) @@ -889,3 +903,22 @@ def prepare_file_info(self, path, anatomy, sites): "hash": source_hash(path), "sites": sites } + + def _is_path_in_project_roots(self, roots, file_path): + """Checks if 'file_path' starts with any of the roots. + + Used to check that published path belongs to project, eg. we are not + trying to publish to local only folder. + Args: + roots (list of RootItem): {ROOT_NAME: ROOT_PATH} + file_path (str) + Returns: + (bool) + """ + file_path = str(file_path).replace("\\", "/") + for root_item in roots.values(): + if file_path.startswith(root_item.clean_value): + return True + + return False + From 0aa0080f1122fdafde739258f127056ab37a7620 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 1 Dec 2022 15:36:33 +0100 Subject: [PATCH 048/213] OP-4504 - fixed check file in project folder --- openpype/plugins/publish/integrate.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 08b59a35742..44cee664b53 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -299,7 +299,7 @@ def register(self, instance, file_transactions, filtered_repres): .format(src, dst)) continue - if not self._is_path_in_project_roots(anatomy.all_root_paths, + if not self._is_path_in_project_roots(anatomy.all_root_paths(), dst): self.log.warning( "Destination '{}' is not in project folder. Skipping" @@ -915,9 +915,9 @@ def _is_path_in_project_roots(self, roots, file_path): Returns: (bool) """ - file_path = str(file_path).replace("\\", "/") - for root_item in roots.values(): - if file_path.startswith(root_item.clean_value): + file_path = str(file_path).replace("\\", "/").lower() + for root_item in roots: + if file_path.startswith(root_item.lower()): return True return False From 232743854e06ad68eff562272372177437d4059e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 1 Dec 2022 15:58:01 +0100 Subject: [PATCH 049/213] OP-4504 - moved path parsing to global plugin CollectSources should run on all hosts whenever some output is expected. It seems to be best location to put parsing of source file, unless we want to create completely new separate plugin. --- openpype/hosts/traypublisher/api/plugin.py | 7 +++++- .../plugins/publish/collect_source.py | 6 ----- .../plugins/publish/collect_resources_path.py | 22 ++++++++++++++++++- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/traypublisher/api/plugin.py b/openpype/hosts/traypublisher/api/plugin.py index 75930f0f318..d559853fd12 100644 --- a/openpype/hosts/traypublisher/api/plugin.py +++ b/openpype/hosts/traypublisher/api/plugin.py @@ -1,4 +1,4 @@ -from openpype.lib.attribute_definitions import FileDef +from openpype.lib.attribute_definitions import FileDef, BoolDef from openpype.lib.transcoding import IMAGE_EXTENSIONS, VIDEO_EXTENSIONS from openpype.pipeline.create import ( Creator, @@ -129,6 +129,11 @@ def get_instance_attr_defs(self): single_item=True, label="Reviewable representations", extensions_label="Single reviewable item" + ), + BoolDef( + "publish_prepared", + default=False, + label="Just publish already prepared" ) ] diff --git a/openpype/hosts/traypublisher/plugins/publish/collect_source.py b/openpype/hosts/traypublisher/plugins/publish/collect_source.py index 3d983a89eee..5121452ca8e 100644 --- a/openpype/hosts/traypublisher/plugins/publish/collect_source.py +++ b/openpype/hosts/traypublisher/plugins/publish/collect_source.py @@ -24,9 +24,3 @@ def process(self, context): self.log.info(( "Source of instance \"{}\" was already set to \"{}\"" ).format(instance.data["name"], source)) - - if not instance.data.get("originalBasename"): - instance.data["originalBasename"] = os.path.basename(source) - - if not instance.data.get("originalDirname"): - instance.data["originalDirname"] = os.path.dirname(source) diff --git a/openpype/plugins/publish/collect_resources_path.py b/openpype/plugins/publish/collect_resources_path.py index 00f65b8b672..383cea4a259 100644 --- a/openpype/plugins/publish/collect_resources_path.py +++ b/openpype/plugins/publish/collect_resources_path.py @@ -15,7 +15,11 @@ class CollectResourcesPath(pyblish.api.InstancePlugin): - """Generate directory path where the files and resources will be stored""" + """Generate directory path where the files and resources will be stored. + + Collects folder name and file name from files, if exists, for in-situ + publishing. + """ label = "Collect Resources Path" order = pyblish.api.CollectorOrder + 0.495 @@ -100,3 +104,19 @@ def process(self, instance): self.log.debug("publishDir: \"{}\"".format(publish_folder)) self.log.debug("resourcesDir: \"{}\"".format(resources_folder)) + + # parse folder name and file name for online and source templates + # currentFile comes from hosts workfiles + # source comes from Publisher + current_file = instance.data.get("currentFile") + source = instance.data.get("source") + source_file = current_file or source + if os.path.exists(source_file): + self.log.debug("Parsing paths for {}".format(source_file)) + if not instance.data.get("originalBasename"): + instance.data["originalBasename"] = \ + os.path.basename(source_file) + + if not instance.data.get("originalDirname"): + instance.data["originalDirname"] = \ + os.path.dirname(source_file) From 499c32110cbf498234032c389585fb02d4aa9d5e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 1 Dec 2022 16:02:47 +0100 Subject: [PATCH 050/213] OP-4504 - revert of unwanted change --- openpype/hosts/traypublisher/api/plugin.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/openpype/hosts/traypublisher/api/plugin.py b/openpype/hosts/traypublisher/api/plugin.py index d559853fd12..75930f0f318 100644 --- a/openpype/hosts/traypublisher/api/plugin.py +++ b/openpype/hosts/traypublisher/api/plugin.py @@ -1,4 +1,4 @@ -from openpype.lib.attribute_definitions import FileDef, BoolDef +from openpype.lib.attribute_definitions import FileDef from openpype.lib.transcoding import IMAGE_EXTENSIONS, VIDEO_EXTENSIONS from openpype.pipeline.create import ( Creator, @@ -129,11 +129,6 @@ def get_instance_attr_defs(self): single_item=True, label="Reviewable representations", extensions_label="Single reviewable item" - ), - BoolDef( - "publish_prepared", - default=False, - label="Just publish already prepared" ) ] From 9c381b4b51e9e017bb4366eb14e647d5f61389a6 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 1 Dec 2022 18:37:47 +0100 Subject: [PATCH 051/213] OP-4504 - added logic for originalDirname into integrate Adding originalDirname to all hosts could be an ordeal, adding logic here is simpler, but might not be best solution. --- openpype/plugins/publish/integrate.py | 35 +++++++++++++++------------ 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 44cee664b53..b48020860b4 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -541,8 +541,25 @@ def prepare_representation(self, repre, template_data["representation"] = repre["name"] template_data["ext"] = repre["ext"] + stagingdir = repre.get("stagingDir") + if not stagingdir: + # Fall back to instance staging dir if not explicitly + # set for representation in the instance + self.log.debug(( + "Representation uses instance staging dir: {}" + ).format(instance_stagingdir)) + stagingdir = instance_stagingdir + + if not stagingdir: + raise KnownPublishError( + "No staging directory set for representation: {}".format(repre) + ) + # optionals # retrieve additional anatomy data from representation if exists + if not instance.data.get("originalDirname"): + instance.data["originalDirname"] = stagingdir + for key, anatomy_key in { # Representation Key: Anatomy data key "resolutionWidth": "resolution_width", @@ -561,20 +578,6 @@ def prepare_representation(self, repre, if value is not None: template_data[anatomy_key] = value - stagingdir = repre.get("stagingDir") - if not stagingdir: - # Fall back to instance staging dir if not explicitly - # set for representation in the instance - self.log.debug(( - "Representation uses instance staging dir: {}" - ).format(instance_stagingdir)) - stagingdir = instance_stagingdir - - if not stagingdir: - raise KnownPublishError( - "No staging directory set for representation: {}".format(repre) - ) - self.log.debug("Anatomy template name: {}".format(template_name)) anatomy = instance.context.data["anatomy"] publish_template_category = anatomy.templates[template_name] @@ -600,6 +603,7 @@ def prepare_representation(self, repre, )) src_collection = src_collections[0] + template_data["originalBasename"] = src_collection.head[:-1] destination_indexes = list(src_collection.indexes) # Use last frame for minimum padding # - that should cover both 'udim' and 'frame' minimum padding @@ -684,7 +688,8 @@ def prepare_representation(self, repre, raise KnownPublishError( "This is a bug. Representation file name is full path" ) - + if not template_data.get("originalBasename"): + template_data["originalBasename"] = fname # Manage anatomy template data template_data.pop("frame", None) if is_udim: From 1106f8cf5ad6d23051643c83fd894846127ec600 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 1 Dec 2022 18:38:24 +0100 Subject: [PATCH 052/213] OP-4504 - check for None --- openpype/plugins/publish/collect_resources_path.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/collect_resources_path.py b/openpype/plugins/publish/collect_resources_path.py index 383cea4a259..0f55e65c9ed 100644 --- a/openpype/plugins/publish/collect_resources_path.py +++ b/openpype/plugins/publish/collect_resources_path.py @@ -111,7 +111,7 @@ def process(self, instance): current_file = instance.data.get("currentFile") source = instance.data.get("source") source_file = current_file or source - if os.path.exists(source_file): + if source_file and os.path.exists(source_file): self.log.debug("Parsing paths for {}".format(source_file)) if not instance.data.get("originalBasename"): instance.data["originalBasename"] = \ From b67c8e28a9115c16970596dfceaf2ada1e425ea0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 1 Dec 2022 19:16:39 +0100 Subject: [PATCH 053/213] OP-4504 - cleanup of logic --- openpype/plugins/publish/integrate.py | 43 ++++++++++++++++++--------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index b48020860b4..dbad90af93f 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -293,17 +293,12 @@ def register(self, instance, file_transactions, filtered_repres): instance) for src, dst in prepared["transfers"]: - if src == dst: - self.log.info( - "Source '{}' same as destination '{}'. Skipping." - .format(src, dst)) + + if self._are_paths_same(src, dst): continue if not self._is_path_in_project_roots(anatomy.all_root_paths(), dst): - self.log.warning( - "Destination '{}' is not in project folder. Skipping" - .format(dst)) continue # todo: add support for hardlink transfers @@ -316,13 +311,20 @@ def register(self, instance, file_transactions, filtered_repres): # .ma representation. Those destination paths are pre-defined, etc. # todo: should we move or simplify this logic? resource_destinations = set() - for src, dst in instance.data.get("transfers", []): - file_transactions.add(src, dst, mode=FileTransaction.MODE_COPY) - resource_destinations.add(os.path.abspath(dst)) - for src, dst in instance.data.get("hardlinks", []): - file_transactions.add(src, dst, mode=FileTransaction.MODE_HARDLINK) - resource_destinations.add(os.path.abspath(dst)) + file_copy_modes = [ + ("transfers", FileTransaction.MODE_COPY), + ("hardlinks", FileTransaction.MODE_HARDLINK) + ] + for files_type, copy_mode in zip(*file_copy_modes): # unpack + for src, dst in instance.data.get(files_type, []): + if self._are_paths_same(src, dst): + continue + if not self._is_path_in_project_roots(anatomy.all_root_paths(), + dst): + continue + file_transactions.add(src, dst, mode=copy_mode) + resource_destinations.add(os.path.abspath(dst)) # Bulk write to the database # We write the subset and version to the database before the File @@ -924,6 +926,19 @@ def _is_path_in_project_roots(self, roots, file_path): for root_item in roots: if file_path.startswith(root_item.lower()): return True - + self.log.warning( + "Destination '{}' is not in project folder. Skipping" + .format(file_path)) return False + def _are_paths_same(self, src, dst): + src = str(src).replace("\\", "/").lower() + dst = str(dst).replace("\\", "/").lower() + + same = src == dst + if same: + self.log.info( + "Source '{}' same as destination '{}'. Skipping." + .format(src, dst)) + return same + From ddfaae8a798c9ba7ea12b56c4547f2e3214c8c6c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 2 Dec 2022 14:28:26 +0100 Subject: [PATCH 054/213] OP-4504 - added source template to defaults Source template is used in-situ publishing, eg. use files at their location, don't copy them anywhere. --- openpype/settings/defaults/project_anatomy/templates.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_anatomy/templates.json b/openpype/settings/defaults/project_anatomy/templates.json index 0ac56a4dad1..e4814257bca 100644 --- a/openpype/settings/defaults/project_anatomy/templates.json +++ b/openpype/settings/defaults/project_anatomy/templates.json @@ -53,11 +53,17 @@ "file": "{originalBasename}<.{@frame}><_{udim}>.{ext}", "path": "{@folder}/{@file}" }, + "source": { + "folder": "{originalBasename}<.{@frame}><_{udim}>.{ext}", + "file": "{originalDirname}", + "path": "{@folder}/{@file}" + }, "__dynamic_keys_labels__": { "maya2unreal": "Maya to Unreal", "simpleUnrealTextureHero": "Simple Unreal Texture - Hero", "simpleUnrealTexture": "Simple Unreal Texture", - "online": "online" + "online": "online", + "source": "source" } } } \ No newline at end of file From af3ebebb264ae559d98fa296c18da534d3590c66 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 2 Dec 2022 14:47:01 +0100 Subject: [PATCH 055/213] OP-4504 - Hound --- openpype/hosts/traypublisher/plugins/publish/collect_source.py | 2 -- openpype/plugins/publish/integrate.py | 1 - 2 files changed, 3 deletions(-) diff --git a/openpype/hosts/traypublisher/plugins/publish/collect_source.py b/openpype/hosts/traypublisher/plugins/publish/collect_source.py index 5121452ca8e..6ff22be13a3 100644 --- a/openpype/hosts/traypublisher/plugins/publish/collect_source.py +++ b/openpype/hosts/traypublisher/plugins/publish/collect_source.py @@ -1,5 +1,3 @@ -import os.path - import pyblish.api diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index dbad90af93f..4c26f288621 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -941,4 +941,3 @@ def _are_paths_same(self, src, dst): "Source '{}' same as destination '{}'. Skipping." .format(src, dst)) return same - From d1ee451b9a547d3ff8d480022efe4d020d9f7bc7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 6 Dec 2022 15:06:21 +0100 Subject: [PATCH 056/213] OP-4504 - update logging --- openpype/plugins/publish/extract_thumbnail_from_source.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_thumbnail_from_source.py b/openpype/plugins/publish/extract_thumbnail_from_source.py index 8da12138071..1165c803184 100644 --- a/openpype/plugins/publish/extract_thumbnail_from_source.py +++ b/openpype/plugins/publish/extract_thumbnail_from_source.py @@ -76,7 +76,7 @@ def process(self, instance): def _create_thumbnail(self, context, thumbnail_source): if not thumbnail_source: - self.log.debug("Thumbnail source not filled. Skipping.") + self.log.debug("Thumbnail source on context not filled. Skipping.") return if not os.path.exists(thumbnail_source): From c9496bcfe3271631124d557af9166c0a2c3879cc Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 7 Dec 2022 12:22:53 +0100 Subject: [PATCH 057/213] OP-4504 - change boolean test to validation with exception It actually shouldn't allow to publish into non project folder (like artists own c:/ drive). --- openpype/plugins/publish/integrate.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index f3683c42148..5e765215506 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -293,11 +293,10 @@ def register(self, instance, file_transactions, filtered_repres): instance) for src, dst in prepared["transfers"]: - if self._are_paths_same(src, dst): - continue + self._validate_path_in_project_roots(anatomy.all_root_paths(), + dst) - if not self._is_path_in_project_roots(anatomy.all_root_paths(), - dst): + if self._are_paths_same(src, dst): continue # todo: add support for hardlink transfers @@ -317,11 +316,10 @@ def register(self, instance, file_transactions, filtered_repres): ] for files_type, copy_mode in zip(*file_copy_modes): # unpack for src, dst in instance.data.get(files_type, []): + self._validate_path_in_project_roots(anatomy.all_root_paths(), + dst) if self._are_paths_same(src, dst): continue - if not self._is_path_in_project_roots(anatomy.all_root_paths(), - dst): - continue file_transactions.add(src, dst, mode=copy_mode) resource_destinations.add(os.path.abspath(dst)) @@ -910,7 +908,7 @@ def prepare_file_info(self, path, anatomy, sites): "sites": sites } - def _is_path_in_project_roots(self, roots, file_path): + def _validate_path_in_project_roots(self, roots, file_path): """Checks if 'file_path' starts with any of the roots. Used to check that published path belongs to project, eg. we are not @@ -918,17 +916,17 @@ def _is_path_in_project_roots(self, roots, file_path): Args: roots (list of RootItem): {ROOT_NAME: ROOT_PATH} file_path (str) - Returns: - (bool) + Raises + (KnownPublishError) """ file_path = str(file_path).replace("\\", "/").lower() for root_item in roots: if file_path.startswith(root_item.lower()): return True - self.log.warning( - "Destination '{}' is not in project folder. Skipping" - .format(file_path)) - return False + raise KnownPublishError(( + "Destination path {} ".format(file_path) + + "must be in project dir" + )) def _are_paths_same(self, src, dst): src = str(src).replace("\\", "/").lower() From 3fabd516ea02b762505377e4d60853e6ebc60c9e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 7 Dec 2022 12:25:16 +0100 Subject: [PATCH 058/213] OP-4504 - weird unpacking not necessary --- openpype/plugins/publish/integrate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 5e765215506..6b359af1d21 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -314,7 +314,7 @@ def register(self, instance, file_transactions, filtered_repres): ("transfers", FileTransaction.MODE_COPY), ("hardlinks", FileTransaction.MODE_HARDLINK) ] - for files_type, copy_mode in zip(*file_copy_modes): # unpack + for files_type, copy_mode in file_copy_modes: for src, dst in instance.data.get(files_type, []): self._validate_path_in_project_roots(anatomy.all_root_paths(), dst) From 0a12a42460cef6770b4761d935cce38a2f6fc1cb Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 7 Dec 2022 12:29:44 +0100 Subject: [PATCH 059/213] OP-4504 - use existing method to check if path in project --- openpype/plugins/publish/integrate.py | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 6b359af1d21..ce31831f1e5 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -293,8 +293,7 @@ def register(self, instance, file_transactions, filtered_repres): instance) for src, dst in prepared["transfers"]: - self._validate_path_in_project_roots(anatomy.all_root_paths(), - dst) + self._validate_path_in_project_roots(anatomy, dst) if self._are_paths_same(src, dst): continue @@ -316,8 +315,7 @@ def register(self, instance, file_transactions, filtered_repres): ] for files_type, copy_mode in file_copy_modes: for src, dst in instance.data.get(files_type, []): - self._validate_path_in_project_roots(anatomy.all_root_paths(), - dst) + self._validate_path_in_project_roots(anatomy, dst) if self._are_paths_same(src, dst): continue file_transactions.add(src, dst, mode=copy_mode) @@ -908,25 +906,23 @@ def prepare_file_info(self, path, anatomy, sites): "sites": sites } - def _validate_path_in_project_roots(self, roots, file_path): + def _validate_path_in_project_roots(self, anatomy, file_path): """Checks if 'file_path' starts with any of the roots. Used to check that published path belongs to project, eg. we are not trying to publish to local only folder. Args: - roots (list of RootItem): {ROOT_NAME: ROOT_PATH} + anatomy (Anatomy) file_path (str) Raises (KnownPublishError) """ - file_path = str(file_path).replace("\\", "/").lower() - for root_item in roots: - if file_path.startswith(root_item.lower()): - return True - raise KnownPublishError(( - "Destination path {} ".format(file_path) + - "must be in project dir" - )) + found, _ = anatomy.find_root_template_from_path(file_path) + if not found: + raise KnownPublishError(( + "Destination path {} ".format(file_path) + + "must be in project dir" + )) def _are_paths_same(self, src, dst): src = str(src).replace("\\", "/").lower() From cccd9c61ddac217d59480c8fafee4eba8b569412 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 7 Dec 2022 12:34:26 +0100 Subject: [PATCH 060/213] OP-4504 - logging message is wrong as it is called on instances also --- openpype/plugins/publish/extract_thumbnail_from_source.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_thumbnail_from_source.py b/openpype/plugins/publish/extract_thumbnail_from_source.py index 084815915f5..03df1455e23 100644 --- a/openpype/plugins/publish/extract_thumbnail_from_source.py +++ b/openpype/plugins/publish/extract_thumbnail_from_source.py @@ -77,7 +77,7 @@ def process(self, instance): def _create_thumbnail(self, context, thumbnail_source): if not thumbnail_source: - self.log.debug("Thumbnail source on context not filled. Skipping.") + self.log.debug("Thumbnail source not filled. Skipping.") return if not os.path.exists(thumbnail_source): From 30eca6f6ca3487bee2b758e85cce6dbeb0d26354 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 7 Dec 2022 14:10:52 +0100 Subject: [PATCH 061/213] OP-4504 - fix default source template --- openpype/settings/defaults/project_anatomy/templates.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/defaults/project_anatomy/templates.json b/openpype/settings/defaults/project_anatomy/templates.json index e4814257bca..32230e0625f 100644 --- a/openpype/settings/defaults/project_anatomy/templates.json +++ b/openpype/settings/defaults/project_anatomy/templates.json @@ -54,8 +54,8 @@ "path": "{@folder}/{@file}" }, "source": { - "folder": "{originalBasename}<.{@frame}><_{udim}>.{ext}", - "file": "{originalDirname}", + "folder": "{root[work]}/{originalDirname}", + "file": "{originalBasename}<.{@frame}><_{udim}>.{ext}", "path": "{@folder}/{@file}" }, "__dynamic_keys_labels__": { From 4c53a77a83fd4d79825f11339d7561c23f3797f4 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 7 Dec 2022 17:48:59 +0100 Subject: [PATCH 062/213] OP-4504 - make root comparison case insensitive for windows find_root_template_from_path tries to find root in passed path with case sensitivity, on Windows it doesn't make sense C:// == c://. Keep all other paths case sensitive. --- openpype/pipeline/anatomy.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/openpype/pipeline/anatomy.py b/openpype/pipeline/anatomy.py index 908dc2b187a..a50f8f67bb0 100644 --- a/openpype/pipeline/anatomy.py +++ b/openpype/pipeline/anatomy.py @@ -1106,17 +1106,21 @@ def find_root_template_from_path(self, path): result = False output = str(path) - root_paths = list(self.cleaned_data.values()) mod_path = self.clean_path(path) - for root_path in root_paths: + for root_os, root_path in self.cleaned_data.items(): # Skip empty paths if not root_path: continue - if mod_path.startswith(root_path): + _mod_path = mod_path # reset to original cleaned value + if root_os == "windows": + root_path = root_path.lower() + _mod_path = _mod_path.lower() + + if _mod_path.startswith(root_path): result = True replacement = "{" + self.full_key() + "}" - output = replacement + mod_path[len(root_path):] + output = replacement + _mod_path[len(root_path):] break return (result, output) @@ -1206,6 +1210,7 @@ def find_root_template_from_path(self, path, roots=None): Raises: ValueError: When roots are not entered and can't be loaded. """ + print("!roots::{}".format(roots)) if roots is None: log.debug( "Looking for matching root in path \"{}\".".format(path) @@ -1216,10 +1221,12 @@ def find_root_template_from_path(self, path, roots=None): raise ValueError("Roots are not set. Can't find path.") if isinstance(roots, RootItem): + print("here") return roots.find_root_template_from_path(path) for root_name, _root in roots.items(): - success, result = self.find_root_template_from_path(path, _root) + print("root::{}".format(_root)) + success, result = self.find_root_template_from_path(path.lower(), _root) if success: log.info("Found match in root \"{}\".".format(root_name)) return success, result From 726c8f2cc12f5362fc255f2b269c5b356794b0e6 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 7 Dec 2022 17:51:41 +0100 Subject: [PATCH 063/213] OP-4504 - fix resolving of originalDirname If instance has originalDirname collected, all repres should use this as a target folder (that allows copying transient items from temporary folders into folder where source item comes from). --- openpype/plugins/publish/integrate.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index ce31831f1e5..45710c8f412 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -554,17 +554,13 @@ def prepare_representation(self, repre, # optionals # retrieve additional anatomy data from representation if exists - if not instance.data.get("originalDirname"): - instance.data["originalDirname"] = stagingdir - for key, anatomy_key in { # Representation Key: Anatomy data key "resolutionWidth": "resolution_width", "resolutionHeight": "resolution_height", "fps": "fps", "outputName": "output", - "originalBasename": "originalBasename", - "originalDirname": "originalDirname" + "originalBasename": "originalBasename" }.items(): # Allow to take value from representation # if not found also consider instance.data @@ -582,6 +578,14 @@ def prepare_representation(self, repre, is_udim = bool(repre.get("udim")) + # store as originalDirname only original value without project root + # if instance collected originalDirname it should be used for all repre + # useful to storing transient items, eg. thumbnails, from temp to final + original_directory = instance.data.get("originalDirname") or stagingdir + _rootless = self.get_rootless_path(anatomy, original_directory) + without_root = _rootless[_rootless.rfind('}')+2:] + template_data["originalDirname"] = without_root + is_sequence_representation = isinstance(files, (list, tuple)) if is_sequence_representation: # Collection of files (sequence) @@ -685,8 +689,7 @@ def prepare_representation(self, repre, raise KnownPublishError( "This is a bug. Representation file name is full path" ) - if not template_data.get("originalBasename"): - template_data["originalBasename"] = fname + template_data["originalBasename"] = fname # Manage anatomy template data template_data.pop("frame", None) if is_udim: @@ -917,8 +920,8 @@ def _validate_path_in_project_roots(self, anatomy, file_path): Raises (KnownPublishError) """ - found, _ = anatomy.find_root_template_from_path(file_path) - if not found: + path = self.get_rootless_path(anatomy, file_path) + if not path: raise KnownPublishError(( "Destination path {} ".format(file_path) + "must be in project dir" From a1f85d3978e0ad87c13c8f415b9b91a375429a67 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 7 Dec 2022 17:57:10 +0100 Subject: [PATCH 064/213] OP-4504 - use always stagingDir from instance instead of repre Representation stagingDir might be in temporary folders (for thumbnails etc.), use value from instance as a backup instead. --- openpype/plugins/publish/integrate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 45710c8f412..1b79b5b858b 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -581,7 +581,8 @@ def prepare_representation(self, repre, # store as originalDirname only original value without project root # if instance collected originalDirname it should be used for all repre # useful to storing transient items, eg. thumbnails, from temp to final - original_directory = instance.data.get("originalDirname") or stagingdir + original_directory = ( + instance.data.get("originalDirname") or instance_stagingdir) _rootless = self.get_rootless_path(anatomy, original_directory) without_root = _rootless[_rootless.rfind('}')+2:] template_data["originalDirname"] = without_root @@ -694,7 +695,6 @@ def prepare_representation(self, repre, template_data.pop("frame", None) if is_udim: template_data["udim"] = repre["udim"][0] - # Construct destination filepath from template anatomy_filled = anatomy.format(template_data) template_filled = anatomy_filled[template_name]["path"] From d8ed8998b2274699760a88f99bb4d09a97fd1d51 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 7 Dec 2022 18:00:24 +0100 Subject: [PATCH 065/213] OP-4504 - removed unwanted lower Removed logging --- openpype/pipeline/anatomy.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/openpype/pipeline/anatomy.py b/openpype/pipeline/anatomy.py index a50f8f67bb0..969e1570fb9 100644 --- a/openpype/pipeline/anatomy.py +++ b/openpype/pipeline/anatomy.py @@ -1210,7 +1210,6 @@ def find_root_template_from_path(self, path, roots=None): Raises: ValueError: When roots are not entered and can't be loaded. """ - print("!roots::{}".format(roots)) if roots is None: log.debug( "Looking for matching root in path \"{}\".".format(path) @@ -1221,12 +1220,10 @@ def find_root_template_from_path(self, path, roots=None): raise ValueError("Roots are not set. Can't find path.") if isinstance(roots, RootItem): - print("here") return roots.find_root_template_from_path(path) for root_name, _root in roots.items(): - print("root::{}".format(_root)) - success, result = self.find_root_template_from_path(path.lower(), _root) + success, result = self.find_root_template_from_path(path, _root) if success: log.info("Found match in root \"{}\".".format(root_name)) return success, result From 98f45c24a6a697ae533a48c17cc5ebc9b7930dbf Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 7 Dec 2022 18:02:54 +0100 Subject: [PATCH 066/213] OP-4504 - Hound --- openpype/plugins/publish/integrate.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 1b79b5b858b..7ef279d787d 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -584,7 +584,8 @@ def prepare_representation(self, repre, original_directory = ( instance.data.get("originalDirname") or instance_stagingdir) _rootless = self.get_rootless_path(anatomy, original_directory) - without_root = _rootless[_rootless.rfind('}')+2:] + relative_path_start = _rootless.rfind('}') + 2 + without_root = _rootless[relative_path_start:] template_data["originalDirname"] = without_root is_sequence_representation = isinstance(files, (list, tuple)) @@ -923,8 +924,8 @@ def _validate_path_in_project_roots(self, anatomy, file_path): path = self.get_rootless_path(anatomy, file_path) if not path: raise KnownPublishError(( - "Destination path {} ".format(file_path) + - "must be in project dir" + "Destination path {} ".format(file_path) + + "must be in project dir" )) def _are_paths_same(self, src, dst): From fc10b26ea0610dc5d32ed7c2ce285dac8fdef9eb Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Thu, 8 Dec 2022 11:20:24 +0000 Subject: [PATCH 067/213] Implemented creator and extractor for uasset --- .../unreal/plugins/create/create_uasset.py | 61 +++++++++++++++++++ .../unreal/plugins/publish/extract_uasset.py | 45 ++++++++++++++ openpype/plugins/publish/integrate.py | 3 +- 3 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 openpype/hosts/unreal/plugins/create/create_uasset.py create mode 100644 openpype/hosts/unreal/plugins/publish/extract_uasset.py diff --git a/openpype/hosts/unreal/plugins/create/create_uasset.py b/openpype/hosts/unreal/plugins/create/create_uasset.py new file mode 100644 index 00000000000..ee584ac00c7 --- /dev/null +++ b/openpype/hosts/unreal/plugins/create/create_uasset.py @@ -0,0 +1,61 @@ +"""Create UAsset.""" +from pathlib import Path + +import unreal + +from openpype.hosts.unreal.api import pipeline +from openpype.pipeline import LegacyCreator + + +class CreateUAsset(LegacyCreator): + """UAsset.""" + + name = "UAsset" + label = "UAsset" + family = "uasset" + icon = "cube" + + root = "/Game/OpenPype" + suffix = "_INS" + + def __init__(self, *args, **kwargs): + super(CreateUAsset, self).__init__(*args, **kwargs) + + def process(self): + ar = unreal.AssetRegistryHelpers.get_asset_registry() + + subset = self.data["subset"] + path = f"{self.root}/PublishInstances/" + + unreal.EditorAssetLibrary.make_directory(path) + + selection = [] + if (self.options or {}).get("useSelection"): + sel_objects = unreal.EditorUtilityLibrary.get_selected_assets() + selection = [a.get_path_name() for a in sel_objects] + + if len(selection) != 1: + raise RuntimeError("Please select only one object.") + + obj = selection[0] + + asset = ar.get_asset_by_object_path(obj).get_asset() + sys_path = unreal.SystemLibrary.get_system_path(asset) + + if not sys_path: + raise RuntimeError( + f"{Path(obj).name} is not on the disk. Likely it needs to" + "be saved first.") + + if Path(sys_path).suffix != ".uasset": + raise RuntimeError(f"{Path(sys_path).name} is not a UAsset.") + + unreal.log("selection: {}".format(selection)) + container_name = f"{subset}{self.suffix}" + pipeline.create_publish_instance( + instance=container_name, path=path) + + data = self.data.copy() + data["members"] = selection + + pipeline.imprint(f"{path}/{container_name}", data) diff --git a/openpype/hosts/unreal/plugins/publish/extract_uasset.py b/openpype/hosts/unreal/plugins/publish/extract_uasset.py new file mode 100644 index 00000000000..99279e38a15 --- /dev/null +++ b/openpype/hosts/unreal/plugins/publish/extract_uasset.py @@ -0,0 +1,45 @@ +from pathlib import Path +import shutil + +import unreal +from unreal import EditorLevelLibrary as ell +from unreal import EditorAssetLibrary as eal + +from openpype.client import get_representation_by_name +from openpype.pipeline import legacy_io, publish + + +class ExtractUAsset(publish.Extractor): + """Extract a UAsset.""" + + label = "Extract UAsset" + hosts = ["unreal"] + families = ["uasset"] + optional = True + + def process(self, instance): + ar = unreal.AssetRegistryHelpers.get_asset_registry() + + self.log.info("Performing extraction..") + + staging_dir = self.staging_dir(instance) + filename = "{}.uasset".format(instance.name) + + obj = instance[0] + + asset = ar.get_asset_by_object_path(obj).get_asset() + sys_path = unreal.SystemLibrary.get_system_path(asset) + filename = Path(sys_path).name + + shutil.copy(sys_path, staging_dir) + + if "representations" not in instance.data: + instance.data["representations"] = [] + + representation = { + 'name': 'uasset', + 'ext': 'uasset', + 'files': filename, + "stagingDir": staging_dir, + } + instance.data["representations"].append(representation) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 6a85a87129e..6efff8440c5 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -130,7 +130,8 @@ class IntegrateAsset(pyblish.api.InstancePlugin): "mvUsdComposition", "mvUsdOverride", "simpleUnrealTexture", - "online" + "online", + "uasset" ] default_template_name = "publish" From 2b566bb594229d452dde7cb871239e07fac9366c Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Thu, 8 Dec 2022 11:20:47 +0000 Subject: [PATCH 068/213] Implemented balidator to check if the uasset has any dependency --- .../publish/validate_no_dependencies.py | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 openpype/hosts/unreal/plugins/publish/validate_no_dependencies.py diff --git a/openpype/hosts/unreal/plugins/publish/validate_no_dependencies.py b/openpype/hosts/unreal/plugins/publish/validate_no_dependencies.py new file mode 100644 index 00000000000..b7f42a772b6 --- /dev/null +++ b/openpype/hosts/unreal/plugins/publish/validate_no_dependencies.py @@ -0,0 +1,40 @@ +import unreal + +import pyblish.api + + +class ValidateNoDependencies(pyblish.api.InstancePlugin): + """Ensure that the uasset has no dependencies + + The uasset is checked for dependencies. If there are any, the instance + cannot be published. + """ + + order = pyblish.api.ValidatorOrder + label = "Check no dependencies" + families = ["uasset"] + hosts = ["unreal"] + optional = True + + def process(self, instance): + ar = unreal.AssetRegistryHelpers.get_asset_registry() + all_dependencies = [] + + for obj in instance[:]: + asset = ar.get_asset_by_object_path(obj) + dependencies = ar.get_dependencies( + asset.package_name, + unreal.AssetRegistryDependencyOptions( + include_soft_package_references=True, + include_hard_package_references=True, + include_searchable_names=False, + include_soft_management_references=False, + include_hard_management_references=False + )) + if dependencies: + for dep in dependencies: + all_dependencies.append(str(dep)) + + if all_dependencies: + raise RuntimeError( + f"Dependencies found: {all_dependencies}") From ee3d88756cbb75deb966fab37c8369d8a4d9df6d Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Thu, 8 Dec 2022 13:00:31 +0000 Subject: [PATCH 069/213] Implemented loading --- .../hosts/unreal/plugins/load/load_uasset.py | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 openpype/hosts/unreal/plugins/load/load_uasset.py diff --git a/openpype/hosts/unreal/plugins/load/load_uasset.py b/openpype/hosts/unreal/plugins/load/load_uasset.py new file mode 100644 index 00000000000..e3f967c43d1 --- /dev/null +++ b/openpype/hosts/unreal/plugins/load/load_uasset.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +"""Load UAsset.""" +from pathlib import Path +import shutil + +from openpype.pipeline import ( + get_representation_path, + AVALON_CONTAINER_ID +) +from openpype.hosts.unreal.api import plugin +from openpype.hosts.unreal.api import pipeline as unreal_pipeline +import unreal # noqa + + +class UAssetLoader(plugin.Loader): + """Load UAsset.""" + + families = ["uasset"] + label = "Load UAsset" + representations = ["uasset"] + icon = "cube" + color = "orange" + + def load(self, context, name, namespace, options): + """Load and containerise representation into Content Browser. + + Args: + context (dict): application context + name (str): subset name + namespace (str): in Unreal this is basically path to container. + This is not passed here, so namespace is set + by `containerise()` because only then we know + real path. + options (dict): Those would be data to be imprinted. This is not + used now, data are imprinted by `containerise()`. + + Returns: + list(str): list of container content + """ + + # Create directory for asset and OpenPype container + root = "/Game/OpenPype/Assets" + if options and options.get("asset_dir"): + root = options["asset_dir"] + asset = context.get('asset').get('name') + suffix = "_CON" + if asset: + asset_name = "{}_{}".format(asset, name) + else: + asset_name = "{}".format(name) + + tools = unreal.AssetToolsHelpers().get_asset_tools() + asset_dir, container_name = tools.create_unique_asset_name( + "{}/{}/{}".format(root, asset, name), suffix="") + + container_name += suffix + + unreal.EditorAssetLibrary.make_directory(asset_dir) + + # Create Asset Container + container = unreal_pipeline.create_container( + container=container_name, path=asset_dir) + + container_path = unreal.SystemLibrary.get_system_path(container) + destination_path = Path(container_path).parent.as_posix() + + shutil.copy(self.fname, destination_path) + + data = { + "schema": "openpype:container-2.0", + "id": AVALON_CONTAINER_ID, + "asset": asset, + "namespace": asset_dir, + "container_name": container_name, + "asset_name": asset_name, + "loader": str(self.__class__.__name__), + "representation": context["representation"]["_id"], + "parent": context["representation"]["parent"], + "family": context["representation"]["context"]["family"] + } + unreal_pipeline.imprint( + "{}/{}".format(asset_dir, container_name), data) + + asset_content = unreal.EditorAssetLibrary.list_assets( + asset_dir, recursive=True, include_folder=True + ) + + for a in asset_content: + unreal.EditorAssetLibrary.save_asset(a) + + return asset_content From a847626aac962d0f954b14a8de0e341793092d69 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Thu, 8 Dec 2022 16:12:48 +0000 Subject: [PATCH 070/213] Don't use container path to get destination folder --- .../hosts/unreal/plugins/load/load_uasset.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/unreal/plugins/load/load_uasset.py b/openpype/hosts/unreal/plugins/load/load_uasset.py index e3f967c43d1..76c4de1fbe7 100644 --- a/openpype/hosts/unreal/plugins/load/load_uasset.py +++ b/openpype/hosts/unreal/plugins/load/load_uasset.py @@ -40,8 +40,6 @@ def load(self, context, name, namespace, options): # Create directory for asset and OpenPype container root = "/Game/OpenPype/Assets" - if options and options.get("asset_dir"): - root = options["asset_dir"] asset = context.get('asset').get('name') suffix = "_CON" if asset: @@ -57,15 +55,17 @@ def load(self, context, name, namespace, options): unreal.EditorAssetLibrary.make_directory(asset_dir) - # Create Asset Container - container = unreal_pipeline.create_container( - container=container_name, path=asset_dir) - - container_path = unreal.SystemLibrary.get_system_path(container) - destination_path = Path(container_path).parent.as_posix() + destination_path = asset_dir.replace( + "/Game", + Path(unreal.Paths.project_content_dir()).as_posix(), + 1) shutil.copy(self.fname, destination_path) + # Create Asset Container + unreal_pipeline.create_container( + container=container_name, path=asset_dir) + data = { "schema": "openpype:container-2.0", "id": AVALON_CONTAINER_ID, From bc574e5e5e8174c69528e3834465de677b857115 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Thu, 8 Dec 2022 17:23:10 +0000 Subject: [PATCH 071/213] Implemented update and remove --- .../hosts/unreal/plugins/load/load_uasset.py | 56 ++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/unreal/plugins/load/load_uasset.py b/openpype/hosts/unreal/plugins/load/load_uasset.py index 76c4de1fbe7..eccfc7b445d 100644 --- a/openpype/hosts/unreal/plugins/load/load_uasset.py +++ b/openpype/hosts/unreal/plugins/load/load_uasset.py @@ -60,7 +60,7 @@ def load(self, context, name, namespace, options): Path(unreal.Paths.project_content_dir()).as_posix(), 1) - shutil.copy(self.fname, destination_path) + shutil.copy(self.fname, f"{destination_path}/{name}.uasset") # Create Asset Container unreal_pipeline.create_container( @@ -89,3 +89,57 @@ def load(self, context, name, namespace, options): unreal.EditorAssetLibrary.save_asset(a) return asset_content + + def update(self, container, representation): + ar = unreal.AssetRegistryHelpers.get_asset_registry() + + asset_dir = container["namespace"] + name = representation["context"]["subset"] + + destination_path = asset_dir.replace( + "/Game", + Path(unreal.Paths.project_content_dir()).as_posix(), + 1) + + asset_content = unreal.EditorAssetLibrary.list_assets( + asset_dir, recursive=False, include_folder=True + ) + + for asset in asset_content: + obj = ar.get_asset_by_object_path(asset).get_asset() + if not obj.get_class().get_name() == 'AssetContainer': + unreal.EditorAssetLibrary.delete_asset(asset) + + update_filepath = get_representation_path(representation) + + shutil.copy(update_filepath, f"{destination_path}/{name}.uasset") + + container_path = "{}/{}".format(container["namespace"], + container["objectName"]) + # update metadata + unreal_pipeline.imprint( + container_path, + { + "representation": str(representation["_id"]), + "parent": str(representation["parent"]) + }) + + asset_content = unreal.EditorAssetLibrary.list_assets( + asset_dir, recursive=True, include_folder=True + ) + + for a in asset_content: + unreal.EditorAssetLibrary.save_asset(a) + + def remove(self, container): + path = container["namespace"] + parent_path = Path(path).parent.as_posix() + + unreal.EditorAssetLibrary.delete_directory(path) + + asset_content = unreal.EditorAssetLibrary.list_assets( + parent_path, recursive=False + ) + + if len(asset_content) == 0: + unreal.EditorAssetLibrary.delete_directory(parent_path) From 976ba9b6ce5396da651d08b032fab052856fd3f4 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Thu, 8 Dec 2022 17:28:26 +0000 Subject: [PATCH 072/213] Hound fixes --- openpype/hosts/unreal/plugins/publish/extract_uasset.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/openpype/hosts/unreal/plugins/publish/extract_uasset.py b/openpype/hosts/unreal/plugins/publish/extract_uasset.py index 99279e38a15..89d779d3681 100644 --- a/openpype/hosts/unreal/plugins/publish/extract_uasset.py +++ b/openpype/hosts/unreal/plugins/publish/extract_uasset.py @@ -2,11 +2,8 @@ import shutil import unreal -from unreal import EditorLevelLibrary as ell -from unreal import EditorAssetLibrary as eal -from openpype.client import get_representation_by_name -from openpype.pipeline import legacy_io, publish +from openpype.pipeline import publish class ExtractUAsset(publish.Extractor): From fe0336c4359c519dd787aea5f5683e4fa871c171 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 12 Dec 2022 10:34:01 +0100 Subject: [PATCH 073/213] OP-4504 - removed path comparison function Obsolete as it is part of file_transaction file --- openpype/plugins/publish/integrate.py | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 0a885733bd9..041f7b1b19c 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -297,9 +297,6 @@ def register(self, instance, file_transactions, filtered_repres): for src, dst in prepared["transfers"]: self._validate_path_in_project_roots(anatomy, dst) - if self._are_paths_same(src, dst): - continue - # todo: add support for hardlink transfers file_transactions.add(src, dst) @@ -318,8 +315,7 @@ def register(self, instance, file_transactions, filtered_repres): for files_type, copy_mode in file_copy_modes: for src, dst in instance.data.get(files_type, []): self._validate_path_in_project_roots(anatomy, dst) - if self._are_paths_same(src, dst): - continue + file_transactions.add(src, dst, mode=copy_mode) resource_destinations.add(os.path.abspath(dst)) @@ -929,14 +925,3 @@ def _validate_path_in_project_roots(self, anatomy, file_path): "Destination path {} ".format(file_path) + "must be in project dir" )) - - def _are_paths_same(self, src, dst): - src = str(src).replace("\\", "/").lower() - dst = str(dst).replace("\\", "/").lower() - - same = src == dst - if same: - self.log.info( - "Source '{}' same as destination '{}'. Skipping." - .format(src, dst)) - return same From d18fc94c01bb2044ef209658f4cab32f8cd87ee8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 12 Dec 2022 12:41:26 +0100 Subject: [PATCH 074/213] OP-4504 - fix for deadline publishing --- openpype/plugins/publish/integrate.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 041f7b1b19c..ce37a53c650 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -581,10 +581,11 @@ def prepare_representation(self, repre, # useful to storing transient items, eg. thumbnails, from temp to final original_directory = ( instance.data.get("originalDirname") or instance_stagingdir) - _rootless = self.get_rootless_path(anatomy, original_directory) - relative_path_start = _rootless.rfind('}') + 2 - without_root = _rootless[relative_path_start:] - template_data["originalDirname"] = without_root + if original_directory: + _rootless = self.get_rootless_path(anatomy, original_directory) + relative_path_start = _rootless.rfind('}') + 2 + without_root = _rootless[relative_path_start:] + template_data["originalDirname"] = without_root is_sequence_representation = isinstance(files, (list, tuple)) if is_sequence_representation: From 8a267f6c340bc31d4c15dd5d90bc7c534d1a03af Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 12 Dec 2022 16:18:25 +0100 Subject: [PATCH 075/213] OP-4504 - handle originalDirname only if in template --- openpype/plugins/publish/integrate.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index ce37a53c650..4692cefe4db 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -295,8 +295,6 @@ def register(self, instance, file_transactions, filtered_repres): instance) for src, dst in prepared["transfers"]: - self._validate_path_in_project_roots(anatomy, dst) - # todo: add support for hardlink transfers file_transactions.add(src, dst) @@ -576,13 +574,21 @@ def prepare_representation(self, repre, is_udim = bool(repre.get("udim")) - # store as originalDirname only original value without project root - # if instance collected originalDirname it should be used for all repre - # useful to storing transient items, eg. thumbnails, from temp to final - original_directory = ( - instance.data.get("originalDirname") or instance_stagingdir) - if original_directory: + # handle publish in place + if "originalDirname" in template: + # store as originalDirname only original value without project root + # if instance collected originalDirname is present, it should be + # used for all represe + # from temp to final + original_directory = ( + instance.data.get("originalDirname") or instance_stagingdir) + _rootless = self.get_rootless_path(anatomy, original_directory) + if _rootless == original_directory: + raise KnownPublishError(( + "Destination path '{}' ".format(original_directory) + + "must be in project dir" + )) relative_path_start = _rootless.rfind('}') + 2 without_root = _rootless[relative_path_start:] template_data["originalDirname"] = without_root @@ -923,6 +929,6 @@ def _validate_path_in_project_roots(self, anatomy, file_path): path = self.get_rootless_path(anatomy, file_path) if not path: raise KnownPublishError(( - "Destination path {} ".format(file_path) + + "Destination path '{}' ".format(file_path) + "must be in project dir" )) From d9e6bedf3b7fbf3c30825df4aff91a4965bd7d7c Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Tue, 13 Dec 2022 10:35:15 +0000 Subject: [PATCH 076/213] Do not check soft references --- .../hosts/unreal/plugins/publish/validate_no_dependencies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/unreal/plugins/publish/validate_no_dependencies.py b/openpype/hosts/unreal/plugins/publish/validate_no_dependencies.py index b7f42a772b6..79d54306c43 100644 --- a/openpype/hosts/unreal/plugins/publish/validate_no_dependencies.py +++ b/openpype/hosts/unreal/plugins/publish/validate_no_dependencies.py @@ -25,7 +25,7 @@ def process(self, instance): dependencies = ar.get_dependencies( asset.package_name, unreal.AssetRegistryDependencyOptions( - include_soft_package_references=True, + include_soft_package_references=False, include_hard_package_references=True, include_searchable_names=False, include_soft_management_references=False, From bf10a77fb6f1dcde764020de3588e54391df9f4a Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Tue, 13 Dec 2022 10:50:38 +0000 Subject: [PATCH 077/213] Check only dependencies that are in the Content folder We ignore native dependencies and dependencies from plugins --- .../hosts/unreal/plugins/publish/validate_no_dependencies.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/unreal/plugins/publish/validate_no_dependencies.py b/openpype/hosts/unreal/plugins/publish/validate_no_dependencies.py index 79d54306c43..c7601295502 100644 --- a/openpype/hosts/unreal/plugins/publish/validate_no_dependencies.py +++ b/openpype/hosts/unreal/plugins/publish/validate_no_dependencies.py @@ -33,7 +33,8 @@ def process(self, instance): )) if dependencies: for dep in dependencies: - all_dependencies.append(str(dep)) + if str(dep).startswith("/Game/"): + all_dependencies.append(str(dep)) if all_dependencies: raise RuntimeError( From 9bf00f9cfebb4c5b0ec6586672308d1a74cd6376 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 14 Dec 2022 14:41:52 +0100 Subject: [PATCH 078/213] OP-4504 - added collector for originalDirname --- .../plugins/publish/collect_resources_path.py | 16 ------- .../publish/collect_source_for_source.py | 43 +++++++++++++++++++ 2 files changed, 43 insertions(+), 16 deletions(-) create mode 100644 openpype/plugins/publish/collect_source_for_source.py diff --git a/openpype/plugins/publish/collect_resources_path.py b/openpype/plugins/publish/collect_resources_path.py index ea86ab93b4b..dcd80fbbdfe 100644 --- a/openpype/plugins/publish/collect_resources_path.py +++ b/openpype/plugins/publish/collect_resources_path.py @@ -106,19 +106,3 @@ def process(self, instance): self.log.debug("publishDir: \"{}\"".format(publish_folder)) self.log.debug("resourcesDir: \"{}\"".format(resources_folder)) - - # parse folder name and file name for online and source templates - # currentFile comes from hosts workfiles - # source comes from Publisher - current_file = instance.data.get("currentFile") - source = instance.data.get("source") - source_file = current_file or source - if source_file and os.path.exists(source_file): - self.log.debug("Parsing paths for {}".format(source_file)) - if not instance.data.get("originalBasename"): - instance.data["originalBasename"] = \ - os.path.basename(source_file) - - if not instance.data.get("originalDirname"): - instance.data["originalDirname"] = \ - os.path.dirname(source_file) diff --git a/openpype/plugins/publish/collect_source_for_source.py b/openpype/plugins/publish/collect_source_for_source.py new file mode 100644 index 00000000000..345daa6fe81 --- /dev/null +++ b/openpype/plugins/publish/collect_source_for_source.py @@ -0,0 +1,43 @@ +""" +Requires: + instance -> currentFile + instance -> source + +Provides: + instance -> originalBasename + instance -> originalDirname +""" + +import os +import copy + +import pyblish.api + + +class CollectSourceForSource(pyblish.api.InstancePlugin): + """Collects source location of file for instance. + + Used for 'source' template name which handles in place publishing. + For this kind of publishing files are present with correct file name + pattern and correct location. + """ + + label = "Collect Source" + order = pyblish.api.CollectorOrder + 0.495 + + def process(self, instance): + # parse folder name and file name for online and source templates + # currentFile comes from hosts workfiles + # source comes from Publisher + current_file = instance.data.get("currentFile") + source = instance.data.get("source") + source_file = current_file or source + if source_file and os.path.exists(source_file): + self.log.debug("Parsing paths for {}".format(source_file)) + if not instance.data.get("originalBasename"): + instance.data["originalBasename"] = \ + os.path.basename(source_file) + + if not instance.data.get("originalDirname"): + instance.data["originalDirname"] = \ + os.path.dirname(source_file) From 3cf3fd65b1fe61ee339caf5fc69352e3c9ab1851 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 14 Dec 2022 14:42:51 +0100 Subject: [PATCH 079/213] OP-4504 - added validator for source template Check is output template is 'source' if originalDirname is collected and if it is inside of project --- .../plugins/publish/validate_publish_dir.py | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 openpype/plugins/publish/validate_publish_dir.py diff --git a/openpype/plugins/publish/validate_publish_dir.py b/openpype/plugins/publish/validate_publish_dir.py new file mode 100644 index 00000000000..03fc47347d1 --- /dev/null +++ b/openpype/plugins/publish/validate_publish_dir.py @@ -0,0 +1,69 @@ +import pyblish.api +from openpype.pipeline.publish import ValidateContentsOrder +from openpype.pipeline.publish import ( + KnownPublishError, + get_publish_template_name, +) + + +class ValidatePublishDir(pyblish.api.InstancePlugin): + """Validates if 'publishDir' is a project directory + + 'publishDir' is collected based on publish templates. In specific cases + ('source' template) source folder of items is used as a 'publishDir', this + validates if it is inside any project dir for the project. + (eg. files are not published from local folder, unaccessible for studio' + + """ + + order = ValidateContentsOrder + label = "Validate publish dir" + + checked_template_names = ["source"] + # validate instances might have interim family, needs to be mapped to final + family_mapping = { + "renderLayer": "render", + "renderLocal": "render" + } + + def process(self, instance): + + template_name = self._get_template_name_from_instance(instance) + + if template_name not in self.checked_template_names: + return + + original_dirname = instance.data.get("originalDirname") + if not original_dirname: + raise KnownPublishError("Instance meant for in place publishing." + " Its 'originalDirname' must be collected." + " Contact OP developer to modify collector" + ) + + anatomy = instance.context.data["anatomy"] + + success, _ = anatomy.find_root_template_from_path(original_dirname) + self.log.info(_) + if not success: + raise KnownPublishError( + "Path '{}' not in project folder.".format(original_dirname) + + " Please publish from inside of project folder." + ) + + def _get_template_name_from_instance(self, instance): + project_name = instance.context.data["projectName"] + host_name = instance.context.data["hostName"] + anatomy_data = instance.data["anatomyData"] + family = anatomy_data["family"] + family = self.family_mapping.get("family") or family + task_info = anatomy_data.get("task") or {} + + return get_publish_template_name( + project_name, + host_name, + family, + task_name=task_info.get("name"), + task_type=task_info.get("type"), + project_settings=instance.context.data["project_settings"], + logger=self.log + ) From d9a7d5cb802d1aec7ab71db7f576b2dde09c5f15 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 14 Dec 2022 14:46:01 +0100 Subject: [PATCH 080/213] OP-4504 - Hound --- openpype/plugins/publish/collect_source_for_source.py | 1 - openpype/plugins/publish/validate_publish_dir.py | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/openpype/plugins/publish/collect_source_for_source.py b/openpype/plugins/publish/collect_source_for_source.py index 345daa6fe81..fd08d28c749 100644 --- a/openpype/plugins/publish/collect_source_for_source.py +++ b/openpype/plugins/publish/collect_source_for_source.py @@ -9,7 +9,6 @@ """ import os -import copy import pyblish.api diff --git a/openpype/plugins/publish/validate_publish_dir.py b/openpype/plugins/publish/validate_publish_dir.py index 03fc47347d1..eabf4810f37 100644 --- a/openpype/plugins/publish/validate_publish_dir.py +++ b/openpype/plugins/publish/validate_publish_dir.py @@ -35,10 +35,10 @@ def process(self, instance): original_dirname = instance.data.get("originalDirname") if not original_dirname: - raise KnownPublishError("Instance meant for in place publishing." - " Its 'originalDirname' must be collected." - " Contact OP developer to modify collector" - ) + raise KnownPublishError( + "Instance meant for in place publishing." + " Its 'originalDirname' must be collected." + " Contact OP developer to modify collector.") anatomy = instance.context.data["anatomy"] From a3969f8d1a6c901cfa2abb6dd8871527004724cf Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 15 Dec 2022 12:51:11 +0100 Subject: [PATCH 081/213] Update openpype/plugins/publish/validate_publish_dir.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ondřej Samohel <33513211+antirotor@users.noreply.github.com> --- openpype/plugins/publish/validate_publish_dir.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/validate_publish_dir.py b/openpype/plugins/publish/validate_publish_dir.py index eabf4810f37..e375fadf49f 100644 --- a/openpype/plugins/publish/validate_publish_dir.py +++ b/openpype/plugins/publish/validate_publish_dir.py @@ -45,7 +45,7 @@ def process(self, instance): success, _ = anatomy.find_root_template_from_path(original_dirname) self.log.info(_) if not success: - raise KnownPublishError( + raise PublishValidationError( "Path '{}' not in project folder.".format(original_dirname) + " Please publish from inside of project folder." ) From ee58c0ce48dc7230a18a575e7089aeaf20616eaf Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 15 Dec 2022 13:15:14 +0100 Subject: [PATCH 082/213] OP-4504 - changed to XMLPublishError --- .../publish/help/validate_publish_dir.xml | 31 +++++++++++++++++++ .../plugins/publish/validate_publish_dir.py | 21 ++++++++----- 2 files changed, 44 insertions(+), 8 deletions(-) create mode 100644 openpype/plugins/publish/help/validate_publish_dir.xml diff --git a/openpype/plugins/publish/help/validate_publish_dir.xml b/openpype/plugins/publish/help/validate_publish_dir.xml new file mode 100644 index 00000000000..9f62b264bfa --- /dev/null +++ b/openpype/plugins/publish/help/validate_publish_dir.xml @@ -0,0 +1,31 @@ + + + +Source directory not collected + +## Source directory not collected + +Instance is marked for in place publishing. Its 'originalDirname' must be collected. Contact OP developer to modify collector. + + + +### __Detailed Info__ (optional) + +In place publishing uses source directory and file name in resulting path and file name of published item. For this instance + all required metadata weren't filled. This is not recoverable error, unless instance itself is removed. + Collector for this instance must be updated for instance to be published. + + + +Source file not in project dir + +## Source file not in project dir + +Path '{original_dirname}' not in project folder. Please publish from inside of project folder. + +### How to repair? + +Restart publish after you moved source file into project directory. + + + \ No newline at end of file diff --git a/openpype/plugins/publish/validate_publish_dir.py b/openpype/plugins/publish/validate_publish_dir.py index e375fadf49f..2f41127548a 100644 --- a/openpype/plugins/publish/validate_publish_dir.py +++ b/openpype/plugins/publish/validate_publish_dir.py @@ -1,7 +1,7 @@ import pyblish.api from openpype.pipeline.publish import ValidateContentsOrder from openpype.pipeline.publish import ( - KnownPublishError, + PublishXmlValidationError, get_publish_template_name, ) @@ -35,20 +35,25 @@ def process(self, instance): original_dirname = instance.data.get("originalDirname") if not original_dirname: - raise KnownPublishError( + raise PublishXmlValidationError( + self, "Instance meant for in place publishing." " Its 'originalDirname' must be collected." - " Contact OP developer to modify collector.") + " Contact OP developer to modify collector." + ) anatomy = instance.context.data["anatomy"] success, _ = anatomy.find_root_template_from_path(original_dirname) - self.log.info(_) + + formatting_data = { + "original_dirname": original_dirname, + } + msg = "Path '{}' not in project folder.".format(original_dirname) + \ + " Please publish from inside of project folder." if not success: - raise PublishValidationError( - "Path '{}' not in project folder.".format(original_dirname) + - " Please publish from inside of project folder." - ) + raise PublishXmlValidationError(self, msg, key="not_in_dir", + formatting_data=formatting_data) def _get_template_name_from_instance(self, instance): project_name = instance.context.data["projectName"] From da274a7c8db1df524efd29a8a00cd54d2f30022a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 15 Dec 2022 16:28:49 +0100 Subject: [PATCH 083/213] OP-4504 - safer comparison of two paths --- openpype/lib/file_transaction.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/openpype/lib/file_transaction.py b/openpype/lib/file_transaction.py index f265b8815cc..4ebede01747 100644 --- a/openpype/lib/file_transaction.py +++ b/openpype/lib/file_transaction.py @@ -92,7 +92,9 @@ def add(self, src, dst, mode=MODE_COPY): def process(self): # Backup any existing files for dst, (src, _) in self._transfers.items(): - if dst == src or not os.path.exists(dst): + self.log.debug("Checking file ... {} -> {}".format(src, dst)) + path_same = self._same_paths(src, dst) + if path_same or not os.path.exists(dst): continue # Backup original file @@ -105,7 +107,8 @@ def process(self): # Copy the files to transfer for dst, (src, opts) in self._transfers.items(): - if dst == src: + path_same = self._same_paths(src, dst) + if path_same: self.log.debug( "Source and destionation are same files {} -> {}".format( src, dst)) @@ -182,3 +185,10 @@ def _create_folder_for_file(self, path): else: self.log.critical("An unexpected error occurred.") six.reraise(*sys.exc_info()) + + def _same_paths(self, src, dst): + # handles same paths but with C:/project vs c:/project + if os.path.exists(src) and os.path.exists(dst): + return os.path.samefile(src, dst) + + return False \ No newline at end of file From b6873a063eaac8f8e3d1463cd0f82681ae3c7f6e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 15 Dec 2022 16:29:56 +0100 Subject: [PATCH 084/213] OP-4504 - added '_thumb' suffix to thumbnail Without it thumbnail would overwrite source file --- openpype/plugins/publish/extract_thumbnail.py | 2 +- openpype/plugins/publish/extract_thumbnail_from_source.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_thumbnail.py b/openpype/plugins/publish/extract_thumbnail.py index 14b43beae85..a3c428fe971 100644 --- a/openpype/plugins/publish/extract_thumbnail.py +++ b/openpype/plugins/publish/extract_thumbnail.py @@ -91,7 +91,7 @@ def process(self, instance): full_input_path = os.path.join(src_staging, input_file) self.log.info("input {}".format(full_input_path)) filename = os.path.splitext(input_file)[0] - jpeg_file = filename + ".jpg" + jpeg_file = filename + "_thumb.jpg" full_output_path = os.path.join(dst_staging, jpeg_file) if oiio_supported: diff --git a/openpype/plugins/publish/extract_thumbnail_from_source.py b/openpype/plugins/publish/extract_thumbnail_from_source.py index 03df1455e23..a92f762cde8 100644 --- a/openpype/plugins/publish/extract_thumbnail_from_source.py +++ b/openpype/plugins/publish/extract_thumbnail_from_source.py @@ -100,7 +100,7 @@ def _create_thumbnail(self, context, thumbnail_source): self.log.info("Thumbnail source: {}".format(thumbnail_source)) src_basename = os.path.basename(thumbnail_source) - dst_filename = os.path.splitext(src_basename)[0] + ".jpg" + dst_filename = os.path.splitext(src_basename)[0] + "_thumb.jpg" full_output_path = os.path.join(dst_staging, dst_filename) if oiio_supported: From e3866dff5abd2022d37d90f0da4eb634e35c995e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 15 Dec 2022 16:37:45 +0100 Subject: [PATCH 085/213] OP-4504 - remove check for existence For sequences source contains `%d` placeholder --- openpype/plugins/publish/collect_source_for_source.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/collect_source_for_source.py b/openpype/plugins/publish/collect_source_for_source.py index fd08d28c749..aa94238b4f2 100644 --- a/openpype/plugins/publish/collect_source_for_source.py +++ b/openpype/plugins/publish/collect_source_for_source.py @@ -31,7 +31,7 @@ def process(self, instance): current_file = instance.data.get("currentFile") source = instance.data.get("source") source_file = current_file or source - if source_file and os.path.exists(source_file): + if source_file: self.log.debug("Parsing paths for {}".format(source_file)) if not instance.data.get("originalBasename"): instance.data["originalBasename"] = \ From 3c09dfc80e003021b930b28288664eedff82002e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 15 Dec 2022 16:38:19 +0100 Subject: [PATCH 086/213] OP-4504 - remove extension from originalBasename It would produce weird concatenation of extensions. --- openpype/plugins/publish/integrate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 4692cefe4db..94789bb778d 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -696,7 +696,7 @@ def prepare_representation(self, repre, raise KnownPublishError( "This is a bug. Representation file name is full path" ) - template_data["originalBasename"] = fname + template_data["originalBasename"], _ = os.path.splitext(fname) # Manage anatomy template data template_data.pop("frame", None) if is_udim: From 5d8ddb6e55cca8a3bc7bb058bc87080f329fc156 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 19 Dec 2022 12:15:09 +0100 Subject: [PATCH 087/213] OP-4504 - added explicit check Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/lib/file_transaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/lib/file_transaction.py b/openpype/lib/file_transaction.py index 4ebede01747..cba361a8d43 100644 --- a/openpype/lib/file_transaction.py +++ b/openpype/lib/file_transaction.py @@ -191,4 +191,4 @@ def _same_paths(self, src, dst): if os.path.exists(src) and os.path.exists(dst): return os.path.samefile(src, dst) - return False \ No newline at end of file + return src == dst From b1c834245072e99126a16d9e714dba8903dc5b95 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 19 Dec 2022 14:13:19 +0100 Subject: [PATCH 088/213] fix access to 'projectName' --- openpype/plugins/publish/integrate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/integrate.py b/openpype/plugins/publish/integrate.py index 5da4c765396..b8f5c4eedbc 100644 --- a/openpype/plugins/publish/integrate.py +++ b/openpype/plugins/publish/integrate.py @@ -243,7 +243,7 @@ def filter_representations(self, instance): return filtered_repres def register(self, instance, file_transactions, filtered_repres): - project_name = instance.context["projectName"] + project_name = instance.context.data["projectName"] instance_stagingdir = instance.data.get("stagingDir") if not instance_stagingdir: From 3d2bfbf5a7447d7894338c12a9b2d41c9c3a363c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 20 Dec 2022 12:08:29 +0100 Subject: [PATCH 089/213] traypublisher: multiple sequences enhancement --- .../plugins/create/create_editorial.py | 71 +++++++++++-------- 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/openpype/hosts/traypublisher/plugins/create/create_editorial.py b/openpype/hosts/traypublisher/plugins/create/create_editorial.py index 28a115629ed..205403d33e2 100644 --- a/openpype/hosts/traypublisher/plugins/create/create_editorial.py +++ b/openpype/hosts/traypublisher/plugins/create/create_editorial.py @@ -239,35 +239,38 @@ def create(self, subset_name, instance_data, pre_create_data): sequence_path_data = pre_create_data["sequence_filepath_data"] media_path_data = pre_create_data["media_filepaths_data"] - sequence_path = self._get_path_from_file_data(sequence_path_data) + sequence_paths = self._get_path_from_file_data( + sequence_path_data, multi=True) media_path = self._get_path_from_file_data(media_path_data) - # get otio timeline - otio_timeline = self._create_otio_timeline( - sequence_path, fps) + for index, seq_path in enumerate(sequence_paths): + # get otio timeline + otio_timeline = self._create_otio_timeline( + seq_path, fps) - # Create all clip instances - clip_instance_properties.update({ - "fps": fps, - "parent_asset_name": asset_name, - "variant": instance_data["variant"] - }) + # Create all clip instances + clip_instance_properties.update({ + "fps": fps, + "parent_asset_name": asset_name, + "variant": instance_data["variant"] + }) - # create clip instances - self._get_clip_instances( - otio_timeline, - media_path, - clip_instance_properties, - family_presets=allowed_family_presets + # create clip instances + self._get_clip_instances( + otio_timeline, + media_path, + clip_instance_properties, + family_presets=allowed_family_presets - ) + ) - # create otio editorial instance - self._create_otio_instance( - subset_name, instance_data, - sequence_path, media_path, - otio_timeline - ) + # create otio editorial instance + self._create_otio_instance( + subset_name + str(index), + instance_data, + seq_path, media_path, + otio_timeline + ) def _create_otio_instance( self, @@ -320,11 +323,12 @@ def _create_otio_timeline(self, sequence_path, fps): self.log.info(f"kwargs: {kwargs}") return otio.adapters.read_from_file(sequence_path, **kwargs) - def _get_path_from_file_data(self, file_path_data): + def _get_path_from_file_data(self, file_path_data, multi=False): """Converting creator path data to single path string Args: file_path_data (FileDefItem): creator path data inputs + multi (bool): switch to multiple files mode Raises: FileExistsError: in case nothing had been set @@ -332,16 +336,23 @@ def _get_path_from_file_data(self, file_path_data): Returns: str: path string """ - # TODO: just temporarly solving only one media file + return_path_list = [] + + self.log.debug(f"type: {type(file_path_data)}") + self.log.debug(f"file_path_data: {file_path_data}") + if isinstance(file_path_data, list): - file_path_data = file_path_data.pop() + return_path_list = [ + os.path.join(f["directory"], f["filenames"][0]) + for f in file_path_data + ] + self.log.debug(f"return_path_list: {return_path_list}") - if len(file_path_data["filenames"]) == 0: + if not return_path_list: raise FileExistsError( f"File path was not added: {file_path_data}") - return os.path.join( - file_path_data["directory"], file_path_data["filenames"][0]) + return return_path_list if multi else return_path_list[0] def _get_clip_instances( self, @@ -833,7 +844,7 @@ def get_pre_create_attr_defs(self): ".fcpxml" ], allow_sequences=False, - single_item=True, + single_item=False, label="Sequence file", ), FileDef( From 409db0fb831fc7447c8acfeb565ce06d57f5a162 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 11:48:27 +0100 Subject: [PATCH 090/213] use qtpy in resolve instead of Qt.py --- openpype/hosts/resolve/api/menu.py | 2 +- openpype/hosts/resolve/api/plugin.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/resolve/api/menu.py b/openpype/hosts/resolve/api/menu.py index 86b292105ac..eeb9e65dec7 100644 --- a/openpype/hosts/resolve/api/menu.py +++ b/openpype/hosts/resolve/api/menu.py @@ -1,7 +1,7 @@ import os import sys -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore from openpype.tools.utils import host_tools diff --git a/openpype/hosts/resolve/api/plugin.py b/openpype/hosts/resolve/api/plugin.py index 0ed7beee599..77e30149fd6 100644 --- a/openpype/hosts/resolve/api/plugin.py +++ b/openpype/hosts/resolve/api/plugin.py @@ -2,7 +2,7 @@ import uuid import qargparse -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore from openpype.settings import get_current_project_settings from openpype.pipeline.context_tools import get_current_project_asset From 227c61d20e5fe7a2746175ebee3e229355312454 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 13:48:15 +0100 Subject: [PATCH 091/213] use qtpy in publisher tool --- openpype/tools/publisher/constants.py | 2 +- openpype/tools/publisher/control_qt.py | 2 +- openpype/tools/publisher/publish_report_viewer/__init__.py | 2 +- openpype/tools/publisher/publish_report_viewer/constants.py | 2 +- openpype/tools/publisher/publish_report_viewer/delegates.py | 2 +- openpype/tools/publisher/publish_report_viewer/model.py | 2 +- openpype/tools/publisher/publish_report_viewer/widgets.py | 2 +- openpype/tools/publisher/publish_report_viewer/window.py | 2 +- openpype/tools/publisher/widgets/assets_widget.py | 2 +- openpype/tools/publisher/widgets/border_label_widget.py | 2 +- openpype/tools/publisher/widgets/card_view_widgets.py | 2 +- openpype/tools/publisher/widgets/create_widget.py | 2 +- openpype/tools/publisher/widgets/help_widget.py | 2 +- openpype/tools/publisher/widgets/icons.py | 2 +- openpype/tools/publisher/widgets/list_view_widgets.py | 2 +- openpype/tools/publisher/widgets/overview_widget.py | 2 +- openpype/tools/publisher/widgets/precreate_widget.py | 2 +- openpype/tools/publisher/widgets/publish_frame.py | 2 +- openpype/tools/publisher/widgets/tabs_widget.py | 2 +- openpype/tools/publisher/widgets/tasks_widget.py | 2 +- openpype/tools/publisher/widgets/thumbnail_widget.py | 2 +- openpype/tools/publisher/widgets/validations_widget.py | 2 +- openpype/tools/publisher/widgets/widgets.py | 2 +- openpype/tools/publisher/window.py | 2 +- 24 files changed, 24 insertions(+), 24 deletions(-) diff --git a/openpype/tools/publisher/constants.py b/openpype/tools/publisher/constants.py index 96f74a5a5c3..e9fdd4774a6 100644 --- a/openpype/tools/publisher/constants.py +++ b/openpype/tools/publisher/constants.py @@ -1,4 +1,4 @@ -from Qt import QtCore +from qtpy import QtCore # ID of context item in instance view CONTEXT_ID = "context" diff --git a/openpype/tools/publisher/control_qt.py b/openpype/tools/publisher/control_qt.py index 8b5856f2343..3639c4bb300 100644 --- a/openpype/tools/publisher/control_qt.py +++ b/openpype/tools/publisher/control_qt.py @@ -1,7 +1,7 @@ import collections from abc import abstractmethod, abstractproperty -from Qt import QtCore +from qtpy import QtCore from openpype.lib.events import Event from openpype.pipeline.create import CreatedInstance diff --git a/openpype/tools/publisher/publish_report_viewer/__init__.py b/openpype/tools/publisher/publish_report_viewer/__init__.py index bf77a6d30b8..2c51e5d7366 100644 --- a/openpype/tools/publisher/publish_report_viewer/__init__.py +++ b/openpype/tools/publisher/publish_report_viewer/__init__.py @@ -1,4 +1,4 @@ -from Qt import QtWidgets +from qtpy import QtWidgets from .report_items import ( PublishReport diff --git a/openpype/tools/publisher/publish_report_viewer/constants.py b/openpype/tools/publisher/publish_report_viewer/constants.py index 8fbb9342cab..529ecfc5c00 100644 --- a/openpype/tools/publisher/publish_report_viewer/constants.py +++ b/openpype/tools/publisher/publish_report_viewer/constants.py @@ -1,4 +1,4 @@ -from Qt import QtCore +from qtpy import QtCore ITEM_ID_ROLE = QtCore.Qt.UserRole + 1 diff --git a/openpype/tools/publisher/publish_report_viewer/delegates.py b/openpype/tools/publisher/publish_report_viewer/delegates.py index 9cd4f52174b..fc06b23900d 100644 --- a/openpype/tools/publisher/publish_report_viewer/delegates.py +++ b/openpype/tools/publisher/publish_report_viewer/delegates.py @@ -1,5 +1,5 @@ import collections -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui from .constants import ( ITEM_IS_GROUP_ROLE, ITEM_ERRORED_ROLE, diff --git a/openpype/tools/publisher/publish_report_viewer/model.py b/openpype/tools/publisher/publish_report_viewer/model.py index 704feeb4bd9..37da4ab3f24 100644 --- a/openpype/tools/publisher/publish_report_viewer/model.py +++ b/openpype/tools/publisher/publish_report_viewer/model.py @@ -1,5 +1,5 @@ import uuid -from Qt import QtCore, QtGui +from qtpy import QtCore, QtGui import pyblish.api diff --git a/openpype/tools/publisher/publish_report_viewer/widgets.py b/openpype/tools/publisher/publish_report_viewer/widgets.py index 0d35ac35120..7066efed325 100644 --- a/openpype/tools/publisher/publish_report_viewer/widgets.py +++ b/openpype/tools/publisher/publish_report_viewer/widgets.py @@ -1,5 +1,5 @@ from math import ceil -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui from openpype.widgets.nice_checkbox import NiceCheckbox diff --git a/openpype/tools/publisher/publish_report_viewer/window.py b/openpype/tools/publisher/publish_report_viewer/window.py index 646ae69e7f1..127a65dd9bf 100644 --- a/openpype/tools/publisher/publish_report_viewer/window.py +++ b/openpype/tools/publisher/publish_report_viewer/window.py @@ -4,7 +4,7 @@ import uuid import appdirs -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui from openpype import style from openpype.resources import get_openpype_icon_filepath diff --git a/openpype/tools/publisher/widgets/assets_widget.py b/openpype/tools/publisher/widgets/assets_widget.py index 996c9029d4d..583e70ad5a9 100644 --- a/openpype/tools/publisher/widgets/assets_widget.py +++ b/openpype/tools/publisher/widgets/assets_widget.py @@ -1,6 +1,6 @@ import collections -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui from openpype.tools.utils import ( PlaceholderLineEdit, diff --git a/openpype/tools/publisher/widgets/border_label_widget.py b/openpype/tools/publisher/widgets/border_label_widget.py index 8e09dd817ef..db575d7b906 100644 --- a/openpype/tools/publisher/widgets/border_label_widget.py +++ b/openpype/tools/publisher/widgets/border_label_widget.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui from openpype.style import get_objected_colors diff --git a/openpype/tools/publisher/widgets/card_view_widgets.py b/openpype/tools/publisher/widgets/card_view_widgets.py index 57336f9304c..47f8ebb914c 100644 --- a/openpype/tools/publisher/widgets/card_view_widgets.py +++ b/openpype/tools/publisher/widgets/card_view_widgets.py @@ -23,7 +23,7 @@ import re import collections -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore from openpype.widgets.nice_checkbox import NiceCheckbox diff --git a/openpype/tools/publisher/widgets/create_widget.py b/openpype/tools/publisher/widgets/create_widget.py index 7bdac46273e..07b124f6163 100644 --- a/openpype/tools/publisher/widgets/create_widget.py +++ b/openpype/tools/publisher/widgets/create_widget.py @@ -1,6 +1,6 @@ import re -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui from openpype.pipeline.create import ( SUBSET_NAME_ALLOWED_SYMBOLS, diff --git a/openpype/tools/publisher/widgets/help_widget.py b/openpype/tools/publisher/widgets/help_widget.py index 00901118893..5d474613dff 100644 --- a/openpype/tools/publisher/widgets/help_widget.py +++ b/openpype/tools/publisher/widgets/help_widget.py @@ -3,7 +3,7 @@ except Exception: commonmark = None -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore class HelpButton(QtWidgets.QPushButton): diff --git a/openpype/tools/publisher/widgets/icons.py b/openpype/tools/publisher/widgets/icons.py index fd5c45f9010..8aa82f580f5 100644 --- a/openpype/tools/publisher/widgets/icons.py +++ b/openpype/tools/publisher/widgets/icons.py @@ -1,6 +1,6 @@ import os -from Qt import QtGui +from qtpy import QtGui def get_icon_path(icon_name=None, filename=None): diff --git a/openpype/tools/publisher/widgets/list_view_widgets.py b/openpype/tools/publisher/widgets/list_view_widgets.py index 1cdb4cdcdbf..57c1e769e48 100644 --- a/openpype/tools/publisher/widgets/list_view_widgets.py +++ b/openpype/tools/publisher/widgets/list_view_widgets.py @@ -24,7 +24,7 @@ """ import collections -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui from openpype.style import get_objected_colors from openpype.widgets.nice_checkbox import NiceCheckbox diff --git a/openpype/tools/publisher/widgets/overview_widget.py b/openpype/tools/publisher/widgets/overview_widget.py index b1aeda9cd4e..2566806228c 100644 --- a/openpype/tools/publisher/widgets/overview_widget.py +++ b/openpype/tools/publisher/widgets/overview_widget.py @@ -1,4 +1,4 @@ -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore from .border_label_widget import BorderedLabelWidget diff --git a/openpype/tools/publisher/widgets/precreate_widget.py b/openpype/tools/publisher/widgets/precreate_widget.py index b688a830539..3037a0e12d3 100644 --- a/openpype/tools/publisher/widgets/precreate_widget.py +++ b/openpype/tools/publisher/widgets/precreate_widget.py @@ -1,4 +1,4 @@ -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore from openpype.tools.attribute_defs import create_widget_for_attr_def diff --git a/openpype/tools/publisher/widgets/publish_frame.py b/openpype/tools/publisher/widgets/publish_frame.py index 00597451a9d..fe349e52a60 100644 --- a/openpype/tools/publisher/widgets/publish_frame.py +++ b/openpype/tools/publisher/widgets/publish_frame.py @@ -2,7 +2,7 @@ import json import time -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore from .widgets import ( StopBtn, diff --git a/openpype/tools/publisher/widgets/tabs_widget.py b/openpype/tools/publisher/widgets/tabs_widget.py index d8ad19cfc09..4b87b761786 100644 --- a/openpype/tools/publisher/widgets/tabs_widget.py +++ b/openpype/tools/publisher/widgets/tabs_widget.py @@ -1,4 +1,4 @@ -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore from openpype.tools.utils import set_style_property diff --git a/openpype/tools/publisher/widgets/tasks_widget.py b/openpype/tools/publisher/widgets/tasks_widget.py index f31fffb9ea4..1b1ddd0c7d5 100644 --- a/openpype/tools/publisher/widgets/tasks_widget.py +++ b/openpype/tools/publisher/widgets/tasks_widget.py @@ -1,4 +1,4 @@ -from Qt import QtCore, QtGui +from qtpy import QtCore, QtGui from openpype.tools.utils.tasks_widget import TasksWidget, TASK_NAME_ROLE from openpype.tools.utils.lib import get_default_task_icon diff --git a/openpype/tools/publisher/widgets/thumbnail_widget.py b/openpype/tools/publisher/widgets/thumbnail_widget.py index 035ec4b04b8..07423d47648 100644 --- a/openpype/tools/publisher/widgets/thumbnail_widget.py +++ b/openpype/tools/publisher/widgets/thumbnail_widget.py @@ -1,7 +1,7 @@ import os import uuid -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui from openpype.style import get_objected_colors from openpype.lib import ( diff --git a/openpype/tools/publisher/widgets/validations_widget.py b/openpype/tools/publisher/widgets/validations_widget.py index 935a12bc730..c5b5a00883e 100644 --- a/openpype/tools/publisher/widgets/validations_widget.py +++ b/openpype/tools/publisher/widgets/validations_widget.py @@ -4,7 +4,7 @@ except Exception: commonmark = None -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui from openpype.tools.utils import BaseClickableFrame, ClickableFrame from .widgets import ( diff --git a/openpype/tools/publisher/widgets/widgets.py b/openpype/tools/publisher/widgets/widgets.py index 4b9626154d6..16641bfb6c5 100644 --- a/openpype/tools/publisher/widgets/widgets.py +++ b/openpype/tools/publisher/widgets/widgets.py @@ -6,7 +6,7 @@ import uuid import shutil import collections -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui import qtawesome from openpype.lib.attribute_definitions import UnknownDef diff --git a/openpype/tools/publisher/window.py b/openpype/tools/publisher/window.py index 0f7fd2c7e33..097e289f325 100644 --- a/openpype/tools/publisher/window.py +++ b/openpype/tools/publisher/window.py @@ -1,6 +1,6 @@ import collections import copy -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui from openpype import ( resources, From fa48c9dce3bf393bf268fcab45270c801b72de0e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 13:48:23 +0100 Subject: [PATCH 092/213] use qtpy in workfiles tool --- openpype/tools/workfiles/files_widget.py | 6 +++--- openpype/tools/workfiles/lock_dialog.py | 2 +- openpype/tools/workfiles/model.py | 2 +- openpype/tools/workfiles/save_as_dialog.py | 2 +- openpype/tools/workfiles/window.py | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openpype/tools/workfiles/files_widget.py b/openpype/tools/workfiles/files_widget.py index b7d31e4af4f..52ec348e459 100644 --- a/openpype/tools/workfiles/files_widget.py +++ b/openpype/tools/workfiles/files_widget.py @@ -3,8 +3,8 @@ import shutil import copy -import Qt -from Qt import QtWidgets, QtCore +import qtpy +from qtpy import QtWidgets, QtCore from openpype.host import IWorkfileHost from openpype.client import get_asset_by_id @@ -618,7 +618,7 @@ def on_browse_pressed(self): "caption": "Work Files", "filter": ext_filter } - if Qt.__binding__ in ("PySide", "PySide2"): + if qtpy.API in ("pyside", "pyside2"): kwargs["dir"] = self._workfiles_root else: kwargs["directory"] = self._workfiles_root diff --git a/openpype/tools/workfiles/lock_dialog.py b/openpype/tools/workfiles/lock_dialog.py index c574a74e32b..29e0d3bd9b8 100644 --- a/openpype/tools/workfiles/lock_dialog.py +++ b/openpype/tools/workfiles/lock_dialog.py @@ -1,4 +1,4 @@ -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui from openpype.style import load_stylesheet, get_app_icon_path from openpype.pipeline.workfile.lock_workfile import get_workfile_lock_data diff --git a/openpype/tools/workfiles/model.py b/openpype/tools/workfiles/model.py index 9a7fd659a9a..bbd67c9b985 100644 --- a/openpype/tools/workfiles/model.py +++ b/openpype/tools/workfiles/model.py @@ -1,7 +1,7 @@ import os import logging -from Qt import QtCore, QtGui +from qtpy import QtCore, QtGui import qtawesome from openpype.client import ( diff --git a/openpype/tools/workfiles/save_as_dialog.py b/openpype/tools/workfiles/save_as_dialog.py index cded4eb1a50..de21deee424 100644 --- a/openpype/tools/workfiles/save_as_dialog.py +++ b/openpype/tools/workfiles/save_as_dialog.py @@ -3,7 +3,7 @@ import copy import logging -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore from openpype.pipeline import ( registered_host, diff --git a/openpype/tools/workfiles/window.py b/openpype/tools/workfiles/window.py index de42b80d643..31ecf50d3b8 100644 --- a/openpype/tools/workfiles/window.py +++ b/openpype/tools/workfiles/window.py @@ -1,7 +1,7 @@ import os import datetime import copy -from Qt import QtCore, QtWidgets, QtGui +from qtpy import QtCore, QtWidgets, QtGui from openpype.client import ( get_asset_by_name, From 0b5b7f57755543d7f2c13211d9d819e82426333d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 13:48:48 +0100 Subject: [PATCH 093/213] use qtpy in subset manager --- openpype/tools/subsetmanager/model.py | 2 +- openpype/tools/subsetmanager/widgets.py | 2 +- openpype/tools/subsetmanager/window.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/tools/subsetmanager/model.py b/openpype/tools/subsetmanager/model.py index 760a167b42f..2df0cb7067b 100644 --- a/openpype/tools/subsetmanager/model.py +++ b/openpype/tools/subsetmanager/model.py @@ -1,6 +1,6 @@ import uuid -from Qt import QtCore, QtGui +from qtpy import QtCore, QtGui from openpype.pipeline import registered_host diff --git a/openpype/tools/subsetmanager/widgets.py b/openpype/tools/subsetmanager/widgets.py index 7a8cb15cbf4..1067474c445 100644 --- a/openpype/tools/subsetmanager/widgets.py +++ b/openpype/tools/subsetmanager/widgets.py @@ -1,5 +1,5 @@ import json -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore class InstanceDetail(QtWidgets.QWidget): diff --git a/openpype/tools/subsetmanager/window.py b/openpype/tools/subsetmanager/window.py index 6314e670154..30de83c142e 100644 --- a/openpype/tools/subsetmanager/window.py +++ b/openpype/tools/subsetmanager/window.py @@ -1,7 +1,7 @@ import os import sys -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore import qtawesome from openpype import style From e6a7de51335ff712d4c8cbd892ebc444f966d24f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 13:49:00 +0100 Subject: [PATCH 094/213] use qtpy in scene inventory --- openpype/tools/sceneinventory/lib.py | 2 +- openpype/tools/sceneinventory/model.py | 2 +- openpype/tools/sceneinventory/switch_dialog.py | 2 +- openpype/tools/sceneinventory/view.py | 2 +- openpype/tools/sceneinventory/widgets.py | 2 +- openpype/tools/sceneinventory/window.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/openpype/tools/sceneinventory/lib.py b/openpype/tools/sceneinventory/lib.py index 7653e1da89b..5db3c479c5a 100644 --- a/openpype/tools/sceneinventory/lib.py +++ b/openpype/tools/sceneinventory/lib.py @@ -1,7 +1,7 @@ import os from openpype_modules import sync_server -from Qt import QtGui +from qtpy import QtGui def walk_hierarchy(node): diff --git a/openpype/tools/sceneinventory/model.py b/openpype/tools/sceneinventory/model.py index 1a3b7c70551..9b45ed6cc1b 100644 --- a/openpype/tools/sceneinventory/model.py +++ b/openpype/tools/sceneinventory/model.py @@ -3,7 +3,7 @@ from collections import defaultdict -from Qt import QtCore, QtGui +from qtpy import QtCore, QtGui import qtawesome from openpype.host import ILoadHost diff --git a/openpype/tools/sceneinventory/switch_dialog.py b/openpype/tools/sceneinventory/switch_dialog.py index 1d1d5cbb918..47baeaebeab 100644 --- a/openpype/tools/sceneinventory/switch_dialog.py +++ b/openpype/tools/sceneinventory/switch_dialog.py @@ -1,6 +1,6 @@ import collections import logging -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore import qtawesome from bson.objectid import ObjectId diff --git a/openpype/tools/sceneinventory/view.py b/openpype/tools/sceneinventory/view.py index e0e43aaba7c..ba3aa069f23 100644 --- a/openpype/tools/sceneinventory/view.py +++ b/openpype/tools/sceneinventory/view.py @@ -2,7 +2,7 @@ import logging from functools import partial -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore import qtawesome from bson.objectid import ObjectId diff --git a/openpype/tools/sceneinventory/widgets.py b/openpype/tools/sceneinventory/widgets.py index 4c4aafad3a0..994e6c8108d 100644 --- a/openpype/tools/sceneinventory/widgets.py +++ b/openpype/tools/sceneinventory/widgets.py @@ -1,4 +1,4 @@ -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore from openpype import style diff --git a/openpype/tools/sceneinventory/window.py b/openpype/tools/sceneinventory/window.py index 8bac1beb305..7b305e8e8f1 100644 --- a/openpype/tools/sceneinventory/window.py +++ b/openpype/tools/sceneinventory/window.py @@ -1,7 +1,7 @@ import os import sys -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore import qtawesome from openpype import style From 40d44898fd244953b431d3a13c5eecb55073fda0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 13:49:25 +0100 Subject: [PATCH 095/213] use qtpy in pyblish pype --- openpype/tools/pyblish_pype/app.py | 3 ++- openpype/tools/pyblish_pype/constants.py | 2 +- openpype/tools/pyblish_pype/control.py | 2 +- openpype/tools/pyblish_pype/delegate.py | 2 +- openpype/tools/pyblish_pype/model.py | 2 +- openpype/tools/pyblish_pype/util.py | 2 +- openpype/tools/pyblish_pype/vendor/qtawesome/animation.py | 2 +- openpype/tools/pyblish_pype/vendor/qtawesome/iconic_font.py | 2 +- openpype/tools/pyblish_pype/view.py | 2 +- openpype/tools/pyblish_pype/widgets.py | 2 +- openpype/tools/pyblish_pype/window.py | 2 +- 11 files changed, 12 insertions(+), 11 deletions(-) diff --git a/openpype/tools/pyblish_pype/app.py b/openpype/tools/pyblish_pype/app.py index a252b964278..bdc204bfbdd 100644 --- a/openpype/tools/pyblish_pype/app.py +++ b/openpype/tools/pyblish_pype/app.py @@ -6,8 +6,9 @@ import platform import contextlib +from qtpy import QtCore, QtGui, QtWidgets + from . import control, settings, util, window -from Qt import QtCore, QtGui, QtWidgets self = sys.modules[__name__] diff --git a/openpype/tools/pyblish_pype/constants.py b/openpype/tools/pyblish_pype/constants.py index 03536fb8297..10f95ca4af2 100644 --- a/openpype/tools/pyblish_pype/constants.py +++ b/openpype/tools/pyblish_pype/constants.py @@ -1,4 +1,4 @@ -from Qt import QtCore +from qtpy import QtCore EXPANDER_WIDTH = 20 diff --git a/openpype/tools/pyblish_pype/control.py b/openpype/tools/pyblish_pype/control.py index 90bb002ba52..f8c6a3e0bcd 100644 --- a/openpype/tools/pyblish_pype/control.py +++ b/openpype/tools/pyblish_pype/control.py @@ -11,7 +11,7 @@ import logging import collections -from Qt import QtCore +from qtpy import QtCore import pyblish.api import pyblish.util diff --git a/openpype/tools/pyblish_pype/delegate.py b/openpype/tools/pyblish_pype/delegate.py index bf3fbc18535..bb253dd1a39 100644 --- a/openpype/tools/pyblish_pype/delegate.py +++ b/openpype/tools/pyblish_pype/delegate.py @@ -1,6 +1,6 @@ import platform -from Qt import QtWidgets, QtGui, QtCore +from qtpy import QtWidgets, QtGui, QtCore from . import model from .awesome import tags as awesome diff --git a/openpype/tools/pyblish_pype/model.py b/openpype/tools/pyblish_pype/model.py index 383d8304a5b..774c3054acb 100644 --- a/openpype/tools/pyblish_pype/model.py +++ b/openpype/tools/pyblish_pype/model.py @@ -29,7 +29,7 @@ from . import settings, util from .awesome import tags as awesome -from Qt import QtCore, QtGui +from qtpy import QtCore, QtGui import qtawesome from six import text_type from .constants import PluginStates, InstanceStates, GroupStates, Roles diff --git a/openpype/tools/pyblish_pype/util.py b/openpype/tools/pyblish_pype/util.py index 9f3697be167..81266370607 100644 --- a/openpype/tools/pyblish_pype/util.py +++ b/openpype/tools/pyblish_pype/util.py @@ -11,7 +11,7 @@ import copy import collections -from Qt import QtCore +from qtpy import QtCore from six import text_type import pyblish.api diff --git a/openpype/tools/pyblish_pype/vendor/qtawesome/animation.py b/openpype/tools/pyblish_pype/vendor/qtawesome/animation.py index e2a701785ad..ac695074448 100644 --- a/openpype/tools/pyblish_pype/vendor/qtawesome/animation.py +++ b/openpype/tools/pyblish_pype/vendor/qtawesome/animation.py @@ -1,4 +1,4 @@ -from Qt import QtCore +from qtpy import QtCore class Spin: diff --git a/openpype/tools/pyblish_pype/vendor/qtawesome/iconic_font.py b/openpype/tools/pyblish_pype/vendor/qtawesome/iconic_font.py index cd937d7e7fb..c25739aff81 100644 --- a/openpype/tools/pyblish_pype/vendor/qtawesome/iconic_font.py +++ b/openpype/tools/pyblish_pype/vendor/qtawesome/iconic_font.py @@ -6,7 +6,7 @@ import os import six -from Qt import QtCore, QtGui +from qtpy import QtCore, QtGui _default_options = { diff --git a/openpype/tools/pyblish_pype/view.py b/openpype/tools/pyblish_pype/view.py index 3b75e67d4c1..69dda6618d3 100644 --- a/openpype/tools/pyblish_pype/view.py +++ b/openpype/tools/pyblish_pype/view.py @@ -1,4 +1,4 @@ -from Qt import QtCore, QtWidgets +from qtpy import QtCore, QtWidgets from . import model from .constants import Roles, EXPANDER_WIDTH # Imported when used diff --git a/openpype/tools/pyblish_pype/widgets.py b/openpype/tools/pyblish_pype/widgets.py index dc4919c13f0..6adcc55f068 100644 --- a/openpype/tools/pyblish_pype/widgets.py +++ b/openpype/tools/pyblish_pype/widgets.py @@ -1,5 +1,5 @@ import sys -from Qt import QtCore, QtWidgets, QtGui +from qtpy import QtCore, QtWidgets, QtGui from . import model, delegate, view, awesome from .constants import PluginStates, InstanceStates, Roles diff --git a/openpype/tools/pyblish_pype/window.py b/openpype/tools/pyblish_pype/window.py index e1674053252..01d373d841c 100644 --- a/openpype/tools/pyblish_pype/window.py +++ b/openpype/tools/pyblish_pype/window.py @@ -45,7 +45,7 @@ from . import delegate, model, settings, util, view, widgets from .awesome import tags as awesome -from Qt import QtCore, QtGui, QtWidgets +from qtpy import QtCore, QtGui, QtWidgets from .constants import ( PluginStates, PluginActionStates, InstanceStates, GroupStates, Roles ) From d8d99f8bd80393b9a9d69a5d2f62e9dceb639ed1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 13:51:33 +0100 Subject: [PATCH 096/213] use qtpy in loader --- openpype/tools/loader/app.py | 2 +- openpype/tools/loader/delegates.py | 2 +- openpype/tools/loader/lib.py | 2 +- openpype/tools/loader/model.py | 2 +- openpype/tools/loader/widgets.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/tools/loader/app.py b/openpype/tools/loader/app.py index 1917f23c60e..302fe6c366e 100644 --- a/openpype/tools/loader/app.py +++ b/openpype/tools/loader/app.py @@ -1,7 +1,7 @@ import sys import traceback -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore from openpype.client import get_projects, get_project from openpype import style diff --git a/openpype/tools/loader/delegates.py b/openpype/tools/loader/delegates.py index e6663d48f11..0686fe78cde 100644 --- a/openpype/tools/loader/delegates.py +++ b/openpype/tools/loader/delegates.py @@ -1,4 +1,4 @@ -from Qt import QtWidgets, QtGui, QtCore +from qtpy import QtWidgets, QtGui, QtCore class LoadedInSceneDelegate(QtWidgets.QStyledItemDelegate): diff --git a/openpype/tools/loader/lib.py b/openpype/tools/loader/lib.py index 78a25d8d85f..552dc91a10b 100644 --- a/openpype/tools/loader/lib.py +++ b/openpype/tools/loader/lib.py @@ -1,5 +1,5 @@ import inspect -from Qt import QtGui +from qtpy import QtGui import qtawesome from openpype.lib.attribute_definitions import AbtractAttrDef diff --git a/openpype/tools/loader/model.py b/openpype/tools/loader/model.py index 77a8669c460..5944808f8bb 100644 --- a/openpype/tools/loader/model.py +++ b/openpype/tools/loader/model.py @@ -4,7 +4,7 @@ import time from uuid import uuid4 -from Qt import QtCore, QtGui +from qtpy import QtCore, QtGui import qtawesome from openpype.client import ( diff --git a/openpype/tools/loader/widgets.py b/openpype/tools/loader/widgets.py index 826c7110da8..c2c6c5dfcd3 100644 --- a/openpype/tools/loader/widgets.py +++ b/openpype/tools/loader/widgets.py @@ -5,7 +5,7 @@ import traceback import collections -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui from openpype.client import ( get_subset_families, From 678f6c6c28a92ab5fb24c36af9b8a5027bb6ce12 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 13:51:46 +0100 Subject: [PATCH 097/213] use qtpy in library loader --- openpype/tools/libraryloader/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/libraryloader/app.py b/openpype/tools/libraryloader/app.py index d2af1b71517..bd105953339 100644 --- a/openpype/tools/libraryloader/app.py +++ b/openpype/tools/libraryloader/app.py @@ -1,6 +1,6 @@ import sys -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui from openpype import style from openpype.client import get_projects, get_project From 9f616c2bcbc7197a5d5b24636fc84fed33c36215 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 13:51:57 +0100 Subject: [PATCH 098/213] use qtpy in mayalookassigner --- openpype/tools/mayalookassigner/app.py | 2 +- openpype/tools/mayalookassigner/models.py | 2 +- openpype/tools/mayalookassigner/views.py | 2 +- openpype/tools/mayalookassigner/widgets.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/tools/mayalookassigner/app.py b/openpype/tools/mayalookassigner/app.py index 5665acea427..f9508657e50 100644 --- a/openpype/tools/mayalookassigner/app.py +++ b/openpype/tools/mayalookassigner/app.py @@ -2,7 +2,7 @@ import time import logging -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore from openpype.client import get_last_version_by_subset_id from openpype import style diff --git a/openpype/tools/mayalookassigner/models.py b/openpype/tools/mayalookassigner/models.py index 77a3c8a5903..ed6a68bee0d 100644 --- a/openpype/tools/mayalookassigner/models.py +++ b/openpype/tools/mayalookassigner/models.py @@ -1,6 +1,6 @@ from collections import defaultdict -from Qt import QtCore +from qtpy import QtCore import qtawesome from openpype.tools.utils import models diff --git a/openpype/tools/mayalookassigner/views.py b/openpype/tools/mayalookassigner/views.py index 8e676ebc7f1..87459ad897c 100644 --- a/openpype/tools/mayalookassigner/views.py +++ b/openpype/tools/mayalookassigner/views.py @@ -1,4 +1,4 @@ -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore class View(QtWidgets.QTreeView): diff --git a/openpype/tools/mayalookassigner/widgets.py b/openpype/tools/mayalookassigner/widgets.py index 10e573342a1..f2df17e68cf 100644 --- a/openpype/tools/mayalookassigner/widgets.py +++ b/openpype/tools/mayalookassigner/widgets.py @@ -1,7 +1,7 @@ import logging from collections import defaultdict -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore from openpype.tools.utils.models import TreeModel from openpype.tools.utils.lib import ( From e9ca36dcfbbee8ca71e4fd8b45f0068182d920d4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 13:52:08 +0100 Subject: [PATCH 099/213] use qtpy in creator --- openpype/tools/creator/constants.py | 2 +- openpype/tools/creator/model.py | 2 +- openpype/tools/creator/widgets.py | 2 +- openpype/tools/creator/window.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/tools/creator/constants.py b/openpype/tools/creator/constants.py index 26a25dc0107..5c4bbdcca3e 100644 --- a/openpype/tools/creator/constants.py +++ b/openpype/tools/creator/constants.py @@ -1,4 +1,4 @@ -from Qt import QtCore +from qtpy import QtCore FAMILY_ROLE = QtCore.Qt.UserRole + 1 diff --git a/openpype/tools/creator/model.py b/openpype/tools/creator/model.py index 307993103bf..d7f13885f54 100644 --- a/openpype/tools/creator/model.py +++ b/openpype/tools/creator/model.py @@ -1,5 +1,5 @@ import uuid -from Qt import QtGui, QtCore +from qtpy import QtGui, QtCore from openpype.pipeline import discover_legacy_creator_plugins diff --git a/openpype/tools/creator/widgets.py b/openpype/tools/creator/widgets.py index 43df08496bd..8ea3cbd03f8 100644 --- a/openpype/tools/creator/widgets.py +++ b/openpype/tools/creator/widgets.py @@ -1,7 +1,7 @@ import re import inspect -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui import qtawesome diff --git a/openpype/tools/creator/window.py b/openpype/tools/creator/window.py index e2396ed29e0..57e2c49576e 100644 --- a/openpype/tools/creator/window.py +++ b/openpype/tools/creator/window.py @@ -2,7 +2,7 @@ import traceback import re -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore from openpype.client import get_asset_by_name, get_subsets from openpype import style From 88939540f743a2c90d4b3a9af3da5564543fc788 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 13:52:28 +0100 Subject: [PATCH 100/213] use qtpy in attribute definitions --- openpype/tools/attribute_defs/dialog.py | 2 +- openpype/tools/attribute_defs/files_widget.py | 2 +- openpype/tools/attribute_defs/widgets.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/tools/attribute_defs/dialog.py b/openpype/tools/attribute_defs/dialog.py index 69923d54e59..ef717d576a1 100644 --- a/openpype/tools/attribute_defs/dialog.py +++ b/openpype/tools/attribute_defs/dialog.py @@ -1,4 +1,4 @@ -from Qt import QtWidgets +from qtpy import QtWidgets from .widgets import AttributeDefinitionsWidget diff --git a/openpype/tools/attribute_defs/files_widget.py b/openpype/tools/attribute_defs/files_widget.py index 2c8ed729c23..71714fdf161 100644 --- a/openpype/tools/attribute_defs/files_widget.py +++ b/openpype/tools/attribute_defs/files_widget.py @@ -3,7 +3,7 @@ import uuid import json -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui from openpype.lib import FileDefItem from openpype.tools.utils import ( diff --git a/openpype/tools/attribute_defs/widgets.py b/openpype/tools/attribute_defs/widgets.py index 1ffb3d37994..ecc899c748d 100644 --- a/openpype/tools/attribute_defs/widgets.py +++ b/openpype/tools/attribute_defs/widgets.py @@ -1,7 +1,7 @@ import uuid import copy -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore from openpype.lib.attribute_definitions import ( AbtractAttrDef, From 3468a285c1d263af0da9c163a9a011476baf0d56 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 13:52:52 +0100 Subject: [PATCH 101/213] use qtpy in remaining qt utils --- openpype/tools/assetlinks/widgets.py | 2 +- openpype/tools/experimental_tools/dialog.py | 2 +- openpype/tools/flickcharm.py | 2 +- openpype/tools/resources/__init__.py | 2 +- openpype/tools/utils/assets_widget.py | 8 ++++---- openpype/tools/utils/constants.py | 2 +- openpype/tools/utils/delegates.py | 8 +------- openpype/tools/utils/error_dialog.py | 2 +- openpype/tools/utils/lib.py | 2 +- openpype/tools/utils/models.py | 6 +++--- openpype/tools/utils/overlay_messages.py | 2 +- openpype/tools/utils/tasks_widget.py | 2 +- openpype/tools/utils/views.py | 3 +-- openpype/tools/utils/widgets.py | 2 +- openpype/tools/workfile_template_build/window.py | 2 +- 15 files changed, 20 insertions(+), 27 deletions(-) diff --git a/openpype/tools/assetlinks/widgets.py b/openpype/tools/assetlinks/widgets.py index 1b168e542c7..7b05eef2d76 100644 --- a/openpype/tools/assetlinks/widgets.py +++ b/openpype/tools/assetlinks/widgets.py @@ -6,7 +6,7 @@ get_output_link_versions, ) -from Qt import QtWidgets +from qtpy import QtWidgets class SimpleLinkView(QtWidgets.QWidget): diff --git a/openpype/tools/experimental_tools/dialog.py b/openpype/tools/experimental_tools/dialog.py index 0099492207b..00b6ae07a40 100644 --- a/openpype/tools/experimental_tools/dialog.py +++ b/openpype/tools/experimental_tools/dialog.py @@ -1,4 +1,4 @@ -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui from openpype.style import ( load_stylesheet, diff --git a/openpype/tools/flickcharm.py b/openpype/tools/flickcharm.py index a5ea5a79d80..8d85dacce41 100644 --- a/openpype/tools/flickcharm.py +++ b/openpype/tools/flickcharm.py @@ -16,7 +16,7 @@ """ import copy -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui class FlickData(object): diff --git a/openpype/tools/resources/__init__.py b/openpype/tools/resources/__init__.py index fd5c45f9010..8aa82f580f5 100644 --- a/openpype/tools/resources/__init__.py +++ b/openpype/tools/resources/__init__.py @@ -1,6 +1,6 @@ import os -from Qt import QtGui +from qtpy import QtGui def get_icon_path(icon_name=None, filename=None): diff --git a/openpype/tools/utils/assets_widget.py b/openpype/tools/utils/assets_widget.py index 2a1fb4567c3..c92e68aa979 100644 --- a/openpype/tools/utils/assets_widget.py +++ b/openpype/tools/utils/assets_widget.py @@ -1,8 +1,8 @@ import time import collections -import Qt -from Qt import QtWidgets, QtCore, QtGui +import qtpy +from qtpy import QtWidgets, QtCore, QtGui import qtawesome from openpype.client import ( @@ -26,9 +26,9 @@ get_asset_icon ) -if Qt.__binding__ == "PySide": +if qtpy.API == "pyside": from PySide.QtGui import QStyleOptionViewItemV4 -elif Qt.__binding__ == "PyQt4": +elif qtpy.API == "pyqt4": from PyQt4.QtGui import QStyleOptionViewItemV4 ASSET_ID_ROLE = QtCore.Qt.UserRole + 1 diff --git a/openpype/tools/utils/constants.py b/openpype/tools/utils/constants.py index 8f12c573211..a3b1557f264 100644 --- a/openpype/tools/utils/constants.py +++ b/openpype/tools/utils/constants.py @@ -1,4 +1,4 @@ -from Qt import QtCore +from qtpy import QtCore DEFAULT_PROJECT_LABEL = "< Default >" diff --git a/openpype/tools/utils/delegates.py b/openpype/tools/utils/delegates.py index d6c2d69e768..17479488003 100644 --- a/openpype/tools/utils/delegates.py +++ b/openpype/tools/utils/delegates.py @@ -3,13 +3,7 @@ import logging import numbers -import Qt -from Qt import QtWidgets, QtGui, QtCore - -if Qt.__binding__ == "PySide": - from PySide.QtGui import QStyleOptionViewItemV4 -elif Qt.__binding__ == "PyQt4": - from PyQt4.QtGui import QStyleOptionViewItemV4 +from qtpy import QtWidgets, QtGui, QtCore from openpype.client import ( get_versions, diff --git a/openpype/tools/utils/error_dialog.py b/openpype/tools/utils/error_dialog.py index 5fe49a53af4..4c275a213b8 100644 --- a/openpype/tools/utils/error_dialog.py +++ b/openpype/tools/utils/error_dialog.py @@ -1,4 +1,4 @@ -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore from .widgets import ClickableFrame, ExpandBtn, SeparatorWidget diff --git a/openpype/tools/utils/lib.py b/openpype/tools/utils/lib.py index 5302946c28b..403811699d6 100644 --- a/openpype/tools/utils/lib.py +++ b/openpype/tools/utils/lib.py @@ -4,7 +4,7 @@ import collections import traceback -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui import qtawesome from openpype.client import ( diff --git a/openpype/tools/utils/models.py b/openpype/tools/utils/models.py index 2e93d94d7ec..b26ec566fee 100644 --- a/openpype/tools/utils/models.py +++ b/openpype/tools/utils/models.py @@ -1,8 +1,8 @@ import re import logging -import Qt -from Qt import QtCore, QtGui +import qtpy +from qtpy import QtCore, QtGui from openpype.client import get_projects from .constants import ( PROJECT_IS_ACTIVE_ROLE, @@ -69,7 +69,7 @@ def setData(self, index, value, role=QtCore.Qt.EditRole): item[key] = value # passing `list()` for PyQt5 (see PYSIDE-462) - if Qt.__binding__ in ("PyQt4", "PySide"): + if qtpy.API in ("pyqt4", "pyside"): self.dataChanged.emit(index, index) else: self.dataChanged.emit(index, index, [role]) diff --git a/openpype/tools/utils/overlay_messages.py b/openpype/tools/utils/overlay_messages.py index b58cd897410..180d7eae97c 100644 --- a/openpype/tools/utils/overlay_messages.py +++ b/openpype/tools/utils/overlay_messages.py @@ -1,6 +1,6 @@ import uuid -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui from openpype.style import get_objected_colors diff --git a/openpype/tools/utils/tasks_widget.py b/openpype/tools/utils/tasks_widget.py index 0353f3dd2f2..0605fd77433 100644 --- a/openpype/tools/utils/tasks_widget.py +++ b/openpype/tools/utils/tasks_widget.py @@ -1,4 +1,4 @@ -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui import qtawesome from openpype.client import ( diff --git a/openpype/tools/utils/views.py b/openpype/tools/utils/views.py index a2f1f15b957..01919d67457 100644 --- a/openpype/tools/utils/views.py +++ b/openpype/tools/utils/views.py @@ -1,6 +1,5 @@ -import os from openpype.resources import get_image_path -from Qt import QtWidgets, QtCore, QtGui, QtSvg +from qtpy import QtWidgets, QtCore, QtGui, QtSvg class DeselectableTreeView(QtWidgets.QTreeView): diff --git a/openpype/tools/utils/widgets.py b/openpype/tools/utils/widgets.py index 88893a57d52..c64b6e3400d 100644 --- a/openpype/tools/utils/widgets.py +++ b/openpype/tools/utils/widgets.py @@ -1,6 +1,6 @@ import logging -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui import qargparse import qtawesome diff --git a/openpype/tools/workfile_template_build/window.py b/openpype/tools/workfile_template_build/window.py index 22e26be451c..24d91052232 100644 --- a/openpype/tools/workfile_template_build/window.py +++ b/openpype/tools/workfile_template_build/window.py @@ -1,4 +1,4 @@ -from Qt import QtWidgets +from qtpy import QtWidgets from openpype import style from openpype.lib import Logger From 5361cfa9a5a7ecf91d7a4d73b57e2cfe439fd0b3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 13:53:02 +0100 Subject: [PATCH 102/213] use qtpy in style --- openpype/style/__init__.py | 2 +- openpype/style/color_defs.py | 2 +- openpype/style/qrc_resources.py | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/style/__init__.py b/openpype/style/__init__.py index 473fb42bb5f..48e943beb1a 100644 --- a/openpype/style/__init__.py +++ b/openpype/style/__init__.py @@ -157,7 +157,7 @@ def _load_stylesheet(): def _load_font(): """Load and register fonts into Qt application.""" - from Qt import QtGui + from qtpy import QtGui # Check if font ids are still loaded if _Cache.font_ids is not None: diff --git a/openpype/style/color_defs.py b/openpype/style/color_defs.py index f1eab38c243..69703583c45 100644 --- a/openpype/style/color_defs.py +++ b/openpype/style/color_defs.py @@ -47,7 +47,7 @@ def create_qcolor(*args): *args (tuple): It is possible to pass initialization arguments for Qcolor. """ - from Qt import QtGui + from qtpy import QtGui return QtGui.QColor(*args) diff --git a/openpype/style/qrc_resources.py b/openpype/style/qrc_resources.py index a9e219c9ad4..beacb75f2cd 100644 --- a/openpype/style/qrc_resources.py +++ b/openpype/style/qrc_resources.py @@ -1,11 +1,11 @@ -import Qt +import qtpy initialized = False resources = None -if Qt.__binding__ == "PySide2": +if qtpy.API == "pyside2": from . import pyside2_resources as resources -elif Qt.__binding__ == "PyQt5": +elif qtpy.API == "pyqt5": from . import pyqt5_resources as resources From c45c1c7e7e071e66f577ba50761a2d4f3bfa9b4d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 13:53:16 +0100 Subject: [PATCH 103/213] non-python host launch script uses qtpy --- openpype/scripts/non_python_host_launch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/scripts/non_python_host_launch.py b/openpype/scripts/non_python_host_launch.py index f795af7bb3c..79fb1cbb523 100644 --- a/openpype/scripts/non_python_host_launch.py +++ b/openpype/scripts/non_python_host_launch.py @@ -14,7 +14,7 @@ def show_error_messagebox(title, message, detail_message=None): """Function will show message and process ends after closing it.""" - from Qt import QtWidgets, QtCore + from qtpy import QtWidgets, QtCore from openpype import style app = QtWidgets.QApplication([]) From ab2299f9212bc86897cd852f6f6bbe2b7824a50a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 13:53:30 +0100 Subject: [PATCH 104/213] use qtpy in helper widgets --- openpype/widgets/color_widgets/color_inputs.py | 2 +- openpype/widgets/color_widgets/color_picker_widget.py | 2 +- openpype/widgets/color_widgets/color_screen_pick.py | 6 +++--- openpype/widgets/color_widgets/color_triangle.py | 2 +- openpype/widgets/color_widgets/color_view.py | 2 +- openpype/widgets/message_window.py | 2 +- openpype/widgets/nice_checkbox.py | 2 +- openpype/widgets/password_dialog.py | 2 +- openpype/widgets/popup.py | 3 +-- openpype/widgets/sliders.py | 2 +- 10 files changed, 12 insertions(+), 13 deletions(-) diff --git a/openpype/widgets/color_widgets/color_inputs.py b/openpype/widgets/color_widgets/color_inputs.py index d6564ca29bb..9c8e7b92e87 100644 --- a/openpype/widgets/color_widgets/color_inputs.py +++ b/openpype/widgets/color_widgets/color_inputs.py @@ -1,5 +1,5 @@ import re -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui from .color_view import draw_checkerboard_tile diff --git a/openpype/widgets/color_widgets/color_picker_widget.py b/openpype/widgets/color_widgets/color_picker_widget.py index 228d35a77c5..688c4f9863c 100644 --- a/openpype/widgets/color_widgets/color_picker_widget.py +++ b/openpype/widgets/color_widgets/color_picker_widget.py @@ -1,5 +1,5 @@ import os -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui from .color_triangle import QtColorTriangle from .color_view import ColorViewer diff --git a/openpype/widgets/color_widgets/color_screen_pick.py b/openpype/widgets/color_widgets/color_screen_pick.py index 87f50745eb9..542db2831a1 100644 --- a/openpype/widgets/color_widgets/color_screen_pick.py +++ b/openpype/widgets/color_widgets/color_screen_pick.py @@ -1,5 +1,5 @@ -import Qt -from Qt import QtWidgets, QtCore, QtGui +import qtpy +from qtpy import QtWidgets, QtCore, QtGui class PickScreenColorWidget(QtWidgets.QWidget): @@ -78,7 +78,7 @@ def pick_color(self, screen_obj): QtWidgets.QApplication.desktop().winId(), geo.x(), geo.y(), geo.width(), geo.height() ) - if Qt.__binding__ in ("PyQt4", "PySide"): + if qtpy.API in ("pyqt4", "pyside"): pix = QtGui.QPixmap.grabWindow(*args) else: pix = screen_obj.grabWindow(*args) diff --git a/openpype/widgets/color_widgets/color_triangle.py b/openpype/widgets/color_widgets/color_triangle.py index e15b9e9f650..290a33f0b07 100644 --- a/openpype/widgets/color_widgets/color_triangle.py +++ b/openpype/widgets/color_widgets/color_triangle.py @@ -1,6 +1,6 @@ from enum import Enum from math import floor, ceil, sqrt, sin, cos, acos, pi as PI -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui TWOPI = PI * 2 diff --git a/openpype/widgets/color_widgets/color_view.py b/openpype/widgets/color_widgets/color_view.py index b5fce288940..76354b6be86 100644 --- a/openpype/widgets/color_widgets/color_view.py +++ b/openpype/widgets/color_widgets/color_view.py @@ -1,4 +1,4 @@ -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui def draw_checkerboard_tile(piece_size=None, color_1=None, color_2=None): diff --git a/openpype/widgets/message_window.py b/openpype/widgets/message_window.py index 94e51f5d4f7..1895ba1b5d5 100644 --- a/openpype/widgets/message_window.py +++ b/openpype/widgets/message_window.py @@ -1,6 +1,6 @@ import sys import logging -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore log = logging.getLogger(__name__) diff --git a/openpype/widgets/nice_checkbox.py b/openpype/widgets/nice_checkbox.py index 6952cb41da0..d28a09a5dc2 100644 --- a/openpype/widgets/nice_checkbox.py +++ b/openpype/widgets/nice_checkbox.py @@ -1,5 +1,5 @@ from math import floor, sqrt, ceil -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui from openpype.style import get_objected_colors diff --git a/openpype/widgets/password_dialog.py b/openpype/widgets/password_dialog.py index 58add7832f3..41329617168 100644 --- a/openpype/widgets/password_dialog.py +++ b/openpype/widgets/password_dialog.py @@ -1,4 +1,4 @@ -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui from openpype import style from openpype.resources import get_resource diff --git a/openpype/widgets/popup.py b/openpype/widgets/popup.py index 9fc33ccbb89..97a84610602 100644 --- a/openpype/widgets/popup.py +++ b/openpype/widgets/popup.py @@ -1,8 +1,7 @@ import sys import contextlib - -from Qt import QtCore, QtWidgets +from qtpy import QtCore, QtWidgets class Popup(QtWidgets.QDialog): diff --git a/openpype/widgets/sliders.py b/openpype/widgets/sliders.py index 32ade58af57..ea1e01b9eac 100644 --- a/openpype/widgets/sliders.py +++ b/openpype/widgets/sliders.py @@ -1,4 +1,4 @@ -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui class NiceSlider(QtWidgets.QSlider): From b03947b8d725c5f54127ce56185ab9a2435292c6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 13:53:37 +0100 Subject: [PATCH 105/213] use qtpy in qargparse --- openpype/vendor/python/common/qargparse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/vendor/python/common/qargparse.py b/openpype/vendor/python/common/qargparse.py index ebde9ae76d2..172c594d1dd 100644 --- a/openpype/vendor/python/common/qargparse.py +++ b/openpype/vendor/python/common/qargparse.py @@ -7,7 +7,7 @@ import logging from collections import OrderedDict as odict -from Qt import QtCore, QtWidgets, QtGui +from qtpy import QtCore, QtWidgets, QtGui import qtawesome __version__ = "0.5.2" From ddba1741da76ae64e7e4b1cd77b00bd6abd0f896 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 13:53:46 +0100 Subject: [PATCH 106/213] scriptsmenu is using qtpy --- .../python/common/scriptsmenu/action.py | 2 +- .../common/scriptsmenu/launchformari.py | 2 +- .../common/scriptsmenu/launchformaya.py | 2 +- .../common/scriptsmenu/launchfornuke.py | 2 +- .../python/common/scriptsmenu/scriptsmenu.py | 2 +- .../python/common/scriptsmenu/vendor/Qt.py | 1989 ----------------- .../common/scriptsmenu/vendor/__init__.py | 0 7 files changed, 5 insertions(+), 1994 deletions(-) delete mode 100644 openpype/vendor/python/common/scriptsmenu/vendor/Qt.py delete mode 100644 openpype/vendor/python/common/scriptsmenu/vendor/__init__.py diff --git a/openpype/vendor/python/common/scriptsmenu/action.py b/openpype/vendor/python/common/scriptsmenu/action.py index 5e68628406d..49b08788f9c 100644 --- a/openpype/vendor/python/common/scriptsmenu/action.py +++ b/openpype/vendor/python/common/scriptsmenu/action.py @@ -1,6 +1,6 @@ import os -from .vendor.Qt import QtWidgets +from qtpy import QtWidgets class Action(QtWidgets.QAction): diff --git a/openpype/vendor/python/common/scriptsmenu/launchformari.py b/openpype/vendor/python/common/scriptsmenu/launchformari.py index 25cfc80d96c..86362a78d3b 100644 --- a/openpype/vendor/python/common/scriptsmenu/launchformari.py +++ b/openpype/vendor/python/common/scriptsmenu/launchformari.py @@ -1,6 +1,6 @@ # Import third-party modules -from vendor.Qt import QtWidgets +from qtpy import QtWidgets # Import local modules import scriptsmenu diff --git a/openpype/vendor/python/common/scriptsmenu/launchformaya.py b/openpype/vendor/python/common/scriptsmenu/launchformaya.py index 7ad66f0ad25..01880b94d79 100644 --- a/openpype/vendor/python/common/scriptsmenu/launchformaya.py +++ b/openpype/vendor/python/common/scriptsmenu/launchformaya.py @@ -4,7 +4,7 @@ import maya.mel as mel import scriptsmenu -from .vendor.Qt import QtCore, QtWidgets +from qtpy import QtCore, QtWidgets log = logging.getLogger(__name__) diff --git a/openpype/vendor/python/common/scriptsmenu/launchfornuke.py b/openpype/vendor/python/common/scriptsmenu/launchfornuke.py index 72302a79a61..3043d22d1c5 100644 --- a/openpype/vendor/python/common/scriptsmenu/launchfornuke.py +++ b/openpype/vendor/python/common/scriptsmenu/launchfornuke.py @@ -1,5 +1,5 @@ import scriptsmenu -from .vendor.Qt import QtWidgets +from qtpy import QtWidgets def _nuke_main_window(): diff --git a/openpype/vendor/python/common/scriptsmenu/scriptsmenu.py b/openpype/vendor/python/common/scriptsmenu/scriptsmenu.py index 9e7c0949023..6f6d0b5715b 100644 --- a/openpype/vendor/python/common/scriptsmenu/scriptsmenu.py +++ b/openpype/vendor/python/common/scriptsmenu/scriptsmenu.py @@ -3,7 +3,7 @@ import logging from collections import defaultdict -from .vendor.Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore from . import action log = logging.getLogger(__name__) diff --git a/openpype/vendor/python/common/scriptsmenu/vendor/Qt.py b/openpype/vendor/python/common/scriptsmenu/vendor/Qt.py deleted file mode 100644 index fe4b45f18fe..00000000000 --- a/openpype/vendor/python/common/scriptsmenu/vendor/Qt.py +++ /dev/null @@ -1,1989 +0,0 @@ -"""Minimal Python 2 & 3 shim around all Qt bindings - -DOCUMENTATION - Qt.py was born in the film and visual effects industry to address - the growing need for the development of software capable of running - with more than one flavour of the Qt bindings for Python - PySide, - PySide2, PyQt4 and PyQt5. - - 1. Build for one, run with all - 2. Explicit is better than implicit - 3. Support co-existence - - Default resolution order: - - PySide2 - - PyQt5 - - PySide - - PyQt4 - - Usage: - >> import sys - >> from Qt import QtWidgets - >> app = QtWidgets.QApplication(sys.argv) - >> button = QtWidgets.QPushButton("Hello World") - >> button.show() - >> app.exec_() - - All members of PySide2 are mapped from other bindings, should they exist. - If no equivalent member exist, it is excluded from Qt.py and inaccessible. - The idea is to highlight members that exist across all supported binding, - and guarantee that code that runs on one binding runs on all others. - - For more details, visit https://github.com/mottosso/Qt.py - -LICENSE - - See end of file for license (MIT, BSD) information. - -""" - -import os -import sys -import types -import shutil -import importlib - - -__version__ = "1.2.3" - -# Enable support for `from Qt import *` -__all__ = [] - -# Flags from environment variables -QT_VERBOSE = bool(os.getenv("QT_VERBOSE")) -QT_PREFERRED_BINDING = os.getenv("QT_PREFERRED_BINDING", "") -QT_SIP_API_HINT = os.getenv("QT_SIP_API_HINT") - -# Reference to Qt.py -Qt = sys.modules[__name__] -Qt.QtCompat = types.ModuleType("QtCompat") - -try: - long -except NameError: - # Python 3 compatibility - long = int - - -"""Common members of all bindings - -This is where each member of Qt.py is explicitly defined. -It is based on a "lowest common denominator" of all bindings; -including members found in each of the 4 bindings. - -The "_common_members" dictionary is generated using the -build_membership.sh script. - -""" - -_common_members = { - "QtCore": [ - "QAbstractAnimation", - "QAbstractEventDispatcher", - "QAbstractItemModel", - "QAbstractListModel", - "QAbstractState", - "QAbstractTableModel", - "QAbstractTransition", - "QAnimationGroup", - "QBasicTimer", - "QBitArray", - "QBuffer", - "QByteArray", - "QByteArrayMatcher", - "QChildEvent", - "QCoreApplication", - "QCryptographicHash", - "QDataStream", - "QDate", - "QDateTime", - "QDir", - "QDirIterator", - "QDynamicPropertyChangeEvent", - "QEasingCurve", - "QElapsedTimer", - "QEvent", - "QEventLoop", - "QEventTransition", - "QFile", - "QFileInfo", - "QFileSystemWatcher", - "QFinalState", - "QGenericArgument", - "QGenericReturnArgument", - "QHistoryState", - "QItemSelectionRange", - "QIODevice", - "QLibraryInfo", - "QLine", - "QLineF", - "QLocale", - "QMargins", - "QMetaClassInfo", - "QMetaEnum", - "QMetaMethod", - "QMetaObject", - "QMetaProperty", - "QMimeData", - "QModelIndex", - "QMutex", - "QMutexLocker", - "QObject", - "QParallelAnimationGroup", - "QPauseAnimation", - "QPersistentModelIndex", - "QPluginLoader", - "QPoint", - "QPointF", - "QProcess", - "QProcessEnvironment", - "QPropertyAnimation", - "QReadLocker", - "QReadWriteLock", - "QRect", - "QRectF", - "QRegExp", - "QResource", - "QRunnable", - "QSemaphore", - "QSequentialAnimationGroup", - "QSettings", - "QSignalMapper", - "QSignalTransition", - "QSize", - "QSizeF", - "QSocketNotifier", - "QState", - "QStateMachine", - "QSysInfo", - "QSystemSemaphore", - "QT_TRANSLATE_NOOP", - "QT_TR_NOOP", - "QT_TR_NOOP_UTF8", - "QTemporaryFile", - "QTextBoundaryFinder", - "QTextCodec", - "QTextDecoder", - "QTextEncoder", - "QTextStream", - "QTextStreamManipulator", - "QThread", - "QThreadPool", - "QTime", - "QTimeLine", - "QTimer", - "QTimerEvent", - "QTranslator", - "QUrl", - "QVariantAnimation", - "QWaitCondition", - "QWriteLocker", - "QXmlStreamAttribute", - "QXmlStreamAttributes", - "QXmlStreamEntityDeclaration", - "QXmlStreamEntityResolver", - "QXmlStreamNamespaceDeclaration", - "QXmlStreamNotationDeclaration", - "QXmlStreamReader", - "QXmlStreamWriter", - "Qt", - "QtCriticalMsg", - "QtDebugMsg", - "QtFatalMsg", - "QtMsgType", - "QtSystemMsg", - "QtWarningMsg", - "qAbs", - "qAddPostRoutine", - "qChecksum", - "qCritical", - "qDebug", - "qFatal", - "qFuzzyCompare", - "qIsFinite", - "qIsInf", - "qIsNaN", - "qIsNull", - "qRegisterResourceData", - "qUnregisterResourceData", - "qVersion", - "qWarning", - "qrand", - "qsrand" - ], - "QtGui": [ - "QAbstractTextDocumentLayout", - "QActionEvent", - "QBitmap", - "QBrush", - "QClipboard", - "QCloseEvent", - "QColor", - "QConicalGradient", - "QContextMenuEvent", - "QCursor", - "QDesktopServices", - "QDoubleValidator", - "QDrag", - "QDragEnterEvent", - "QDragLeaveEvent", - "QDragMoveEvent", - "QDropEvent", - "QFileOpenEvent", - "QFocusEvent", - "QFont", - "QFontDatabase", - "QFontInfo", - "QFontMetrics", - "QFontMetricsF", - "QGradient", - "QHelpEvent", - "QHideEvent", - "QHoverEvent", - "QIcon", - "QIconDragEvent", - "QIconEngine", - "QImage", - "QImageIOHandler", - "QImageReader", - "QImageWriter", - "QInputEvent", - "QInputMethodEvent", - "QIntValidator", - "QKeyEvent", - "QKeySequence", - "QLinearGradient", - "QMatrix2x2", - "QMatrix2x3", - "QMatrix2x4", - "QMatrix3x2", - "QMatrix3x3", - "QMatrix3x4", - "QMatrix4x2", - "QMatrix4x3", - "QMatrix4x4", - "QMouseEvent", - "QMoveEvent", - "QMovie", - "QPaintDevice", - "QPaintEngine", - "QPaintEngineState", - "QPaintEvent", - "QPainter", - "QPainterPath", - "QPainterPathStroker", - "QPalette", - "QPen", - "QPicture", - "QPictureIO", - "QPixmap", - "QPixmapCache", - "QPolygon", - "QPolygonF", - "QQuaternion", - "QRadialGradient", - "QRegExpValidator", - "QRegion", - "QResizeEvent", - "QSessionManager", - "QShortcutEvent", - "QShowEvent", - "QStandardItem", - "QStandardItemModel", - "QStatusTipEvent", - "QSyntaxHighlighter", - "QTabletEvent", - "QTextBlock", - "QTextBlockFormat", - "QTextBlockGroup", - "QTextBlockUserData", - "QTextCharFormat", - "QTextCursor", - "QTextDocument", - "QTextDocumentFragment", - "QTextFormat", - "QTextFragment", - "QTextFrame", - "QTextFrameFormat", - "QTextImageFormat", - "QTextInlineObject", - "QTextItem", - "QTextLayout", - "QTextLength", - "QTextLine", - "QTextList", - "QTextListFormat", - "QTextObject", - "QTextObjectInterface", - "QTextOption", - "QTextTable", - "QTextTableCell", - "QTextTableCellFormat", - "QTextTableFormat", - "QTouchEvent", - "QTransform", - "QValidator", - "QVector2D", - "QVector3D", - "QVector4D", - "QWhatsThisClickedEvent", - "QWheelEvent", - "QWindowStateChangeEvent", - "qAlpha", - "qBlue", - "qGray", - "qGreen", - "qIsGray", - "qRed", - "qRgb", - "qRgba" - ], - "QtHelp": [ - "QHelpContentItem", - "QHelpContentModel", - "QHelpContentWidget", - "QHelpEngine", - "QHelpEngineCore", - "QHelpIndexModel", - "QHelpIndexWidget", - "QHelpSearchEngine", - "QHelpSearchQuery", - "QHelpSearchQueryWidget", - "QHelpSearchResultWidget" - ], - "QtMultimedia": [ - "QAbstractVideoBuffer", - "QAbstractVideoSurface", - "QAudio", - "QAudioDeviceInfo", - "QAudioFormat", - "QAudioInput", - "QAudioOutput", - "QVideoFrame", - "QVideoSurfaceFormat" - ], - "QtNetwork": [ - "QAbstractNetworkCache", - "QAbstractSocket", - "QAuthenticator", - "QHostAddress", - "QHostInfo", - "QLocalServer", - "QLocalSocket", - "QNetworkAccessManager", - "QNetworkAddressEntry", - "QNetworkCacheMetaData", - "QNetworkConfiguration", - "QNetworkConfigurationManager", - "QNetworkCookie", - "QNetworkCookieJar", - "QNetworkDiskCache", - "QNetworkInterface", - "QNetworkProxy", - "QNetworkProxyFactory", - "QNetworkProxyQuery", - "QNetworkReply", - "QNetworkRequest", - "QNetworkSession", - "QSsl", - "QTcpServer", - "QTcpSocket", - "QUdpSocket" - ], - "QtOpenGL": [ - "QGL", - "QGLContext", - "QGLFormat", - "QGLWidget" - ], - "QtPrintSupport": [ - "QAbstractPrintDialog", - "QPageSetupDialog", - "QPrintDialog", - "QPrintEngine", - "QPrintPreviewDialog", - "QPrintPreviewWidget", - "QPrinter", - "QPrinterInfo" - ], - "QtSql": [ - "QSql", - "QSqlDatabase", - "QSqlDriver", - "QSqlDriverCreatorBase", - "QSqlError", - "QSqlField", - "QSqlIndex", - "QSqlQuery", - "QSqlQueryModel", - "QSqlRecord", - "QSqlRelation", - "QSqlRelationalDelegate", - "QSqlRelationalTableModel", - "QSqlResult", - "QSqlTableModel" - ], - "QtSvg": [ - "QGraphicsSvgItem", - "QSvgGenerator", - "QSvgRenderer", - "QSvgWidget" - ], - "QtTest": [ - "QTest" - ], - "QtWidgets": [ - "QAbstractButton", - "QAbstractGraphicsShapeItem", - "QAbstractItemDelegate", - "QAbstractItemView", - "QAbstractScrollArea", - "QAbstractSlider", - "QAbstractSpinBox", - "QAction", - "QActionGroup", - "QApplication", - "QBoxLayout", - "QButtonGroup", - "QCalendarWidget", - "QCheckBox", - "QColorDialog", - "QColumnView", - "QComboBox", - "QCommandLinkButton", - "QCommonStyle", - "QCompleter", - "QDataWidgetMapper", - "QDateEdit", - "QDateTimeEdit", - "QDesktopWidget", - "QDial", - "QDialog", - "QDialogButtonBox", - "QDirModel", - "QDockWidget", - "QDoubleSpinBox", - "QErrorMessage", - "QFileDialog", - "QFileIconProvider", - "QFileSystemModel", - "QFocusFrame", - "QFontComboBox", - "QFontDialog", - "QFormLayout", - "QFrame", - "QGesture", - "QGestureEvent", - "QGestureRecognizer", - "QGraphicsAnchor", - "QGraphicsAnchorLayout", - "QGraphicsBlurEffect", - "QGraphicsColorizeEffect", - "QGraphicsDropShadowEffect", - "QGraphicsEffect", - "QGraphicsEllipseItem", - "QGraphicsGridLayout", - "QGraphicsItem", - "QGraphicsItemGroup", - "QGraphicsLayout", - "QGraphicsLayoutItem", - "QGraphicsLineItem", - "QGraphicsLinearLayout", - "QGraphicsObject", - "QGraphicsOpacityEffect", - "QGraphicsPathItem", - "QGraphicsPixmapItem", - "QGraphicsPolygonItem", - "QGraphicsProxyWidget", - "QGraphicsRectItem", - "QGraphicsRotation", - "QGraphicsScale", - "QGraphicsScene", - "QGraphicsSceneContextMenuEvent", - "QGraphicsSceneDragDropEvent", - "QGraphicsSceneEvent", - "QGraphicsSceneHelpEvent", - "QGraphicsSceneHoverEvent", - "QGraphicsSceneMouseEvent", - "QGraphicsSceneMoveEvent", - "QGraphicsSceneResizeEvent", - "QGraphicsSceneWheelEvent", - "QGraphicsSimpleTextItem", - "QGraphicsTextItem", - "QGraphicsTransform", - "QGraphicsView", - "QGraphicsWidget", - "QGridLayout", - "QGroupBox", - "QHBoxLayout", - "QHeaderView", - "QInputDialog", - "QItemDelegate", - "QItemEditorCreatorBase", - "QItemEditorFactory", - "QKeyEventTransition", - "QLCDNumber", - "QLabel", - "QLayout", - "QLayoutItem", - "QLineEdit", - "QListView", - "QListWidget", - "QListWidgetItem", - "QMainWindow", - "QMdiArea", - "QMdiSubWindow", - "QMenu", - "QMenuBar", - "QMessageBox", - "QMouseEventTransition", - "QPanGesture", - "QPinchGesture", - "QPlainTextDocumentLayout", - "QPlainTextEdit", - "QProgressBar", - "QProgressDialog", - "QPushButton", - "QRadioButton", - "QRubberBand", - "QScrollArea", - "QScrollBar", - "QShortcut", - "QSizeGrip", - "QSizePolicy", - "QSlider", - "QSpacerItem", - "QSpinBox", - "QSplashScreen", - "QSplitter", - "QSplitterHandle", - "QStackedLayout", - "QStackedWidget", - "QStatusBar", - "QStyle", - "QStyleFactory", - "QStyleHintReturn", - "QStyleHintReturnMask", - "QStyleHintReturnVariant", - "QStyleOption", - "QStyleOptionButton", - "QStyleOptionComboBox", - "QStyleOptionComplex", - "QStyleOptionDockWidget", - "QStyleOptionFocusRect", - "QStyleOptionFrame", - "QStyleOptionGraphicsItem", - "QStyleOptionGroupBox", - "QStyleOptionHeader", - "QStyleOptionMenuItem", - "QStyleOptionProgressBar", - "QStyleOptionRubberBand", - "QStyleOptionSizeGrip", - "QStyleOptionSlider", - "QStyleOptionSpinBox", - "QStyleOptionTab", - "QStyleOptionTabBarBase", - "QStyleOptionTabWidgetFrame", - "QStyleOptionTitleBar", - "QStyleOptionToolBar", - "QStyleOptionToolBox", - "QStyleOptionToolButton", - "QStyleOptionViewItem", - "QStylePainter", - "QStyledItemDelegate", - "QSwipeGesture", - "QSystemTrayIcon", - "QTabBar", - "QTabWidget", - "QTableView", - "QTableWidget", - "QTableWidgetItem", - "QTableWidgetSelectionRange", - "QTapAndHoldGesture", - "QTapGesture", - "QTextBrowser", - "QTextEdit", - "QTimeEdit", - "QToolBar", - "QToolBox", - "QToolButton", - "QToolTip", - "QTreeView", - "QTreeWidget", - "QTreeWidgetItem", - "QTreeWidgetItemIterator", - "QUndoCommand", - "QUndoGroup", - "QUndoStack", - "QUndoView", - "QVBoxLayout", - "QWhatsThis", - "QWidget", - "QWidgetAction", - "QWidgetItem", - "QWizard", - "QWizardPage" - ], - "QtX11Extras": [ - "QX11Info" - ], - "QtXml": [ - "QDomAttr", - "QDomCDATASection", - "QDomCharacterData", - "QDomComment", - "QDomDocument", - "QDomDocumentFragment", - "QDomDocumentType", - "QDomElement", - "QDomEntity", - "QDomEntityReference", - "QDomImplementation", - "QDomNamedNodeMap", - "QDomNode", - "QDomNodeList", - "QDomNotation", - "QDomProcessingInstruction", - "QDomText", - "QXmlAttributes", - "QXmlContentHandler", - "QXmlDTDHandler", - "QXmlDeclHandler", - "QXmlDefaultHandler", - "QXmlEntityResolver", - "QXmlErrorHandler", - "QXmlInputSource", - "QXmlLexicalHandler", - "QXmlLocator", - "QXmlNamespaceSupport", - "QXmlParseException", - "QXmlReader", - "QXmlSimpleReader" - ], - "QtXmlPatterns": [ - "QAbstractMessageHandler", - "QAbstractUriResolver", - "QAbstractXmlNodeModel", - "QAbstractXmlReceiver", - "QSourceLocation", - "QXmlFormatter", - "QXmlItem", - "QXmlName", - "QXmlNamePool", - "QXmlNodeModelIndex", - "QXmlQuery", - "QXmlResultItems", - "QXmlSchema", - "QXmlSchemaValidator", - "QXmlSerializer" - ] -} - -""" Missing members - -This mapping describes members that have been deprecated -in one or more bindings and have been left out of the -_common_members mapping. - -The member can provide an extra details string to be -included in exceptions and warnings. -""" - -_missing_members = { - "QtGui": { - "QMatrix": "Deprecated in PyQt5", - }, -} - - -def _qInstallMessageHandler(handler): - """Install a message handler that works in all bindings - - Args: - handler: A function that takes 3 arguments, or None - """ - def messageOutputHandler(*args): - # In Qt4 bindings, message handlers are passed 2 arguments - # In Qt5 bindings, message handlers are passed 3 arguments - # The first argument is a QtMsgType - # The last argument is the message to be printed - # The Middle argument (if passed) is a QMessageLogContext - if len(args) == 3: - msgType, logContext, msg = args - elif len(args) == 2: - msgType, msg = args - logContext = None - else: - raise TypeError( - "handler expected 2 or 3 arguments, got {0}".format(len(args))) - - if isinstance(msg, bytes): - # In python 3, some bindings pass a bytestring, which cannot be - # used elsewhere. Decoding a python 2 or 3 bytestring object will - # consistently return a unicode object. - msg = msg.decode() - - handler(msgType, logContext, msg) - - passObject = messageOutputHandler if handler else handler - if Qt.IsPySide or Qt.IsPyQt4: - return Qt._QtCore.qInstallMsgHandler(passObject) - elif Qt.IsPySide2 or Qt.IsPyQt5: - return Qt._QtCore.qInstallMessageHandler(passObject) - - -def _getcpppointer(object): - if hasattr(Qt, "_shiboken2"): - return getattr(Qt, "_shiboken2").getCppPointer(object)[0] - elif hasattr(Qt, "_shiboken"): - return getattr(Qt, "_shiboken").getCppPointer(object)[0] - elif hasattr(Qt, "_sip"): - return getattr(Qt, "_sip").unwrapinstance(object) - raise AttributeError("'module' has no attribute 'getCppPointer'") - - -def _wrapinstance(ptr, base=None): - """Enable implicit cast of pointer to most suitable class - - This behaviour is available in sip per default. - - Based on http://nathanhorne.com/pyqtpyside-wrap-instance - - Usage: - This mechanism kicks in under these circumstances. - 1. Qt.py is using PySide 1 or 2. - 2. A `base` argument is not provided. - - See :func:`QtCompat.wrapInstance()` - - Arguments: - ptr (long): Pointer to QObject in memory - base (QObject, optional): Base class to wrap with. Defaults to QObject, - which should handle anything. - - """ - - assert isinstance(ptr, long), "Argument 'ptr' must be of type " - assert (base is None) or issubclass(base, Qt.QtCore.QObject), ( - "Argument 'base' must be of type ") - - if Qt.IsPyQt4 or Qt.IsPyQt5: - func = getattr(Qt, "_sip").wrapinstance - elif Qt.IsPySide2: - func = getattr(Qt, "_shiboken2").wrapInstance - elif Qt.IsPySide: - func = getattr(Qt, "_shiboken").wrapInstance - else: - raise AttributeError("'module' has no attribute 'wrapInstance'") - - if base is None: - q_object = func(long(ptr), Qt.QtCore.QObject) - meta_object = q_object.metaObject() - class_name = meta_object.className() - super_class_name = meta_object.superClass().className() - - if hasattr(Qt.QtWidgets, class_name): - base = getattr(Qt.QtWidgets, class_name) - - elif hasattr(Qt.QtWidgets, super_class_name): - base = getattr(Qt.QtWidgets, super_class_name) - - else: - base = Qt.QtCore.QObject - - return func(long(ptr), base) - - -def _isvalid(object): - """Check if the object is valid to use in Python runtime. - - Usage: - See :func:`QtCompat.isValid()` - - Arguments: - object (QObject): QObject to check the validity of. - - """ - - assert isinstance(object, Qt.QtCore.QObject) - - if hasattr(Qt, "_shiboken2"): - return getattr(Qt, "_shiboken2").isValid(object) - - elif hasattr(Qt, "_shiboken"): - return getattr(Qt, "_shiboken").isValid(object) - - elif hasattr(Qt, "_sip"): - return not getattr(Qt, "_sip").isdeleted(object) - - else: - raise AttributeError("'module' has no attribute isValid") - - -def _translate(context, sourceText, *args): - # In Qt4 bindings, translate can be passed 2 or 3 arguments - # In Qt5 bindings, translate can be passed 2 arguments - # The first argument is disambiguation[str] - # The last argument is n[int] - # The middle argument can be encoding[QtCore.QCoreApplication.Encoding] - if len(args) == 3: - disambiguation, encoding, n = args - elif len(args) == 2: - disambiguation, n = args - encoding = None - else: - raise TypeError( - "Expected 4 or 5 arguments, got {0}.".format(len(args) + 2)) - - if hasattr(Qt.QtCore, "QCoreApplication"): - app = getattr(Qt.QtCore, "QCoreApplication") - else: - raise NotImplementedError( - "Missing QCoreApplication implementation for {binding}".format( - binding=Qt.__binding__, - ) - ) - if Qt.__binding__ in ("PySide2", "PyQt5"): - sanitized_args = [context, sourceText, disambiguation, n] - else: - sanitized_args = [ - context, - sourceText, - disambiguation, - encoding or app.CodecForTr, - n - ] - return app.translate(*sanitized_args) - - -def _loadUi(uifile, baseinstance=None): - """Dynamically load a user interface from the given `uifile` - - This function calls `uic.loadUi` if using PyQt bindings, - else it implements a comparable binding for PySide. - - Documentation: - http://pyqt.sourceforge.net/Docs/PyQt5/designer.html#PyQt5.uic.loadUi - - Arguments: - uifile (str): Absolute path to Qt Designer file. - baseinstance (QWidget): Instantiated QWidget or subclass thereof - - Return: - baseinstance if `baseinstance` is not `None`. Otherwise - return the newly created instance of the user interface. - - """ - if hasattr(Qt, "_uic"): - return Qt._uic.loadUi(uifile, baseinstance) - - elif hasattr(Qt, "_QtUiTools"): - # Implement `PyQt5.uic.loadUi` for PySide(2) - - class _UiLoader(Qt._QtUiTools.QUiLoader): - """Create the user interface in a base instance. - - Unlike `Qt._QtUiTools.QUiLoader` itself this class does not - create a new instance of the top-level widget, but creates the user - interface in an existing instance of the top-level class if needed. - - This mimics the behaviour of `PyQt5.uic.loadUi`. - - """ - - def __init__(self, baseinstance): - super(_UiLoader, self).__init__(baseinstance) - self.baseinstance = baseinstance - self.custom_widgets = {} - - def _loadCustomWidgets(self, etree): - """ - Workaround to pyside-77 bug. - - From QUiLoader doc we should use registerCustomWidget method. - But this causes a segfault on some platforms. - - Instead we fetch from customwidgets DOM node the python class - objects. Then we can directly use them in createWidget method. - """ - - def headerToModule(header): - """ - Translate a header file to python module path - foo/bar.h => foo.bar - """ - # Remove header extension - module = os.path.splitext(header)[0] - - # Replace os separator by python module separator - return module.replace("/", ".").replace("\\", ".") - - custom_widgets = etree.find("customwidgets") - - if custom_widgets is None: - return - - for custom_widget in custom_widgets: - class_name = custom_widget.find("class").text - header = custom_widget.find("header").text - module = importlib.import_module(headerToModule(header)) - self.custom_widgets[class_name] = getattr(module, - class_name) - - def load(self, uifile, *args, **kwargs): - from xml.etree.ElementTree import ElementTree - - # For whatever reason, if this doesn't happen then - # reading an invalid or non-existing .ui file throws - # a RuntimeError. - etree = ElementTree() - etree.parse(uifile) - self._loadCustomWidgets(etree) - - widget = Qt._QtUiTools.QUiLoader.load( - self, uifile, *args, **kwargs) - - # Workaround for PySide 1.0.9, see issue #208 - widget.parentWidget() - - return widget - - def createWidget(self, class_name, parent=None, name=""): - """Called for each widget defined in ui file - - Overridden here to populate `baseinstance` instead. - - """ - - if parent is None and self.baseinstance: - # Supposed to create the top-level widget, - # return the base instance instead - return self.baseinstance - - # For some reason, Line is not in the list of available - # widgets, but works fine, so we have to special case it here. - if class_name in self.availableWidgets() + ["Line"]: - # Create a new widget for child widgets - widget = Qt._QtUiTools.QUiLoader.createWidget(self, - class_name, - parent, - name) - elif class_name in self.custom_widgets: - widget = self.custom_widgets[class_name](parent) - else: - raise Exception("Custom widget '%s' not supported" - % class_name) - - if self.baseinstance: - # Set an attribute for the new child widget on the base - # instance, just like PyQt5.uic.loadUi does. - setattr(self.baseinstance, name, widget) - - return widget - - widget = _UiLoader(baseinstance).load(uifile) - Qt.QtCore.QMetaObject.connectSlotsByName(widget) - - return widget - - else: - raise NotImplementedError("No implementation available for loadUi") - - -"""Misplaced members - -These members from the original submodule are misplaced relative PySide2 - -""" -_misplaced_members = { - "PySide2": { - "QtCore.QStringListModel": "QtCore.QStringListModel", - "QtGui.QStringListModel": "QtCore.QStringListModel", - "QtCore.Property": "QtCore.Property", - "QtCore.Signal": "QtCore.Signal", - "QtCore.Slot": "QtCore.Slot", - "QtCore.QAbstractProxyModel": "QtCore.QAbstractProxyModel", - "QtCore.QSortFilterProxyModel": "QtCore.QSortFilterProxyModel", - "QtCore.QItemSelection": "QtCore.QItemSelection", - "QtCore.QItemSelectionModel": "QtCore.QItemSelectionModel", - "QtCore.QItemSelectionRange": "QtCore.QItemSelectionRange", - "QtUiTools.QUiLoader": ["QtCompat.loadUi", _loadUi], - "shiboken2.wrapInstance": ["QtCompat.wrapInstance", _wrapinstance], - "shiboken2.getCppPointer": ["QtCompat.getCppPointer", _getcpppointer], - "shiboken2.isValid": ["QtCompat.isValid", _isvalid], - "QtWidgets.qApp": "QtWidgets.QApplication.instance()", - "QtCore.QCoreApplication.translate": [ - "QtCompat.translate", _translate - ], - "QtWidgets.QApplication.translate": [ - "QtCompat.translate", _translate - ], - "QtCore.qInstallMessageHandler": [ - "QtCompat.qInstallMessageHandler", _qInstallMessageHandler - ], - "QtWidgets.QStyleOptionViewItem": "QtCompat.QStyleOptionViewItemV4", - }, - "PyQt5": { - "QtCore.pyqtProperty": "QtCore.Property", - "QtCore.pyqtSignal": "QtCore.Signal", - "QtCore.pyqtSlot": "QtCore.Slot", - "QtCore.QAbstractProxyModel": "QtCore.QAbstractProxyModel", - "QtCore.QSortFilterProxyModel": "QtCore.QSortFilterProxyModel", - "QtCore.QStringListModel": "QtCore.QStringListModel", - "QtCore.QItemSelection": "QtCore.QItemSelection", - "QtCore.QItemSelectionModel": "QtCore.QItemSelectionModel", - "QtCore.QItemSelectionRange": "QtCore.QItemSelectionRange", - "uic.loadUi": ["QtCompat.loadUi", _loadUi], - "sip.wrapinstance": ["QtCompat.wrapInstance", _wrapinstance], - "sip.unwrapinstance": ["QtCompat.getCppPointer", _getcpppointer], - "sip.isdeleted": ["QtCompat.isValid", _isvalid], - "QtWidgets.qApp": "QtWidgets.QApplication.instance()", - "QtCore.QCoreApplication.translate": [ - "QtCompat.translate", _translate - ], - "QtWidgets.QApplication.translate": [ - "QtCompat.translate", _translate - ], - "QtCore.qInstallMessageHandler": [ - "QtCompat.qInstallMessageHandler", _qInstallMessageHandler - ], - "QtWidgets.QStyleOptionViewItem": "QtCompat.QStyleOptionViewItemV4", - }, - "PySide": { - "QtGui.QAbstractProxyModel": "QtCore.QAbstractProxyModel", - "QtGui.QSortFilterProxyModel": "QtCore.QSortFilterProxyModel", - "QtGui.QStringListModel": "QtCore.QStringListModel", - "QtGui.QItemSelection": "QtCore.QItemSelection", - "QtGui.QItemSelectionModel": "QtCore.QItemSelectionModel", - "QtCore.Property": "QtCore.Property", - "QtCore.Signal": "QtCore.Signal", - "QtCore.Slot": "QtCore.Slot", - "QtGui.QItemSelectionRange": "QtCore.QItemSelectionRange", - "QtGui.QAbstractPrintDialog": "QtPrintSupport.QAbstractPrintDialog", - "QtGui.QPageSetupDialog": "QtPrintSupport.QPageSetupDialog", - "QtGui.QPrintDialog": "QtPrintSupport.QPrintDialog", - "QtGui.QPrintEngine": "QtPrintSupport.QPrintEngine", - "QtGui.QPrintPreviewDialog": "QtPrintSupport.QPrintPreviewDialog", - "QtGui.QPrintPreviewWidget": "QtPrintSupport.QPrintPreviewWidget", - "QtGui.QPrinter": "QtPrintSupport.QPrinter", - "QtGui.QPrinterInfo": "QtPrintSupport.QPrinterInfo", - "QtUiTools.QUiLoader": ["QtCompat.loadUi", _loadUi], - "shiboken.wrapInstance": ["QtCompat.wrapInstance", _wrapinstance], - "shiboken.unwrapInstance": ["QtCompat.getCppPointer", _getcpppointer], - "shiboken.isValid": ["QtCompat.isValid", _isvalid], - "QtGui.qApp": "QtWidgets.QApplication.instance()", - "QtCore.QCoreApplication.translate": [ - "QtCompat.translate", _translate - ], - "QtGui.QApplication.translate": [ - "QtCompat.translate", _translate - ], - "QtCore.qInstallMsgHandler": [ - "QtCompat.qInstallMessageHandler", _qInstallMessageHandler - ], - "QtGui.QStyleOptionViewItemV4": "QtCompat.QStyleOptionViewItemV4", - }, - "PyQt4": { - "QtGui.QAbstractProxyModel": "QtCore.QAbstractProxyModel", - "QtGui.QSortFilterProxyModel": "QtCore.QSortFilterProxyModel", - "QtGui.QItemSelection": "QtCore.QItemSelection", - "QtGui.QStringListModel": "QtCore.QStringListModel", - "QtGui.QItemSelectionModel": "QtCore.QItemSelectionModel", - "QtCore.pyqtProperty": "QtCore.Property", - "QtCore.pyqtSignal": "QtCore.Signal", - "QtCore.pyqtSlot": "QtCore.Slot", - "QtGui.QItemSelectionRange": "QtCore.QItemSelectionRange", - "QtGui.QAbstractPrintDialog": "QtPrintSupport.QAbstractPrintDialog", - "QtGui.QPageSetupDialog": "QtPrintSupport.QPageSetupDialog", - "QtGui.QPrintDialog": "QtPrintSupport.QPrintDialog", - "QtGui.QPrintEngine": "QtPrintSupport.QPrintEngine", - "QtGui.QPrintPreviewDialog": "QtPrintSupport.QPrintPreviewDialog", - "QtGui.QPrintPreviewWidget": "QtPrintSupport.QPrintPreviewWidget", - "QtGui.QPrinter": "QtPrintSupport.QPrinter", - "QtGui.QPrinterInfo": "QtPrintSupport.QPrinterInfo", - # "QtCore.pyqtSignature": "QtCore.Slot", - "uic.loadUi": ["QtCompat.loadUi", _loadUi], - "sip.wrapinstance": ["QtCompat.wrapInstance", _wrapinstance], - "sip.unwrapinstance": ["QtCompat.getCppPointer", _getcpppointer], - "sip.isdeleted": ["QtCompat.isValid", _isvalid], - "QtCore.QString": "str", - "QtGui.qApp": "QtWidgets.QApplication.instance()", - "QtCore.QCoreApplication.translate": [ - "QtCompat.translate", _translate - ], - "QtGui.QApplication.translate": [ - "QtCompat.translate", _translate - ], - "QtCore.qInstallMsgHandler": [ - "QtCompat.qInstallMessageHandler", _qInstallMessageHandler - ], - "QtGui.QStyleOptionViewItemV4": "QtCompat.QStyleOptionViewItemV4", - } -} - -""" Compatibility Members - -This dictionary is used to build Qt.QtCompat objects that provide a consistent -interface for obsolete members, and differences in binding return values. - -{ - "binding": { - "classname": { - "targetname": "binding_namespace", - } - } -} -""" -_compatibility_members = { - "PySide2": { - "QWidget": { - "grab": "QtWidgets.QWidget.grab", - }, - "QHeaderView": { - "sectionsClickable": "QtWidgets.QHeaderView.sectionsClickable", - "setSectionsClickable": - "QtWidgets.QHeaderView.setSectionsClickable", - "sectionResizeMode": "QtWidgets.QHeaderView.sectionResizeMode", - "setSectionResizeMode": - "QtWidgets.QHeaderView.setSectionResizeMode", - "sectionsMovable": "QtWidgets.QHeaderView.sectionsMovable", - "setSectionsMovable": "QtWidgets.QHeaderView.setSectionsMovable", - }, - "QFileDialog": { - "getOpenFileName": "QtWidgets.QFileDialog.getOpenFileName", - "getOpenFileNames": "QtWidgets.QFileDialog.getOpenFileNames", - "getSaveFileName": "QtWidgets.QFileDialog.getSaveFileName", - }, - }, - "PyQt5": { - "QWidget": { - "grab": "QtWidgets.QWidget.grab", - }, - "QHeaderView": { - "sectionsClickable": "QtWidgets.QHeaderView.sectionsClickable", - "setSectionsClickable": - "QtWidgets.QHeaderView.setSectionsClickable", - "sectionResizeMode": "QtWidgets.QHeaderView.sectionResizeMode", - "setSectionResizeMode": - "QtWidgets.QHeaderView.setSectionResizeMode", - "sectionsMovable": "QtWidgets.QHeaderView.sectionsMovable", - "setSectionsMovable": "QtWidgets.QHeaderView.setSectionsMovable", - }, - "QFileDialog": { - "getOpenFileName": "QtWidgets.QFileDialog.getOpenFileName", - "getOpenFileNames": "QtWidgets.QFileDialog.getOpenFileNames", - "getSaveFileName": "QtWidgets.QFileDialog.getSaveFileName", - }, - }, - "PySide": { - "QWidget": { - "grab": "QtWidgets.QPixmap.grabWidget", - }, - "QHeaderView": { - "sectionsClickable": "QtWidgets.QHeaderView.isClickable", - "setSectionsClickable": "QtWidgets.QHeaderView.setClickable", - "sectionResizeMode": "QtWidgets.QHeaderView.resizeMode", - "setSectionResizeMode": "QtWidgets.QHeaderView.setResizeMode", - "sectionsMovable": "QtWidgets.QHeaderView.isMovable", - "setSectionsMovable": "QtWidgets.QHeaderView.setMovable", - }, - "QFileDialog": { - "getOpenFileName": "QtWidgets.QFileDialog.getOpenFileName", - "getOpenFileNames": "QtWidgets.QFileDialog.getOpenFileNames", - "getSaveFileName": "QtWidgets.QFileDialog.getSaveFileName", - }, - }, - "PyQt4": { - "QWidget": { - "grab": "QtWidgets.QPixmap.grabWidget", - }, - "QHeaderView": { - "sectionsClickable": "QtWidgets.QHeaderView.isClickable", - "setSectionsClickable": "QtWidgets.QHeaderView.setClickable", - "sectionResizeMode": "QtWidgets.QHeaderView.resizeMode", - "setSectionResizeMode": "QtWidgets.QHeaderView.setResizeMode", - "sectionsMovable": "QtWidgets.QHeaderView.isMovable", - "setSectionsMovable": "QtWidgets.QHeaderView.setMovable", - }, - "QFileDialog": { - "getOpenFileName": "QtWidgets.QFileDialog.getOpenFileName", - "getOpenFileNames": "QtWidgets.QFileDialog.getOpenFileNames", - "getSaveFileName": "QtWidgets.QFileDialog.getSaveFileName", - }, - }, -} - - -def _apply_site_config(): - try: - import QtSiteConfig - except ImportError: - # If no QtSiteConfig module found, no modifications - # to _common_members are needed. - pass - else: - # Provide the ability to modify the dicts used to build Qt.py - if hasattr(QtSiteConfig, 'update_members'): - QtSiteConfig.update_members(_common_members) - - if hasattr(QtSiteConfig, 'update_misplaced_members'): - QtSiteConfig.update_misplaced_members(members=_misplaced_members) - - if hasattr(QtSiteConfig, 'update_compatibility_members'): - QtSiteConfig.update_compatibility_members( - members=_compatibility_members) - - -def _new_module(name): - return types.ModuleType(__name__ + "." + name) - - -def _import_sub_module(module, name): - """import_sub_module will mimic the function of importlib.import_module""" - module = __import__(module.__name__ + "." + name) - for level in name.split("."): - module = getattr(module, level) - return module - - -def _setup(module, extras): - """Install common submodules""" - - Qt.__binding__ = module.__name__ - - for name in list(_common_members) + extras: - try: - submodule = _import_sub_module( - module, name) - except ImportError: - try: - # For extra modules like sip and shiboken that may not be - # children of the binding. - submodule = __import__(name) - except ImportError: - continue - - setattr(Qt, "_" + name, submodule) - - if name not in extras: - # Store reference to original binding, - # but don't store speciality modules - # such as uic or QtUiTools - setattr(Qt, name, _new_module(name)) - - -def _reassign_misplaced_members(binding): - """Apply misplaced members from `binding` to Qt.py - - Arguments: - binding (dict): Misplaced members - - """ - - for src, dst in _misplaced_members[binding].items(): - dst_value = None - - src_parts = src.split(".") - src_module = src_parts[0] - src_member = None - if len(src_parts) > 1: - src_member = src_parts[1:] - - if isinstance(dst, (list, tuple)): - dst, dst_value = dst - - dst_parts = dst.split(".") - dst_module = dst_parts[0] - dst_member = None - if len(dst_parts) > 1: - dst_member = dst_parts[1] - - # Get the member we want to store in the namesapce. - if not dst_value: - try: - _part = getattr(Qt, "_" + src_module) - while src_member: - member = src_member.pop(0) - _part = getattr(_part, member) - dst_value = _part - except AttributeError: - # If the member we want to store in the namespace does not - # exist, there is no need to continue. This can happen if a - # request was made to rename a member that didn't exist, for - # example if QtWidgets isn't available on the target platform. - _log("Misplaced member has no source: {0}".format(src)) - continue - - try: - src_object = getattr(Qt, dst_module) - except AttributeError: - if dst_module not in _common_members: - # Only create the Qt parent module if its listed in - # _common_members. Without this check, if you remove QtCore - # from _common_members, the default _misplaced_members will add - # Qt.QtCore so it can add Signal, Slot, etc. - msg = 'Not creating missing member module "{m}" for "{c}"' - _log(msg.format(m=dst_module, c=dst_member)) - continue - # If the dst is valid but the Qt parent module does not exist - # then go ahead and create a new module to contain the member. - setattr(Qt, dst_module, _new_module(dst_module)) - src_object = getattr(Qt, dst_module) - # Enable direct import of the new module - sys.modules[__name__ + "." + dst_module] = src_object - - if not dst_value: - dst_value = getattr(Qt, "_" + src_module) - if src_member: - dst_value = getattr(dst_value, src_member) - - setattr( - src_object, - dst_member or dst_module, - dst_value - ) - - -def _build_compatibility_members(binding, decorators=None): - """Apply `binding` to QtCompat - - Arguments: - binding (str): Top level binding in _compatibility_members. - decorators (dict, optional): Provides the ability to decorate the - original Qt methods when needed by a binding. This can be used - to change the returned value to a standard value. The key should - be the classname, the value is a dict where the keys are the - target method names, and the values are the decorator functions. - - """ - - decorators = decorators or dict() - - # Allow optional site-level customization of the compatibility members. - # This method does not need to be implemented in QtSiteConfig. - try: - import QtSiteConfig - except ImportError: - pass - else: - if hasattr(QtSiteConfig, 'update_compatibility_decorators'): - QtSiteConfig.update_compatibility_decorators(binding, decorators) - - _QtCompat = type("QtCompat", (object,), {}) - - for classname, bindings in _compatibility_members[binding].items(): - attrs = {} - for target, binding in bindings.items(): - namespaces = binding.split('.') - try: - src_object = getattr(Qt, "_" + namespaces[0]) - except AttributeError as e: - _log("QtCompat: AttributeError: %s" % e) - # Skip reassignment of non-existing members. - # This can happen if a request was made to - # rename a member that didn't exist, for example - # if QtWidgets isn't available on the target platform. - continue - - # Walk down any remaining namespace getting the object assuming - # that if the first namespace exists the rest will exist. - for namespace in namespaces[1:]: - src_object = getattr(src_object, namespace) - - # decorate the Qt method if a decorator was provided. - if target in decorators.get(classname, []): - # staticmethod must be called on the decorated method to - # prevent a TypeError being raised when the decorated method - # is called. - src_object = staticmethod( - decorators[classname][target](src_object)) - - attrs[target] = src_object - - # Create the QtCompat class and install it into the namespace - compat_class = type(classname, (_QtCompat,), attrs) - setattr(Qt.QtCompat, classname, compat_class) - - -def _pyside2(): - """Initialise PySide2 - - These functions serve to test the existence of a binding - along with set it up in such a way that it aligns with - the final step; adding members from the original binding - to Qt.py - - """ - - import PySide2 as module - extras = ["QtUiTools"] - try: - try: - # Before merge of PySide and shiboken - import shiboken2 - except ImportError: - # After merge of PySide and shiboken, May 2017 - from PySide2 import shiboken2 - extras.append("shiboken2") - except ImportError: - pass - - _setup(module, extras) - Qt.__binding_version__ = module.__version__ - - if hasattr(Qt, "_shiboken2"): - Qt.QtCompat.wrapInstance = _wrapinstance - Qt.QtCompat.getCppPointer = _getcpppointer - Qt.QtCompat.delete = shiboken2.delete - - if hasattr(Qt, "_QtUiTools"): - Qt.QtCompat.loadUi = _loadUi - - if hasattr(Qt, "_QtCore"): - Qt.__qt_version__ = Qt._QtCore.qVersion() - Qt.QtCompat.dataChanged = ( - lambda self, topleft, bottomright, roles=None: - self.dataChanged.emit(topleft, bottomright, roles or []) - ) - - if hasattr(Qt, "_QtWidgets"): - Qt.QtCompat.setSectionResizeMode = \ - Qt._QtWidgets.QHeaderView.setSectionResizeMode - - _reassign_misplaced_members("PySide2") - _build_compatibility_members("PySide2") - - -def _pyside(): - """Initialise PySide""" - - import PySide as module - extras = ["QtUiTools"] - try: - try: - # Before merge of PySide and shiboken - import shiboken - except ImportError: - # After merge of PySide and shiboken, May 2017 - from PySide import shiboken - extras.append("shiboken") - except ImportError: - pass - - _setup(module, extras) - Qt.__binding_version__ = module.__version__ - - if hasattr(Qt, "_shiboken"): - Qt.QtCompat.wrapInstance = _wrapinstance - Qt.QtCompat.getCppPointer = _getcpppointer - Qt.QtCompat.delete = shiboken.delete - - if hasattr(Qt, "_QtUiTools"): - Qt.QtCompat.loadUi = _loadUi - - if hasattr(Qt, "_QtGui"): - setattr(Qt, "QtWidgets", _new_module("QtWidgets")) - setattr(Qt, "_QtWidgets", Qt._QtGui) - if hasattr(Qt._QtGui, "QX11Info"): - setattr(Qt, "QtX11Extras", _new_module("QtX11Extras")) - Qt.QtX11Extras.QX11Info = Qt._QtGui.QX11Info - - Qt.QtCompat.setSectionResizeMode = Qt._QtGui.QHeaderView.setResizeMode - - if hasattr(Qt, "_QtCore"): - Qt.__qt_version__ = Qt._QtCore.qVersion() - Qt.QtCompat.dataChanged = ( - lambda self, topleft, bottomright, roles=None: - self.dataChanged.emit(topleft, bottomright) - ) - - _reassign_misplaced_members("PySide") - _build_compatibility_members("PySide") - - -def _pyqt5(): - """Initialise PyQt5""" - - import PyQt5 as module - extras = ["uic"] - - try: - import sip - extras += ["sip"] - except ImportError: - - # Relevant to PyQt5 5.11 and above - try: - from PyQt5 import sip - extras += ["sip"] - except ImportError: - sip = None - - _setup(module, extras) - if hasattr(Qt, "_sip"): - Qt.QtCompat.wrapInstance = _wrapinstance - Qt.QtCompat.getCppPointer = _getcpppointer - Qt.QtCompat.delete = sip.delete - - if hasattr(Qt, "_uic"): - Qt.QtCompat.loadUi = _loadUi - - if hasattr(Qt, "_QtCore"): - Qt.__binding_version__ = Qt._QtCore.PYQT_VERSION_STR - Qt.__qt_version__ = Qt._QtCore.QT_VERSION_STR - Qt.QtCompat.dataChanged = ( - lambda self, topleft, bottomright, roles=None: - self.dataChanged.emit(topleft, bottomright, roles or []) - ) - - if hasattr(Qt, "_QtWidgets"): - Qt.QtCompat.setSectionResizeMode = \ - Qt._QtWidgets.QHeaderView.setSectionResizeMode - - _reassign_misplaced_members("PyQt5") - _build_compatibility_members('PyQt5') - - -def _pyqt4(): - """Initialise PyQt4""" - - import sip - - # Validation of envivornment variable. Prevents an error if - # the variable is invalid since it's just a hint. - try: - hint = int(QT_SIP_API_HINT) - except TypeError: - hint = None # Variable was None, i.e. not set. - except ValueError: - raise ImportError("QT_SIP_API_HINT=%s must be a 1 or 2") - - for api in ("QString", - "QVariant", - "QDate", - "QDateTime", - "QTextStream", - "QTime", - "QUrl"): - try: - sip.setapi(api, hint or 2) - except AttributeError: - raise ImportError("PyQt4 < 4.6 isn't supported by Qt.py") - except ValueError: - actual = sip.getapi(api) - if not hint: - raise ImportError("API version already set to %d" % actual) - else: - # Having provided a hint indicates a soft constraint, one - # that doesn't throw an exception. - sys.stderr.write( - "Warning: API '%s' has already been set to %d.\n" - % (api, actual) - ) - - import PyQt4 as module - extras = ["uic"] - try: - import sip - extras.append(sip.__name__) - except ImportError: - sip = None - - _setup(module, extras) - if hasattr(Qt, "_sip"): - Qt.QtCompat.wrapInstance = _wrapinstance - Qt.QtCompat.getCppPointer = _getcpppointer - Qt.QtCompat.delete = sip.delete - - if hasattr(Qt, "_uic"): - Qt.QtCompat.loadUi = _loadUi - - if hasattr(Qt, "_QtGui"): - setattr(Qt, "QtWidgets", _new_module("QtWidgets")) - setattr(Qt, "_QtWidgets", Qt._QtGui) - if hasattr(Qt._QtGui, "QX11Info"): - setattr(Qt, "QtX11Extras", _new_module("QtX11Extras")) - Qt.QtX11Extras.QX11Info = Qt._QtGui.QX11Info - - Qt.QtCompat.setSectionResizeMode = \ - Qt._QtGui.QHeaderView.setResizeMode - - if hasattr(Qt, "_QtCore"): - Qt.__binding_version__ = Qt._QtCore.PYQT_VERSION_STR - Qt.__qt_version__ = Qt._QtCore.QT_VERSION_STR - Qt.QtCompat.dataChanged = ( - lambda self, topleft, bottomright, roles=None: - self.dataChanged.emit(topleft, bottomright) - ) - - _reassign_misplaced_members("PyQt4") - - # QFileDialog QtCompat decorator - def _standardizeQFileDialog(some_function): - """Decorator that makes PyQt4 return conform to other bindings""" - def wrapper(*args, **kwargs): - ret = (some_function(*args, **kwargs)) - - # PyQt4 only returns the selected filename, force it to a - # standard return of the selected filename, and a empty string - # for the selected filter - return ret, '' - - wrapper.__doc__ = some_function.__doc__ - wrapper.__name__ = some_function.__name__ - - return wrapper - - decorators = { - "QFileDialog": { - "getOpenFileName": _standardizeQFileDialog, - "getOpenFileNames": _standardizeQFileDialog, - "getSaveFileName": _standardizeQFileDialog, - } - } - _build_compatibility_members('PyQt4', decorators) - - -def _none(): - """Internal option (used in installer)""" - - Mock = type("Mock", (), {"__getattr__": lambda Qt, attr: None}) - - Qt.__binding__ = "None" - Qt.__qt_version__ = "0.0.0" - Qt.__binding_version__ = "0.0.0" - Qt.QtCompat.loadUi = lambda uifile, baseinstance=None: None - Qt.QtCompat.setSectionResizeMode = lambda *args, **kwargs: None - - for submodule in _common_members.keys(): - setattr(Qt, submodule, Mock()) - setattr(Qt, "_" + submodule, Mock()) - - -def _log(text): - if QT_VERBOSE: - sys.stdout.write(text + "\n") - - -def _convert(lines): - """Convert compiled .ui file from PySide2 to Qt.py - - Arguments: - lines (list): Each line of of .ui file - - Usage: - >> with open("myui.py") as f: - .. lines = _convert(f.readlines()) - - """ - - def parse(line): - line = line.replace("from PySide2 import", "from Qt import QtCompat,") - line = line.replace("QtWidgets.QApplication.translate", - "QtCompat.translate") - if "QtCore.SIGNAL" in line: - raise NotImplementedError("QtCore.SIGNAL is missing from PyQt5 " - "and so Qt.py does not support it: you " - "should avoid defining signals inside " - "your ui files.") - return line - - parsed = list() - for line in lines: - line = parse(line) - parsed.append(line) - - return parsed - - -def _cli(args): - """Qt.py command-line interface""" - import argparse - - parser = argparse.ArgumentParser() - parser.add_argument("--convert", - help="Path to compiled Python module, e.g. my_ui.py") - parser.add_argument("--compile", - help="Accept raw .ui file and compile with native " - "PySide2 compiler.") - parser.add_argument("--stdout", - help="Write to stdout instead of file", - action="store_true") - parser.add_argument("--stdin", - help="Read from stdin instead of file", - action="store_true") - - args = parser.parse_args(args) - - if args.stdout: - raise NotImplementedError("--stdout") - - if args.stdin: - raise NotImplementedError("--stdin") - - if args.compile: - raise NotImplementedError("--compile") - - if args.convert: - sys.stdout.write("#\n" - "# WARNING: --convert is an ALPHA feature.\n#\n" - "# See https://github.com/mottosso/Qt.py/pull/132\n" - "# for details.\n" - "#\n") - - # - # ------> Read - # - with open(args.convert) as f: - lines = _convert(f.readlines()) - - backup = "%s_backup%s" % os.path.splitext(args.convert) - sys.stdout.write("Creating \"%s\"..\n" % backup) - shutil.copy(args.convert, backup) - - # - # <------ Write - # - with open(args.convert, "w") as f: - f.write("".join(lines)) - - sys.stdout.write("Successfully converted \"%s\"\n" % args.convert) - - -class MissingMember(object): - """ - A placeholder type for a missing Qt object not - included in Qt.py - - Args: - name (str): The name of the missing type - details (str): An optional custom error message - """ - ERR_TMPL = ("{} is not a common object across PySide2 " - "and the other Qt bindings. It is not included " - "as a common member in the Qt.py layer") - - def __init__(self, name, details=''): - self.__name = name - self.__err = self.ERR_TMPL.format(name) - - if details: - self.__err = "{}: {}".format(self.__err, details) - - def __repr__(self): - return "<{}: {}>".format(self.__class__.__name__, self.__name) - - def __getattr__(self, name): - raise NotImplementedError(self.__err) - - def __call__(self, *a, **kw): - raise NotImplementedError(self.__err) - - -def _install(): - # Default order (customise order and content via QT_PREFERRED_BINDING) - default_order = ("PySide2", "PyQt5", "PySide", "PyQt4") - preferred_order = list( - b for b in QT_PREFERRED_BINDING.split(os.pathsep) if b - ) - - order = preferred_order or default_order - - available = { - "PySide2": _pyside2, - "PyQt5": _pyqt5, - "PySide": _pyside, - "PyQt4": _pyqt4, - "None": _none - } - - _log("Order: '%s'" % "', '".join(order)) - - # Allow site-level customization of the available modules. - _apply_site_config() - - found_binding = False - for name in order: - _log("Trying %s" % name) - - try: - available[name]() - found_binding = True - break - - except ImportError as e: - _log("ImportError: %s" % e) - - except KeyError: - _log("ImportError: Preferred binding '%s' not found." % name) - - if not found_binding: - # If not binding were found, throw this error - raise ImportError("No Qt binding were found.") - - # Install individual members - for name, members in _common_members.items(): - try: - their_submodule = getattr(Qt, "_%s" % name) - except AttributeError: - continue - - our_submodule = getattr(Qt, name) - - # Enable import * - __all__.append(name) - - # Enable direct import of submodule, - # e.g. import Qt.QtCore - sys.modules[__name__ + "." + name] = our_submodule - - for member in members: - # Accept that a submodule may miss certain members. - try: - their_member = getattr(their_submodule, member) - except AttributeError: - _log("'%s.%s' was missing." % (name, member)) - continue - - setattr(our_submodule, member, their_member) - - # Install missing member placeholders - for name, members in _missing_members.items(): - our_submodule = getattr(Qt, name) - - for member in members: - - # If the submodule already has this member installed, - # either by the common members, or the site config, - # then skip installing this one over it. - if hasattr(our_submodule, member): - continue - - placeholder = MissingMember("{}.{}".format(name, member), - details=members[member]) - setattr(our_submodule, member, placeholder) - - # Enable direct import of QtCompat - sys.modules['Qt.QtCompat'] = Qt.QtCompat - - # Backwards compatibility - if hasattr(Qt.QtCompat, 'loadUi'): - Qt.QtCompat.load_ui = Qt.QtCompat.loadUi - - -_install() - -# Setup Binding Enum states -Qt.IsPySide2 = Qt.__binding__ == 'PySide2' -Qt.IsPyQt5 = Qt.__binding__ == 'PyQt5' -Qt.IsPySide = Qt.__binding__ == 'PySide' -Qt.IsPyQt4 = Qt.__binding__ == 'PyQt4' - -"""Augment QtCompat - -QtCompat contains wrappers and added functionality -to the original bindings, such as the CLI interface -and otherwise incompatible members between bindings, -such as `QHeaderView.setSectionResizeMode`. - -""" - -Qt.QtCompat._cli = _cli -Qt.QtCompat._convert = _convert - -# Enable command-line interface -if __name__ == "__main__": - _cli(sys.argv[1:]) - - -# The MIT License (MIT) -# -# Copyright (c) 2016-2017 Marcus Ottosson -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# -# In PySide(2), loadUi does not exist, so we implement it -# -# `_UiLoader` is adapted from the qtpy project, which was further influenced -# by qt-helpers which was released under a 3-clause BSD license which in turn -# is based on a solution at: -# -# - https://gist.github.com/cpbotha/1b42a20c8f3eb9bb7cb8 -# -# The License for this code is as follows: -# -# qt-helpers - a common front-end to various Qt modules -# -# Copyright (c) 2015, Chris Beaumont and Thomas Robitaille -# -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the -# distribution. -# * Neither the name of the Glue project nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -# Which itself was based on the solution at -# -# https://gist.github.com/cpbotha/1b42a20c8f3eb9bb7cb8 -# -# which was released under the MIT license: -# -# Copyright (c) 2011 Sebastian Wiesner -# Modifications by Charl Botha -# -# Permission is hereby granted, free of charge, to any person obtaining a -# copy of this software and associated documentation files -# (the "Software"),to deal in the Software without restriction, -# including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, -# and/or sell copies of the Software, and to permit persons to whom the -# Software is furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/openpype/vendor/python/common/scriptsmenu/vendor/__init__.py b/openpype/vendor/python/common/scriptsmenu/vendor/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 From e51453466c8069b224743689fb5b2ed2cfd01f88 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 13:54:00 +0100 Subject: [PATCH 107/213] global loader plugins are using qtpy --- openpype/plugins/load/copy_file.py | 2 +- openpype/plugins/load/copy_file_path.py | 2 +- openpype/plugins/load/delete_old_versions.py | 2 +- openpype/plugins/load/delivery.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/plugins/load/copy_file.py b/openpype/plugins/load/copy_file.py index 60db094cfbb..163f56a83a9 100644 --- a/openpype/plugins/load/copy_file.py +++ b/openpype/plugins/load/copy_file.py @@ -19,7 +19,7 @@ def load(self, context, name=None, namespace=None, data=None): @staticmethod def copy_file_to_clipboard(path): - from Qt import QtCore, QtWidgets + from qtpy import QtCore, QtWidgets clipboard = QtWidgets.QApplication.clipboard() assert clipboard, "Must have running QApplication instance" diff --git a/openpype/plugins/load/copy_file_path.py b/openpype/plugins/load/copy_file_path.py index 565d8d1ff18..569e5c8780f 100644 --- a/openpype/plugins/load/copy_file_path.py +++ b/openpype/plugins/load/copy_file_path.py @@ -19,7 +19,7 @@ def load(self, context, name=None, namespace=None, data=None): @staticmethod def copy_path_to_clipboard(path): - from Qt import QtWidgets + from qtpy import QtWidgets clipboard = QtWidgets.QApplication.clipboard() assert clipboard, "Must have running QApplication instance" diff --git a/openpype/plugins/load/delete_old_versions.py b/openpype/plugins/load/delete_old_versions.py index b7ac0152687..c7ad88a924b 100644 --- a/openpype/plugins/load/delete_old_versions.py +++ b/openpype/plugins/load/delete_old_versions.py @@ -5,7 +5,7 @@ import clique from pymongo import UpdateOne import qargparse -from Qt import QtWidgets, QtCore +from qtpy import QtWidgets, QtCore from openpype import style from openpype.client import get_versions, get_representations diff --git a/openpype/plugins/load/delivery.py b/openpype/plugins/load/delivery.py index 89c24f2402e..d1d5659118f 100644 --- a/openpype/plugins/load/delivery.py +++ b/openpype/plugins/load/delivery.py @@ -1,7 +1,7 @@ import copy from collections import defaultdict -from Qt import QtWidgets, QtCore, QtGui +from qtpy import QtWidgets, QtCore, QtGui from openpype.client import get_representations from openpype.pipeline import load, Anatomy From 727b17b1b52ed91d49c1535475b1fd9276743c0c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 13:56:22 +0100 Subject: [PATCH 108/213] added qtpy 1.11.3 into python 2 vendor --- .../python/python_2/qtpy/Qt3DAnimation.py | 26 ++ .../vendor/python/python_2/qtpy/Qt3DCore.py | 26 ++ .../vendor/python/python_2/qtpy/Qt3DExtras.py | 26 ++ .../vendor/python/python_2/qtpy/Qt3DInput.py | 26 ++ .../vendor/python/python_2/qtpy/Qt3DLogic.py | 26 ++ .../vendor/python/python_2/qtpy/Qt3DRender.py | 26 ++ .../vendor/python/python_2/qtpy/QtCharts.py | 22 ++ .../vendor/python/python_2/qtpy/QtCore.py | 111 +++++++ .../python_2/qtpy/QtDataVisualization.py | 22 ++ .../vendor/python/python_2/qtpy/QtDesigner.py | 20 ++ openpype/vendor/python/python_2/qtpy/QtGui.py | 161 ++++++++++ .../vendor/python/python_2/qtpy/QtHelp.py | 24 ++ .../vendor/python/python_2/qtpy/QtLocation.py | 18 ++ .../python/python_2/qtpy/QtMultimedia.py | 17 ++ .../python_2/qtpy/QtMultimediaWidgets.py | 18 ++ .../vendor/python/python_2/qtpy/QtNetwork.py | 25 ++ .../vendor/python/python_2/qtpy/QtOpenGL.py | 24 ++ .../python/python_2/qtpy/QtPositioning.py | 18 ++ .../python/python_2/qtpy/QtPrintSupport.py | 28 ++ openpype/vendor/python/python_2/qtpy/QtQml.py | 18 ++ .../vendor/python/python_2/qtpy/QtQuick.py | 18 ++ .../python/python_2/qtpy/QtQuickWidgets.py | 18 ++ .../python/python_2/qtpy/QtSerialPort.py | 17 ++ openpype/vendor/python/python_2/qtpy/QtSql.py | 24 ++ openpype/vendor/python/python_2/qtpy/QtSvg.py | 24 ++ .../vendor/python/python_2/qtpy/QtTest.py | 30 ++ .../python/python_2/qtpy/QtWebChannel.py | 18 ++ .../python_2/qtpy/QtWebEngineWidgets.py | 49 +++ .../python/python_2/qtpy/QtWebSockets.py | 18 ++ .../vendor/python/python_2/qtpy/QtWidgets.py | 135 +++++++++ .../python/python_2/qtpy/QtWinExtras.py | 16 + .../python/python_2/qtpy/QtXmlPatterns.py | 22 ++ .../vendor/python/python_2/qtpy/__init__.py | 278 ++++++++++++++++++ .../python/python_2/qtpy/_patch/__init__.py | 0 .../python/python_2/qtpy/_patch/qcombobox.py | 101 +++++++ .../python_2/qtpy/_patch/qheaderview.py | 96 ++++++ .../vendor/python/python_2/qtpy/_version.py | 2 + .../vendor/python/python_2/qtpy/compat.py | 195 ++++++++++++ .../vendor/python/python_2/qtpy/py3compat.py | 262 +++++++++++++++++ .../python/python_2/qtpy/tests/__init__.py | 0 .../python/python_2/qtpy/tests/conftest.py | 71 +++++ .../python/python_2/qtpy/tests/runtests.py | 26 ++ .../python_2/qtpy/tests/test_macos_checks.py | 110 +++++++ .../python/python_2/qtpy/tests/test_main.py | 82 ++++++ .../qtpy/tests/test_patch_qcombobox.py | 107 +++++++ .../qtpy/tests/test_patch_qheaderview.py | 98 ++++++ .../qtpy/tests/test_qdesktopservice_split.py | 41 +++ .../python_2/qtpy/tests/test_qt3danimation.py | 25 ++ .../python_2/qtpy/tests/test_qt3dcore.py | 44 +++ .../python_2/qtpy/tests/test_qt3dextras.py | 47 +++ .../python_2/qtpy/tests/test_qt3dinput.py | 33 +++ .../python_2/qtpy/tests/test_qt3dlogic.py | 12 + .../python_2/qtpy/tests/test_qt3drender.py | 119 ++++++++ .../python_2/qtpy/tests/test_qtcharts.py | 11 + .../python/python_2/qtpy/tests/test_qtcore.py | 29 ++ .../qtpy/tests/test_qtdatavisualization.py | 86 ++++++ .../python_2/qtpy/tests/test_qtdesigner.py | 28 ++ .../python/python_2/qtpy/tests/test_qthelp.py | 22 ++ .../python_2/qtpy/tests/test_qtlocation.py | 48 +++ .../python_2/qtpy/tests/test_qtmultimedia.py | 18 ++ .../qtpy/tests/test_qtmultimediawidgets.py | 18 ++ .../python_2/qtpy/tests/test_qtnetwork.py | 43 +++ .../python_2/qtpy/tests/test_qtpositioning.py | 28 ++ .../qtpy/tests/test_qtprintsupport.py | 18 ++ .../python/python_2/qtpy/tests/test_qtqml.py | 34 +++ .../python_2/qtpy/tests/test_qtquick.py | 53 ++++ .../qtpy/tests/test_qtquickwidgets.py | 10 + .../python_2/qtpy/tests/test_qtserialport.py | 12 + .../python/python_2/qtpy/tests/test_qtsql.py | 24 ++ .../python/python_2/qtpy/tests/test_qtsvg.py | 13 + .../python/python_2/qtpy/tests/test_qttest.py | 9 + .../python_2/qtpy/tests/test_qtwebchannel.py | 13 + .../qtpy/tests/test_qtwebenginewidgets.py | 12 + .../python_2/qtpy/tests/test_qtwebsockets.py | 15 + .../python_2/qtpy/tests/test_qtwinextras.py | 29 ++ .../python_2/qtpy/tests/test_qtxmlpatterns.py | 25 ++ .../python/python_2/qtpy/tests/test_uic.py | 116 ++++++++ openpype/vendor/python/python_2/qtpy/uic.py | 277 +++++++++++++++++ 78 files changed, 3811 insertions(+) create mode 100644 openpype/vendor/python/python_2/qtpy/Qt3DAnimation.py create mode 100644 openpype/vendor/python/python_2/qtpy/Qt3DCore.py create mode 100644 openpype/vendor/python/python_2/qtpy/Qt3DExtras.py create mode 100644 openpype/vendor/python/python_2/qtpy/Qt3DInput.py create mode 100644 openpype/vendor/python/python_2/qtpy/Qt3DLogic.py create mode 100644 openpype/vendor/python/python_2/qtpy/Qt3DRender.py create mode 100644 openpype/vendor/python/python_2/qtpy/QtCharts.py create mode 100644 openpype/vendor/python/python_2/qtpy/QtCore.py create mode 100644 openpype/vendor/python/python_2/qtpy/QtDataVisualization.py create mode 100644 openpype/vendor/python/python_2/qtpy/QtDesigner.py create mode 100644 openpype/vendor/python/python_2/qtpy/QtGui.py create mode 100644 openpype/vendor/python/python_2/qtpy/QtHelp.py create mode 100644 openpype/vendor/python/python_2/qtpy/QtLocation.py create mode 100644 openpype/vendor/python/python_2/qtpy/QtMultimedia.py create mode 100644 openpype/vendor/python/python_2/qtpy/QtMultimediaWidgets.py create mode 100644 openpype/vendor/python/python_2/qtpy/QtNetwork.py create mode 100644 openpype/vendor/python/python_2/qtpy/QtOpenGL.py create mode 100644 openpype/vendor/python/python_2/qtpy/QtPositioning.py create mode 100644 openpype/vendor/python/python_2/qtpy/QtPrintSupport.py create mode 100644 openpype/vendor/python/python_2/qtpy/QtQml.py create mode 100644 openpype/vendor/python/python_2/qtpy/QtQuick.py create mode 100644 openpype/vendor/python/python_2/qtpy/QtQuickWidgets.py create mode 100644 openpype/vendor/python/python_2/qtpy/QtSerialPort.py create mode 100644 openpype/vendor/python/python_2/qtpy/QtSql.py create mode 100644 openpype/vendor/python/python_2/qtpy/QtSvg.py create mode 100644 openpype/vendor/python/python_2/qtpy/QtTest.py create mode 100644 openpype/vendor/python/python_2/qtpy/QtWebChannel.py create mode 100644 openpype/vendor/python/python_2/qtpy/QtWebEngineWidgets.py create mode 100644 openpype/vendor/python/python_2/qtpy/QtWebSockets.py create mode 100644 openpype/vendor/python/python_2/qtpy/QtWidgets.py create mode 100644 openpype/vendor/python/python_2/qtpy/QtWinExtras.py create mode 100644 openpype/vendor/python/python_2/qtpy/QtXmlPatterns.py create mode 100644 openpype/vendor/python/python_2/qtpy/__init__.py create mode 100644 openpype/vendor/python/python_2/qtpy/_patch/__init__.py create mode 100644 openpype/vendor/python/python_2/qtpy/_patch/qcombobox.py create mode 100644 openpype/vendor/python/python_2/qtpy/_patch/qheaderview.py create mode 100644 openpype/vendor/python/python_2/qtpy/_version.py create mode 100644 openpype/vendor/python/python_2/qtpy/compat.py create mode 100644 openpype/vendor/python/python_2/qtpy/py3compat.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/__init__.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/conftest.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/runtests.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_macos_checks.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_main.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_patch_qcombobox.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_patch_qheaderview.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qdesktopservice_split.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qt3danimation.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qt3dcore.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qt3dextras.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qt3dinput.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qt3dlogic.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qt3drender.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qtcharts.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qtcore.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qtdatavisualization.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qtdesigner.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qthelp.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qtlocation.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qtmultimedia.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qtmultimediawidgets.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qtnetwork.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qtpositioning.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qtprintsupport.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qtqml.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qtquick.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qtquickwidgets.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qtserialport.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qtsql.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qtsvg.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qttest.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qtwebchannel.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qtwebenginewidgets.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qtwebsockets.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qtwinextras.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_qtxmlpatterns.py create mode 100644 openpype/vendor/python/python_2/qtpy/tests/test_uic.py create mode 100644 openpype/vendor/python/python_2/qtpy/uic.py diff --git a/openpype/vendor/python/python_2/qtpy/Qt3DAnimation.py b/openpype/vendor/python/python_2/qtpy/Qt3DAnimation.py new file mode 100644 index 00000000000..c6625b2d209 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/Qt3DAnimation.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides Qt3DAnimation classes and functions.""" + +# Local imports +from . import PYQT5, PYSIDE2, PythonQtError, PYSIDE_VERSION +from .py3compat import PY2 + +if PYQT5: + from PyQt5.Qt3DAnimation import * +elif PYSIDE2: + if not PY2 or (PY2 and PYSIDE_VERSION < '5.12.4'): + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide2.Qt3DAnimation as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DAnimation): + globals()[__name[0]] = __name[1] + else: + raise PythonQtError('A bug in Shiboken prevents this') +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/openpype/vendor/python/python_2/qtpy/Qt3DCore.py b/openpype/vendor/python/python_2/qtpy/Qt3DCore.py new file mode 100644 index 00000000000..523e1deda78 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/Qt3DCore.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides Qt3DCore classes and functions.""" + +# Local imports +from . import PYQT5, PYSIDE2, PythonQtError, PYSIDE_VERSION +from .py3compat import PY2 + +if PYQT5: + from PyQt5.Qt3DCore import * +elif PYSIDE2: + if not PY2 or (PY2 and PYSIDE_VERSION < '5.12.4'): + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide2.Qt3DCore as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DCore): + globals()[__name[0]] = __name[1] + else: + raise PythonQtError('A bug in Shiboken prevents this') +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/openpype/vendor/python/python_2/qtpy/Qt3DExtras.py b/openpype/vendor/python/python_2/qtpy/Qt3DExtras.py new file mode 100644 index 00000000000..4f3a9c13eee --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/Qt3DExtras.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides Qt3DExtras classes and functions.""" + +# Local imports +from . import PYQT5, PYSIDE2, PythonQtError, PYSIDE_VERSION +from .py3compat import PY2 + +if PYQT5: + from PyQt5.Qt3DExtras import * +elif PYSIDE2: + if not PY2 or (PY2 and PYSIDE_VERSION < '5.12.4'): + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide2.Qt3DExtras as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DExtras): + globals()[__name[0]] = __name[1] + else: + raise PythonQtError('A bug in Shiboken prevents this') +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/openpype/vendor/python/python_2/qtpy/Qt3DInput.py b/openpype/vendor/python/python_2/qtpy/Qt3DInput.py new file mode 100644 index 00000000000..87b9a96a46a --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/Qt3DInput.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides Qt3DInput classes and functions.""" + +# Local imports +from . import PYQT5, PYSIDE2, PythonQtError, PYSIDE_VERSION +from .py3compat import PY2 + +if PYQT5: + from PyQt5.Qt3DInput import * +elif PYSIDE2: + if not PY2 or (PY2 and PYSIDE_VERSION < '5.12.4'): + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide2.Qt3DInput as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DInput): + globals()[__name[0]] = __name[1] + else: + raise PythonQtError('A bug in Shiboken prevents this') +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/openpype/vendor/python/python_2/qtpy/Qt3DLogic.py b/openpype/vendor/python/python_2/qtpy/Qt3DLogic.py new file mode 100644 index 00000000000..d17f13671e2 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/Qt3DLogic.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides Qt3DLogic classes and functions.""" + +# Local imports +from . import PYQT5, PYSIDE2, PythonQtError, PYSIDE_VERSION +from .py3compat import PY2 + +if PYQT5: + from PyQt5.Qt3DLogic import * +elif PYSIDE2: + if not PY2 or (PY2 and PYSIDE_VERSION < '5.12.4'): + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide2.Qt3DLogic as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DLogic): + globals()[__name[0]] = __name[1] + else: + raise PythonQtError('A bug in Shiboken prevents this') +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/openpype/vendor/python/python_2/qtpy/Qt3DRender.py b/openpype/vendor/python/python_2/qtpy/Qt3DRender.py new file mode 100644 index 00000000000..f30331ae170 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/Qt3DRender.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides Qt3DRender classes and functions.""" + +# Local imports +from . import PYQT5, PYSIDE2, PythonQtError, PYSIDE_VERSION +from .py3compat import PY2 + +if PYQT5: + from PyQt5.Qt3DRender import * +elif PYSIDE2: + if not PY2 or (PY2 and PYSIDE_VERSION < '5.12.4'): + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide2.Qt3DRender as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DRender): + globals()[__name[0]] = __name[1] + else: + raise PythonQtError('A bug in Shiboken prevents this') +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/openpype/vendor/python/python_2/qtpy/QtCharts.py b/openpype/vendor/python/python_2/qtpy/QtCharts.py new file mode 100644 index 00000000000..74671230f8d --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/QtCharts.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2019- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtChart classes and functions.""" + +# Local imports +from . import PYQT5, PYSIDE2, PythonQtError + +if PYQT5: + try: + from PyQt5 import QtChart as QtCharts + except ImportError: + raise PythonQtError('The QtChart module was not found. ' + 'It needs to be installed separately for PyQt5.') +elif PYSIDE2: + from PySide2.QtCharts import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/openpype/vendor/python/python_2/qtpy/QtCore.py b/openpype/vendor/python/python_2/qtpy/QtCore.py new file mode 100644 index 00000000000..d4bbd0d3eac --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/QtCore.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- +# +# Copyright © 2014-2015 Colin Duquesnoy +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) + +""" +Provides QtCore classes and functions. +""" + +from . import PYQT5, PYSIDE2, PYQT4, PYSIDE, PythonQtError + + +if PYQT5: + from PyQt5.QtCore import * + from PyQt5.QtCore import pyqtSignal as Signal + from PyQt5.QtCore import pyqtBoundSignal as SignalInstance + from PyQt5.QtCore import pyqtSlot as Slot + from PyQt5.QtCore import pyqtProperty as Property + from PyQt5.QtCore import QT_VERSION_STR as __version__ + + # For issue #153 + from PyQt5.QtCore import QDateTime + QDateTime.toPython = QDateTime.toPyDateTime + + # Those are imported from `import *` + del pyqtSignal, pyqtBoundSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR +elif PYSIDE2: + from PySide2.QtCore import * + + try: # may be limited to PySide-5.11a1 only + from PySide2.QtGui import QStringListModel + except: + pass + + import PySide2.QtCore + __version__ = PySide2.QtCore.__version__ +elif PYQT4: + from PyQt4.QtCore import * + # Those are things we inherited from Spyder that fix crazy crashes under + # some specific situations. (See #34) + from PyQt4.QtCore import QCoreApplication + from PyQt4.QtCore import Qt + from PyQt4.QtCore import pyqtSignal as Signal + from PyQt4.QtCore import pyqtBoundSignal as SignalInstance + from PyQt4.QtCore import pyqtSlot as Slot + from PyQt4.QtCore import pyqtProperty as Property + from PyQt4.QtGui import (QItemSelection, QItemSelectionModel, + QItemSelectionRange, QSortFilterProxyModel, + QStringListModel) + from PyQt4.QtCore import QT_VERSION_STR as __version__ + from PyQt4.QtCore import qInstallMsgHandler as qInstallMessageHandler + + # QDesktopServices has has been split into (QDesktopServices and + # QStandardPaths) in Qt5 + # This creates a dummy class that emulates QStandardPaths + from PyQt4.QtGui import QDesktopServices as _QDesktopServices + + class QStandardPaths(): + StandardLocation = _QDesktopServices.StandardLocation + displayName = _QDesktopServices.displayName + DesktopLocation = _QDesktopServices.DesktopLocation + DocumentsLocation = _QDesktopServices.DocumentsLocation + FontsLocation = _QDesktopServices.FontsLocation + ApplicationsLocation = _QDesktopServices.ApplicationsLocation + MusicLocation = _QDesktopServices.MusicLocation + MoviesLocation = _QDesktopServices.MoviesLocation + PicturesLocation = _QDesktopServices.PicturesLocation + TempLocation = _QDesktopServices.TempLocation + HomeLocation = _QDesktopServices.HomeLocation + DataLocation = _QDesktopServices.DataLocation + CacheLocation = _QDesktopServices.CacheLocation + writableLocation = _QDesktopServices.storageLocation + + # Those are imported from `import *` + del pyqtSignal, pyqtBoundSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR, qInstallMsgHandler +elif PYSIDE: + from PySide.QtCore import * + from PySide.QtGui import (QItemSelection, QItemSelectionModel, + QItemSelectionRange, QSortFilterProxyModel, + QStringListModel) + from PySide.QtCore import qInstallMsgHandler as qInstallMessageHandler + del qInstallMsgHandler + + # QDesktopServices has has been split into (QDesktopServices and + # QStandardPaths) in Qt5 + # This creates a dummy class that emulates QStandardPaths + from PySide.QtGui import QDesktopServices as _QDesktopServices + + class QStandardPaths(): + StandardLocation = _QDesktopServices.StandardLocation + displayName = _QDesktopServices.displayName + DesktopLocation = _QDesktopServices.DesktopLocation + DocumentsLocation = _QDesktopServices.DocumentsLocation + FontsLocation = _QDesktopServices.FontsLocation + ApplicationsLocation = _QDesktopServices.ApplicationsLocation + MusicLocation = _QDesktopServices.MusicLocation + MoviesLocation = _QDesktopServices.MoviesLocation + PicturesLocation = _QDesktopServices.PicturesLocation + TempLocation = _QDesktopServices.TempLocation + HomeLocation = _QDesktopServices.HomeLocation + DataLocation = _QDesktopServices.DataLocation + CacheLocation = _QDesktopServices.CacheLocation + writableLocation = _QDesktopServices.storageLocation + + import PySide.QtCore + __version__ = PySide.QtCore.__version__ +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/openpype/vendor/python/python_2/qtpy/QtDataVisualization.py b/openpype/vendor/python/python_2/qtpy/QtDataVisualization.py new file mode 100644 index 00000000000..cfb2b3b6b90 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/QtDataVisualization.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtDataVisualization classes and functions.""" + +# Local imports +from . import PYQT5, PYSIDE2, PythonQtError + +if PYQT5: + from PyQt5.QtDataVisualization import * +elif PYSIDE2: + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide2.QtDataVisualization as __temp + import inspect + for __name in inspect.getmembers(__temp.QtDataVisualization): + globals()[__name[0]] = __name[1] +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/openpype/vendor/python/python_2/qtpy/QtDesigner.py b/openpype/vendor/python/python_2/qtpy/QtDesigner.py new file mode 100644 index 00000000000..4aaafc815a6 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/QtDesigner.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# +# Copyright © 2014-2015 Colin Duquesnoy +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) + +""" +Provides QtDesigner classes and functions. +""" + +from . import PYQT5, PYQT4, PythonQtError + + +if PYQT5: + from PyQt5.QtDesigner import * +elif PYQT4: + from PyQt4.QtDesigner import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/openpype/vendor/python/python_2/qtpy/QtGui.py b/openpype/vendor/python/python_2/qtpy/QtGui.py new file mode 100644 index 00000000000..be8f5688653 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/QtGui.py @@ -0,0 +1,161 @@ +# -*- coding: utf-8 -*- +# +# Copyright © 2014-2015 Colin Duquesnoy +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) + +""" +Provides QtGui classes and functions. +.. warning:: Only PyQt4/PySide QtGui classes compatible with PyQt5.QtGui are + exposed here. Therefore, you need to treat/use this package as if it were + the ``PyQt5.QtGui`` module. +""" +import warnings + +from . import PYQT5, PYQT4, PYSIDE, PYSIDE2, PythonQtError + + +if PYQT5: + from PyQt5.QtGui import * +elif PYSIDE2: + from PySide2.QtGui import * +elif PYQT4: + try: + # Older versions of PyQt4 do not provide these + from PyQt4.QtGui import (QGlyphRun, QMatrix2x2, QMatrix2x3, + QMatrix2x4, QMatrix3x2, QMatrix3x3, + QMatrix3x4, QMatrix4x2, QMatrix4x3, + QMatrix4x4, QTouchEvent, QQuaternion, + QRadialGradient, QRawFont, QStaticText, + QVector2D, QVector3D, QVector4D, + qFuzzyCompare) + except ImportError: + pass + try: + from PyQt4.Qt import QKeySequence, QTextCursor + except ImportError: + # In PyQt4-sip 4.19.13 QKeySequence and QTextCursor are in PyQt4.QtGui + from PyQt4.QtGui import QKeySequence, QTextCursor + from PyQt4.QtGui import (QAbstractTextDocumentLayout, QActionEvent, QBitmap, + QBrush, QClipboard, QCloseEvent, QColor, + QConicalGradient, QContextMenuEvent, QCursor, + QDoubleValidator, QDrag, + QDragEnterEvent, QDragLeaveEvent, QDragMoveEvent, + QDropEvent, QFileOpenEvent, QFocusEvent, QFont, + QFontDatabase, QFontInfo, QFontMetrics, + QFontMetricsF, QGradient, QHelpEvent, + QHideEvent, QHoverEvent, QIcon, QIconDragEvent, + QIconEngine, QImage, QImageIOHandler, QImageReader, + QImageWriter, QInputEvent, QInputMethodEvent, + QKeyEvent, QLinearGradient, + QMouseEvent, QMoveEvent, QMovie, + QPaintDevice, QPaintEngine, QPaintEngineState, + QPaintEvent, QPainter, QPainterPath, + QPainterPathStroker, QPalette, QPen, QPicture, + QPictureIO, QPixmap, QPixmapCache, QPolygon, + QPolygonF, QRegExpValidator, QRegion, QResizeEvent, + QSessionManager, QShortcutEvent, QShowEvent, + QStandardItem, QStandardItemModel, + QStatusTipEvent, QSyntaxHighlighter, QTabletEvent, + QTextBlock, QTextBlockFormat, QTextBlockGroup, + QTextBlockUserData, QTextCharFormat, + QTextDocument, QTextDocumentFragment, + QTextDocumentWriter, QTextFormat, QTextFragment, + QTextFrame, QTextFrameFormat, QTextImageFormat, + QTextInlineObject, QTextItem, QTextLayout, + QTextLength, QTextLine, QTextList, QTextListFormat, + QTextObject, QTextObjectInterface, QTextOption, + QTextTable, QTextTableCell, QTextTableCellFormat, + QTextTableFormat, QTransform, + QValidator, QWhatsThisClickedEvent, QWheelEvent, + QWindowStateChangeEvent, qAlpha, qBlue, + qGray, qGreen, qIsGray, qRed, qRgb, + qRgba, QIntValidator) + + # QDesktopServices has has been split into (QDesktopServices and + # QStandardPaths) in Qt5 + # It only exposes QDesktopServices that are still in pyqt5 + from PyQt4.QtGui import QDesktopServices as _QDesktopServices + + class QDesktopServices(): + openUrl = _QDesktopServices.openUrl + setUrlHandler = _QDesktopServices.setUrlHandler + unsetUrlHandler = _QDesktopServices.unsetUrlHandler + + def __getattr__(self, name): + attr = getattr(_QDesktopServices, name) + + new_name = name + if name == 'storageLocation': + new_name = 'writableLocation' + warnings.warn(("Warning QDesktopServices.{} is deprecated in Qt5" + "we recommend you use QDesktopServices.{} instead").format(name, new_name), + DeprecationWarning) + return attr + QDesktopServices = QDesktopServices() + +elif PYSIDE: + from PySide.QtGui import (QAbstractTextDocumentLayout, QActionEvent, QBitmap, + QBrush, QClipboard, QCloseEvent, QColor, + QConicalGradient, QContextMenuEvent, QCursor, + QDoubleValidator, QDrag, + QDragEnterEvent, QDragLeaveEvent, QDragMoveEvent, + QDropEvent, QFileOpenEvent, QFocusEvent, QFont, + QFontDatabase, QFontInfo, QFontMetrics, + QFontMetricsF, QGradient, QHelpEvent, + QHideEvent, QHoverEvent, QIcon, QIconDragEvent, + QIconEngine, QImage, QImageIOHandler, QImageReader, + QImageWriter, QInputEvent, QInputMethodEvent, + QKeyEvent, QKeySequence, QLinearGradient, + QMatrix2x2, QMatrix2x3, QMatrix2x4, QMatrix3x2, + QMatrix3x3, QMatrix3x4, QMatrix4x2, QMatrix4x3, + QMatrix4x4, QMouseEvent, QMoveEvent, QMovie, + QPaintDevice, QPaintEngine, QPaintEngineState, + QPaintEvent, QPainter, QPainterPath, + QPainterPathStroker, QPalette, QPen, QPicture, + QPictureIO, QPixmap, QPixmapCache, QPolygon, + QPolygonF, QQuaternion, QRadialGradient, + QRegExpValidator, QRegion, QResizeEvent, + QSessionManager, QShortcutEvent, QShowEvent, + QStandardItem, QStandardItemModel, + QStatusTipEvent, QSyntaxHighlighter, QTabletEvent, + QTextBlock, QTextBlockFormat, QTextBlockGroup, + QTextBlockUserData, QTextCharFormat, QTextCursor, + QTextDocument, QTextDocumentFragment, + QTextFormat, QTextFragment, + QTextFrame, QTextFrameFormat, QTextImageFormat, + QTextInlineObject, QTextItem, QTextLayout, + QTextLength, QTextLine, QTextList, QTextListFormat, + QTextObject, QTextObjectInterface, QTextOption, + QTextTable, QTextTableCell, QTextTableCellFormat, + QTextTableFormat, QTouchEvent, QTransform, + QValidator, QVector2D, QVector3D, QVector4D, + QWhatsThisClickedEvent, QWheelEvent, + QWindowStateChangeEvent, qAlpha, qBlue, + qGray, qGreen, qIsGray, qRed, qRgb, qRgba, + QIntValidator) + # QDesktopServices has has been split into (QDesktopServices and + # QStandardPaths) in Qt5 + # It only exposes QDesktopServices that are still in pyqt5 + from PySide.QtGui import QDesktopServices as _QDesktopServices + + class QDesktopServices(): + openUrl = _QDesktopServices.openUrl + setUrlHandler = _QDesktopServices.setUrlHandler + unsetUrlHandler = _QDesktopServices.unsetUrlHandler + + def __getattr__(self, name): + attr = getattr(_QDesktopServices, name) + + new_name = name + if name == 'storageLocation': + new_name = 'writableLocation' + warnings.warn(("Warning QDesktopServices.{} is deprecated in Qt5" + "we recommend you use QDesktopServices.{} instead").format(name, new_name), + DeprecationWarning) + return attr + QDesktopServices = QDesktopServices() +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/openpype/vendor/python/python_2/qtpy/QtHelp.py b/openpype/vendor/python/python_2/qtpy/QtHelp.py new file mode 100644 index 00000000000..ca9d93ddee9 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/QtHelp.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) + +"""QtHelp Wrapper.""" + +import warnings + +from . import PYQT5 +from . import PYQT4 +from . import PYSIDE +from . import PYSIDE2 + +if PYQT5: + from PyQt5.QtHelp import * +elif PYSIDE2: + from PySide2.QtHelp import * +elif PYQT4: + from PyQt4.QtHelp import * +elif PYSIDE: + from PySide.QtHelp import * diff --git a/openpype/vendor/python/python_2/qtpy/QtLocation.py b/openpype/vendor/python/python_2/qtpy/QtLocation.py new file mode 100644 index 00000000000..9dfe874ae57 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/QtLocation.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtLocation classes and functions.""" + +# Local imports +from . import PYQT5, PYSIDE2, PythonQtError + +if PYQT5: + from PyQt5.QtLocation import * +elif PYSIDE2: + from PySide2.QtLocation import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/openpype/vendor/python/python_2/qtpy/QtMultimedia.py b/openpype/vendor/python/python_2/qtpy/QtMultimedia.py new file mode 100644 index 00000000000..9015ece9c16 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/QtMultimedia.py @@ -0,0 +1,17 @@ +import warnings + +from . import PYQT5 +from . import PYQT4 +from . import PYSIDE +from . import PYSIDE2 + +if PYQT5: + from PyQt5.QtMultimedia import * +elif PYSIDE2: + from PySide2.QtMultimedia import * +elif PYQT4: + from PyQt4.QtMultimedia import * + from PyQt4.QtGui import QSound +elif PYSIDE: + from PySide.QtMultimedia import * + from PySide.QtGui import QSound diff --git a/openpype/vendor/python/python_2/qtpy/QtMultimediaWidgets.py b/openpype/vendor/python/python_2/qtpy/QtMultimediaWidgets.py new file mode 100644 index 00000000000..697845d9c82 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/QtMultimediaWidgets.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtMultimediaWidgets classes and functions.""" + +# Local imports +from . import PYSIDE2, PYQT5, PythonQtError + +if PYQT5: + from PyQt5.QtMultimediaWidgets import * +elif PYSIDE2: + from PySide2.QtMultimediaWidgets import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/openpype/vendor/python/python_2/qtpy/QtNetwork.py b/openpype/vendor/python/python_2/qtpy/QtNetwork.py new file mode 100644 index 00000000000..49faded7963 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/QtNetwork.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# +# Copyright © 2014-2015 Colin Duquesnoy +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) + +""" +Provides QtNetwork classes and functions. +""" + +from . import PYQT5, PYSIDE2, PYQT4, PYSIDE, PythonQtError + + +if PYQT5: + from PyQt5.QtNetwork import * +elif PYSIDE2: + from PySide2.QtNetwork import * +elif PYQT4: + from PyQt4.QtNetwork import * +elif PYSIDE: + from PySide.QtNetwork import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/openpype/vendor/python/python_2/qtpy/QtOpenGL.py b/openpype/vendor/python/python_2/qtpy/QtOpenGL.py new file mode 100644 index 00000000000..69ef82280d1 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/QtOpenGL.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtOpenGL classes and functions.""" + +# Local imports +from . import PYQT4, PYQT5, PYSIDE, PYSIDE2, PythonQtError + +if PYQT5: + from PyQt5.QtOpenGL import * +elif PYSIDE2: + from PySide2.QtOpenGL import * +elif PYQT4: + from PyQt4.QtOpenGL import * +elif PYSIDE: + from PySide.QtOpenGL import * +else: + raise PythonQtError('No Qt bindings could be found') + +del PYQT4, PYQT5, PYSIDE, PYSIDE2 diff --git a/openpype/vendor/python/python_2/qtpy/QtPositioning.py b/openpype/vendor/python/python_2/qtpy/QtPositioning.py new file mode 100644 index 00000000000..2b46d356897 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/QtPositioning.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright 2020 Antonio Valentino +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtPositioning classes and functions.""" + +# Local imports +from . import PYQT5, PYSIDE2, PythonQtError + +if PYQT5: + from PyQt5.QtPositioning import * +elif PYSIDE2: + from PySide2.QtPositioning import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/openpype/vendor/python/python_2/qtpy/QtPrintSupport.py b/openpype/vendor/python/python_2/qtpy/QtPrintSupport.py new file mode 100644 index 00000000000..b821d4118cf --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/QtPrintSupport.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) + +""" +Provides QtPrintSupport classes and functions. +""" + +from . import PYQT5, PYQT4,PYSIDE2, PYSIDE, PythonQtError + + +if PYQT5: + from PyQt5.QtPrintSupport import * +elif PYSIDE2: + from PySide2.QtPrintSupport import * +elif PYQT4: + from PyQt4.QtGui import (QAbstractPrintDialog, QPageSetupDialog, + QPrintDialog, QPrintEngine, QPrintPreviewDialog, + QPrintPreviewWidget, QPrinter, QPrinterInfo) +elif PYSIDE: + from PySide.QtGui import (QAbstractPrintDialog, QPageSetupDialog, + QPrintDialog, QPrintEngine, QPrintPreviewDialog, + QPrintPreviewWidget, QPrinter, QPrinterInfo) +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/openpype/vendor/python/python_2/qtpy/QtQml.py b/openpype/vendor/python/python_2/qtpy/QtQml.py new file mode 100644 index 00000000000..117f977f2b1 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/QtQml.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtQml classes and functions.""" + +# Local imports +from . import PYQT5, PYSIDE2, PythonQtError + +if PYQT5: + from PyQt5.QtQml import * +elif PYSIDE2: + from PySide2.QtQml import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/openpype/vendor/python/python_2/qtpy/QtQuick.py b/openpype/vendor/python/python_2/qtpy/QtQuick.py new file mode 100644 index 00000000000..8291066724d --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/QtQuick.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtQuick classes and functions.""" + +# Local imports +from . import PYQT5, PYSIDE2, PythonQtError + +if PYQT5: + from PyQt5.QtQuick import * +elif PYSIDE2: + from PySide2.QtQuick import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/openpype/vendor/python/python_2/qtpy/QtQuickWidgets.py b/openpype/vendor/python/python_2/qtpy/QtQuickWidgets.py new file mode 100644 index 00000000000..545d52b6811 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/QtQuickWidgets.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtQuickWidgets classes and functions.""" + +# Local imports +from . import PYQT5, PYSIDE2, PythonQtError + +if PYQT5: + from PyQt5.QtQuickWidgets import * +elif PYSIDE2: + from PySide2.QtQuickWidgets import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/openpype/vendor/python/python_2/qtpy/QtSerialPort.py b/openpype/vendor/python/python_2/qtpy/QtSerialPort.py new file mode 100644 index 00000000000..26fcae180e1 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/QtSerialPort.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2020 Marcin Stano +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtSerialPort classes and functions.""" + +# Local imports +from . import PYQT5, PythonQtError + +if PYQT5: + from PyQt5.QtSerialPort import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/openpype/vendor/python/python_2/qtpy/QtSql.py b/openpype/vendor/python/python_2/qtpy/QtSql.py new file mode 100644 index 00000000000..98520bef513 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/QtSql.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtSql classes and functions.""" + +# Local imports +from . import PYQT5, PYSIDE2, PYQT4, PYSIDE, PythonQtError + +if PYQT5: + from PyQt5.QtSql import * +elif PYSIDE2: + from PySide2.QtSql import * +elif PYQT4: + from PyQt4.QtSql import * +elif PYSIDE: + from PySide.QtSql import * +else: + raise PythonQtError('No Qt bindings could be found') + +del PYQT4, PYQT5, PYSIDE, PYSIDE2 diff --git a/openpype/vendor/python/python_2/qtpy/QtSvg.py b/openpype/vendor/python/python_2/qtpy/QtSvg.py new file mode 100644 index 00000000000..edc075eac83 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/QtSvg.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtSvg classes and functions.""" + +# Local imports +from . import PYQT4, PYSIDE2, PYQT5, PYSIDE, PythonQtError + +if PYQT5: + from PyQt5.QtSvg import * +elif PYSIDE2: + from PySide2.QtSvg import * +elif PYQT4: + from PyQt4.QtSvg import * +elif PYSIDE: + from PySide.QtSvg import * +else: + raise PythonQtError('No Qt bindings could be found') + +del PYQT4, PYQT5, PYSIDE, PYSIDE2 diff --git a/openpype/vendor/python/python_2/qtpy/QtTest.py b/openpype/vendor/python/python_2/qtpy/QtTest.py new file mode 100644 index 00000000000..cca5e192288 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/QtTest.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# +# Copyright © 2014-2015 Colin Duquesnoy +# Copyright © 2009- The Spyder Developmet Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) + +""" +Provides QtTest and functions +""" + +from . import PYQT5,PYSIDE2, PYQT4, PYSIDE, PythonQtError + + +if PYQT5: + from PyQt5.QtTest import QTest +elif PYSIDE2: + from PySide2.QtTest import QTest +elif PYQT4: + from PyQt4.QtTest import QTest as OldQTest + + class QTest(OldQTest): + @staticmethod + def qWaitForWindowActive(QWidget): + OldQTest.qWaitForWindowShown(QWidget) +elif PYSIDE: + from PySide.QtTest import QTest +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/openpype/vendor/python/python_2/qtpy/QtWebChannel.py b/openpype/vendor/python/python_2/qtpy/QtWebChannel.py new file mode 100644 index 00000000000..2862a0569c8 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/QtWebChannel.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtWebChannel classes and functions.""" + +# Local imports +from . import PYSIDE2, PYQT5, PythonQtError + +if PYQT5: + from PyQt5.QtWebChannel import * +elif PYSIDE2: + from PySide2.QtWebChannel import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/openpype/vendor/python/python_2/qtpy/QtWebEngineWidgets.py b/openpype/vendor/python/python_2/qtpy/QtWebEngineWidgets.py new file mode 100644 index 00000000000..33a66575c50 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/QtWebEngineWidgets.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +# +# Copyright © 2014-2015 Colin Duquesnoy +# Copyright © 2009- The Spyder development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) + +""" +Provides QtWebEngineWidgets classes and functions. +""" + +from . import PYQT5,PYSIDE2, PYQT4, PYSIDE, PythonQtError + + +# To test if we are using WebEngine or WebKit +WEBENGINE = True + + +if PYQT5: + try: + from PyQt5.QtWebEngineWidgets import QWebEnginePage + from PyQt5.QtWebEngineWidgets import QWebEngineView + from PyQt5.QtWebEngineWidgets import QWebEngineSettings + # Based on the work at https://github.com/spyder-ide/qtpy/pull/203 + from PyQt5.QtWebEngineWidgets import QWebEngineProfile + except ImportError: + from PyQt5.QtWebKitWidgets import QWebPage as QWebEnginePage + from PyQt5.QtWebKitWidgets import QWebView as QWebEngineView + from PyQt5.QtWebKit import QWebSettings as QWebEngineSettings + WEBENGINE = False +elif PYSIDE2: + from PySide2.QtWebEngineWidgets import QWebEnginePage + from PySide2.QtWebEngineWidgets import QWebEngineView + from PySide2.QtWebEngineWidgets import QWebEngineSettings + # Based on the work at https://github.com/spyder-ide/qtpy/pull/203 + from PySide2.QtWebEngineWidgets import QWebEngineProfile +elif PYQT4: + from PyQt4.QtWebKit import QWebPage as QWebEnginePage + from PyQt4.QtWebKit import QWebView as QWebEngineView + from PyQt4.QtWebKit import QWebSettings as QWebEngineSettings + WEBENGINE = False +elif PYSIDE: + from PySide.QtWebKit import QWebPage as QWebEnginePage + from PySide.QtWebKit import QWebView as QWebEngineView + from PySide.QtWebKit import QWebSettings as QWebEngineSettings + WEBENGINE = False +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/openpype/vendor/python/python_2/qtpy/QtWebSockets.py b/openpype/vendor/python/python_2/qtpy/QtWebSockets.py new file mode 100644 index 00000000000..4b6a8204c92 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/QtWebSockets.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtWebSockets classes and functions.""" + +# Local imports +from . import PYSIDE2, PYQT5, PythonQtError + +if PYQT5: + from PyQt5.QtWebSockets import * +elif PYSIDE2: + from PySide2.QtWebSockets import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/openpype/vendor/python/python_2/qtpy/QtWidgets.py b/openpype/vendor/python/python_2/qtpy/QtWidgets.py new file mode 100644 index 00000000000..66ef3abad85 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/QtWidgets.py @@ -0,0 +1,135 @@ +# -*- coding: utf-8 -*- +# +# Copyright © 2014-2015 Colin Duquesnoy +# Copyright © 2009- The Spyder Developmet Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) + +""" +Provides widget classes and functions. +.. warning:: Only PyQt4/PySide QtGui classes compatible with PyQt5.QtWidgets + are exposed here. Therefore, you need to treat/use this package as if it + were the ``PyQt5.QtWidgets`` module. +""" + +from . import PYQT5, PYSIDE2, PYQT4, PYSIDE, PythonQtError +from ._patch.qcombobox import patch_qcombobox +from ._patch.qheaderview import introduce_renamed_methods_qheaderview + + +if PYQT5: + from PyQt5.QtWidgets import * +elif PYSIDE2: + from PySide2.QtWidgets import * +elif PYQT4: + from PyQt4.QtGui import * + QStyleOptionViewItem = QStyleOptionViewItemV4 + del QStyleOptionViewItemV4 + QStyleOptionFrame = QStyleOptionFrameV3 + del QStyleOptionFrameV3 + + # These objects belong to QtGui + try: + # Older versions of PyQt4 do not provide these + del (QGlyphRun, + QMatrix2x2, QMatrix2x3, QMatrix2x4, QMatrix3x2, QMatrix3x3, + QMatrix3x4, QMatrix4x2, QMatrix4x3, QMatrix4x4, + QQuaternion, QRadialGradient, QRawFont, QRegExpValidator, + QStaticText, QTouchEvent, QVector2D, QVector3D, QVector4D, + qFuzzyCompare) + except NameError: + pass + del (QAbstractTextDocumentLayout, QActionEvent, QBitmap, QBrush, QClipboard, + QCloseEvent, QColor, QConicalGradient, QContextMenuEvent, QCursor, + QDesktopServices, QDoubleValidator, QDrag, QDragEnterEvent, + QDragLeaveEvent, QDragMoveEvent, QDropEvent, QFileOpenEvent, + QFocusEvent, QFont, QFontDatabase, QFontInfo, QFontMetrics, + QFontMetricsF, QGradient, QHelpEvent, QHideEvent, + QHoverEvent, QIcon, QIconDragEvent, QIconEngine, QImage, + QImageIOHandler, QImageReader, QImageWriter, QInputEvent, + QInputMethodEvent, QKeyEvent, QKeySequence, QLinearGradient, + QMouseEvent, QMoveEvent, QMovie, QPaintDevice, QPaintEngine, + QPaintEngineState, QPaintEvent, QPainter, QPainterPath, + QPainterPathStroker, QPalette, QPen, QPicture, QPictureIO, QPixmap, + QPixmapCache, QPolygon, QPolygonF, + QRegion, QResizeEvent, QSessionManager, QShortcutEvent, QShowEvent, + QStandardItem, QStandardItemModel, QStatusTipEvent, + QSyntaxHighlighter, QTabletEvent, QTextBlock, QTextBlockFormat, + QTextBlockGroup, QTextBlockUserData, QTextCharFormat, QTextCursor, + QTextDocument, QTextDocumentFragment, QTextDocumentWriter, + QTextFormat, QTextFragment, QTextFrame, QTextFrameFormat, + QTextImageFormat, QTextInlineObject, QTextItem, QTextLayout, + QTextLength, QTextLine, QTextList, QTextListFormat, QTextObject, + QTextObjectInterface, QTextOption, QTextTable, QTextTableCell, + QTextTableCellFormat, QTextTableFormat, QTransform, + QValidator, QWhatsThisClickedEvent, + QWheelEvent, QWindowStateChangeEvent, qAlpha, qBlue, + qGray, qGreen, qIsGray, qRed, qRgb, qRgba, QIntValidator, + QStringListModel) + + # These objects belong to QtPrintSupport + del (QAbstractPrintDialog, QPageSetupDialog, QPrintDialog, QPrintEngine, + QPrintPreviewDialog, QPrintPreviewWidget, QPrinter, QPrinterInfo) + + # These objects belong to QtCore + del (QItemSelection, QItemSelectionModel, QItemSelectionRange, + QSortFilterProxyModel) + + # Patch QComboBox to allow Python objects to be passed to userData + patch_qcombobox(QComboBox) + + # QHeaderView: renamed methods + introduce_renamed_methods_qheaderview(QHeaderView) + +elif PYSIDE: + from PySide.QtGui import * + QStyleOptionViewItem = QStyleOptionViewItemV4 + del QStyleOptionViewItemV4 + + # These objects belong to QtGui + del (QAbstractTextDocumentLayout, QActionEvent, QBitmap, QBrush, QClipboard, + QCloseEvent, QColor, QConicalGradient, QContextMenuEvent, QCursor, + QDesktopServices, QDoubleValidator, QDrag, QDragEnterEvent, + QDragLeaveEvent, QDragMoveEvent, QDropEvent, QFileOpenEvent, + QFocusEvent, QFont, QFontDatabase, QFontInfo, QFontMetrics, + QFontMetricsF, QGradient, QHelpEvent, QHideEvent, + QHoverEvent, QIcon, QIconDragEvent, QIconEngine, QImage, + QImageIOHandler, QImageReader, QImageWriter, QInputEvent, + QInputMethodEvent, QKeyEvent, QKeySequence, QLinearGradient, + QMatrix2x2, QMatrix2x3, QMatrix2x4, QMatrix3x2, QMatrix3x3, + QMatrix3x4, QMatrix4x2, QMatrix4x3, QMatrix4x4, QMouseEvent, + QMoveEvent, QMovie, QPaintDevice, QPaintEngine, QPaintEngineState, + QPaintEvent, QPainter, QPainterPath, QPainterPathStroker, QPalette, + QPen, QPicture, QPictureIO, QPixmap, QPixmapCache, QPolygon, + QPolygonF, QQuaternion, QRadialGradient, QRegExpValidator, + QRegion, QResizeEvent, QSessionManager, QShortcutEvent, QShowEvent, + QStandardItem, QStandardItemModel, QStatusTipEvent, + QSyntaxHighlighter, QTabletEvent, QTextBlock, QTextBlockFormat, + QTextBlockGroup, QTextBlockUserData, QTextCharFormat, QTextCursor, + QTextDocument, QTextDocumentFragment, + QTextFormat, QTextFragment, QTextFrame, QTextFrameFormat, + QTextImageFormat, QTextInlineObject, QTextItem, QTextLayout, + QTextLength, QTextLine, QTextList, QTextListFormat, QTextObject, + QTextObjectInterface, QTextOption, QTextTable, QTextTableCell, + QTextTableCellFormat, QTextTableFormat, QTouchEvent, QTransform, + QValidator, QVector2D, QVector3D, QVector4D, QWhatsThisClickedEvent, + QWheelEvent, QWindowStateChangeEvent, qAlpha, qBlue, qGray, qGreen, + qIsGray, qRed, qRgb, qRgba, QIntValidator, QStringListModel) + + # These objects belong to QtPrintSupport + del (QAbstractPrintDialog, QPageSetupDialog, QPrintDialog, QPrintEngine, + QPrintPreviewDialog, QPrintPreviewWidget, QPrinter, QPrinterInfo) + + # These objects belong to QtCore + del (QItemSelection, QItemSelectionModel, QItemSelectionRange, + QSortFilterProxyModel) + + # Patch QComboBox to allow Python objects to be passed to userData + patch_qcombobox(QComboBox) + + # QHeaderView: renamed methods + introduce_renamed_methods_qheaderview(QHeaderView) + +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/openpype/vendor/python/python_2/qtpy/QtWinExtras.py b/openpype/vendor/python/python_2/qtpy/QtWinExtras.py new file mode 100644 index 00000000000..c033ff98859 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/QtWinExtras.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) + +from . import PYQT5, PYSIDE2, PythonQtError + + +if PYQT5: + from PyQt5.QtWinExtras import * +elif PYSIDE2: + from PySide2.QtWinExtras import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/openpype/vendor/python/python_2/qtpy/QtXmlPatterns.py b/openpype/vendor/python/python_2/qtpy/QtXmlPatterns.py new file mode 100644 index 00000000000..b41e13df7f2 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/QtXmlPatterns.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtXmlPatterns classes and functions.""" + +# Local imports +from . import PYQT4, PYSIDE2, PYQT5, PYSIDE, PythonQtError + +if PYQT5: + from PyQt5.QtXmlPatterns import * +elif PYSIDE2: + from PySide2.QtXmlPatterns import * +elif PYQT4: + from PyQt4.QtXmlPatterns import * +elif PYSIDE: + from PySide.QtXmlPatterns import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/openpype/vendor/python/python_2/qtpy/__init__.py b/openpype/vendor/python/python_2/qtpy/__init__.py new file mode 100644 index 00000000000..6d978ae3735 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/__init__.py @@ -0,0 +1,278 @@ +# -*- coding: utf-8 -*- +# +# Copyright © 2009- The Spyder Development Team +# Copyright © 2014-2015 Colin Duquesnoy +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) + +""" +**QtPy** is a shim over the various Python Qt bindings. It is used to write +Qt binding independent libraries or applications. + +If one of the APIs has already been imported, then it will be used. + +Otherwise, the shim will automatically select the first available API (PyQt5, +PySide2, PyQt4 and finally PySide); in that case, you can force the use of one +specific bindings (e.g. if your application is using one specific bindings and +you need to use library that use QtPy) by setting up the ``QT_API`` environment +variable. + +PyQt5 +===== + +For PyQt5, you don't have to set anything as it will be used automatically:: + + >>> from qtpy import QtGui, QtWidgets, QtCore + >>> print(QtWidgets.QWidget) + + +PySide2 +====== + +Set the QT_API environment variable to 'pyside2' before importing other +packages:: + + >>> import os + >>> os.environ['QT_API'] = 'pyside2' + >>> from qtpy import QtGui, QtWidgets, QtCore + >>> print(QtWidgets.QWidget) + +PyQt4 +===== + +Set the ``QT_API`` environment variable to 'pyqt' before importing any python +package:: + + >>> import os + >>> os.environ['QT_API'] = 'pyqt' + >>> from qtpy import QtGui, QtWidgets, QtCore + >>> print(QtWidgets.QWidget) + +PySide +====== + +Set the QT_API environment variable to 'pyside' before importing other +packages:: + + >>> import os + >>> os.environ['QT_API'] = 'pyside' + >>> from qtpy import QtGui, QtWidgets, QtCore + >>> print(QtWidgets.QWidget) + +""" + +from distutils.version import LooseVersion +import os +import platform +import sys +import warnings + +# Version of QtPy +from ._version import __version__ +from .py3compat import PY2 + + +class PythonQtError(RuntimeError): + """Error raise if no bindings could be selected.""" + pass + + +class PythonQtWarning(Warning): + """Warning if some features are not implemented in a binding.""" + pass + + +# Qt API environment variable name +QT_API = 'QT_API' + +# Names of the expected PyQt5 api +PYQT5_API = ['pyqt5'] + +# Names of the expected PyQt4 api +PYQT4_API = [ + 'pyqt', # name used in IPython.qt + 'pyqt4' # pyqode.qt original name +] + +# Names of the expected PySide api +PYSIDE_API = ['pyside'] + +# Names of the expected PySide2 api +PYSIDE2_API = ['pyside2'] + +# Names of the legacy APIs that we should warn users about +LEGACY_APIS = PYQT4_API + PYSIDE_API + +# Minimum fully supported versions of Qt and the bindings +PYQT_VERSION_MIN = '5.9.0' +PYSIDE_VERSION_MIN = '5.12.0' +QT_VERSION_MIN = '5.9.0' + +# Detecting if a binding was specified by the user +binding_specified = QT_API in os.environ + +# Setting a default value for QT_API +os.environ.setdefault(QT_API, 'pyqt5') + +API = os.environ[QT_API].lower() +initial_api = API +assert API in (PYQT5_API + PYQT4_API + PYSIDE_API + PYSIDE2_API) + +is_old_pyqt = is_pyqt46 = False +PYQT5 = True +PYQT4 = PYSIDE = PYSIDE2 = False + +# When `FORCE_QT_API` is set, we disregard +# any previously imported python bindings. +if not os.environ.get('FORCE_QT_API'): + if 'PyQt5' in sys.modules: + API = initial_api if initial_api in PYQT5_API else 'pyqt5' + elif 'PySide2' in sys.modules: + API = initial_api if initial_api in PYSIDE2_API else 'pyside2' + elif 'PyQt4' in sys.modules: + API = initial_api if initial_api in PYQT4_API else 'pyqt4' + elif 'PySide' in sys.modules: + API = initial_api if initial_api in PYSIDE_API else 'pyside' + + +if API in PYQT5_API: + try: + from PyQt5.QtCore import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore + from PyQt5.QtCore import QT_VERSION_STR as QT_VERSION # analysis:ignore + PYSIDE_VERSION = None + + if sys.platform == 'darwin': + macos_version = LooseVersion(platform.mac_ver()[0]) + if macos_version < LooseVersion('10.10'): + if LooseVersion(QT_VERSION) >= LooseVersion('5.9'): + raise PythonQtError("Qt 5.9 or higher only works in " + "macOS 10.10 or higher. Your " + "program will fail in this " + "system.") + elif macos_version < LooseVersion('10.11'): + if LooseVersion(QT_VERSION) >= LooseVersion('5.11'): + raise PythonQtError("Qt 5.11 or higher only works in " + "macOS 10.11 or higher. Your " + "program will fail in this " + "system.") + + del macos_version + except ImportError: + API = os.environ['QT_API'] = 'pyside2' + +if API in PYSIDE2_API: + try: + from PySide2 import __version__ as PYSIDE_VERSION # analysis:ignore + from PySide2.QtCore import __version__ as QT_VERSION # analysis:ignore + + PYQT_VERSION = None + PYQT5 = False + PYSIDE2 = True + + if sys.platform == 'darwin': + macos_version = LooseVersion(platform.mac_ver()[0]) + if macos_version < LooseVersion('10.11'): + if LooseVersion(QT_VERSION) >= LooseVersion('5.11'): + raise PythonQtError("Qt 5.11 or higher only works in " + "macOS 10.11 or higher. Your " + "program will fail in this " + "system.") + + del macos_version + except ImportError: + API = os.environ['QT_API'] = 'pyqt' + +if API in PYQT4_API: + try: + import sip + try: + sip.setapi('QString', 2) + sip.setapi('QVariant', 2) + sip.setapi('QDate', 2) + sip.setapi('QDateTime', 2) + sip.setapi('QTextStream', 2) + sip.setapi('QTime', 2) + sip.setapi('QUrl', 2) + except (AttributeError, ValueError): + # PyQt < v4.6 + pass + try: + from PyQt4.Qt import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore + from PyQt4.Qt import QT_VERSION_STR as QT_VERSION # analysis:ignore + except ImportError: + # In PyQt4-sip 4.19.13 PYQT_VERSION_STR and QT_VERSION_STR are in PyQt4.QtCore + from PyQt4.QtCore import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore + from PyQt4.QtCore import QT_VERSION_STR as QT_VERSION # analysis:ignore + PYSIDE_VERSION = None + PYQT5 = False + PYQT4 = True + except ImportError: + API = os.environ['QT_API'] = 'pyside' + else: + is_old_pyqt = PYQT_VERSION.startswith(('4.4', '4.5', '4.6', '4.7')) + is_pyqt46 = PYQT_VERSION.startswith('4.6') + +if API in PYSIDE_API: + try: + from PySide import __version__ as PYSIDE_VERSION # analysis:ignore + from PySide.QtCore import __version__ as QT_VERSION # analysis:ignore + PYQT_VERSION = None + PYQT5 = PYSIDE2 = False + PYSIDE = True + except ImportError: + raise PythonQtError('No Qt bindings could be found') + +# If a correct API name is passed to QT_API and it could not be found, +# switches to another and informs through the warning +if API != initial_api and binding_specified: + warnings.warn('Selected binding "{}" could not be found, ' + 'using "{}"'.format(initial_api, API), RuntimeWarning) + +API_NAME = {'pyqt5': 'PyQt5', 'pyqt': 'PyQt4', 'pyqt4': 'PyQt4', + 'pyside': 'PySide', 'pyside2':'PySide2'}[API] + +if PYQT4: + import sip + try: + API_NAME += (" (API v{0})".format(sip.getapi('QString'))) + except AttributeError: + pass + +try: + # QtDataVisualization backward compatibility (QtDataVisualization vs. QtDatavisualization) + # Only available for Qt5 bindings > 5.9 on Windows + from . import QtDataVisualization as QtDatavisualization +except (ImportError, PythonQtError): + pass + + +def _warn_old_minor_version(name, old_version, min_version): + warning_message = ( + "{name} version {old_version} is unsupported upstream and " + "deprecated by QtPy. To ensure your application is still supported " + "in QtPy 2.0, please make sure it doesn't depend upon {name} versions " + "older than {min_version}.".format( + name=name, old_version=old_version, min_version=min_version)) + warnings.warn(warning_message, DeprecationWarning) + + +# Warn if using a legacy, soon to be unsupported Qt API/binding +if API in LEGACY_APIS or initial_api in LEGACY_APIS: + warnings.warn( + "A deprecated Qt4-based binding (PyQt4/PySide) was installed, " + "imported or set via the 'QT_API' environment variable. " + "To ensure your application is still supported in QtPy 2.0, " + "please make sure it doesn't depend upon, import or " + "set the 'QT_API' env var to 'pyqt', 'pyqt4' or 'pyside'.", + DeprecationWarning, + ) +else: + if LooseVersion(QT_VERSION) < LooseVersion(QT_VERSION_MIN): + _warn_old_minor_version('Qt', QT_VERSION, QT_VERSION_MIN) + if PYQT_VERSION and (LooseVersion(PYQT_VERSION) + < LooseVersion(PYQT_VERSION_MIN)): + _warn_old_minor_version('PyQt', PYQT_VERSION, PYQT_VERSION_MIN) + elif PYSIDE_VERSION and (LooseVersion(PYSIDE_VERSION) + < LooseVersion(PYSIDE_VERSION_MIN)): + _warn_old_minor_version('PySide', PYSIDE_VERSION, PYSIDE_VERSION_MIN) diff --git a/openpype/vendor/python/python_2/qtpy/_patch/__init__.py b/openpype/vendor/python/python_2/qtpy/_patch/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openpype/vendor/python/python_2/qtpy/_patch/qcombobox.py b/openpype/vendor/python/python_2/qtpy/_patch/qcombobox.py new file mode 100644 index 00000000000..d3e98bed16f --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/_patch/qcombobox.py @@ -0,0 +1,101 @@ +# The code below, as well as the associated test were adapted from +# qt-helpers, which was released under a 3-Clause BSD license: +# +# Copyright (c) 2015, Chris Beaumont and Thomas Robitaille +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the +# distribution. +# * Neither the name of the Glue project nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +def patch_qcombobox(QComboBox): + """ + In PySide, using Python objects as userData in QComboBox causes + Segmentation faults under certain conditions. Even in cases where it + doesn't, findData does not work correctly. Likewise, findData also does not + work correctly with Python objects when using PyQt4. On the other hand, + PyQt5 deals with this case correctly. We therefore patch QComboBox when + using PyQt4 and PySide to avoid issues. + """ + + from ..QtGui import QIcon + from ..QtCore import Qt, QObject + + class userDataWrapper(): + """ + This class is used to wrap any userData object. If we don't do this, + then certain types of objects can cause segmentation faults or issues + depending on whether/how __getitem__ is defined. + """ + def __init__(self, data): + self.data = data + + _addItem = QComboBox.addItem + + def addItem(self, *args, **kwargs): + if len(args) == 3 or (not isinstance(args[0], QIcon) + and len(args) == 2): + args, kwargs['userData'] = args[:-1], args[-1] + if 'userData' in kwargs: + kwargs['userData'] = userDataWrapper(kwargs['userData']) + _addItem(self, *args, **kwargs) + + _insertItem = QComboBox.insertItem + + def insertItem(self, *args, **kwargs): + if len(args) == 4 or (not isinstance(args[1], QIcon) + and len(args) == 3): + args, kwargs['userData'] = args[:-1], args[-1] + if 'userData' in kwargs: + kwargs['userData'] = userDataWrapper(kwargs['userData']) + _insertItem(self, *args, **kwargs) + + _setItemData = QComboBox.setItemData + + def setItemData(self, index, value, role=Qt.UserRole): + value = userDataWrapper(value) + _setItemData(self, index, value, role=role) + + _itemData = QComboBox.itemData + + def itemData(self, index, role=Qt.UserRole): + userData = _itemData(self, index, role=role) + if isinstance(userData, userDataWrapper): + userData = userData.data + return userData + + def findData(self, value): + for i in range(self.count()): + if self.itemData(i) == value: + return i + return -1 + + QComboBox.addItem = addItem + QComboBox.insertItem = insertItem + QComboBox.setItemData = setItemData + QComboBox.itemData = itemData + QComboBox.findData = findData \ No newline at end of file diff --git a/openpype/vendor/python/python_2/qtpy/_patch/qheaderview.py b/openpype/vendor/python/python_2/qtpy/_patch/qheaderview.py new file mode 100644 index 00000000000..b6baddbb22b --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/_patch/qheaderview.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- +# +# Copyright © The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +import warnings + +def introduce_renamed_methods_qheaderview(QHeaderView): + + _isClickable = QHeaderView.isClickable + def sectionsClickable(self): + """ + QHeaderView.sectionsClickable() -> bool + """ + return _isClickable(self) + QHeaderView.sectionsClickable = sectionsClickable + def isClickable(self): + warnings.warn('isClickable is only available in Qt4. Use ' + 'sectionsClickable instead.', stacklevel=2) + return _isClickable(self) + QHeaderView.isClickable = isClickable + + + _isMovable = QHeaderView.isMovable + def sectionsMovable(self): + """ + QHeaderView.sectionsMovable() -> bool + """ + return _isMovable(self) + QHeaderView.sectionsMovable = sectionsMovable + def isMovable(self): + warnings.warn('isMovable is only available in Qt4. Use ' + 'sectionsMovable instead.', stacklevel=2) + return _isMovable(self) + QHeaderView.isMovable = isMovable + + + _resizeMode = QHeaderView.resizeMode + def sectionResizeMode(self, logicalIndex): + """ + QHeaderView.sectionResizeMode(int) -> QHeaderView.ResizeMode + """ + return _resizeMode(self, logicalIndex) + QHeaderView.sectionResizeMode = sectionResizeMode + def resizeMode(self, logicalIndex): + warnings.warn('resizeMode is only available in Qt4. Use ' + 'sectionResizeMode instead.', stacklevel=2) + return _resizeMode(self, logicalIndex) + QHeaderView.resizeMode = resizeMode + + _setClickable = QHeaderView.setClickable + def setSectionsClickable(self, clickable): + """ + QHeaderView.setSectionsClickable(bool) + """ + return _setClickable(self, clickable) + QHeaderView.setSectionsClickable = setSectionsClickable + def setClickable(self, clickable): + warnings.warn('setClickable is only available in Qt4. Use ' + 'setSectionsClickable instead.', stacklevel=2) + return _setClickable(self, clickable) + QHeaderView.setClickable = setClickable + + + _setMovable = QHeaderView.setMovable + def setSectionsMovable(self, movable): + """ + QHeaderView.setSectionsMovable(bool) + """ + return _setMovable(self, movable) + QHeaderView.setSectionsMovable = setSectionsMovable + def setMovable(self, movable): + warnings.warn('setMovable is only available in Qt4. Use ' + 'setSectionsMovable instead.', stacklevel=2) + return _setMovable(self, movable) + QHeaderView.setMovable = setMovable + + + _setResizeMode = QHeaderView.setResizeMode + def setSectionResizeMode(self, *args): + """ + QHeaderView.setSectionResizeMode(QHeaderView.ResizeMode) + QHeaderView.setSectionResizeMode(int, QHeaderView.ResizeMode) + """ + _setResizeMode(self, *args) + QHeaderView.setSectionResizeMode = setSectionResizeMode + def setResizeMode(self, *args): + warnings.warn('setResizeMode is only available in Qt4. Use ' + 'setSectionResizeMode instead.', stacklevel=2) + _setResizeMode(self, *args) + QHeaderView.setResizeMode = setResizeMode + + + + diff --git a/openpype/vendor/python/python_2/qtpy/_version.py b/openpype/vendor/python/python_2/qtpy/_version.py new file mode 100644 index 00000000000..310a76d68c4 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/_version.py @@ -0,0 +1,2 @@ +version_info = (1, 11, 3) +__version__ = '.'.join(map(str, version_info)) diff --git a/openpype/vendor/python/python_2/qtpy/compat.py b/openpype/vendor/python/python_2/qtpy/compat.py new file mode 100644 index 00000000000..949d8854d19 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/compat.py @@ -0,0 +1,195 @@ +# -*- coding: utf-8 -*- +# +# Copyright © 2009- The Spyder Development Team +# Licensed under the terms of the MIT License + +""" +Compatibility functions +""" + +from __future__ import print_function +import sys + +from . import PYQT4 +from .QtWidgets import QFileDialog +from .py3compat import Callable, is_text_string, to_text_string, TEXT_TYPES + + +# ============================================================================= +# QVariant conversion utilities +# ============================================================================= +PYQT_API_1 = False +if PYQT4: + import sip + try: + PYQT_API_1 = sip.getapi('QVariant') == 1 # PyQt API #1 + except AttributeError: + # PyQt =v4.4 (API #1 and #2) and PySide >=v1.0""" + # Calling QFileDialog static method + if sys.platform == "win32": + # On Windows platforms: redirect standard outputs + _temp1, _temp2 = sys.stdout, sys.stderr + sys.stdout, sys.stderr = None, None + try: + result = QFileDialog.getExistingDirectory(parent, caption, basedir, + options) + finally: + if sys.platform == "win32": + # On Windows platforms: restore standard outputs + sys.stdout, sys.stderr = _temp1, _temp2 + if not is_text_string(result): + # PyQt API #1 + result = to_text_string(result) + return result + + +def _qfiledialog_wrapper(attr, parent=None, caption='', basedir='', + filters='', selectedfilter='', options=None): + if options is None: + options = QFileDialog.Options(0) + try: + # PyQt =v4.6 + QString = None # analysis:ignore + tuple_returned = True + try: + # PyQt >=v4.6 + func = getattr(QFileDialog, attr+'AndFilter') + except AttributeError: + # PySide or PyQt =v4.6 + output, selectedfilter = result + else: + # PyQt =v4.4 (API #1 and #2) and PySide >=v1.0""" + return _qfiledialog_wrapper('getOpenFileName', parent=parent, + caption=caption, basedir=basedir, + filters=filters, selectedfilter=selectedfilter, + options=options) + + +def getopenfilenames(parent=None, caption='', basedir='', filters='', + selectedfilter='', options=None): + """Wrapper around QtGui.QFileDialog.getOpenFileNames static method + Returns a tuple (filenames, selectedfilter) -- when dialog box is canceled, + returns a tuple (empty list, empty string) + Compatible with PyQt >=v4.4 (API #1 and #2) and PySide >=v1.0""" + return _qfiledialog_wrapper('getOpenFileNames', parent=parent, + caption=caption, basedir=basedir, + filters=filters, selectedfilter=selectedfilter, + options=options) + + +def getsavefilename(parent=None, caption='', basedir='', filters='', + selectedfilter='', options=None): + """Wrapper around QtGui.QFileDialog.getSaveFileName static method + Returns a tuple (filename, selectedfilter) -- when dialog box is canceled, + returns a tuple of empty strings + Compatible with PyQt >=v4.4 (API #1 and #2) and PySide >=v1.0""" + return _qfiledialog_wrapper('getSaveFileName', parent=parent, + caption=caption, basedir=basedir, + filters=filters, selectedfilter=selectedfilter, + options=options) diff --git a/openpype/vendor/python/python_2/qtpy/py3compat.py b/openpype/vendor/python/python_2/qtpy/py3compat.py new file mode 100644 index 00000000000..e92871a02b8 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/py3compat.py @@ -0,0 +1,262 @@ +# -*- coding: utf-8 -*- +# +# Copyright © 2012-2013 Pierre Raybaut +# Licensed under the terms of the MIT License +# (see spyderlib/__init__.py for details) + +""" +spyderlib.py3compat +------------------- + +Transitional module providing compatibility functions intended to help +migrating from Python 2 to Python 3. + +This module should be fully compatible with: + * Python >=v2.6 + * Python 3 +""" + +from __future__ import print_function + +import sys +import os + +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 +PY33 = PY3 and sys.version_info[1] >= 3 + + +# ============================================================================= +# Data types +# ============================================================================= +if PY2: + # Python 2 + TEXT_TYPES = (str, unicode) + INT_TYPES = (int, long) +else: + # Python 3 + TEXT_TYPES = (str,) + INT_TYPES = (int,) +NUMERIC_TYPES = tuple(list(INT_TYPES) + [float, complex]) + + +# ============================================================================= +# Renamed/Reorganized modules +# ============================================================================= +if PY2: + # Python 2 + import __builtin__ as builtins + from collections import Callable, MutableMapping + import ConfigParser as configparser + try: + import _winreg as winreg + except ImportError: + pass + from sys import maxint as maxsize + try: + import CStringIO as io + except ImportError: + import StringIO as io + try: + import cPickle as pickle + except ImportError: + import pickle + from UserDict import DictMixin as MutableMapping + import thread as _thread + import repr as reprlib +else: + # Python 3 + import builtins + import configparser + try: + import winreg + except ImportError: + pass + from sys import maxsize + import io + import pickle + if PY33: + from collections.abc import Callable, MutableMapping + else: + from collections import Callable, MutableMapping + import _thread + import reprlib + + +# ============================================================================= +# Strings +# ============================================================================= +if PY2: + # Python 2 + import codecs + + def u(obj): + """Make unicode object""" + return codecs.unicode_escape_decode(obj)[0] +else: + # Python 3 + def u(obj): + """Return string as it is""" + return obj + + +def is_text_string(obj): + """Return True if `obj` is a text string, False if it is anything else, + like binary data (Python 3) or QString (Python 2, PyQt API #1)""" + if PY2: + # Python 2 + return isinstance(obj, basestring) + else: + # Python 3 + return isinstance(obj, str) + + +def is_binary_string(obj): + """Return True if `obj` is a binary string, False if it is anything else""" + if PY2: + # Python 2 + return isinstance(obj, str) + else: + # Python 3 + return isinstance(obj, bytes) + + +def is_string(obj): + """Return True if `obj` is a text or binary Python string object, + False if it is anything else, like a QString (Python 2, PyQt API #1)""" + return is_text_string(obj) or is_binary_string(obj) + + +def is_unicode(obj): + """Return True if `obj` is unicode""" + if PY2: + # Python 2 + return isinstance(obj, unicode) + else: + # Python 3 + return isinstance(obj, str) + + +def to_text_string(obj, encoding=None): + """Convert `obj` to (unicode) text string""" + if PY2: + # Python 2 + if encoding is None: + return unicode(obj) + else: + return unicode(obj, encoding) + else: + # Python 3 + if encoding is None: + return str(obj) + elif isinstance(obj, str): + # In case this function is not used properly, this could happen + return obj + else: + return str(obj, encoding) + + +def to_binary_string(obj, encoding=None): + """Convert `obj` to binary string (bytes in Python 3, str in Python 2)""" + if PY2: + # Python 2 + if encoding is None: + return str(obj) + else: + return obj.encode(encoding) + else: + # Python 3 + return bytes(obj, 'utf-8' if encoding is None else encoding) + + +# ============================================================================= +# Function attributes +# ============================================================================= +def get_func_code(func): + """Return function code object""" + if PY2: + # Python 2 + return func.func_code + else: + # Python 3 + return func.__code__ + + +def get_func_name(func): + """Return function name""" + if PY2: + # Python 2 + return func.func_name + else: + # Python 3 + return func.__name__ + + +def get_func_defaults(func): + """Return function default argument values""" + if PY2: + # Python 2 + return func.func_defaults + else: + # Python 3 + return func.__defaults__ + + +# ============================================================================= +# Special method attributes +# ============================================================================= +def get_meth_func(obj): + """Return method function object""" + if PY2: + # Python 2 + return obj.im_func + else: + # Python 3 + return obj.__func__ + + +def get_meth_class_inst(obj): + """Return method class instance""" + if PY2: + # Python 2 + return obj.im_self + else: + # Python 3 + return obj.__self__ + + +def get_meth_class(obj): + """Return method class""" + if PY2: + # Python 2 + return obj.im_class + else: + # Python 3 + return obj.__self__.__class__ + + +# ============================================================================= +# Misc. +# ============================================================================= +if PY2: + # Python 2 + input = raw_input + getcwd = os.getcwdu + cmp = cmp + import string + str_lower = string.lower + from itertools import izip_longest as zip_longest +else: + # Python 3 + input = input + getcwd = os.getcwd + + def cmp(a, b): + return (a > b) - (a < b) + str_lower = str.lower + from itertools import zip_longest + + +def qbytearray_to_str(qba): + """Convert QByteArray object to str in a way compatible with Python 2/3""" + return str(bytes(qba.toHex().data()).decode()) diff --git a/openpype/vendor/python/python_2/qtpy/tests/__init__.py b/openpype/vendor/python/python_2/qtpy/tests/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openpype/vendor/python/python_2/qtpy/tests/conftest.py b/openpype/vendor/python/python_2/qtpy/tests/conftest.py new file mode 100644 index 00000000000..c631886fb59 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/conftest.py @@ -0,0 +1,71 @@ +import os + + +def pytest_configure(config): + """ + This function gets run by py.test at the very start + """ + + if 'USE_QT_API' in os.environ: + os.environ['QT_API'] = os.environ['USE_QT_API'].lower() + + # We need to import qtpy here to make sure that the API versions get set + # straight away. + import qtpy + + +def pytest_report_header(config): + """ + This function is used by py.test to insert a customized header into the + test report. + """ + + versions = os.linesep + versions += 'PyQt4: ' + + try: + from PyQt4 import Qt + versions += "PyQt: {0} - Qt: {1}".format(Qt.PYQT_VERSION_STR, Qt.QT_VERSION_STR) + except ImportError: + versions += 'not installed' + except AttributeError: + versions += 'unknown version' + + versions += os.linesep + versions += 'PyQt5: ' + + try: + from PyQt5 import Qt + versions += "PyQt: {0} - Qt: {1}".format(Qt.PYQT_VERSION_STR, Qt.QT_VERSION_STR) + except ImportError: + versions += 'not installed' + except AttributeError: + versions += 'unknown version' + + versions += os.linesep + versions += 'PySide: ' + + try: + import PySide + from PySide import QtCore + versions += "PySide: {0} - Qt: {1}".format(PySide.__version__, QtCore.__version__) + except ImportError: + versions += 'not installed' + except AttributeError: + versions += 'unknown version' + + versions += os.linesep + versions += 'PySide2: ' + + try: + import PySide2 + from PySide2 import QtCore + versions += "PySide: {0} - Qt: {1}".format(PySide2.__version__, QtCore.__version__) + except ImportError: + versions += 'not installed' + except AttributeError: + versions += 'unknown version' + + versions += os.linesep + + return versions diff --git a/openpype/vendor/python/python_2/qtpy/tests/runtests.py b/openpype/vendor/python/python_2/qtpy/tests/runtests.py new file mode 100644 index 00000000000..b54fbb45b24 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/runtests.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# ---------------------------------------------------------------------------- +# Copyright © 2015- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# ---------------------------------------------------------------------------- + +"""File for running tests programmatically.""" + +# Standard library imports +import sys + +# Third party imports +import qtpy # to ensure that Qt4 uses API v2 +import pytest + + +def main(): + """Run pytest tests.""" + errno = pytest.main(['-x', 'qtpy', '-v', '-rw', '--durations=10', + '--cov=qtpy', '--cov-report=term-missing']) + sys.exit(errno) + +if __name__ == '__main__': + main() diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_macos_checks.py b/openpype/vendor/python/python_2/qtpy/tests/test_macos_checks.py new file mode 100644 index 00000000000..01aa8091c09 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_macos_checks.py @@ -0,0 +1,110 @@ +from __future__ import absolute_import + +import mock +import platform +import sys + +import pytest +from qtpy import PYQT5, PYSIDE2 + + +@pytest.mark.skipif(not PYQT5, reason="Targeted to PyQt5") +@mock.patch.object(platform, 'mac_ver') +def test_qt59_exception(mac_ver, monkeypatch): + # Remove qtpy to reimport it again + try: + del sys.modules["qtpy"] + except KeyError: + pass + + # Patch stdlib to emulate a macOS system + monkeypatch.setattr("sys.platform", 'darwin') + mac_ver.return_value = ('10.9.2',) + + # Patch Qt version + monkeypatch.setattr("PyQt5.QtCore.QT_VERSION_STR", '5.9.1') + + # This should raise an Exception + with pytest.raises(Exception) as e: + import qtpy + + assert '10.10' in str(e.value) + assert '5.9' in str(e.value) + + +@pytest.mark.skipif(not PYQT5, reason="Targeted to PyQt5") +@mock.patch.object(platform, 'mac_ver') +def test_qt59_no_exception(mac_ver, monkeypatch): + # Remove qtpy to reimport it again + try: + del sys.modules["qtpy"] + except KeyError: + pass + + # Patch stdlib to emulate a macOS system + monkeypatch.setattr("sys.platform", 'darwin') + mac_ver.return_value = ('10.10.1',) + + # Patch Qt version + monkeypatch.setattr("PyQt5.QtCore.QT_VERSION_STR", '5.9.5') + + # This should not raise an Exception + try: + import qtpy + except Exception: + pytest.fail("Error!") + + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), + reason="Targeted to PyQt5 or PySide2") +@mock.patch.object(platform, 'mac_ver') +def test_qt511_exception(mac_ver, monkeypatch): + # Remove qtpy to reimport it again + try: + del sys.modules["qtpy"] + except KeyError: + pass + + # Patch stdlib to emulate a macOS system + monkeypatch.setattr("sys.platform", 'darwin') + mac_ver.return_value = ('10.10.3',) + + # Patch Qt version + if PYQT5: + monkeypatch.setattr("PyQt5.QtCore.QT_VERSION_STR", '5.11.1') + else: + monkeypatch.setattr("PySide2.QtCore.__version__", '5.11.1') + + # This should raise an Exception + with pytest.raises(Exception) as e: + import qtpy + + assert '10.11' in str(e.value) + assert '5.11' in str(e.value) + + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), + reason="Targeted to PyQt5 or PySide2") +@mock.patch.object(platform, 'mac_ver') +def test_qt511_no_exception(mac_ver, monkeypatch): + # Remove qtpy to reimport it again + try: + del sys.modules["qtpy"] + except KeyError: + pass + + # Patch stdlib to emulate a macOS system + monkeypatch.setattr("sys.platform", 'darwin') + mac_ver.return_value = ('10.13.2',) + + # Patch Qt version + if PYQT5: + monkeypatch.setattr("PyQt5.QtCore.QT_VERSION_STR", '5.11.1') + else: + monkeypatch.setattr("PySide2.QtCore.__version__", '5.11.1') + + # This should not raise an Exception + try: + import qtpy + except Exception: + pytest.fail("Error!") diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_main.py b/openpype/vendor/python/python_2/qtpy/tests/test_main.py new file mode 100644 index 00000000000..2449249cc94 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_main.py @@ -0,0 +1,82 @@ +import os + +from qtpy import QtCore, QtGui, QtWidgets, QtWebEngineWidgets + + +def assert_pyside(): + """ + Make sure that we are using PySide + """ + import PySide + assert QtCore.QEvent is PySide.QtCore.QEvent + assert QtGui.QPainter is PySide.QtGui.QPainter + assert QtWidgets.QWidget is PySide.QtGui.QWidget + assert QtWebEngineWidgets.QWebEnginePage is PySide.QtWebKit.QWebPage + +def assert_pyside2(): + """ + Make sure that we are using PySide + """ + import PySide2 + assert QtCore.QEvent is PySide2.QtCore.QEvent + assert QtGui.QPainter is PySide2.QtGui.QPainter + assert QtWidgets.QWidget is PySide2.QtWidgets.QWidget + assert QtWebEngineWidgets.QWebEnginePage is PySide2.QtWebEngineWidgets.QWebEnginePage + +def assert_pyqt4(): + """ + Make sure that we are using PyQt4 + """ + import PyQt4 + assert QtCore.QEvent is PyQt4.QtCore.QEvent + assert QtGui.QPainter is PyQt4.QtGui.QPainter + assert QtWidgets.QWidget is PyQt4.QtGui.QWidget + assert QtWebEngineWidgets.QWebEnginePage is PyQt4.QtWebKit.QWebPage + + +def assert_pyqt5(): + """ + Make sure that we are using PyQt5 + """ + import PyQt5 + assert QtCore.QEvent is PyQt5.QtCore.QEvent + assert QtGui.QPainter is PyQt5.QtGui.QPainter + assert QtWidgets.QWidget is PyQt5.QtWidgets.QWidget + if QtWebEngineWidgets.WEBENGINE: + assert QtWebEngineWidgets.QWebEnginePage is PyQt5.QtWebEngineWidgets.QWebEnginePage + else: + assert QtWebEngineWidgets.QWebEnginePage is PyQt5.QtWebKitWidgets.QWebPage + + +def test_qt_api(): + """ + If QT_API is specified, we check that the correct Qt wrapper was used + """ + + QT_API = os.environ.get('QT_API', '').lower() + + if QT_API == 'pyside': + assert_pyside() + elif QT_API in ('pyqt', 'pyqt4'): + assert_pyqt4() + elif QT_API == 'pyqt5': + assert_pyqt5() + elif QT_API == 'pyside2': + assert_pyside2() + else: + # If the tests are run locally, USE_QT_API and QT_API may not be + # defined, but we still want to make sure qtpy is behaving sensibly. + # We should then be loading, in order of decreasing preference, PyQt5, + # PyQt4, and PySide. + try: + import PyQt5 + except ImportError: + try: + import PyQt4 + except ImportError: + import PySide + assert_pyside() + else: + assert_pyqt4() + else: + assert_pyqt5() diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_patch_qcombobox.py b/openpype/vendor/python/python_2/qtpy/tests/test_patch_qcombobox.py new file mode 100644 index 00000000000..1e1f04a38af --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_patch_qcombobox.py @@ -0,0 +1,107 @@ +from __future__ import absolute_import + +import os +import sys + +import pytest +from qtpy import PYQT5, PYSIDE2, QtGui, QtWidgets + + +PY3 = sys.version[0] == "3" + + +def get_qapp(icon_path=None): + qapp = QtWidgets.QApplication.instance() + if qapp is None: + qapp = QtWidgets.QApplication(['']) + return qapp + + +class Data(object): + """ + Test class to store in userData. The __getitem__ is needed in order to + reproduce the segmentation fault. + """ + def __getitem__(self, item): + raise ValueError("Failing") + + +@pytest.mark.skipif(PY3 or (PYSIDE2 and os.environ.get('CI', None) is not None), + reason="It segfaults in Python 3 and in our CIs with PySide2") +def test_patched_qcombobox(): + """ + In PySide, using Python objects as userData in QComboBox causes + Segmentation faults under certain conditions. Even in cases where it + doesn't, findData does not work correctly. Likewise, findData also + does not work correctly with Python objects when using PyQt4. On the + other hand, PyQt5 deals with this case correctly. We therefore patch + QComboBox when using PyQt4 and PySide to avoid issues. + """ + + app = get_qapp() + + data1 = Data() + data2 = Data() + data3 = Data() + data4 = Data() + data5 = Data() + data6 = Data() + + icon1 = QtGui.QIcon() + icon2 = QtGui.QIcon() + + widget = QtWidgets.QComboBox() + widget.addItem('a', data1) + widget.insertItem(0, 'b', data2) + widget.addItem('c', data1) + widget.setItemData(2, data3) + widget.addItem(icon1, 'd', data4) + widget.insertItem(3, icon2, 'e', data5) + widget.addItem(icon1, 'f') + widget.insertItem(5, icon2, 'g') + + widget.show() + + assert widget.findData(data1) == 1 + assert widget.findData(data2) == 0 + assert widget.findData(data3) == 2 + assert widget.findData(data4) == 4 + assert widget.findData(data5) == 3 + assert widget.findData(data6) == -1 + + assert widget.itemData(0) == data2 + assert widget.itemData(1) == data1 + assert widget.itemData(2) == data3 + assert widget.itemData(3) == data5 + assert widget.itemData(4) == data4 + assert widget.itemData(5) is None + assert widget.itemData(6) is None + + assert widget.itemText(0) == 'b' + assert widget.itemText(1) == 'a' + assert widget.itemText(2) == 'c' + assert widget.itemText(3) == 'e' + assert widget.itemText(4) == 'd' + assert widget.itemText(5) == 'g' + assert widget.itemText(6) == 'f' + + +@pytest.mark.skipif(((PYSIDE2 or PYQT5) + and os.environ.get('CI', None) is not None), + reason="It segfaults in our CIs with PYSIDE2 or PYQT5") +def test_model_item(): + """ + This is a regression test for an issue that caused the call to item(0) + below to trigger segmentation faults in PySide. The issue is + non-deterministic when running the call once, so we include a loop to make + sure that we trigger the fault. + """ + app = get_qapp() + combo = QtWidgets.QComboBox() + label_data = [('a', None)] + for iter in range(10000): + combo.clear() + for i, (label, data) in enumerate(label_data): + combo.addItem(label, userData=data) + model = combo.model() + model.item(0) diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_patch_qheaderview.py b/openpype/vendor/python/python_2/qtpy/tests/test_patch_qheaderview.py new file mode 100644 index 00000000000..17037f34a37 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_patch_qheaderview.py @@ -0,0 +1,98 @@ +from __future__ import absolute_import + +import sys + +import pytest +from qtpy import PYSIDE, PYSIDE2, PYQT4 +from qtpy.QtWidgets import QApplication +from qtpy.QtWidgets import QHeaderView +from qtpy.QtCore import Qt +from qtpy.QtCore import QAbstractListModel + + +PY3 = sys.version[0] == "3" + + +def get_qapp(icon_path=None): + qapp = QApplication.instance() + if qapp is None: + qapp = QApplication(['']) + return qapp + + +@pytest.mark.skipif(PY3 or PYSIDE2, reason="It fails on Python 3 and PySide2") +def test_patched_qheaderview(): + """ + This will test whether QHeaderView has the new methods introduced in Qt5. + It will then create an instance of QHeaderView and test that no exceptions + are raised and that some basic behaviour works. + """ + assert QHeaderView.sectionsClickable is not None + assert QHeaderView.sectionsMovable is not None + assert QHeaderView.sectionResizeMode is not None + assert QHeaderView.setSectionsClickable is not None + assert QHeaderView.setSectionsMovable is not None + assert QHeaderView.setSectionResizeMode is not None + + # setup a model and add it to a headerview + qapp = get_qapp() + headerview = QHeaderView(Qt.Horizontal) + class Model(QAbstractListModel): + pass + model = Model() + headerview.setModel(model) + assert headerview.count() == 1 + + # test it + assert isinstance(headerview.sectionsClickable(), bool) + assert isinstance(headerview.sectionsMovable(), bool) + if PYSIDE: + assert isinstance(headerview.sectionResizeMode(0), + QHeaderView.ResizeMode) + else: + assert isinstance(headerview.sectionResizeMode(0), int) + + headerview.setSectionsClickable(True) + assert headerview.sectionsClickable() == True + headerview.setSectionsClickable(False) + assert headerview.sectionsClickable() == False + + headerview.setSectionsMovable(True) + assert headerview.sectionsMovable() == True + headerview.setSectionsMovable(False) + assert headerview.sectionsMovable() == False + + headerview.setSectionResizeMode(QHeaderView.Interactive) + assert headerview.sectionResizeMode(0) == QHeaderView.Interactive + headerview.setSectionResizeMode(QHeaderView.Fixed) + assert headerview.sectionResizeMode(0) == QHeaderView.Fixed + headerview.setSectionResizeMode(QHeaderView.Stretch) + assert headerview.sectionResizeMode(0) == QHeaderView.Stretch + headerview.setSectionResizeMode(QHeaderView.ResizeToContents) + assert headerview.sectionResizeMode(0) == QHeaderView.ResizeToContents + + headerview.setSectionResizeMode(0, QHeaderView.Interactive) + assert headerview.sectionResizeMode(0) == QHeaderView.Interactive + headerview.setSectionResizeMode(0, QHeaderView.Fixed) + assert headerview.sectionResizeMode(0) == QHeaderView.Fixed + headerview.setSectionResizeMode(0, QHeaderView.Stretch) + assert headerview.sectionResizeMode(0) == QHeaderView.Stretch + headerview.setSectionResizeMode(0, QHeaderView.ResizeToContents) + assert headerview.sectionResizeMode(0) == QHeaderView.ResizeToContents + + # test that the old methods in Qt4 raise exceptions + if PYQT4 or PYSIDE: + with pytest.warns(UserWarning): + headerview.isClickable() + with pytest.warns(UserWarning): + headerview.isMovable() + with pytest.warns(UserWarning): + headerview.resizeMode(0) + with pytest.warns(UserWarning): + headerview.setClickable(True) + with pytest.warns(UserWarning): + headerview.setMovable(True) + with pytest.warns(UserWarning): + headerview.setResizeMode(0, QHeaderView.Interactive) + + diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qdesktopservice_split.py b/openpype/vendor/python/python_2/qtpy/tests/test_qdesktopservice_split.py new file mode 100644 index 00000000000..472f2df1d00 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qdesktopservice_split.py @@ -0,0 +1,41 @@ +"""Test QDesktopServices split in Qt5.""" + +from __future__ import absolute_import + +import pytest +import warnings +from qtpy import PYQT4, PYSIDE + + +def test_qstandarpath(): + """Test the qtpy.QStandardPaths namespace""" + from qtpy.QtCore import QStandardPaths + + assert QStandardPaths.StandardLocation is not None + + # Attributes from QDesktopServices shouldn't be in QStandardPaths + with pytest.raises(AttributeError) as excinfo: + QStandardPaths.setUrlHandler + + +def test_qdesktopservice(): + """Test the qtpy.QDesktopServices namespace""" + from qtpy.QtGui import QDesktopServices + + assert QDesktopServices.setUrlHandler is not None + + +@pytest.mark.skipif(not (PYQT4 or PYSIDE), reason="Warning is only raised in old bindings") +def test_qdesktopservice_qt4_pyside(): + from qtpy.QtGui import QDesktopServices + # Attributes from QStandardPaths should raise a warning when imported + # from QDesktopServices + with warnings.catch_warnings(record=True) as w: + # Cause all warnings to always be triggered. + warnings.simplefilter("always") + # Try to import QtHelp. + QDesktopServices.StandardLocation + + assert len(w) == 1 + assert issubclass(w[-1].category, DeprecationWarning) + assert "deprecated" in str(w[-1].message) diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qt3danimation.py b/openpype/vendor/python/python_2/qtpy/tests/test_qt3danimation.py new file mode 100644 index 00000000000..650be19e184 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qt3danimation.py @@ -0,0 +1,25 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qt3danimation(): + """Test the qtpy.Qt3DAnimation namespace""" + Qt3DAnimation = pytest.importorskip("qtpy.Qt3DAnimation") + + assert Qt3DAnimation.QAnimationController is not None + assert Qt3DAnimation.QAdditiveClipBlend is not None + assert Qt3DAnimation.QAbstractClipBlendNode is not None + assert Qt3DAnimation.QAbstractAnimation is not None + assert Qt3DAnimation.QKeyframeAnimation is not None + assert Qt3DAnimation.QAbstractAnimationClip is not None + assert Qt3DAnimation.QAbstractClipAnimator is not None + assert Qt3DAnimation.QClipAnimator is not None + assert Qt3DAnimation.QAnimationGroup is not None + assert Qt3DAnimation.QLerpClipBlend is not None + assert Qt3DAnimation.QMorphingAnimation is not None + assert Qt3DAnimation.QAnimationAspect is not None + assert Qt3DAnimation.QVertexBlendAnimation is not None + assert Qt3DAnimation.QBlendedClipAnimator is not None + assert Qt3DAnimation.QMorphTarget is not None diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qt3dcore.py b/openpype/vendor/python/python_2/qtpy/tests/test_qt3dcore.py new file mode 100644 index 00000000000..821fbd4525b --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qt3dcore.py @@ -0,0 +1,44 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qt3dcore(): + """Test the qtpy.Qt3DCore namespace""" + Qt3DCore = pytest.importorskip("qtpy.Qt3DCore") + + assert Qt3DCore.QPropertyValueAddedChange is not None + assert Qt3DCore.QSkeletonLoader is not None + assert Qt3DCore.QPropertyNodeRemovedChange is not None + assert Qt3DCore.QPropertyUpdatedChange is not None + assert Qt3DCore.QAspectEngine is not None + assert Qt3DCore.QPropertyValueAddedChangeBase is not None + assert Qt3DCore.QStaticPropertyValueRemovedChangeBase is not None + assert Qt3DCore.QPropertyNodeAddedChange is not None + assert Qt3DCore.QDynamicPropertyUpdatedChange is not None + assert Qt3DCore.QStaticPropertyUpdatedChangeBase is not None + assert Qt3DCore.ChangeFlags is not None + assert Qt3DCore.QAbstractAspect is not None + assert Qt3DCore.QBackendNode is not None + assert Qt3DCore.QTransform is not None + assert Qt3DCore.QPropertyUpdatedChangeBase is not None + assert Qt3DCore.QNodeId is not None + assert Qt3DCore.QJoint is not None + assert Qt3DCore.QSceneChange is not None + assert Qt3DCore.QNodeIdTypePair is not None + assert Qt3DCore.QAbstractSkeleton is not None + assert Qt3DCore.QComponentRemovedChange is not None + assert Qt3DCore.QComponent is not None + assert Qt3DCore.QEntity is not None + assert Qt3DCore.QNodeCommand is not None + assert Qt3DCore.QNode is not None + assert Qt3DCore.QPropertyValueRemovedChange is not None + assert Qt3DCore.QPropertyValueRemovedChangeBase is not None + assert Qt3DCore.QComponentAddedChange is not None + assert Qt3DCore.QNodeCreatedChangeBase is not None + assert Qt3DCore.QNodeDestroyedChange is not None + assert Qt3DCore.QArmature is not None + assert Qt3DCore.QStaticPropertyValueAddedChangeBase is not None + assert Qt3DCore.ChangeFlag is not None + assert Qt3DCore.QSkeleton is not None diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qt3dextras.py b/openpype/vendor/python/python_2/qtpy/tests/test_qt3dextras.py new file mode 100644 index 00000000000..f63c7d57993 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qt3dextras.py @@ -0,0 +1,47 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qt3dextras(): + """Test the qtpy.Qt3DExtras namespace""" + Qt3DExtras = pytest.importorskip("qtpy.Qt3DExtras") + + assert Qt3DExtras.QTextureMaterial is not None + assert Qt3DExtras.QPhongAlphaMaterial is not None + assert Qt3DExtras.QOrbitCameraController is not None + assert Qt3DExtras.QAbstractSpriteSheet is not None + assert Qt3DExtras.QNormalDiffuseMapMaterial is not None + assert Qt3DExtras.QDiffuseSpecularMaterial is not None + assert Qt3DExtras.QSphereGeometry is not None + assert Qt3DExtras.QCuboidGeometry is not None + assert Qt3DExtras.QForwardRenderer is not None + assert Qt3DExtras.QPhongMaterial is not None + assert Qt3DExtras.QSpriteGrid is not None + assert Qt3DExtras.QDiffuseMapMaterial is not None + assert Qt3DExtras.QConeGeometry is not None + assert Qt3DExtras.QSpriteSheetItem is not None + assert Qt3DExtras.QPlaneGeometry is not None + assert Qt3DExtras.QSphereMesh is not None + assert Qt3DExtras.QNormalDiffuseSpecularMapMaterial is not None + assert Qt3DExtras.QCuboidMesh is not None + assert Qt3DExtras.QGoochMaterial is not None + assert Qt3DExtras.QText2DEntity is not None + assert Qt3DExtras.QTorusMesh is not None + assert Qt3DExtras.Qt3DWindow is not None + assert Qt3DExtras.QPerVertexColorMaterial is not None + assert Qt3DExtras.QExtrudedTextGeometry is not None + assert Qt3DExtras.QSkyboxEntity is not None + assert Qt3DExtras.QAbstractCameraController is not None + assert Qt3DExtras.QExtrudedTextMesh is not None + assert Qt3DExtras.QCylinderGeometry is not None + assert Qt3DExtras.QTorusGeometry is not None + assert Qt3DExtras.QMorphPhongMaterial is not None + assert Qt3DExtras.QPlaneMesh is not None + assert Qt3DExtras.QDiffuseSpecularMapMaterial is not None + assert Qt3DExtras.QSpriteSheet is not None + assert Qt3DExtras.QConeMesh is not None + assert Qt3DExtras.QFirstPersonCameraController is not None + assert Qt3DExtras.QMetalRoughMaterial is not None + assert Qt3DExtras.QCylinderMesh is not None diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qt3dinput.py b/openpype/vendor/python/python_2/qtpy/tests/test_qt3dinput.py new file mode 100644 index 00000000000..48d73d03065 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qt3dinput.py @@ -0,0 +1,33 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qt3dinput(): + """Test the qtpy.Qt3DInput namespace""" + Qt3DInput = pytest.importorskip("qtpy.Qt3DInput") + + assert Qt3DInput.QAxisAccumulator is not None + assert Qt3DInput.QInputSettings is not None + assert Qt3DInput.QAnalogAxisInput is not None + assert Qt3DInput.QAbstractAxisInput is not None + assert Qt3DInput.QMouseHandler is not None + assert Qt3DInput.QButtonAxisInput is not None + assert Qt3DInput.QInputSequence is not None + assert Qt3DInput.QWheelEvent is not None + assert Qt3DInput.QActionInput is not None + assert Qt3DInput.QKeyboardDevice is not None + assert Qt3DInput.QMouseDevice is not None + assert Qt3DInput.QAxis is not None + assert Qt3DInput.QInputChord is not None + assert Qt3DInput.QMouseEvent is not None + assert Qt3DInput.QKeyboardHandler is not None + assert Qt3DInput.QKeyEvent is not None + assert Qt3DInput.QAbstractActionInput is not None + assert Qt3DInput.QInputAspect is not None + assert Qt3DInput.QLogicalDevice is not None + assert Qt3DInput.QAction is not None + assert Qt3DInput.QAbstractPhysicalDevice is not None + assert Qt3DInput.QAxisSetting is not None + diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qt3dlogic.py b/openpype/vendor/python/python_2/qtpy/tests/test_qt3dlogic.py new file mode 100644 index 00000000000..34f7de67e47 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qt3dlogic.py @@ -0,0 +1,12 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qt3dlogic(): + """Test the qtpy.Qt3DLogic namespace""" + Qt3DLogic = pytest.importorskip("qtpy.Qt3DLogic") + + assert Qt3DLogic.QLogicAspect is not None + assert Qt3DLogic.QFrameAction is not None diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qt3drender.py b/openpype/vendor/python/python_2/qtpy/tests/test_qt3drender.py new file mode 100644 index 00000000000..f4647682606 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qt3drender.py @@ -0,0 +1,119 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qt3drender(): + """Test the qtpy.Qt3DRender namespace""" + Qt3DRender = pytest.importorskip("qtpy.Qt3DRender") + + assert Qt3DRender.QPointSize is not None + assert Qt3DRender.QFrustumCulling is not None + assert Qt3DRender.QPickPointEvent is not None + assert Qt3DRender.QRenderPassFilter is not None + assert Qt3DRender.QMesh is not None + assert Qt3DRender.QRayCaster is not None + assert Qt3DRender.QStencilMask is not None + assert Qt3DRender.QPickLineEvent is not None + assert Qt3DRender.QPickTriangleEvent is not None + assert Qt3DRender.QRenderState is not None + assert Qt3DRender.QTextureWrapMode is not None + assert Qt3DRender.QRenderPass is not None + assert Qt3DRender.QGeometryRenderer is not None + assert Qt3DRender.QAttribute is not None + assert Qt3DRender.QStencilOperation is not None + assert Qt3DRender.QScissorTest is not None + assert Qt3DRender.QTextureCubeMapArray is not None + assert Qt3DRender.QRenderTarget is not None + assert Qt3DRender.QStencilTest is not None + assert Qt3DRender.QTextureData is not None + assert Qt3DRender.QBuffer is not None + assert Qt3DRender.QLineWidth is not None + assert Qt3DRender.QLayer is not None + assert Qt3DRender.QTextureRectangle is not None + assert Qt3DRender.QRenderTargetSelector is not None + assert Qt3DRender.QPickingSettings is not None + assert Qt3DRender.QCullFace is not None + assert Qt3DRender.QAbstractFunctor is not None + assert Qt3DRender.PropertyReaderInterface is not None + assert Qt3DRender.QMaterial is not None + assert Qt3DRender.QAlphaCoverage is not None + assert Qt3DRender.QClearBuffers is not None + assert Qt3DRender.QAlphaTest is not None + assert Qt3DRender.QStencilOperationArguments is not None + assert Qt3DRender.QTexture2DMultisample is not None + assert Qt3DRender.QLevelOfDetailSwitch is not None + assert Qt3DRender.QRenderStateSet is not None + assert Qt3DRender.QViewport is not None + assert Qt3DRender.QObjectPicker is not None + assert Qt3DRender.QPolygonOffset is not None + assert Qt3DRender.QRenderSettings is not None + assert Qt3DRender.QFrontFace is not None + assert Qt3DRender.QTexture3D is not None + assert Qt3DRender.QTextureBuffer is not None + assert Qt3DRender.QTechniqueFilter is not None + assert Qt3DRender.QLayerFilter is not None + assert Qt3DRender.QFilterKey is not None + assert Qt3DRender.QRenderSurfaceSelector is not None + assert Qt3DRender.QEnvironmentLight is not None + assert Qt3DRender.QMemoryBarrier is not None + assert Qt3DRender.QNoDepthMask is not None + assert Qt3DRender.QBlitFramebuffer is not None + assert Qt3DRender.QGraphicsApiFilter is not None + assert Qt3DRender.QAbstractTexture is not None + assert Qt3DRender.QRenderCaptureReply is not None + assert Qt3DRender.QAbstractLight is not None + assert Qt3DRender.QAbstractRayCaster is not None + assert Qt3DRender.QDirectionalLight is not None + assert Qt3DRender.QDispatchCompute is not None + assert Qt3DRender.QBufferDataGenerator is not None + assert Qt3DRender.QPointLight is not None + assert Qt3DRender.QStencilTestArguments is not None + assert Qt3DRender.QTexture1D is not None + assert Qt3DRender.QCameraSelector is not None + assert Qt3DRender.QProximityFilter is not None + assert Qt3DRender.QTexture1DArray is not None + assert Qt3DRender.QBlendEquation is not None + assert Qt3DRender.QTextureImageDataGenerator is not None + assert Qt3DRender.QSpotLight is not None + assert Qt3DRender.QEffect is not None + assert Qt3DRender.QSeamlessCubemap is not None + assert Qt3DRender.QTexture2DMultisampleArray is not None + assert Qt3DRender.QComputeCommand is not None + assert Qt3DRender.QFrameGraphNode is not None + assert Qt3DRender.QSortPolicy is not None + assert Qt3DRender.QTextureImageData is not None + assert Qt3DRender.QCamera is not None + assert Qt3DRender.QGeometry is not None + assert Qt3DRender.QScreenRayCaster is not None + assert Qt3DRender.QClipPlane is not None + assert Qt3DRender.QMultiSampleAntiAliasing is not None + assert Qt3DRender.QRayCasterHit is not None + assert Qt3DRender.QAbstractTextureImage is not None + assert Qt3DRender.QNoDraw is not None + assert Qt3DRender.QPickEvent is not None + assert Qt3DRender.QRenderCapture is not None + assert Qt3DRender.QDepthTest is not None + assert Qt3DRender.QParameter is not None + assert Qt3DRender.QLevelOfDetail is not None + assert Qt3DRender.QGeometryFactory is not None + assert Qt3DRender.QTexture2D is not None + assert Qt3DRender.QRenderAspect is not None + assert Qt3DRender.QPaintedTextureImage is not None + assert Qt3DRender.QDithering is not None + assert Qt3DRender.QTextureGenerator is not None + assert Qt3DRender.QBlendEquationArguments is not None + assert Qt3DRender.QLevelOfDetailBoundingSphere is not None + assert Qt3DRender.QColorMask is not None + assert Qt3DRender.QSceneLoader is not None + assert Qt3DRender.QTextureLoader is not None + assert Qt3DRender.QShaderProgram is not None + assert Qt3DRender.QTextureCubeMap is not None + assert Qt3DRender.QTexture2DArray is not None + assert Qt3DRender.QTextureImage is not None + assert Qt3DRender.QCameraLens is not None + assert Qt3DRender.QRenderTargetOutput is not None + assert Qt3DRender.QShaderProgramBuilder is not None + assert Qt3DRender.QTechnique is not None + assert Qt3DRender.QShaderData is not None diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qtcharts.py b/openpype/vendor/python/python_2/qtpy/tests/test_qtcharts.py new file mode 100644 index 00000000000..4c72dbc30db --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qtcharts.py @@ -0,0 +1,11 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYSIDE2 + + +@pytest.mark.skipif(not PYSIDE2, reason="Only available by default in PySide2") +def test_qtcharts(): + """Test the qtpy.QtCharts namespace""" + from qtpy import QtCharts + assert QtCharts.QtCharts.QChart is not None diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qtcore.py b/openpype/vendor/python/python_2/qtpy/tests/test_qtcore.py new file mode 100644 index 00000000000..81c1e6c495f --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qtcore.py @@ -0,0 +1,29 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2, QtCore + +"""Test QtCore.""" + + +def test_qtmsghandler(): + """Test qtpy.QtMsgHandler""" + assert QtCore.qInstallMessageHandler is not None + + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), + reason="Targeted to PyQt5 or PySide2") +def test_DateTime_toPython(): + """Test QDateTime.toPython""" + assert QtCore.QDateTime.toPython is not None + + +@pytest.mark.skipif(PYSIDE2, + reason="Doesn't seem to be present on PySide2") +def test_QtCore_SignalInstance(): + class ClassWithSignal(QtCore.QObject): + signal = QtCore.Signal() + + instance = ClassWithSignal() + + assert isinstance(instance.signal, QtCore.SignalInstance) diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qtdatavisualization.py b/openpype/vendor/python/python_2/qtpy/tests/test_qtdatavisualization.py new file mode 100644 index 00000000000..8e287da622f --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qtdatavisualization.py @@ -0,0 +1,86 @@ +from __future__ import absolute_import + +import sys + +import pytest +from qtpy import PYQT5, PYSIDE2 +from qtpy.py3compat import PY3 + +@pytest.mark.skipif( + sys.platform != "win32" or not (PYQT5 or PYSIDE2) or PY3, + reason="Only available in Qt5 bindings and Python 2 on Windows") +def test_qtdatavisualization(): + """Test the qtpy.QtDataVisualization namespace""" + # QtDataVisualization + assert qtpy.QtDataVisualization.QScatter3DSeries is not None + assert qtpy.QtDataVisualization.QSurfaceDataItem is not None + assert qtpy.QtDataVisualization.QSurface3DSeries is not None + assert qtpy.QtDataVisualization.QAbstract3DInputHandler is not None + assert qtpy.QtDataVisualization.QHeightMapSurfaceDataProxy is not None + assert qtpy.QtDataVisualization.QAbstractDataProxy is not None + assert qtpy.QtDataVisualization.Q3DCamera is not None + assert qtpy.QtDataVisualization.QAbstract3DGraph is not None + assert qtpy.QtDataVisualization.QCustom3DVolume is not None + assert qtpy.QtDataVisualization.Q3DInputHandler is not None + assert qtpy.QtDataVisualization.QBarDataProxy is not None + assert qtpy.QtDataVisualization.QSurfaceDataProxy is not None + assert qtpy.QtDataVisualization.QScatterDataItem is not None + assert qtpy.QtDataVisualization.Q3DLight is not None + assert qtpy.QtDataVisualization.QScatterDataProxy is not None + assert qtpy.QtDataVisualization.QValue3DAxis is not None + assert qtpy.QtDataVisualization.Q3DBars is not None + assert qtpy.QtDataVisualization.QBarDataItem is not None + assert qtpy.QtDataVisualization.QItemModelBarDataProxy is not None + assert qtpy.QtDataVisualization.Q3DTheme is not None + assert qtpy.QtDataVisualization.QCustom3DItem is not None + assert qtpy.QtDataVisualization.QItemModelScatterDataProxy is not None + assert qtpy.QtDataVisualization.QValue3DAxisFormatter is not None + assert qtpy.QtDataVisualization.QItemModelSurfaceDataProxy is not None + assert qtpy.QtDataVisualization.Q3DScatter is not None + assert qtpy.QtDataVisualization.QTouch3DInputHandler is not None + assert qtpy.QtDataVisualization.QBar3DSeries is not None + assert qtpy.QtDataVisualization.QAbstract3DAxis is not None + assert qtpy.QtDataVisualization.Q3DScene is not None + assert qtpy.QtDataVisualization.QCategory3DAxis is not None + assert qtpy.QtDataVisualization.QAbstract3DSeries is not None + assert qtpy.QtDataVisualization.Q3DObject is not None + assert qtpy.QtDataVisualization.QCustom3DLabel is not None + assert qtpy.QtDataVisualization.Q3DSurface is not None + assert qtpy.QtDataVisualization.QLogValue3DAxisFormatter is not None + + # QtDatavisualization + assert qtpy.QtDatavisualization.QScatter3DSeries is not None + assert qtpy.QtDatavisualization.QSurfaceDataItem is not None + assert qtpy.QtDatavisualization.QSurface3DSeries is not None + assert qtpy.QtDatavisualization.QAbstract3DInputHandler is not None + assert qtpy.QtDatavisualization.QHeightMapSurfaceDataProxy is not None + assert qtpy.QtDatavisualization.QAbstractDataProxy is not None + assert qtpy.QtDatavisualization.Q3DCamera is not None + assert qtpy.QtDatavisualization.QAbstract3DGraph is not None + assert qtpy.QtDatavisualization.QCustom3DVolume is not None + assert qtpy.QtDatavisualization.Q3DInputHandler is not None + assert qtpy.QtDatavisualization.QBarDataProxy is not None + assert qtpy.QtDatavisualization.QSurfaceDataProxy is not None + assert qtpy.QtDatavisualization.QScatterDataItem is not None + assert qtpy.QtDatavisualization.Q3DLight is not None + assert qtpy.QtDatavisualization.QScatterDataProxy is not None + assert qtpy.QtDatavisualization.QValue3DAxis is not None + assert qtpy.QtDatavisualization.Q3DBars is not None + assert qtpy.QtDatavisualization.QBarDataItem is not None + assert qtpy.QtDatavisualization.QItemModelBarDataProxy is not None + assert qtpy.QtDatavisualization.Q3DTheme is not None + assert qtpy.QtDatavisualization.QCustom3DItem is not None + assert qtpy.QtDatavisualization.QItemModelScatterDataProxy is not None + assert qtpy.QtDatavisualization.QValue3DAxisFormatter is not None + assert qtpy.QtDatavisualization.QItemModelSurfaceDataProxy is not None + assert qtpy.QtDatavisualization.Q3DScatter is not None + assert qtpy.QtDatavisualization.QTouch3DInputHandler is not None + assert qtpy.QtDatavisualization.QBar3DSeries is not None + assert qtpy.QtDatavisualization.QAbstract3DAxis is not None + assert qtpy.QtDatavisualization.Q3DScene is not None + assert qtpy.QtDatavisualization.QCategory3DAxis is not None + assert qtpy.QtDatavisualization.QAbstract3DSeries is not None + assert qtpy.QtDatavisualization.Q3DObject is not None + assert qtpy.QtDatavisualization.QCustom3DLabel is not None + assert qtpy.QtDatavisualization.Q3DSurface is not None + assert qtpy.QtDatavisualization.QLogValue3DAxisFormatter is not None diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qtdesigner.py b/openpype/vendor/python/python_2/qtpy/tests/test_qtdesigner.py new file mode 100644 index 00000000000..0327c6f7e3c --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qtdesigner.py @@ -0,0 +1,28 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYSIDE2, PYSIDE + +@pytest.mark.skipif(PYSIDE2 or PYSIDE, reason="QtDesigner is not avalaible in PySide/PySide2") +def test_qtdesigner(): + from qtpy import QtDesigner + """Test the qtpy.QtDesigner namespace""" + assert QtDesigner.QAbstractExtensionFactory is not None + assert QtDesigner.QAbstractExtensionManager is not None + assert QtDesigner.QDesignerActionEditorInterface is not None + assert QtDesigner.QDesignerContainerExtension is not None + assert QtDesigner.QDesignerCustomWidgetCollectionInterface is not None + assert QtDesigner.QDesignerCustomWidgetInterface is not None + assert QtDesigner.QDesignerFormEditorInterface is not None + assert QtDesigner.QDesignerFormWindowCursorInterface is not None + assert QtDesigner.QDesignerFormWindowInterface is not None + assert QtDesigner.QDesignerFormWindowManagerInterface is not None + assert QtDesigner.QDesignerMemberSheetExtension is not None + assert QtDesigner.QDesignerObjectInspectorInterface is not None + assert QtDesigner.QDesignerPropertyEditorInterface is not None + assert QtDesigner.QDesignerPropertySheetExtension is not None + assert QtDesigner.QDesignerTaskMenuExtension is not None + assert QtDesigner.QDesignerWidgetBoxInterface is not None + assert QtDesigner.QExtensionFactory is not None + assert QtDesigner.QExtensionManager is not None + assert QtDesigner.QFormBuilder is not None \ No newline at end of file diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qthelp.py b/openpype/vendor/python/python_2/qtpy/tests/test_qthelp.py new file mode 100644 index 00000000000..2b70ca755c7 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qthelp.py @@ -0,0 +1,22 @@ +"""Test for QtHelp namespace.""" + +from __future__ import absolute_import + +import pytest + + +def test_qthelp(): + """Test the qtpy.QtHelp namespace.""" + from qtpy import QtHelp + + assert QtHelp.QHelpContentItem is not None + assert QtHelp.QHelpContentModel is not None + assert QtHelp.QHelpContentWidget is not None + assert QtHelp.QHelpEngine is not None + assert QtHelp.QHelpEngineCore is not None + assert QtHelp.QHelpIndexModel is not None + assert QtHelp.QHelpIndexWidget is not None + assert QtHelp.QHelpSearchEngine is not None + assert QtHelp.QHelpSearchQuery is not None + assert QtHelp.QHelpSearchQueryWidget is not None + assert QtHelp.QHelpSearchResultWidget is not None diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qtlocation.py b/openpype/vendor/python/python_2/qtpy/tests/test_qtlocation.py new file mode 100644 index 00000000000..78bf93374fc --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qtlocation.py @@ -0,0 +1,48 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qtlocation(): + """Test the qtpy.QtLocation namespace""" + from qtpy import QtLocation + assert QtLocation.QGeoCodeReply is not None + assert QtLocation.QGeoCodingManager is not None + assert QtLocation.QGeoCodingManagerEngine is not None + assert QtLocation.QGeoManeuver is not None + assert QtLocation.QGeoRoute is not None + assert QtLocation.QGeoRouteReply is not None + assert QtLocation.QGeoRouteRequest is not None + assert QtLocation.QGeoRouteSegment is not None + assert QtLocation.QGeoRoutingManager is not None + assert QtLocation.QGeoRoutingManagerEngine is not None + assert QtLocation.QGeoServiceProvider is not None + #assert QtLocation.QGeoServiceProviderFactory is not None + assert QtLocation.QPlace is not None + assert QtLocation.QPlaceAttribute is not None + assert QtLocation.QPlaceCategory is not None + assert QtLocation.QPlaceContactDetail is not None + assert QtLocation.QPlaceContent is not None + assert QtLocation.QPlaceContentReply is not None + assert QtLocation.QPlaceContentRequest is not None + assert QtLocation.QPlaceDetailsReply is not None + assert QtLocation.QPlaceEditorial is not None + assert QtLocation.QPlaceIcon is not None + assert QtLocation.QPlaceIdReply is not None + assert QtLocation.QPlaceImage is not None + assert QtLocation.QPlaceManager is not None + assert QtLocation.QPlaceManagerEngine is not None + assert QtLocation.QPlaceMatchReply is not None + assert QtLocation.QPlaceMatchRequest is not None + assert QtLocation.QPlaceProposedSearchResult is not None + assert QtLocation.QPlaceRatings is not None + assert QtLocation.QPlaceReply is not None + assert QtLocation.QPlaceResult is not None + assert QtLocation.QPlaceReview is not None + assert QtLocation.QPlaceSearchReply is not None + assert QtLocation.QPlaceSearchRequest is not None + assert QtLocation.QPlaceSearchResult is not None + assert QtLocation.QPlaceSearchSuggestionReply is not None + assert QtLocation.QPlaceSupplier is not None + assert QtLocation.QPlaceUser is not None diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qtmultimedia.py b/openpype/vendor/python/python_2/qtpy/tests/test_qtmultimedia.py new file mode 100644 index 00000000000..1fc7ec97b8c --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qtmultimedia.py @@ -0,0 +1,18 @@ +from __future__ import absolute_import +import os +import sys + +import pytest + + +@pytest.mark.skipif(sys.version_info[0] == 3, + reason="Conda packages don't seem to include QtMultimedia") +def test_qtmultimedia(): + """Test the qtpy.QtMultimedia namespace""" + from qtpy import QtMultimedia + + assert QtMultimedia.QAbstractVideoBuffer is not None + assert QtMultimedia.QAudio is not None + assert QtMultimedia.QAudioDeviceInfo is not None + assert QtMultimedia.QAudioInput is not None + assert QtMultimedia.QSound is not None diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qtmultimediawidgets.py b/openpype/vendor/python/python_2/qtpy/tests/test_qtmultimediawidgets.py new file mode 100644 index 00000000000..bd659e5183d --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qtmultimediawidgets.py @@ -0,0 +1,18 @@ +from __future__ import absolute_import +import os +import sys + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +@pytest.mark.skipif(sys.version_info[0] == 3, + reason="Conda packages don't seem to include QtMultimedia") +def test_qtmultimediawidgets(): + """Test the qtpy.QtMultimediaWidgets namespace""" + from qtpy import QtMultimediaWidgets + + assert QtMultimediaWidgets.QCameraViewfinder is not None + assert QtMultimediaWidgets.QGraphicsVideoItem is not None + assert QtMultimediaWidgets.QVideoWidget is not None + #assert QtMultimediaWidgets.QVideoWidgetControl is not None diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qtnetwork.py b/openpype/vendor/python/python_2/qtpy/tests/test_qtnetwork.py new file mode 100644 index 00000000000..7f645910a5f --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qtnetwork.py @@ -0,0 +1,43 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYSIDE, PYSIDE2, QtNetwork + + +def test_qtnetwork(): + """Test the qtpy.QtNetwork namespace""" + assert QtNetwork.QAbstractNetworkCache is not None + assert QtNetwork.QNetworkCacheMetaData is not None + if not PYSIDE and not PYSIDE2: + assert QtNetwork.QHttpMultiPart is not None + assert QtNetwork.QHttpPart is not None + assert QtNetwork.QNetworkAccessManager is not None + assert QtNetwork.QNetworkCookie is not None + assert QtNetwork.QNetworkCookieJar is not None + assert QtNetwork.QNetworkDiskCache is not None + assert QtNetwork.QNetworkReply is not None + assert QtNetwork.QNetworkRequest is not None + assert QtNetwork.QNetworkConfigurationManager is not None + assert QtNetwork.QNetworkConfiguration is not None + assert QtNetwork.QNetworkSession is not None + assert QtNetwork.QAuthenticator is not None + assert QtNetwork.QHostAddress is not None + assert QtNetwork.QHostInfo is not None + assert QtNetwork.QNetworkAddressEntry is not None + assert QtNetwork.QNetworkInterface is not None + assert QtNetwork.QNetworkProxy is not None + assert QtNetwork.QNetworkProxyFactory is not None + assert QtNetwork.QNetworkProxyQuery is not None + assert QtNetwork.QAbstractSocket is not None + assert QtNetwork.QLocalServer is not None + assert QtNetwork.QLocalSocket is not None + assert QtNetwork.QTcpServer is not None + assert QtNetwork.QTcpSocket is not None + assert QtNetwork.QUdpSocket is not None + if not PYSIDE: + assert QtNetwork.QSslCertificate is not None + assert QtNetwork.QSslCipher is not None + assert QtNetwork.QSslConfiguration is not None + assert QtNetwork.QSslError is not None + assert QtNetwork.QSslKey is not None + assert QtNetwork.QSslSocket is not None diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qtpositioning.py b/openpype/vendor/python/python_2/qtpy/tests/test_qtpositioning.py new file mode 100644 index 00000000000..f6b5bffa9c6 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qtpositioning.py @@ -0,0 +1,28 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qtpositioning(): + """Test the qtpy.QtPositioning namespace""" + from qtpy import QtPositioning + assert QtPositioning.QGeoAddress is not None + assert QtPositioning.QGeoAreaMonitorInfo is not None + assert QtPositioning.QGeoAreaMonitorSource is not None + assert QtPositioning.QGeoCircle is not None + assert QtPositioning.QGeoCoordinate is not None + assert QtPositioning.QGeoLocation is not None + assert QtPositioning.QGeoPath is not None + # CI for Python 2.7 and 3.6 uses Qt 5.9 + # assert QtPositioning.QGeoPolygon is not None # New in Qt 5.10 + assert QtPositioning.QGeoPositionInfo is not None + assert QtPositioning.QGeoPositionInfoSource is not None + # QGeoPositionInfoSourceFactory is not available in PyQt + # assert QtPositioning.QGeoPositionInfoSourceFactory is not None # New in Qt 5.2 + # assert QtPositioning.QGeoPositionInfoSourceFactoryV2 is not None # New in Qt 5.14 + assert QtPositioning.QGeoRectangle is not None + assert QtPositioning.QGeoSatelliteInfo is not None + assert QtPositioning.QGeoSatelliteInfoSource is not None + assert QtPositioning.QGeoShape is not None + assert QtPositioning.QNmeaPositionInfoSource is not None diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qtprintsupport.py b/openpype/vendor/python/python_2/qtpy/tests/test_qtprintsupport.py new file mode 100644 index 00000000000..2e8f786136d --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qtprintsupport.py @@ -0,0 +1,18 @@ +from __future__ import absolute_import + +import pytest +from qtpy import QtPrintSupport + + +def test_qtprintsupport(): + """Test the qtpy.QtPrintSupport namespace""" + assert QtPrintSupport.QAbstractPrintDialog is not None + assert QtPrintSupport.QPageSetupDialog is not None + assert QtPrintSupport.QPrintDialog is not None + assert QtPrintSupport.QPrintPreviewDialog is not None + assert QtPrintSupport.QPrintEngine is not None + assert QtPrintSupport.QPrinter is not None + assert QtPrintSupport.QPrinterInfo is not None + assert QtPrintSupport.QPrintPreviewWidget is not None + + diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qtqml.py b/openpype/vendor/python/python_2/qtpy/tests/test_qtqml.py new file mode 100644 index 00000000000..a6d7ca951f2 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qtqml.py @@ -0,0 +1,34 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qtqml(): + """Test the qtpy.QtQml namespace""" + from qtpy import QtQml + assert QtQml.QJSEngine is not None + assert QtQml.QJSValue is not None + assert QtQml.QJSValueIterator is not None + assert QtQml.QQmlAbstractUrlInterceptor is not None + assert QtQml.QQmlApplicationEngine is not None + assert QtQml.QQmlComponent is not None + assert QtQml.QQmlContext is not None + assert QtQml.QQmlEngine is not None + assert QtQml.QQmlImageProviderBase is not None + assert QtQml.QQmlError is not None + assert QtQml.QQmlExpression is not None + assert QtQml.QQmlExtensionPlugin is not None + assert QtQml.QQmlFileSelector is not None + assert QtQml.QQmlIncubationController is not None + assert QtQml.QQmlIncubator is not None + if not PYSIDE2: + # https://wiki.qt.io/Qt_for_Python_Missing_Bindings#QtQml + assert QtQml.QQmlListProperty is not None + assert QtQml.QQmlListReference is not None + assert QtQml.QQmlNetworkAccessManagerFactory is not None + assert QtQml.QQmlParserStatus is not None + assert QtQml.QQmlProperty is not None + assert QtQml.QQmlPropertyValueSource is not None + assert QtQml.QQmlScriptString is not None + assert QtQml.QQmlPropertyMap is not None diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qtquick.py b/openpype/vendor/python/python_2/qtpy/tests/test_qtquick.py new file mode 100644 index 00000000000..257fd7405e8 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qtquick.py @@ -0,0 +1,53 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qtquick(): + """Test the qtpy.QtQuick namespace""" + from qtpy import QtQuick + assert QtQuick.QQuickAsyncImageProvider is not None + if not PYSIDE2: + assert QtQuick.QQuickCloseEvent is not None + assert QtQuick.QQuickFramebufferObject is not None + assert QtQuick.QQuickImageProvider is not None + assert QtQuick.QQuickImageResponse is not None + assert QtQuick.QQuickItem is not None + assert QtQuick.QQuickItemGrabResult is not None + assert QtQuick.QQuickPaintedItem is not None + assert QtQuick.QQuickRenderControl is not None + assert QtQuick.QQuickTextDocument is not None + assert QtQuick.QQuickTextureFactory is not None + assert QtQuick.QQuickView is not None + assert QtQuick.QQuickWindow is not None + assert QtQuick.QSGAbstractRenderer is not None + assert QtQuick.QSGBasicGeometryNode is not None + assert QtQuick.QSGClipNode is not None + assert QtQuick.QSGDynamicTexture is not None + assert QtQuick.QSGEngine is not None + if not PYSIDE2: + assert QtQuick.QSGFlatColorMaterial is not None + assert QtQuick.QSGGeometry is not None + assert QtQuick.QSGGeometryNode is not None + #assert QtQuick.QSGImageNode is not None + if not PYSIDE2: + assert QtQuick.QSGMaterial is not None + assert QtQuick.QSGMaterialShader is not None + assert QtQuick.QSGMaterialType is not None + assert QtQuick.QSGNode is not None + assert QtQuick.QSGOpacityNode is not None + if not PYSIDE2: + assert QtQuick.QSGOpaqueTextureMaterial is not None + #assert QtQuick.QSGRectangleNode is not None + #assert QtQuick.QSGRenderNode is not None + #assert QtQuick.QSGRendererInterface is not None + assert QtQuick.QSGSimpleRectNode is not None + assert QtQuick.QSGSimpleTextureNode is not None + assert QtQuick.QSGTexture is not None + if not PYSIDE2: + assert QtQuick.QSGTextureMaterial is not None + assert QtQuick.QSGTextureProvider is not None + assert QtQuick.QSGTransformNode is not None + if not PYSIDE2: + assert QtQuick.QSGVertexColorMaterial is not None diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qtquickwidgets.py b/openpype/vendor/python/python_2/qtpy/tests/test_qtquickwidgets.py new file mode 100644 index 00000000000..0b41a8bd8ea --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qtquickwidgets.py @@ -0,0 +1,10 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qtquickwidgets(): + """Test the qtpy.QtQuickWidgets namespace""" + from qtpy import QtQuickWidgets + assert QtQuickWidgets.QQuickWidget is not None diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qtserialport.py b/openpype/vendor/python/python_2/qtpy/tests/test_qtserialport.py new file mode 100644 index 00000000000..26daaf76bbe --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qtserialport.py @@ -0,0 +1,12 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5 + +@pytest.mark.skipif(not PYQT5, reason="Only available in Qt5 bindings, but still not in PySide2") +def test_qtserialport(): + """Test the qtpy.QtSerialPort namespace""" + from qtpy import QtSerialPort + + assert QtSerialPort.QSerialPort is not None + assert QtSerialPort.QSerialPortInfo is not None diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qtsql.py b/openpype/vendor/python/python_2/qtpy/tests/test_qtsql.py new file mode 100644 index 00000000000..1e7404ffdcf --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qtsql.py @@ -0,0 +1,24 @@ +from __future__ import absolute_import + +import pytest +from qtpy import QtSql + +def test_qtsql(): + """Test the qtpy.QtSql namespace""" + assert QtSql.QSqlDatabase is not None + assert QtSql.QSqlDriverCreatorBase is not None + assert QtSql.QSqlDriver is not None + assert QtSql.QSqlError is not None + assert QtSql.QSqlField is not None + assert QtSql.QSqlIndex is not None + assert QtSql.QSqlQuery is not None + assert QtSql.QSqlRecord is not None + assert QtSql.QSqlResult is not None + assert QtSql.QSqlQueryModel is not None + assert QtSql.QSqlRelationalDelegate is not None + assert QtSql.QSqlRelation is not None + assert QtSql.QSqlRelationalTableModel is not None + assert QtSql.QSqlTableModel is not None + + # Following modules are not (yet) part of any wrapper: + # QSqlDriverCreator, QSqlDriverPlugin diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qtsvg.py b/openpype/vendor/python/python_2/qtpy/tests/test_qtsvg.py new file mode 100644 index 00000000000..74d8522e725 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qtsvg.py @@ -0,0 +1,13 @@ +from __future__ import absolute_import + +import pytest + + +def test_qtsvg(): + """Test the qtpy.QtSvg namespace""" + from qtpy import QtSvg + + assert QtSvg.QGraphicsSvgItem is not None + assert QtSvg.QSvgGenerator is not None + assert QtSvg.QSvgRenderer is not None + assert QtSvg.QSvgWidget is not None diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qttest.py b/openpype/vendor/python/python_2/qtpy/tests/test_qttest.py new file mode 100644 index 00000000000..5d2ab9e156d --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qttest.py @@ -0,0 +1,9 @@ +from __future__ import absolute_import + +import pytest +from qtpy import QtTest + + +def test_qttest(): + """Test the qtpy.QtTest namespace""" + assert QtTest.QTest is not None diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qtwebchannel.py b/openpype/vendor/python/python_2/qtpy/tests/test_qtwebchannel.py new file mode 100644 index 00000000000..2beb70c0afc --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qtwebchannel.py @@ -0,0 +1,13 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qtwebchannel(): + """Test the qtpy.QtWebChannel namespace""" + from qtpy import QtWebChannel + + assert QtWebChannel.QWebChannel is not None + assert QtWebChannel.QWebChannelAbstractTransport is not None + diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qtwebenginewidgets.py b/openpype/vendor/python/python_2/qtpy/tests/test_qtwebenginewidgets.py new file mode 100644 index 00000000000..77c8e1f5fb1 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qtwebenginewidgets.py @@ -0,0 +1,12 @@ +from __future__ import absolute_import + +import pytest +from qtpy import QtWebEngineWidgets + + +def test_qtwebenginewidgets(): + """Test the qtpy.QtWebSockets namespace""" + + assert QtWebEngineWidgets.QWebEnginePage is not None + assert QtWebEngineWidgets.QWebEngineView is not None + assert QtWebEngineWidgets.QWebEngineSettings is not None diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qtwebsockets.py b/openpype/vendor/python/python_2/qtpy/tests/test_qtwebsockets.py new file mode 100644 index 00000000000..5bdcc32565a --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qtwebsockets.py @@ -0,0 +1,15 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qtwebsockets(): + """Test the qtpy.QtWebSockets namespace""" + from qtpy import QtWebSockets + + assert QtWebSockets.QMaskGenerator is not None + assert QtWebSockets.QWebSocket is not None + assert QtWebSockets.QWebSocketCorsAuthenticator is not None + assert QtWebSockets.QWebSocketProtocol is not None + assert QtWebSockets.QWebSocketServer is not None diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qtwinextras.py b/openpype/vendor/python/python_2/qtpy/tests/test_qtwinextras.py new file mode 100644 index 00000000000..f41f9ff6998 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qtwinextras.py @@ -0,0 +1,29 @@ +from __future__ import absolute_import + +import os +import sys + +import pytest +from qtpy import PYSIDE2 + +@pytest.mark.skipif( + sys.platform != "win32" or os.environ['USE_CONDA'] == 'Yes', + reason="Only available in Qt5 bindings > 5.9 (only available with pip in the current CI setup) and Windows platform") +def test_qtwinextras(): + """Test the qtpy.QtWinExtras namespace""" + from qtpy import QtWinExtras + assert QtWinExtras.QWinJumpList is not None + assert QtWinExtras.QWinJumpListCategory is not None + assert QtWinExtras.QWinJumpListItem is not None + assert QtWinExtras.QWinTaskbarButton is not None + assert QtWinExtras.QWinTaskbarProgress is not None + assert QtWinExtras.QWinThumbnailToolBar is not None + assert QtWinExtras.QWinThumbnailToolButton is not None + if not PYSIDE2: # See https://bugreports.qt.io/browse/PYSIDE-1047 + assert QtWinExtras.QtWin is not None + + if PYSIDE2: + assert QtWinExtras.QWinColorizationChangeEvent is not None + assert QtWinExtras.QWinCompositionChangeEvent is not None + assert QtWinExtras.QWinEvent is not None + diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_qtxmlpatterns.py b/openpype/vendor/python/python_2/qtpy/tests/test_qtxmlpatterns.py new file mode 100644 index 00000000000..4c6d4cb9aad --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_qtxmlpatterns.py @@ -0,0 +1,25 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYSIDE2, PYSIDE + +def test_qtxmlpatterns(): + """Test the qtpy.QtXmlPatterns namespace""" + from qtpy import QtXmlPatterns + assert QtXmlPatterns.QAbstractMessageHandler is not None + assert QtXmlPatterns.QAbstractUriResolver is not None + assert QtXmlPatterns.QAbstractXmlNodeModel is not None + assert QtXmlPatterns.QAbstractXmlReceiver is not None + if not PYSIDE2 and not PYSIDE: + assert QtXmlPatterns.QSimpleXmlNodeModel is not None + assert QtXmlPatterns.QSourceLocation is not None + assert QtXmlPatterns.QXmlFormatter is not None + assert QtXmlPatterns.QXmlItem is not None + assert QtXmlPatterns.QXmlName is not None + assert QtXmlPatterns.QXmlNamePool is not None + assert QtXmlPatterns.QXmlNodeModelIndex is not None + assert QtXmlPatterns.QXmlQuery is not None + assert QtXmlPatterns.QXmlResultItems is not None + assert QtXmlPatterns.QXmlSchema is not None + assert QtXmlPatterns.QXmlSchemaValidator is not None + assert QtXmlPatterns.QXmlSerializer is not None diff --git a/openpype/vendor/python/python_2/qtpy/tests/test_uic.py b/openpype/vendor/python/python_2/qtpy/tests/test_uic.py new file mode 100644 index 00000000000..d7d3b599ec0 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/tests/test_uic.py @@ -0,0 +1,116 @@ +import os +import sys +import contextlib + +import pytest +from qtpy import PYQT5, PYSIDE2, PYSIDE, QtWidgets +from qtpy.QtWidgets import QComboBox + +if PYSIDE2 or PYSIDE: + pytest.importorskip("pyside2uic", reason="pyside2uic not installed") + +from qtpy import uic +from qtpy.uic import loadUi, loadUiType + + +QCOMBOBOX_SUBCLASS = """ +from qtpy.QtWidgets import QComboBox +class _QComboBoxSubclass(QComboBox): + pass +""" + +@contextlib.contextmanager +def enabled_qcombobox_subclass(tmpdir): + """ + Context manager that sets up a temporary module with a QComboBox subclass + and then removes it once we are done. + """ + + with open(tmpdir.join('qcombobox_subclass.py').strpath, 'w') as f: + f.write(QCOMBOBOX_SUBCLASS) + + sys.path.insert(0, tmpdir.strpath) + + yield + + sys.path.pop(0) + + +def get_qapp(icon_path=None): + """ + Helper function to return a QApplication instance + """ + qapp = QtWidgets.QApplication.instance() + if qapp is None: + qapp = QtWidgets.QApplication(['']) + return qapp + + +@pytest.mark.skipif(((PYSIDE2 or PYQT5) + and os.environ.get('CI', None) is not None), + reason="It segfaults in our CIs with PYSIDE2 or PYQT5") +def test_load_ui(): + """ + Make sure that the patched loadUi function behaves as expected with a + simple .ui file. + """ + app = get_qapp() + ui = loadUi(os.path.join(os.path.dirname(__file__), 'test.ui')) + assert isinstance(ui.pushButton, QtWidgets.QPushButton) + assert isinstance(ui.comboBox, QComboBox) + + +@pytest.mark.skipif(((PYSIDE2 or PYQT5) + and os.environ.get('CI', None) is not None), + reason="It segfaults in our CIs with PYSIDE2 or PYQT5") +def test_load_ui_type(): + """ + Make sure that the patched loadUiType function behaves as expected with a + simple .ui file. + """ + app = get_qapp() + ui_type, ui_base_type = loadUiType( + os.path.join(os.path.dirname(__file__), 'test.ui')) + assert ui_type.__name__ == 'Ui_Form' + + class Widget(ui_base_type, ui_type): + def __init__(self): + super(Widget, self).__init__() + self.setupUi(self) + + ui = Widget() + assert isinstance(ui, QtWidgets.QWidget) + assert isinstance(ui.pushButton, QtWidgets.QPushButton) + assert isinstance(ui.comboBox, QComboBox) + + +@pytest.mark.skipif(((PYSIDE2 or PYQT5) + and os.environ.get('CI', None) is not None), + reason="It segfaults in our CIs with PYSIDE2 or PYQT5") +def test_load_ui_custom_auto(tmpdir): + """ + Test that we can load a .ui file with custom widgets without having to + explicitly specify a dictionary of custom widgets, even in the case of + PySide. + """ + + app = get_qapp() + + with enabled_qcombobox_subclass(tmpdir): + from qcombobox_subclass import _QComboBoxSubclass + ui = loadUi(os.path.join(os.path.dirname(__file__), 'test_custom.ui')) + + assert isinstance(ui.pushButton, QtWidgets.QPushButton) + assert isinstance(ui.comboBox, _QComboBoxSubclass) + + +def test_load_full_uic(): + """Test that we load the full uic objects for PyQt5 and PyQt4.""" + QT_API = os.environ.get('QT_API', '').lower() + if QT_API.startswith('pyside'): + assert hasattr(uic, 'loadUi') + assert hasattr(uic, 'loadUiType') + else: + objects = ['compileUi', 'compileUiDir', 'loadUi', 'loadUiType', + 'widgetPluginPath'] + assert all([hasattr(uic, o) for o in objects]) diff --git a/openpype/vendor/python/python_2/qtpy/uic.py b/openpype/vendor/python/python_2/qtpy/uic.py new file mode 100644 index 00000000000..d26a25a15d4 --- /dev/null +++ b/openpype/vendor/python/python_2/qtpy/uic.py @@ -0,0 +1,277 @@ +import os + +from . import PYSIDE, PYSIDE2, PYQT4, PYQT5 +from .QtWidgets import QComboBox + + +if PYQT5: + + from PyQt5.uic import * + +elif PYQT4: + + from PyQt4.uic import * + +else: + + __all__ = ['loadUi', 'loadUiType'] + + # In PySide, loadUi does not exist, so we define it using QUiLoader, and + # then make sure we expose that function. This is adapted from qt-helpers + # which was released under a 3-clause BSD license: + # qt-helpers - a common front-end to various Qt modules + # + # Copyright (c) 2015, Chris Beaumont and Thomas Robitaille + # + # All rights reserved. + # + # Redistribution and use in source and binary forms, with or without + # modification, are permitted provided that the following conditions are + # met: + # + # * Redistributions of source code must retain the above copyright + # notice, this list of conditions and the following disclaimer. + # * Redistributions in binary form must reproduce the above copyright + # notice, this list of conditions and the following disclaimer in the + # documentation and/or other materials provided with the + # distribution. + # * Neither the name of the Glue project nor the names of its contributors + # may be used to endorse or promote products derived from this software + # without specific prior written permission. + # + # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + # + # Which itself was based on the solution at + # + # https://gist.github.com/cpbotha/1b42a20c8f3eb9bb7cb8 + # + # which was released under the MIT license: + # + # Copyright (c) 2011 Sebastian Wiesner + # Modifications by Charl Botha + # + # Permission is hereby granted, free of charge, to any person obtaining a + # copy of this software and associated documentation files (the "Software"), + # to deal in the Software without restriction, including without limitation + # the rights to use, copy, modify, merge, publish, distribute, sublicense, + # and/or sell copies of the Software, and to permit persons to whom the + # Software is furnished to do so, subject to the following conditions: + # + # The above copyright notice and this permission notice shall be included in + # all copies or substantial portions of the Software. + # + # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + # DEALINGS IN THE SOFTWARE. + + if PYSIDE: + from PySide.QtCore import QMetaObject + from PySide.QtUiTools import QUiLoader + try: + from pysideuic import compileUi + except ImportError: + pass + elif PYSIDE2: + from PySide2.QtCore import QMetaObject + from PySide2.QtUiTools import QUiLoader + try: + from pyside2uic import compileUi + except ImportError: + pass + + class UiLoader(QUiLoader): + """ + Subclass of :class:`~PySide.QtUiTools.QUiLoader` to create the user + interface in a base instance. + + Unlike :class:`~PySide.QtUiTools.QUiLoader` itself this class does not + create a new instance of the top-level widget, but creates the user + interface in an existing instance of the top-level class if needed. + + This mimics the behaviour of :func:`PyQt4.uic.loadUi`. + """ + + def __init__(self, baseinstance, customWidgets=None): + """ + Create a loader for the given ``baseinstance``. + + The user interface is created in ``baseinstance``, which must be an + instance of the top-level class in the user interface to load, or a + subclass thereof. + + ``customWidgets`` is a dictionary mapping from class name to class + object for custom widgets. Usually, this should be done by calling + registerCustomWidget on the QUiLoader, but with PySide 1.1.2 on + Ubuntu 12.04 x86_64 this causes a segfault. + + ``parent`` is the parent object of this loader. + """ + + QUiLoader.__init__(self, baseinstance) + + self.baseinstance = baseinstance + + if customWidgets is None: + self.customWidgets = {} + else: + self.customWidgets = customWidgets + + def createWidget(self, class_name, parent=None, name=''): + """ + Function that is called for each widget defined in ui file, + overridden here to populate baseinstance instead. + """ + + if parent is None and self.baseinstance: + # supposed to create the top-level widget, return the base + # instance instead + return self.baseinstance + + else: + + # For some reason, Line is not in the list of available + # widgets, but works fine, so we have to special case it here. + if class_name in self.availableWidgets() or class_name == 'Line': + # create a new widget for child widgets + widget = QUiLoader.createWidget(self, class_name, parent, name) + + else: + # If not in the list of availableWidgets, must be a custom + # widget. This will raise KeyError if the user has not + # supplied the relevant class_name in the dictionary or if + # customWidgets is empty. + try: + widget = self.customWidgets[class_name](parent) + except KeyError: + raise Exception('No custom widget ' + class_name + ' ' + 'found in customWidgets') + + if self.baseinstance: + # set an attribute for the new child widget on the base + # instance, just like PyQt4.uic.loadUi does. + setattr(self.baseinstance, name, widget) + + return widget + + def _get_custom_widgets(ui_file): + """ + This function is used to parse a ui file and look for the + section, then automatically load all the custom widget classes. + """ + + import sys + import importlib + from xml.etree.ElementTree import ElementTree + + # Parse the UI file + etree = ElementTree() + ui = etree.parse(ui_file) + + # Get the customwidgets section + custom_widgets = ui.find('customwidgets') + + if custom_widgets is None: + return {} + + custom_widget_classes = {} + + for custom_widget in list(custom_widgets): + + cw_class = custom_widget.find('class').text + cw_header = custom_widget.find('header').text + + module = importlib.import_module(cw_header) + + custom_widget_classes[cw_class] = getattr(module, cw_class) + + return custom_widget_classes + + def loadUi(uifile, baseinstance=None, workingDirectory=None): + """ + Dynamically load a user interface from the given ``uifile``. + + ``uifile`` is a string containing a file name of the UI file to load. + + If ``baseinstance`` is ``None``, the a new instance of the top-level + widget will be created. Otherwise, the user interface is created within + the given ``baseinstance``. In this case ``baseinstance`` must be an + instance of the top-level widget class in the UI file to load, or a + subclass thereof. In other words, if you've created a ``QMainWindow`` + interface in the designer, ``baseinstance`` must be a ``QMainWindow`` + or a subclass thereof, too. You cannot load a ``QMainWindow`` UI file + with a plain :class:`~PySide.QtGui.QWidget` as ``baseinstance``. + + :method:`~PySide.QtCore.QMetaObject.connectSlotsByName()` is called on + the created user interface, so you can implemented your slots according + to its conventions in your widget class. + + Return ``baseinstance``, if ``baseinstance`` is not ``None``. Otherwise + return the newly created instance of the user interface. + """ + + # We parse the UI file and import any required custom widgets + customWidgets = _get_custom_widgets(uifile) + + loader = UiLoader(baseinstance, customWidgets) + + if workingDirectory is not None: + loader.setWorkingDirectory(workingDirectory) + + widget = loader.load(uifile) + QMetaObject.connectSlotsByName(widget) + return widget + + def loadUiType(uifile, from_imports=False): + """Load a .ui file and return the generated form class and + the Qt base class. + + The "loadUiType" command convert the ui file to py code + in-memory first and then execute it in a special frame to + retrieve the form_class. + + Credit: https://stackoverflow.com/a/14195313/15954282 + """ + + import sys + if sys.version_info >= (3, 0): + from io import StringIO + else: + from io import BytesIO as StringIO + from xml.etree.ElementTree import ElementTree + from . import QtWidgets + + # Parse the UI file + etree = ElementTree() + ui = etree.parse(uifile) + + widget_class = ui.find('widget').get('class') + form_class = ui.find('class').text + + with open(uifile, 'r') as fd: + code_stream = StringIO() + frame = {} + + compileUi(fd, code_stream, indent=0, from_imports=from_imports) + pyc = compile(code_stream.getvalue(), '', 'exec') + exec(pyc, frame) + + # Fetch the base_class and form class based on their type in the + # xml from designer + form_class = frame['Ui_%s' % form_class] + base_class = getattr(QtWidgets, widget_class) + + return form_class, base_class From 8277adc7df0cb253ab35b3a67c4432955e5e5510 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 14:13:48 +0100 Subject: [PATCH 109/213] updated QtPy to 2.3.0 --- poetry.lock | 17 ++++++++++------- pyproject.toml | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/poetry.lock b/poetry.lock index 5873ce50b64..a400a19c278 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1316,11 +1316,17 @@ six = "*" [[package]] name = "qtpy" -version = "1.11.3" -description = "Provides an abstraction layer on top of the various Qt bindings (PyQt5, PyQt4 and PySide) and additional custom QWidgets." +version = "2.3.0" +description = "Provides an abstraction layer on top of the various Qt bindings (PyQt5/6 and PySide2/6)." category = "main" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" +python-versions = ">=3.7" + +[package.dependencies] +packaging = "*" + +[package.extras] +test = ["pytest (>=6,!=7.0.0,!=7.0.1)", "pytest-cov (>=3.0.0)", "pytest-qt"] [[package]] name = "recommonmark" @@ -2925,10 +2931,7 @@ qtawesome = [ {file = "QtAwesome-0.7.3-py2.py3-none-any.whl", hash = "sha256:ddf4530b4af71cec13b24b88a4cdb56ec85b1e44c43c42d0698804c7137b09b0"}, {file = "QtAwesome-0.7.3.tar.gz", hash = "sha256:b98b9038d19190e83ab26d91c4d8fc3a36591ee2bc7f5016d4438b8240d097bd"}, ] -qtpy = [ - {file = "QtPy-1.11.3-py2.py3-none-any.whl", hash = "sha256:e121fbee8e95645af29c5a4aceba8d657991551fc1aa3b6b6012faf4725a1d20"}, - {file = "QtPy-1.11.3.tar.gz", hash = "sha256:d427addd37386a8d786db81864a5536700861d95bf085cb31d1bea855d699557"}, -] +qtpy = [] recommonmark = [ {file = "recommonmark-0.7.1-py2.py3-none-any.whl", hash = "sha256:1b1db69af0231efce3fa21b94ff627ea33dee7079a01dd0a7f8482c3da148b3f"}, {file = "recommonmark-0.7.1.tar.gz", hash = "sha256:bdb4db649f2222dcd8d2d844f0006b958d627f732415d399791ee436a3686d67"}, diff --git a/pyproject.toml b/pyproject.toml index 2a4bb5e86e2..72193f732e6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,7 +52,7 @@ pyblish-base = "^1.8.8" pynput = "^1.7.2" # idle manager in tray pymongo = "^3.11.2" "Qt.py" = "^1.3.3" -qtpy = "^1.11.3" +QtPy = "^2.3.0" qtawesome = "0.7.3" speedcopy = "^2.1" six = "^1.15" From 8e19d0957d97396133bc817c383c6fd7ab60aee2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 15:19:40 +0100 Subject: [PATCH 110/213] use class attributes from classes instead of objects --- openpype/modules/sync_server/tray/widgets.py | 12 ++++++++---- openpype/tools/launcher/window.py | 2 +- .../publisher/publish_report_viewer/widgets.py | 2 +- .../publisher/widgets/list_view_widgets.py | 9 ++++++--- openpype/tools/publisher/widgets/widgets.py | 6 ++++-- openpype/tools/pyblish_pype/view.py | 4 ++-- openpype/tools/sceneinventory/view.py | 8 ++++---- .../settings/settings/breadcrumbs_widget.py | 4 ++-- .../standalonepublish/widgets/widget_asset.py | 10 ++++++++-- openpype/tools/utils/assets_widget.py | 10 +++++++--- openpype/tools/utils/lib.py | 17 ++++++++++++----- openpype/tools/utils/tasks_widget.py | 5 ++++- 12 files changed, 59 insertions(+), 30 deletions(-) diff --git a/openpype/modules/sync_server/tray/widgets.py b/openpype/modules/sync_server/tray/widgets.py index b9ef45727a2..75a6d20d3cf 100644 --- a/openpype/modules/sync_server/tray/widgets.py +++ b/openpype/modules/sync_server/tray/widgets.py @@ -156,8 +156,10 @@ def refresh(self): if selected_index and \ selected_index.isValid() and \ not self._selection_changed: - mode = QtCore.QItemSelectionModel.Select | \ - QtCore.QItemSelectionModel.Rows + mode = ( + QtCore.QItemSelectionModel.Select + | QtCore.QItemSelectionModel.Rows + ) self.project_list.selectionModel().select(selected_index, mode) if self.current_project: @@ -271,8 +273,10 @@ def _set_selection(self): for selected_id in self._selected_ids: index = self.model.get_index(selected_id) if index and index.isValid(): - mode = QtCore.QItemSelectionModel.Select | \ - QtCore.QItemSelectionModel.Rows + mode = ( + QtCore.QItemSelectionModel.Select + | QtCore.QItemSelectionModel.Rows + ) self.selection_model.select(index, mode) existing_ids.add(selected_id) diff --git a/openpype/tools/launcher/window.py b/openpype/tools/launcher/window.py index f68fc4befc7..fcc8c0ba385 100644 --- a/openpype/tools/launcher/window.py +++ b/openpype/tools/launcher/window.py @@ -40,7 +40,7 @@ def __init__(self, parent=None, mode=ListMode): # Workaround for scrolling being super slow or fast when # toggling between the two visual modes - self.setVerticalScrollMode(self.ScrollPerPixel) + self.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) self.setObjectName("IconView") self._mode = None diff --git a/openpype/tools/publisher/publish_report_viewer/widgets.py b/openpype/tools/publisher/publish_report_viewer/widgets.py index 7066efed325..84af9bb1b86 100644 --- a/openpype/tools/publisher/publish_report_viewer/widgets.py +++ b/openpype/tools/publisher/publish_report_viewer/widgets.py @@ -80,7 +80,7 @@ def __init__(self, parent): view.setTextElideMode(QtCore.Qt.ElideLeft) view.setHeaderHidden(True) view.setAlternatingRowColors(True) - view.setVerticalScrollMode(view.ScrollPerPixel) + view.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) model = PluginLoadReportModel() view.setModel(model) diff --git a/openpype/tools/publisher/widgets/list_view_widgets.py b/openpype/tools/publisher/widgets/list_view_widgets.py index 57c1e769e48..28feeee7aa8 100644 --- a/openpype/tools/publisher/widgets/list_view_widgets.py +++ b/openpype/tools/publisher/widgets/list_view_widgets.py @@ -1031,17 +1031,20 @@ def set_selected_items( selection_model.setCurrentIndex( first_index, - selection_model.ClearAndSelect | selection_model.Rows + QtCore.QItemSelectionModel.ClearAndSelect + | QtCore.QItemSelectionModel.Rows ) for index in select_indexes: proxy_index = proxy_model.mapFromSource(index) selection_model.select( proxy_index, - selection_model.Select | selection_model.Rows + QtCore.QItemSelectionModel.Select + | QtCore.QItemSelectionModel.Rows ) selection_model.setCurrentIndex( last_index, - selection_model.Select | selection_model.Rows + QtCore.QItemSelectionModel.Select + | QtCore.QItemSelectionModel.Rows ) diff --git a/openpype/tools/publisher/widgets/widgets.py b/openpype/tools/publisher/widgets/widgets.py index 16641bfb6c5..f65d0302c53 100644 --- a/openpype/tools/publisher/widgets/widgets.py +++ b/openpype/tools/publisher/widgets/widgets.py @@ -412,7 +412,8 @@ def __init__(self, controller, parent): icon_btn ): size_policy = widget.sizePolicy() - size_policy.setVerticalPolicy(size_policy.MinimumExpanding) + size_policy.setVerticalPolicy( + QtWidgets.QSizePolicy.MinimumExpanding) widget.setSizePolicy(size_policy) name_input.clicked.connect(self._mouse_release_callback) icon_btn.clicked.connect(self._mouse_release_callback) @@ -595,7 +596,8 @@ def __init__(self, controller, parent): # Make sure combobox is extended horizontally size_policy = self.sizePolicy() - size_policy.setHorizontalPolicy(size_policy.MinimumExpanding) + size_policy.setHorizontalPolicy( + QtWidgets.QSizePolicy.MinimumExpanding) self.setSizePolicy(size_policy) def set_invalid_empty_task(self, invalid=True): diff --git a/openpype/tools/pyblish_pype/view.py b/openpype/tools/pyblish_pype/view.py index 69dda6618d3..cc6604fc635 100644 --- a/openpype/tools/pyblish_pype/view.py +++ b/openpype/tools/pyblish_pype/view.py @@ -24,7 +24,7 @@ def __init__(self, parent=None): self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) self.setItemsExpandable(True) - self.setVerticalScrollMode(QtWidgets.QTreeView.ScrollPerPixel) + self.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) self.setHeaderHidden(True) self.setRootIsDecorated(False) self.setIndentation(0) @@ -248,7 +248,7 @@ def __init__(self, parent=None): self.setAutoScroll(False) self.setHeaderHidden(True) self.setIndentation(0) - self.setVerticalScrollMode(QtWidgets.QTreeView.ScrollPerPixel) + self.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) self.verticalScrollBar().setSingleStep(10) self.setRootIsDecorated(False) diff --git a/openpype/tools/sceneinventory/view.py b/openpype/tools/sceneinventory/view.py index ba3aa069f23..183bf2362bb 100644 --- a/openpype/tools/sceneinventory/view.py +++ b/openpype/tools/sceneinventory/view.py @@ -546,9 +546,9 @@ def _select_items_by_action(self, object_names, options=None): selection_model = self.selectionModel() select_mode = { - "select": selection_model.Select, - "deselect": selection_model.Deselect, - "toggle": selection_model.Toggle, + "select": QtCore.QItemSelectionModel.Select, + "deselect": QtCore.QItemSelectionModel.Deselect, + "toggle": QtCore.QItemSelectionModel.Toggle, }[options.get("mode", "select")] for index in iter_model_rows(model, 0): @@ -559,7 +559,7 @@ def _select_items_by_action(self, object_names, options=None): name = item.get("objectName") if name in object_names: self.scrollTo(index) # Ensure item is visible - flags = select_mode | selection_model.Rows + flags = select_mode | QtCore.QItemSelectionModel.Rows selection_model.select(index, flags) object_names.remove(name) diff --git a/openpype/tools/settings/settings/breadcrumbs_widget.py b/openpype/tools/settings/settings/breadcrumbs_widget.py index 2676d2f52db..c93e2108555 100644 --- a/openpype/tools/settings/settings/breadcrumbs_widget.py +++ b/openpype/tools/settings/settings/breadcrumbs_widget.py @@ -339,7 +339,7 @@ def __init__(self, path, model, parent): # fixed size breadcrumbs self.setMinimumSize(self.minimumSizeHint()) size_policy = self.sizePolicy() - size_policy.setVerticalPolicy(size_policy.Minimum) + size_policy.setVerticalPolicy(QtWidgets.QSizePolicy.Minimum) self.setSizePolicy(size_policy) menu.triggered.connect(self._on_menu_click) @@ -369,7 +369,7 @@ def __init__(self, parent=None): super(BreadcrumbsAddressBar, self).__init__(parent) self.setAutoFillBackground(True) - self.setFrameShape(self.StyledPanel) + self.setFrameShape(QtWidgets.QFrame.StyledPanel) # Edit presented path textually proxy_model = BreadcrumbsProxy() diff --git a/openpype/tools/standalonepublish/widgets/widget_asset.py b/openpype/tools/standalonepublish/widgets/widget_asset.py index 01f49b79ec3..5da25a0c3e3 100644 --- a/openpype/tools/standalonepublish/widgets/widget_asset.py +++ b/openpype/tools/standalonepublish/widgets/widget_asset.py @@ -86,7 +86,10 @@ def preserve_selection(tree_view, model = tree_view.model() selection_model = tree_view.selectionModel() - flags = selection_model.Select | selection_model.Rows + flags = ( + QtCore.QItemSelectionModel.Select + | QtCore.QItemSelectionModel.Rows + ) if current_index: current_index_value = tree_view.currentIndex().data(role) @@ -410,7 +413,10 @@ def select_assets(self, assets, expand=True, key="name"): selection_model.clearSelection() # Select - mode = selection_model.Select | selection_model.Rows + mode = ( + QtCore.QItemSelectionModel.Select + | QtCore.QItemSelectionModel.Rows + ) for index in _iter_model_rows( self.proxy, column=0, include_root=False ): diff --git a/openpype/tools/utils/assets_widget.py b/openpype/tools/utils/assets_widget.py index c92e68aa979..f33a412a7bb 100644 --- a/openpype/tools/utils/assets_widget.py +++ b/openpype/tools/utils/assets_widget.py @@ -60,7 +60,7 @@ def activate_flick_charm(self): self._flick_charm_activated = True self._before_flick_scroll_mode = self.verticalScrollMode() self._flick_charm.activateOn(self) - self.setVerticalScrollMode(self.ScrollPerPixel) + self.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) def deactivate_flick_charm(self): if not self._flick_charm_activated: @@ -623,7 +623,8 @@ def __init__(self, dbcon, parent=None): filter_input, ): size_policy = widget.sizePolicy() - size_policy.setVerticalPolicy(size_policy.MinimumExpanding) + size_policy.setVerticalPolicy( + QtWidgets.QSizePolicy.MinimumExpanding) widget.setSizePolicy(size_policy) # Layout @@ -778,7 +779,10 @@ def _select_indexes(self, indexes): selection_model = self._view.selectionModel() selection_model.clearSelection() - mode = selection_model.Select | selection_model.Rows + mode = ( + QtCore.QItemSelectionModel.Select + | QtCore.QItemSelectionModel.Rows + ) for index in valid_indexes: self._view.expand(self._proxy.parent(index)) selection_model.select(index, mode) diff --git a/openpype/tools/utils/lib.py b/openpype/tools/utils/lib.py index 403811699d6..ae9c5bc96c5 100644 --- a/openpype/tools/utils/lib.py +++ b/openpype/tools/utils/lib.py @@ -79,11 +79,15 @@ def paint_image_with_color(image, color): pixmap.fill(QtCore.Qt.transparent) painter = QtGui.QPainter(pixmap) - painter.setRenderHints( - painter.Antialiasing - | painter.SmoothPixmapTransform - | painter.HighQualityAntialiasing + render_hints = ( + QtGui.QPainter.Antialiasing + | QtGui.QPainter.SmoothPixmapTransform ) + # Deprecated since 5.14 + if hasattr(QtGui.QPainter, "HighQualityAntialiasing"): + render_hints |= QtGui.QPainter.HighQualityAntialiasing + painter.setRenderHints(render_hints) + painter.setClipRegion(alpha_region) painter.setPen(QtCore.Qt.NoPen) painter.setBrush(color) @@ -329,7 +333,10 @@ def preserve_selection(tree_view, column=0, role=None, current_index=True): role = QtCore.Qt.DisplayRole model = tree_view.model() selection_model = tree_view.selectionModel() - flags = selection_model.Select | selection_model.Rows + flags = ( + QtCore.QItemSelectionModel.Select + | QtCore.QItemSelectionModel.Rows + ) if current_index: current_index_value = tree_view.currentIndex().data(role) diff --git a/openpype/tools/utils/tasks_widget.py b/openpype/tools/utils/tasks_widget.py index 0605fd77433..b0ddedcb6e1 100644 --- a/openpype/tools/utils/tasks_widget.py +++ b/openpype/tools/utils/tasks_widget.py @@ -257,7 +257,10 @@ def select_task_name(self, task_name): selection_model.clearSelection() # Select the task - mode = selection_model.Select | selection_model.Rows + mode = ( + QtCore.QItemSelectionModel.Select + | QtCore.QItemSelectionModel.Rows + ) for row in range(task_view_model.rowCount()): index = task_view_model.index(row, 0) name = index.data(TASK_NAME_ROLE) From 7cd57e051a93b74af4b1e1b49085299a2f2e10a1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 15:19:57 +0100 Subject: [PATCH 111/213] support new method for regex filtering --- openpype/tools/loader/widgets.py | 11 +++++++++-- openpype/tools/sceneinventory/window.py | 5 ++++- openpype/tools/settings/settings/search_dialog.py | 5 ++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/openpype/tools/loader/widgets.py b/openpype/tools/loader/widgets.py index c2c6c5dfcd3..8d58e822307 100644 --- a/openpype/tools/loader/widgets.py +++ b/openpype/tools/loader/widgets.py @@ -264,8 +264,8 @@ def __init__( group_checkbox.stateChanged.connect(self.set_grouping) - subset_filter.textChanged.connect(proxy.setFilterRegExp) - subset_filter.textChanged.connect(view.expandAll) + subset_filter.textChanged.connect(self._subset_changed) + model.refreshed.connect(self.refreshed) self.proxy = proxy @@ -293,6 +293,13 @@ def set_grouping(self, state): current_index=False): self.model.set_grouping(state) + def _subset_changed(self, text): + if hasattr(self.proxy, "setFilterRegularExpression"): + self.proxy.setFilterRegularExpression(text) + else: + self.proxy.setFilterRegExp(text) + self.view.expandAll() + def set_loading_state(self, loading, empty): view = self.view diff --git a/openpype/tools/sceneinventory/window.py b/openpype/tools/sceneinventory/window.py index 7b305e8e8f1..8a6e43f796b 100644 --- a/openpype/tools/sceneinventory/window.py +++ b/openpype/tools/sceneinventory/window.py @@ -160,7 +160,10 @@ def _on_hierarchy_view_change(self, enabled): self._model.set_hierarchy_view(enabled) def _on_text_filter_change(self, text_filter): - self._proxy.setFilterRegExp(text_filter) + if hasattr(self._proxy, "setFilterRegularExpression"): + self._proxy.setFilterRegularExpression(text_filter) + else: + self._proxy.setFilterRegExp(text_filter) def _on_outdated_state_change(self): self._proxy.set_filter_outdated( diff --git a/openpype/tools/settings/settings/search_dialog.py b/openpype/tools/settings/settings/search_dialog.py index 2860e7c943a..71d258febea 100644 --- a/openpype/tools/settings/settings/search_dialog.py +++ b/openpype/tools/settings/settings/search_dialog.py @@ -106,7 +106,10 @@ def _on_filter_changed(self, txt): def _on_filter_timer(self): text = self._filter_edit.text() - self._proxy.setFilterRegExp(text) + if hasattr(self._proxy, "setFilterRegularExpression"): + self._proxy.setFilterRegularExpression(text) + else: + self._proxy.setFilterRegExp(text) # WARNING This expanding and resizing is relatively slow. self._view.expandAll() From c849c83da49da705748ba58d2b9f7718abba7785 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 15:20:21 +0100 Subject: [PATCH 112/213] added pyside6 resources --- openpype/style/pyside6_resources.py | 1520 +++++++++++++++++++++++++++ openpype/style/qrc_resources.py | 4 +- 2 files changed, 1523 insertions(+), 1 deletion(-) create mode 100644 openpype/style/pyside6_resources.py diff --git a/openpype/style/pyside6_resources.py b/openpype/style/pyside6_resources.py new file mode 100644 index 00000000000..c7de95d14f3 --- /dev/null +++ b/openpype/style/pyside6_resources.py @@ -0,0 +1,1520 @@ +# Resource object code (Python 3) +# Created by: object code +# Created by: The Resource Compiler for Qt version 6.4.1 +# WARNING! All changes made in this file will be lost! + +from PySide6 import QtCore + +qt_resource_data = b"\ +\x00\x00\x00\xa6\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x06\x00\x00\x00\x09\x08\x04\x00\x00\x00\xbb\x93\x95\x16\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\ +HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\ +\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x14\x1f \xb9\ +\x8dw\xe9\x00\x00\x00*IDAT\x08\xd7c`\xc0\ +\x06\xe6|```B0\xa1\x1c\x08\x93\x81\x81\x09\xc1\ +d``b`H\x11@\xe2 s\x19\x90\x8d@\x02\ +\x00#\xed\x08\xafd\x9f\x0f\x15\x00\x00\x00\x00IEN\ +D\xaeB`\x82\ +\x00\x00\x04\x12\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x000\x00\x00\x000\x08\x06\x00\x00\x00W\x02\xf9\x87\ +\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\ +\x01\x00\x9a\x9c\x18\x00\x00\x03\xc4IDATh\x81\xed\ +\x9a_\x88\x94U\x18\xc6\x7f3;\x1a\x0b\x19\x15f\x17\ +\xca\x03IPM\x09J7Q^D)&f\x05[\ +\xb9\xd2\x82\xb1P\x17\x91$t\x11\x08z\xa1D\x17]\ +T\x94\x04\xd6E\xe1\xa2\x15L\xb1\x18\x99\xfd%\xd8\x08\ +\x82nj]\xa4\x22\xe2\x81\xa8\x96\xd5(\xe8\x9f\xae\xd5\ +\xc5\xf9\xb6\xc6\xd9\xf9\xbesf\xc6vf\xc0\xdf\xdd\x9c\ +\xef=\xefy\x9fs\xe6\x9c\xf3~\xdf9%2Fj\ +SK\x81\xdd\xc0Z\xe0:`\x11\xbd\xc5i`\x12\x98\ +\x00\xf6\x8c\x0dUg\x00J\x00#\xb5\xa9[\x80C\xc0\ +\xb2\xae\x85\xd7\x1a\xd3\xc0\xd6\xb1\xa1\xea\x07\xa5\xac\xe7\x8f\ +\xd1?\xc1\xcf1\x0d\x5c[&\xfcm\xfa-x\x081\ +\xef\xaa\x10\xfe\xf3\x8d\x9cY\xe0`R\x19h\xf8\xbd\xb6\ +B\x98\xb0\xf5\x9c\x19\x1b\xaaV\x16(\xa0\x96\x18\xa9M\ +\xcdr\xb6\x88Uezo\xb5i\x85E\xe5nG\xd0\ +)\xe7\x05t\x9b\xf3\x02\xfe\x0fl\xdfc\xfb\x90\xed\xf5\ +1\xdb\x9e\x13`\xfb\x11\xe05`\x188j{\x9f\xed\ +\xdc\x95\xb2\xa7\x04\xd8\x1e\x06\x9e\xaa+*\x01\x0f\x01/\ +\xe4\xd5\xe9\x19\x01\xb6\xd7\x01/\x93%\x98\x0dl\xb3\xfd\ +h\xb3z=!\xc0\xf6\xf5\xc0\xeb\xc0\xe2\x02\xb3\x8d\xcd\ +\x0a\xbb.\xc0\xf6\x95\xc0[\xc0\x92\x88\xe9\x8b\xcd\x0a\xbb\ +*\xc0\xf6\xe5\xc0Q\xe2\xd9\xf0\x93\x92^i\xf6\xa0k\ +\x02l/\x01\x8e\x00+#\xa6\x07\x80\xc7\xf2\x1evE\ +\x80\xed\xc5\xc0\x1b\xc0\x9a\x88\xe9\xdb\xc0\xa8\xa4\xbf\xf3\x0c\ +\x16\x5c\x80\xed2\xa1Wo\x8d\x98~\x0a\xdc-i\xb6\ +\xc8\xa8\x1b#\xf04po\xc4\xe6K`\x93\xa4_c\ +\xce\x92\x04\xd8.e=\xd7\x11\xb6w\x02\xdb#f\xdf\ +\x03\x1b$\xcd\xa4\xf8,\x0c*\x0b|\x07\xf0;0i\ +{UR\xa4\xcd}\x8d\x02\x8fG\xcc~\x06n\x93\xf4\ +m\xaa\xdf\x5c\x01Y\xfe1N\xd8\xda/\x00\xae\x01\xde\ +\xb3}U\xaa\xf3:_\x9b\x81\xfd\x11\xb3?\x81;%\ +}\xde\x8a\xef\xa2\x11x\x0e\xd8\xdcP\xb6\x0cx\xdf\xf6\ +\x15\xa9\x0d\xd8\xbe\x11x\x95\xf9/\xe4\xf5\xfc\x05\xdc'\ +\xe9\xa3T\xbfs4\x15\x90\xf5\xd8\x839u\x96\x13F\ +by\xcc\xb9\xed*p\x18\x18\x8c\x98>,\xa9\x16\xf3\ +\xd7\x8c\xbc\x11\xb84Ro%A\xc4ey\x06\xb6W\ +\x10v\xd9\x98\xaf\xbd\x92\x9e\x8f\xd8\xe4\x92'\xe0 a\ +\x1d.\xe2j\xe0\x1d\xdb\x177>\xb0}\x09!\xf8\x15\ +\x11\x1f\xfb%\xed\x8eFY@S\x01\x92N\x13\xb2\xbf\ +/\x22\xf5W\x03Gl_8W`{\x90\xf0\xb7\xa9\ +F\xea\x8e\x13r\xfd\x8e\xc8\x9d\xc4\x92N\x02\xeb\x81\xaf\ +\x22>n\x00\x0e\xdb\x1e\xb4=@\x98\xb07E\xeaL\ +\x00\xc3\x92:\xfe\x02X\xb8\x0fH\xfa\x11X\x078\xe2\ +\xe7f\xa0FX*\x1bW\xaeF&\x81;$\xfd\x91\ +\x18c!\xd1\xddU\x92\x09y\xcb\x0f\x11\xd3\x8d\xc0h\ +\xc4\xc6\x84\x8d\xea\xa7\xb4\xf0\xe2$\xa5\x07\x92\xbe&\x8c\ +\xc4\x89\x0e\xda:IH\x11\xbe\xeb\xc0\xc7<\x92\xf3\x1b\ +I\xc7\x80\x0d\xc0/m\xb4\xf3\x1bp\xbb\xa4\xe3m\xd4\ +-\xa4\xa5\x04M\xd2g\xc0\xa6,\xa0Tf\x81-\x92\ +>i\xa5\xadTZ\xce0%M\x00w\x11r\x97\x14\ +\x1e\x90\xf4f\xab\xed\xa4\xd2V\x8a,\xe9]`\x0b\xa1\ +w\x8b\xd8)\xe9\xa5v\xdaH\xa5\xed\x1c_\xd28\xb0\ +\x8d\x90\x885\xe3YIO\xb4\xeb?\x95\x8e^R$\ +\x1d\x04\xeeg\xfe\x9c\xd8\x07\xec\xe8\xc4w*\x1d\x1f%\ +I:`\xfbc\xc2\x06v\x11\xf0a6O\x16\x84s\ +r\x16&\xe9\x1b\xe0\x99s\xe1\xabU\xca\x84\x13\xf0~\ +\xe5T\x85\x90\x9b\xd4\x7f\x9f\x19\xc8N\x03{\x91\xc6\xb7\ +\xba\xc9\x0a!3l\xfc\xc0T\xf4\xfa\xd7KL\x94\x81\ +=\x84c\xfb~c\x1a\xd8[\xcen}l\xa5\xbfD\ +\xcc]\xf6\x98\xf9\xf70!\xbb\xf4\xb1\x8b\xff\xae\xdb\x14\ +}\xab\xef\x06\xa78\xfb\xba\xcd\x09\x80\x7f\x00\xc4\x1e\x10\ +)3[\x85\xf7\x00\x00\x00\x00IEND\xaeB`\ +\x82\ +\x00\x00\x01\xef\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x000\x00\x00\x000\x08\x06\x00\x00\x00W\x02\xf9\x87\ +\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\ +\x01\x00\x9a\x9c\x18\x00\x00\x01\xa1IDATh\x81\xed\ +\x9a\xbfN\xc2P\x14\x87\xbf\x96\xe2\xa4\x9bq\xbc\x8b\x1b\ +\xea\xc0\xe2D\x1c\x8c\x83\x83\xd1\x81\x89\x84\xd1\x17\xf0\x01\ +p\xc0\x07\xf0\x05\x1cI\x9c\xba\xa8#q0<\x02v\ +\x92\xe5\x8e\x04'\xe3\xc2\x9f\xc4\xa1m\x04B\x8b\x95\xc2\ +\xa1\xe4~[{\xee\xf0\xfb\x9a{o\x9a\x9cc\x11P\ +u\xbd]\xe0\x16(\x01\x87@\x9e\xf5b\x00\xb4\x81\x16\ +Po\x94\x0b=\x00\x0b\xa0\xeaz\xa7\xc0#\xb0'\x16\ +/\x19]\xa0\xd2(\x17^\xad\xe0\xcb\xbf\x93\x9d\xf0!\ +]\xe0\xc0\xc6\xdf6Y\x0b\x0f~\xe6\x9a\x83\xbf\xe7\xa7\ +\x19\xad8\xcc_\xc9M=\x97\x1c\xfc\x03;\xce\xa8Q\ +.8+\x0a\x94\x88\xaa\xeb\x0d\x99\x948\xb2Y\xbf\xdb\ +&\x09y[:\xc1\xa2\x18\x01i\x8c\x804F@\x1a\ +# \x8d\x11\x90\xc6\x08H3\xf7\xb7Yk}\x06\x1c\ +\x03\xdb\xcb\x8f3\xc1\x10\xf0\x80g\xa5\xd4w\xd4\xa2H\ +\x01\xad\xb5\x03\xb8\xc0e\xfa\xd9\x12\xd1\xd1Z\x9f+\xa5\ +>f\x15\xe3\xb6\xd0\x0d\xf2\xe1\x01\xf6\x81\x87\xa8b\x9c\ +\xc0E\xfaY\xfe\xcd\x89\xd6zgV!\xf3\x878N\ +\xe0ee)\xe6\xf3\xa6\x94\xfa\x9aU\x88\x13\xb8\x07\x9e\ +\x96\x93'\x11\x1d\xe0:\xaa\x18y\x0b)\xa5\x86\xc0U\ +f\xaf\xd1\x10\xa5T\x13h\xa6\x18,U6\xfa\x10g\ +\x02# \x8d\x11\x90\xc6\x08Hc\x04\xa41\x02\xd2l\ +\x84\xc0@:\xc4\x02\xf4\x1d\xfc\xf6}q\xece.\xe8\ +\x06\xae#\xd3m\xd6\xb6\x83?{P\x9c\xb3p]i\ +\xd9@\x1d\xbfm\x9f5\xba\xc0\x9d\x1dL}T\xc8\x96\ +D8\xec\xd1\xb3\xc27\xc1\xd0G\x8d\xdfq\x9b-\xa1\ +pQ\xf4\x99\x1c\xb7\xf9\x04\xf8\x01o\xedXc-\xfd\ +\xb2Y\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x00\xa6\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\ +HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\ +\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x08\x15;\xdc\ +;\x0c\x9b\x00\x00\x00*IDAT\x08\xd7c`\xc0\ +\x00\x8c\x0c\x0cs> \x0b\xa4\x08020 \x0b\xa6\ +\x08000B\x98\x10\xc1\x14\x01\x14\x13P\xb5\xa3\x01\ +\x00\xc6\xb9\x07\x90]f\x1f\x83\x00\x00\x00\x00IEN\ +D\xaeB`\x82\ +\x00\x00\x00\xa5\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x02bKGD\x00\x9cS4\xfc]\x00\x00\x00\x09p\ +HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\ +\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x0b\x02\x04m\ +\x98\x1bi\x00\x00\x00)IDAT\x08\xd7c`\xc0\ +\x00\x8c\x0c\x0c\xff\xcf\xa3\x08\x18220 \x0b2\x1a\ +200B\x98\x10AFC\x14\x13P\xb5\xa3\x01\x00\ +\xd6\x10\x07\xd2/H\xdfJ\x00\x00\x00\x00IEND\ +\xaeB`\x82\ +\x00\x00\x00\xa5\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x02bKGD\x00\x9cS4\xfc]\x00\x00\x00\x09p\ +HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\ +\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x0b\x02\x04m\ +\x98\x1bi\x00\x00\x00)IDAT\x08\xd7c`\xc0\ +\x00\x8c\x0c\x0c\xff\xcf\xa3\x08\x18220 \x0b2\x1a\ +200B\x98\x10AFC\x14\x13P\xb5\xa3\x01\x00\ +\xd6\x10\x07\xd2/H\xdfJ\x00\x00\x00\x00IEND\ +\xaeB`\x82\ +\x00\x00\x00\xa5\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x02bKGD\x00\x9cS4\xfc]\x00\x00\x00\x09p\ +HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\ +\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x0b\x02\x04m\ +\x98\x1bi\x00\x00\x00)IDAT\x08\xd7c`\xc0\ +\x00\x8c\x0c\x0c\xff\xcf\xa3\x08\x18220 \x0b2\x1a\ +200B\x98\x10AFC\x14\x13P\xb5\xa3\x01\x00\ +\xd6\x10\x07\xd2/H\xdfJ\x00\x00\x00\x00IEND\ +\xaeB`\x82\ +\x00\x00\x00\xa0\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x06\x00\x00\x00\x09\x08\x04\x00\x00\x00\xbb\x93\x95\x16\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\ +HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\ +\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x14\x1c\x1f$\ +\xc6\x09\x17\x00\x00\x00$IDAT\x08\xd7c`@\ +\x05\xff\xcf\xc3XL\xc8\x5c&dY&d\xc5p\x0e\ +\xa3!\x9c\xc3h\x88a\x1a\x0a\x00\x00m\x84\x09u7\ +\x9e\xd9#\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x00\xa6\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\ +HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\ +\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x08\x15;\xdc\ +;\x0c\x9b\x00\x00\x00*IDAT\x08\xd7c`\xc0\ +\x00\x8c\x0c\x0cs> \x0b\xa4\x08020 \x0b\xa6\ +\x08000B\x98\x10\xc1\x14\x01\x14\x13P\xb5\xa3\x01\ +\x00\xc6\xb9\x07\x90]f\x1f\x83\x00\x00\x00\x00IEN\ +D\xaeB`\x82\ +\x00\x00\x01i\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x000\x00\x00\x000\x08\x06\x00\x00\x00W\x02\xf9\x87\ +\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\ +\x01\x00\x9a\x9c\x18\x00\x00\x01\x1bIDATh\x81\xed\ +\xda\xb1m\xc2@\x14\x87\xf1\xcf\xc7\x91\x09P\x86pB\ +AO\xc5\x0a\xae\x90\xbc\x0a)\xc8*\x96Ry\x05*\ +F \x1e\xc2\x82\x05H\x90R\xdcY\x01KQbE\ +\xe2\xef\x93\xde\xaf\xb3E\xf1>\xcb\xa6\xb9\x97\x11\x95u\ +3\x03^\x80%\xf0\x0cL\x19\x97\x0f\xe0\x00\xec\x81m\ +U\xe4G\x80\x0c\xa0\xac\x9b\x15\xf0\x06<\xca\xc6\x1b\xa6\ +\x05\xd6U\x91\xef\xb2\xf8\xe4\xdfIg\xf8N\x0b<9\ +\xc2k\x93\xda\xf0\x10f\xdex\xc2;\xdfw\xb9\xf30\ +\x7f5\xe9]/=\xe1\x83\xbdv\xa9\x8a\xdc\xdfi\xa0\ +A\xca\xba\xf9\xe46b\xee\x18\xdf\xbf\xcd\x10S\xa7\x9e\ +\xe0\xbf,@\xcd\x02\xd4,@\xcd\x02\xd4,@\xcd\x02\ +\xd4,@\xcd\x02\xd4,@\xcd\x02\xd4,@\xcd\x02\xd4\ +,@\xcd\x02\xd4,@\xcd\x02\xd4,@\xcd\x11N\xc0\ +Su\xf6\x84\xe3\xfb\xc5\xd5\xcdI<\x0d\x1c\xa3\xfe1\ +\xeb\xc1\x13v\x0f\x16\xbf\xfcp\xac\xf6\x0e\xd8\x12\x8e\xed\ +S\xd3\x02\xaf.n}\xacI+\xa2[\xf68f\xdd\ +\x9d\xb8\xf4\xb1\xe1{\xdd\xe6A4\xdcO\xce\xdc\xae\xdb\ +\x9c\x00\xbe\x00\x9f\xf64>6O7\x81\x00\x00\x00\x00\ +IEND\xaeB`\x82\ +\x00\x00\x07\x06\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x0a\x00\x00\x00\x07\x08\x06\x00\x00\x001\xac\xdcc\ +\x00\x00\x04\xb0iTXtXML:com.\ +adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a\x0a\x85\x9d\x9f\x08\x00\x00\x01\x83\ +iCCPsRGB IEC6196\ +6-2.1\x00\x00(\x91u\x91\xcf+DQ\x14\ +\xc7?fh\xfc\x18\x8dba1e\x12\x16B\x83\x12\ +\x1b\x8b\x99\x18\x0a\x8b\x99Q~mf\x9ey3j\xde\ +x\xbd7\xd2d\xabl\xa7(\xb1\xf1k\xc1_\xc0V\ +Y+E\xa4d\xa7\xac\x89\x0dz\xce\x9bQ#\x99s\ +;\xf7|\xee\xf7\xdes\xba\xf7\x5cpD\xd3\x8afV\ +\xfaA\xcbd\x8dp(\xe0\x9b\x99\x9d\xf3\xb9\x9e\xa8\xa2\ +\x85\x1a:\xf1\xc6\x14S\x9f\x8c\x8cF)k\xef\xb7T\ +\xd8\xf1\xba\xdb\xaeU\xfe\xdc\xbfV\xb7\x980\x15\xa8\xa8\ +\x16\x1eVt#+<&<\xb1\x9a\xd5m\xde\x12n\ +RR\xb1E\xe1\x13\xe1.C.(|c\xeb\xf1\x22\ +?\xdb\x9c,\xf2\xa7\xcdF4\x1c\x04G\x83\xb0/\xf9\ +\x8b\xe3\xbfXI\x19\x9a\xb0\xbc\x9c6-\xbd\xa2\xfc\xdc\ +\xc7~\x89;\x91\x99\x8eHl\x15\xf7b\x12&D\x00\ +\x1f\xe3\x8c\x10d\x80^\x86d\x1e\xa0\x9b>zdE\ +\x99|\x7f!\x7f\x8ae\xc9Ud\xd6\xc9a\xb0D\x92\ +\x14Y\xbaD]\x91\xea\x09\x89\xaa\xe8\x09\x19irv\ +\xff\xff\xf6\xd5T\xfb\xfb\x8a\xd5\xdd\x01\xa8z\xb4\xac\xd7\ +vpm\xc2W\xde\xb2>\x0e,\xeb\xeb\x10\x9c\x0fp\ +\x9e)\xe5/\xef\xc3\xe0\x9b\xe8\xf9\x92\xd6\xb6\x07\x9eu\ +8\xbd(i\xf1m8\xdb\x80\xe6{=f\xc4\x0a\x92\ +S\xdc\xa1\xaa\xf0r\x0c\xf5\xb3\xd0x\x05\xb5\xf3\xc5\x9e\ +\xfd\xecst\x07\xd15\xf9\xaaK\xd8\xd9\x85\x0e9\xef\ +Y\xf8\x06\x8e\xfdg\xf8\xfd\x8a\x18\x97\x00\x00\x00\x09p\ +HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\ +\x00\x00\x00mIDAT\x18\x95u\xcf\xc1\x09\xc2P\ +\x10\x84\xe1\xd7\x85\x07\x9b\xd0C@\xd2\x82x\x14{0\ +W!\x8d\x84`?bKzH\xcc\x97\x83\xfb0\x04\ +\xdf\x9c\x86\x7fg\x99\xdd\x84\x0d\xaaT\x10jl\x13\x1e\ +\xbe\xba\xfe\x0951{\xe6\x8d\x0f&\x1c\x17\xa1S\xb0\ +\x11\x87\x0c/\x01\x07\xec\xb0\x0f?\xe1\xbc\xaei\xa3\xe6\ +\x85w\xf8[\xe9\xf0\xbb\x9f\xfa\xd2\x839\xdc\xa3[\xf3\ +\x19.\xa8\x89\xb50\xf7C\xa0\x00\x00\x00\x00IEN\ +D\xaeB`\x82\ +\x00\x00\x00\xa6\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x06\x00\x00\x00\x09\x08\x04\x00\x00\x00\xbb\x93\x95\x16\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\ +HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\ +\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x14\x1d\x00\xb0\ +\xd55\xa3\x00\x00\x00*IDAT\x08\xd7c`\xc0\ +\x06\xfe\x9fg``B0\xa1\x1c\x08\x93\x81\x81\x09\xc1\ +d``b``4D\xe2 s\x19\x90\x8d@\x02\ +\x00d@\x09u\x86\xb3\xad\x9c\x00\x00\x00\x00IEN\ +D\xaeB`\x82\ +\x00\x00\x07\xad\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x07\x00\x00\x00\x0a\x08\x06\x00\x00\x00x\xccD\x0d\ +\x00\x00\x05RiTXtXML:com.\ +adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \ +\x0a branch_close<\ +/rdf:li>\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a <\ +/rdf:RDF>\x0a\x0a$\xe15\x97\x00\x00\ +\x01\x83iCCPsRGB IEC61\ +966-2.1\x00\x00(\x91u\x91\xcf+D\ +Q\x14\xc7?fh\xfc\x18\x8dba1e\x12\x16B\ +\x83\x12\x1b\x8b\x99\x18\x0a\x8b\x99Q~mf\x9ey3\ +j\xdex\xbd7\xd2d\xabl\xa7(\xb1\xf1k\xc1_\ +\xc0VY+E\xa4d\xa7\xac\x89\x0dz\xce\x9bQ#\ +\x99s;\xf7|\xee\xf7\xdes\xba\xf7\x5cpD\xd3\x8a\ +fV\xfaA\xcbd\x8dp(\xe0\x9b\x99\x9d\xf3\xb9\x9e\ +\xa8\xa2\x85\x1a:\xf1\xc6\x14S\x9f\x8c\x8cF)k\xef\ +\xb7T\xd8\xf1\xba\xdb\xaeU\xfe\xdc\xbfV\xb7\x980\x15\ +\xa8\xa8\x16\x1eVt#+<&<\xb1\x9a\xd5m\xde\ +\x12nRR\xb1E\xe1\x13\xe1.C.(|c\xeb\ +\xf1\x22?\xdb\x9c,\xf2\xa7\xcdF4\x1c\x04G\x83\xb0\ +/\xf9\x8b\xe3\xbfXI\x19\x9a\xb0\xbc\x9c6-\xbd\xa2\ +\xfc\xdc\xc7~\x89;\x91\x99\x8eHl\x15\xf7b\x12&\ +D\x00\x1f\xe3\x8c\x10d\x80^\x86d\x1e\xa0\x9b>z\ +dE\x99|\x7f!\x7f\x8ae\xc9Ud\xd6\xc9a\xb0\ +D\x92\x14Y\xbaD]\x91\xea\x09\x89\xaa\xe8\x09\x19i\ +rv\xff\xff\xf6\xd5T\xfb\xfb\x8a\xd5\xdd\x01\xa8z\xb4\ +\xac\xd7vpm\xc2W\xde\xb2>\x0e,\xeb\xeb\x10\x9c\ +\x0fp\x9e)\xe5/\xef\xc3\xe0\x9b\xe8\xf9\x92\xd6\xb6\x07\ +\x9eu8\xbd(i\xf1m8\xdb\x80\xe6{=f\xc4\ +\x0a\x92S\xdc\xa1\xaa\xf0r\x0c\xf5\xb3\xd0x\x05\xb5\xf3\ +\xc5\x9e\xfd\xecst\x07\xd15\xf9\xaaK\xd8\xd9\x85\x0e\ +9\xefY\xf8\x06\x8e\xfdg\xf8\xfd\x8a\x18\x97\x00\x00\x00\ +\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\ +\x9c\x18\x00\x00\x00rIDAT\x18\x95m\xcf1\x0a\ +\xc2P\x14D\xd1\xe8\x02\xb4W\x08\xd6Ia\x99JC\ +t\x15\x82\xabI6(\xee@\x04\xdb\xa8\x95Xx,\ +\xf2\x09\xe1\xf3\x07\xa6\x9a\xfb\xe0\xbe\x0c\x1b\xb4Xdq\ +p0\xe4\x82U\x0a8\xe3\x8b\x1b\x8a\x14p\xc4\x1b=\ +v)`\x8b\x07>\xa8\xe6\xd1\xfe\x0b\x9d\x85\x8eW\x0d\ +^x\xa2\x9e\x0e\xa7 tG9\x1d\xf6\xe1\x95+\xd6\ +\xb1D\x8e\x0e\xcbX\xf0\x0fR\x8ay\x18\xdc\xe2\x02p\ +\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x00\x9f\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\ +HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\ +\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x08\x14\x1f\xf9\ +#\xd9\x0b\x00\x00\x00#IDAT\x08\xd7c`\xc0\ +\x0d\xe6|\x80\xb1\x18\x91\x05R\x04\xe0B\x08\x15)\x02\ +\x0c\x0c\x8c\xc8\x02\x08\x95h\x00\x00\xac\xac\x07\x90Ne\ +4\xac\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x070\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x0a\x00\x00\x00\x07\x08\x06\x00\x00\x001\xac\xdcc\ +\x00\x00\x04\xb0iTXtXML:com.\ +adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a\x0aH\x8b[^\x00\x00\x01\x83\ +iCCPsRGB IEC6196\ +6-2.1\x00\x00(\x91u\x91\xcf+DQ\x14\ +\xc7?fh\xfc\x18\x8dba1e\x12\x16B\x83\x12\ +\x1b\x8b\x99\x18\x0a\x8b\x99Q~mf\x9ey3j\xde\ +x\xbd7\xd2d\xabl\xa7(\xb1\xf1k\xc1_\xc0V\ +Y+E\xa4d\xa7\xac\x89\x0dz\xce\x9bQ#\x99s\ +;\xf7|\xee\xf7\xdes\xba\xf7\x5cpD\xd3\x8afV\ +\xfaA\xcbd\x8dp(\xe0\x9b\x99\x9d\xf3\xb9\x9e\xa8\xa2\ +\x85\x1a:\xf1\xc6\x14S\x9f\x8c\x8cF)k\xef\xb7T\ +\xd8\xf1\xba\xdb\xaeU\xfe\xdc\xbfV\xb7\x980\x15\xa8\xa8\ +\x16\x1eVt#+<&<\xb1\x9a\xd5m\xde\x12n\ +RR\xb1E\xe1\x13\xe1.C.(|c\xeb\xf1\x22\ +?\xdb\x9c,\xf2\xa7\xcdF4\x1c\x04G\x83\xb0/\xf9\ +\x8b\xe3\xbfXI\x19\x9a\xb0\xbc\x9c6-\xbd\xa2\xfc\xdc\ +\xc7~\x89;\x91\x99\x8eHl\x15\xf7b\x12&D\x00\ +\x1f\xe3\x8c\x10d\x80^\x86d\x1e\xa0\x9b>zdE\ +\x99|\x7f!\x7f\x8ae\xc9Ud\xd6\xc9a\xb0D\x92\ +\x14Y\xbaD]\x91\xea\x09\x89\xaa\xe8\x09\x19irv\ +\xff\xff\xf6\xd5T\xfb\xfb\x8a\xd5\xdd\x01\xa8z\xb4\xac\xd7\ +vpm\xc2W\xde\xb2>\x0e,\xeb\xeb\x10\x9c\x0fp\ +\x9e)\xe5/\xef\xc3\xe0\x9b\xe8\xf9\x92\xd6\xb6\x07\x9eu\ +8\xbd(i\xf1m8\xdb\x80\xe6{=f\xc4\x0a\x92\ +S\xdc\xa1\xaa\xf0r\x0c\xf5\xb3\xd0x\x05\xb5\xf3\xc5\x9e\ +\xfd\xecst\x07\xd15\xf9\xaaK\xd8\xd9\x85\x0e9\xef\ +Y\xf8\x06\x8e\xfdg\xf8\xfd\x8a\x18\x97\x00\x00\x00\x09p\ +HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\ +\x00\x00\x00\x97IDAT\x18\x95m\xcf\xb1j\x02A\ +\x14\x85\xe1o\xb7\xb6\xd0'H=Vi\x03\xb1\xb4H\ +;l\xa5\xf19\xf6Y\x02VB\xbaa\x0a\x0b;\x1b\ +\x1bkA\x18\x02)m\xe3\xbe\x82\xcd\x06\x16\xd9\xdb\xdd\ +\x9f\xff\x5c\xee\xa9b*\x13Ls\x13nF&\xa6\xf2\ +\x82\xaeF\x8b\xdf\x98\xca\xfb\x88\xb4\xc0\x0f\xda\x1a[t\ +\xd8\xc7T\xc2@\x9ac\x8f?|U=|\xc5\x09w\ +\xbc\xa1\xc2\x193,r\x13.\xd5\xe0\xc2\x12\x07\x5cQ\ +#\xe0#7\xe1\xa8O\x0e\x7f\xda`\xd7\xaf\x9f\xb9\x09\ +\xdfc\x05\xff\xe5uLe\xf5\xcc\x1f\x0d3,\x83\xb6\ +\x06D\x83\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x03\xff\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x000\x00\x00\x000\x08\x06\x00\x00\x00W\x02\xf9\x87\ +\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\ +\x01\x00\x9a\x9c\x18\x00\x00\x03\xb1IDATh\x81\xed\ +\x9aOh\x1eE\x18\xc6\x7f\x9b&\x85\x82\x15\x15\xabB\ +\xcb\x03\x06\x05\xa9\x0a\x8a\xb7R<\xd4\x96\xaa\xb5\xe0A\ +\xad\xc5C%\xa0\x07Q\xccM(\xb4\x07E\x80\xae9\x1f\xc0\xff\ +\x81\xed\xbbm\xbff{W\xca6\x0b!\x84BY\xa7\ +\xdb\xa8\xedG\x81#\xf9\xcf\x00\x1c\x05&%-\x94l\ +\xa3\xc35\x03\xb6\xef\x05\x9e\xe9)\xca\x80\x87\x80\x17\xab\ +\xda\x0c\xcd\x81e{'\xf0\x0aQt\x91\x03\xb6\xbf*\ +k7\x143`\xfb&\xe0M`}\x8d\xd9me\x85\ +\x9d\x07`\xfb*\xe0]`c\xc2\xf4\xa5\xb2\xc2N\x03\ +\xb0}9\xf0>\xe9l\xf8iI\xaf\x97Ut\x16\x80\ +\xed\x8d\xc0\x140\x9e0}\x15x\xac\xaa\xb2\x93\x00l\ +\xaf\x07\xde\x02nL\x98\xbe\x07LH*n\xf5K\xac\ +z\x00\xb6G\x88\xa3zK\xc2\xf4\x0b\xe0.I\x8bu\ +F]\xcc\xc0\x11\xe0\x9e\x84\xcd\xb7\xc0\x1eI\xbf\xa5\x9c\ +5\x0a\xc0v\x96\x8f\xdc@\xd8>\x08<\x920\xfb\x11\ +\xd8-i.a\x07$\x02\xc8\x85O\x02\x7f\x003\xb6\ +\xafo\xa4\xb4\xdc\xd7\x04\xf0d\xc2\xec\x17\xe0VI\xdf\ +7\xf5[\x99\x0b\xd9\x1e\x03\x8e\x03{{\xeaf\x81\x9b\ +%}\xd3\xb4\x03\x00\xdb{\x89\x8b\xb6x\xa7\xed\xe5,\ +q\xe4?\xab2\xe87\x17z\x8e\xe5\xe2!\xee\xd7\x1f\ +\xdb\xbe\xb2Vq\x0f\xb6\xb7\x01o\x14;.\xf0\x17p\ +_\x9d\xf8*J\x03\xc8G\xec\xc1\x8a6\x9b\x81\x8fl\ +oN9\xb7\xbd\x15x\x1b\xd8\x900}X\xd2\xf1\x94\ +\xbf2\xaaf\xe0\x92D\xbbqb\x10\x9b\xaa\x0clo\ +!\x9e\xb2)_OH:\x9a\xb0\xa9\xa4*\x80c\xc4\ +}\xb8\x8ek\x80\x0fl_T\xac\xb0}1Q\xfc\x96\ +\x84\x8f\x17$\x1dN\xaa\xac\xa14\x00I\x0b\xc4\xec\xaf\ +4\x85\xed\xe1\x06`\xca\xf6\x05\xff\x14\xd8\xde@|l\ +\xb6&\xda\x9e \xe6\xfa\x03Q{#\xcb\x93\xad\x93\xc0\ +\xd5\x09?\x9f\x02\xb7\x03\xf3\xc4\xdd\xa6\xb8\xf8\x8bL\x03\ +\xbb$\xfd\xd9\x8f\xd8\xb2](y\xa5\xb4-b\x10J\ +\xf8\x9f\x22\x1eB\x13\x09\xbb\x19\xe2V\xfcs#\xd5=\ +\xb4\x0a\x00\x96r\xf6\x93\xc0\x15\xfdvZ\xc0\xc06I\ +?\xb4i\xdc\xfaN,\xe9;`'p\xa6M\xc79\ +?\x11\x0f\xaaV\xe2\xabh\x9c\xdfH:\x05\xec\x06~\ +m\xd1\xcf\xef\xc0\x1d\x92\xben\xd1\xb6\x96\xbe\x124I\ +_\x02{rAMY\x04\xf6I\xfa\xbc\x9f\xbe\x9a\xd2\ +w\x86)i\x1a\xb8\x93\x98\xbb4\xe1\x01I\xef\xf4\xdb\ +OSZ\xa5\xc8\x92>\x04\xf6\x11G\xb7\x8e\x83\x92^\ +n\xd3GSZ\xe7\xf8\x92N\x00\x07\x88\x89X\x19\xcf\ +Jz\xaa\xad\xff\xa6\x0ctI\x91t\x0c\xb8\x9f\xff\xae\ +\x89\xe7\x81\xc9A|7eE\xde\x8d\xda\x1e'\x9e\xbe\ +\x17\x02\x9f\xe4\xebd\xc5i}\x90\x0d\x0bU\x07\xd9B\ +7rV\x84\xf9Qbn\xd2\xfb~f]\x1e\xe90\ +R\xbc\xd5\xcd\x8c\x123\xc3\xe2\x0b\xa6\xba\xeb\xdf01\ +\xbd\xf6?5\xc8\xbf\xfa\xd8\x9f\x17\xac\x15f\x81\xfdY\ +\x96\xcd-\xfd\x99\x90\xcf\xc4!\xfe\xfd\xdc\xa6\xee]}\ +\x17\xcc\xb3\xfcs\x9b3\x00\x7f\x03\xd9\x1a\xfb\xdb\xbb\xa7\ +\x8f\x07\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x01[\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x000\x00\x00\x000\x08\x06\x00\x00\x00W\x02\xf9\x87\ +\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\ +\x01\x00\x9a\x9c\x18\x00\x00\x01\x0dIDATh\x81\xed\ +\xda\xb1m\x02A\x10F\xe1w\xc7\xe2\x0a,\x87\xd3\x00\ +8 'r\x17\x14\x83\x037\xe3.\x1cQ\x0240\ +!\xc2\x0d`\x90\x1c\xec\x9e\x0c'Y\xf6\x09\x89\xffV\ +\x9a/cE0\x0f\x0e\x92\x9d\x86\xc2\xdd\x1f\x81W`\ +\x09\xcc\x81)\xe3\xf2\x05l\x81\x0d\xf0ff\x07\x80\x06\ +\xc0\xdd_\x80w\xe0I6\xde0{`ef\x1fM\ +\xf9\xe4w\xd43|g\x0f\xccZ\xf2cS\xdb\xf0\x90\ +g^'\xf23\xdfw\xbe\xf30\xff5\xe9\xbd^&\ +\xf2\x0f\xf6\xd2\xd9\xcc\xd2\x9d\x06\x1a\xc4\xddO\x5cG<\ +\xb7\x8c\xef\xdff\x88i\xab\x9e\xe0V\x11\xa0\x16\x01j\ +\x11\xa0\x16\x01j\x11\xa0\x16\x01j\x11\xa0\x16\x01j\x11\ +\xa0\x16\x01j\x11\xa0\x16\x01j\x11\xa0\x16\x01j\x11\xa0\ +\x16\x01j\x11\xa0\xd6\x92o\xc0kuL\xe4\xeb\xfb\xc5\ +\xc5\xe1\xa4\xdc\x06\x8eQ\xff\x9au\x9b\xc8\xbb\x07\x8b?\ +\xde8V\x9b\xfaW\x0d\xca\xd6\xc7\xaa\x1c\xd4\xa2[\xf6\ +84\xddI\xf9&\xd6\xfc\xac\xdb<\x88\x86\xfb\xcd\x91\ +\xebu\x9bO\x80oV\x016\x1ew\x0d\xa5B\x00\x00\ +\x00\x00IEND\xaeB`\x82\ +\x00\x00\x05~\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\ +\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\ +\x01\x00\x9a\x9c\x18\x00\x00\x05\x17iTXtXML\ +:com.adobe.xmp\x00\x00\ +\x00\x00\x00 \ + \x07b\x0c\x81\x00\x00\x00\x0dIDAT\ +\x08\x1dc\xf8\xff\xff?\x03\x00\x08\xfc\x02\xfe\xe6\x0c\xff\ +\xab\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x00\xa6\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\ +HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\ +\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x08\x15;\xdc\ +;\x0c\x9b\x00\x00\x00*IDAT\x08\xd7c`\xc0\ +\x00\x8c\x0c\x0cs> \x0b\xa4\x08020 \x0b\xa6\ +\x08000B\x98\x10\xc1\x14\x01\x14\x13P\xb5\xa3\x01\ +\x00\xc6\xb9\x07\x90]f\x1f\x83\x00\x00\x00\x00IEN\ +D\xaeB`\x82\ +\x00\x00\x043\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x000\x00\x00\x000\x08\x06\x00\x00\x00W\x02\xf9\x87\ +\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\ +\x01\x00\x9a\x9c\x18\x00\x00\x03\xe5IDATh\x81\xed\ +\x9aM\x88\x1cE\x14\xc7\x7f3;\x89.\x18\xf13\x1e\ +\x92\x8b\x8b\xa2&\x06\x14/\xa29\x88\x15\x89\xa9\x18\x15\ +\xd1\xca\x06\x0f\x91\x05=\x88bnB\xc0\x1c\x12\xc4\x83\ +\x07\x15\x0dB\x14\x89\x04\xa2\x96DY\x22/F}\x8a\ +\xb0\x22\x08\x1e4\xbb\x22F\x8c,\x88\xba\xc4\x88\x82_\ +\xc9F=T\x0f\x8c\xbd\xdd]\xdd\xd3a\xa7\x07\xfc\xdd\ +\xa6\xfa\xd5\xab\xf7\xea\xf3\xdf\xd5\xd3\x22\xc1Xw\x11\xb0\ +\x03X\x0b\x5c\x0d,\xa1Y\x9c\x02\xa6\x81)`\xa7\x8a\ +?\x0e\xd0\x020\xd6\xdd\x0c\xbc\x02,\x1fXx\xd5\x98\ +\x03\xb6\xa8\xf8\xf7[I\xcf\xcf0<\xc1w\x99\x03V\ +\xb7\x09\xd3f\xd8\x82\x87\x10\xf3c\x1d\xc2\x9cOsz\ +\x91\x83)\xcbH\xea\xf7\xda\x0ea\xc1\xf6rZ\xc5w\ +\x16)\xa0J\x18\xeb\xe6\xf9o\x12k\xda4o\xb7\xa9\ +\xc2\x92\xf6\xa0#\xa8\xcb\xff\x09\x0c\x9a\xa1O\xa0\xa9\xbb\ +\xcd=\xc0]\xc0K*\xfe\xdd\x22\xdb\xc6\x8d\x80\xb1\xee\ +\x11\xc0\x03\xe3\xc0ac\xddnc]\xeeN\xd9\xa8\x04\ +\x8cu\xe3\xc0S=E-\xe0A\xe0\x85\xbc:\x8d\x99\ +B\xc6\xbau\xc0\xcb$\x023\xc5Vc\xdd\x91\xacz\ +\x8d\x18\x01c\xddu\xc0\x1b\xc0\xd2\x02\xb3\x0dY\x85\x03\ +O\xc0Xw\x19 \xc0\xb2\x88\xe9\x8bY\x85\x03M\xc0\ +Xw\x09p\x98\xb8\x1a~R\xc5\xbf\x9a\xf5``\x09\ +\x18\xeb\x96\x01\x87\x80\xb1\x88\xe9>\xe0\xd1\xbc\x87\x03I\ +\xc0X\xb7\x14x\x13\xb86b\xfa60\xa1\xe2\xff\xc9\ +3X\xf4\x04\x8cumB\xaf\x9a\x88\xe9'\xc0\xdd*\ +~\xbe\xc8h\x10#\xf04\xe0\x226_\x01\x1bU\xfc\ +o1g\xa5\x120\xd6\xb5\x92\x9e\xab\x85\xb1n;\xf0\ +p\xc4\xec{`}\xf7\xd6!FaPI\xe0\xdb\x80\ +?\x80ic\xdd\x9aR\x91f\xfb\x9a\x00\x1e\x8f\x98\xfd\ +\x02\xdc\xaa\xe2\xbf-\xeb77\x81D\x7fL\x12\x8e\xf6\ +\xb3\x80\xab\x80\xf7\x8cuW\x94u\xde\xe3k\x13\xb0'\ +b\xf6\x17p\x87\x8a\xff\xbc\x8a\xef\xa2\x11x\x0e\xd8\x94\ +*[\x0e\xa8\xb1\xee\xd2\xb2\x0d\x18\xebn\x00^c\xe1\ +\x0by/\x7f\x03\xf7\xaa\xf8\x0f\xcb\xfa\xed\x92\x99@\xd2\ +c\x0f\xe4\xd4YA\x18\x89\x151\xe7\xc6\xbaU\xc0A\ +`4b\xfa\x90\x8a?\x10\xf3\x97E\xde\x08\x5c\x10\xa9\ +7FH\xe2\xe2<\x03c\xddJ\xc2)\x1b\xf3\xb5K\ +\xc5?\x1f\xb1\xc9%/\x81\xfd\x84}\xb8\x88+\x81w\ +\x8cu\xe7\xa5\x1f\x18\xeb\xce'\x04\xbf2\xe2c\x8f\x8a\ +\xdf\x11\x8d\xb2\x80\xcc\x04T\xfc)\x82\xfa\xcb\x94\xb0=\ +\x5c\x03\x1c2\xd6\x9d\xd3-0\xd6\x8d\x12\xa6\xcd\xaaH\ +\xddI\x82\xd6\xafE\xee\x22V\xf1'\x80[\x80\xa3\x11\ +\x1f\xd7\x03\x07\x8du\xa3\xc6\xba\x11\xc2\x82\xbd1Rg\ +\x0a\x18W\xf1\xb5o\x00\x0b\xcf\x01\x15\xff#\xb0\x0e\x98\ +\x8d\xf8\xb9\x098@\xd8*\xd3;W\x9ai\xe0v\x15\ +\xffg\xc9\x18\x0b\x89\x9e\xae*~\x96\xa0[~\x88\x98\ +n\x00&\x226\xb3\x84\x83\xea\xe7r\xe1\xc5)%\x0f\ +T\xfc\xd7\x84\x91\xf8\xa9F['\x08\x12\xe1\xbb\x1a>\ +\x16PZ\xdf\xa8\xf8\x19`=\xf0k\x1f\xed\xfc\x0e\xdc\ +\xa6\xe2\xbf\xec\xa3n!\x95\x04\x9a\x8a\xff\x14\xd8\x98\x04\ +T\x96y`\xb3\x8a\xff\xb8J[e\xa9\xac0U\xfc\ +\x14p'A\xbb\x94\xe1~\x15\xffV\xd5v\xca\xd2\x97\ +DNn\xcb6\x13z\xb7\x88\xed*~o?m\x94\ +\xa5o\x8d\xaf\xe2'\x81\xad\x04!\x96\xc5\xb3*\xfe\x89\ +~\xfd\x97\xa5\xd6K\x8a\x8a\xdf\x0f\xdc\xc7\xc25\xb1\x1b\ +\xd8V\xc7wYj\xdf\xcc\xa9\xf8}\xc6\xba\x8f\x08\x07\ +\xd8\xb9\xc0\x07\xc9:Y\x14\xce\xc8\xd5\xa2\x8a\xff\x06x\ +\xe6L\xf8\xaaJ\x9b\xf0\x05|X9\xd9!h\x93\xde\ +\xfb\x99\x91\xe4k`\x13I\xbf\xd5Mw\x08\xca0}\ +\xc1T\xf4\xfa\xd7$\xa6\xda\xc0N\xc2g\xfbac\x0e\ +\xd8\xd5N\xee_\xb60\x5cIt\xff\xecq|\x04\xe0\ +\xd8\xd1\x99cc\x97\xaf\xde\x0b\x9cM\xf8\xf0}!\xcd\ +\x9bF'\x81\xcf\x80\xd7\x01\xa7\xe2\xbf\x00\xf8\x17]\x81\ +\x0b8\xb3\xfa \x9c\x00\x00\x00\x00IEND\xaeB\ +`\x82\ +\x00\x00\x00\xa0\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x06\x00\x00\x00\x09\x08\x04\x00\x00\x00\xbb\x93\x95\x16\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\ +HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\ +\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x14\x1c\x1f$\ +\xc6\x09\x17\x00\x00\x00$IDAT\x08\xd7c`@\ +\x05\xff\xcf\xc3XL\xc8\x5c&dY&d\xc5p\x0e\ +\xa3!\x9c\xc3h\x88a\x1a\x0a\x00\x00m\x84\x09u7\ +\x9e\xd9#\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x01\xdc\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x000\x00\x00\x000\x08\x06\x00\x00\x00W\x02\xf9\x87\ +\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\ +\x01\x00\x9a\x9c\x18\x00\x00\x01\x8eIDATh\x81\xed\ +\x9a\xafN\xc3P\x14\x87\xbfn\x1d\x0a\x1cA\x1e\x83\x04\ +\xc4\x0cjA\x10\x04\x82\x80\x9e\xe7\x05x\x80!x\x01\ +^\x00\x8f\xc2\x00rA\x90=\xc2@1s\xe42\x14\ +\xc1\xecO\x82h\x1b\xb6e\xed(\xebv\xda\xe5~\xae\ +\xf7\x5c\xf1\xfb\xda{o\x9a\xdc\xe3\x11\xa2\xaa\xdb\xc05\ +P\x03\xf6\x81\x0a\xf9b\x00\xb4\x81\x16p#\x22=\x00\ +\x0f@U\x8f\x81{`\xc7,^:\xba@]D^\ +\xbc\xf0\xcd\xbfQ\x9c\xf0\x11]`\xafD\xb0l\x8a\x16\ +\x1e\x82\xcc\x0d\x9f`\xcdO3Zq\x98\xbfR\x9ez\ +\xae\xf9\x04\x1bv\x9c\x91\x88\xf8+\x0a\x94\x0aU\x1d2\ +)qP\x22\x7f\xa7M\x1a*%\xeb\x04\x8b\xe2\x04\xac\ +q\x02\xd68\x01k\x9c\x805N\xc0\x1a'`\xcd\xdc\ +\xdffU=\x01\x0e\x81\xcd\xe5\xc7\x99`\x08\xbc\x03O\ +\x22\xf2\x1d7)V@U}\xe0\x018\xcf>[*\ +:\xaaz*\x22\x1f\xb3\x8aIK\xe8\x0a\xfb\xf0\x00\xbb\ +\xc0]\x5c1I\xe0,\xfb,\xff\xe6HU\xb7f\x15\ +\x0a\xbf\x89\x93\x04\x9eW\x96b>\xaf\x22\xf25\xab\x90\ +$p\x0b<.'O*:\xc0e\x5c1\xf6\x14\x12\ +\x91!pQ\xd8c4BD\x9a@3\xc3`\x99\xb2\ +\xd6\x9b\xb8\x108\x01k\x9c\x805N\xc0\x1a'`\x8d\ +\x13\xb0f-\x04\x06\xd6!\x16\xa0\xef\x13\x5c\xdfW\xc7\ +\x06\xcb\xe1m`\x1e\x99\xbefm\xfb\x04\xbd\x07\xd59\ +\x13\xf3J\xab\xf8\xad\x06a\xd7G=\x1c(\x0aQ\xb3\ +G\xcf\x8bF\xc2/\xd1\xe0\xb7\xddf\xc3(\x5c\x1c}\ +&\xdbm>\x01~\x00%\xf8ZCUN:\x7f\x00\ +\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x01\xfc\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x000\x00\x00\x000\x08\x06\x00\x00\x00W\x02\xf9\x87\ +\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\ +\x01\x00\x9a\x9c\x18\x00\x00\x01\xaeIDATh\x81\xed\ +\x9a\xbdJ\x03A\x14FO6\x1b\xb0\xd0J\xf1\x01\x14\ +\xabh\x91\xc6*X\xb8\x16\xb2\x88v\x0b\xe9}\x01\x1f\ +@\x8b\xf8\x00\xbe\x80\x85\x9d0b\xa32U\xc6B\xf2\ +\x02B\x92F\x83}H'6\xf9\x01\x8b\xdd@\x12\xb2\ +\x89k~f7\xcc\xe9v\xef\x14\xdfY\xee\x0c\x0bw\ +R\x048\xae\xb7\x01\x5c\x01y`\x17\xc8\x10/\xda@\ +\x05(\x03E%E\x13 \x05\xe0\xb8\xde!p\x0fl\ +j\x8b\x17\x8d\x06PPR\xbc\xa6\x82/_%9\xe1\ +{4\x80\xac\x85\xdf6I\x0b\x0f~\xe6K\x1b\xbf\xe7\ +\x87\xe9.8\xcc_I\x0f=\xe7m\xfc\x0d\xdbOW\ +Ia/(P$\x1c\xd7\xeb0(\xb1g\x11\xbf\xd3\ +&\x0a\x19Kw\x82i1\x02\xba1\x02\xba1\x02\xba\ +1\x02\xba1\x02\xba1\x02\xba\x99\xf8\xdb\xec\xb8\xde\x11\ +\xb0\x0f\xac\xce?\xce\x00\x1d\xa0\x06<+)~\xc2\x16\ +\x85\x0a8\xaeg\x03\x8f\xc0\xe9\xec\xb3E\xa2\xee\xb8\xde\ +\xb1\x92\xe2sTq\x5c\x0b]\xa0?<\xc06p\x1b\ +V\x1c'p2\xfb,\xff\xe6\xc0q\xbd\xb5Q\x85\xc4\ +o\xe2q\x02/\x0bK1\x997%\xc5\xf7\xa8\xc28\ +\x81\x1b\xe0i>y\x22Q\x07\xce\xc3\x8a\xa1\xa7\x90\x92\ +\xa2\x03\x9c%\xf6\x18\xed\xa1\xa4(\x01\xa5\x19\x06\x9b)\ +K\xbd\x89\x13\x81\x11\xd0\x8d\x11\xd0\x8d\x11\xd0\x8d\x11\xd0\ +\x8d\x11\xd0\xcdR\x08\xb4u\x87\x98\x82\x96\x8d?\xbe\xcf\ +\xf5\xbdL\x07\xd3\xc082\xaa_[\ +;\xd9;`\x05\x7f\xf0\xbdN\xfc\xda\xa8\x05\xbc\x03\x0f\ +\x80\xa7\xa4\xa8\x01\xfc\x02Q\xab\x5c\x8a?\xde\xe3Y\x00\ +\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x00\x9e\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\ +HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\ +\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x08\x15\x0f\xfd\ +\x8f\xf8.\x00\x00\x00\x22IDAT\x08\xd7c`\xc0\ +\x0d\xfe\x9f\x87\xb1\x18\x91\x05\x18\x0d\xe1BH*\x0c\x19\ +\x18\x18\x91\x05\x10*\xd1\x00\x00\xca\xb5\x07\xd2v\xbb\xb2\ +\xc5\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x00\xa6\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x06\x00\x00\x00\x09\x08\x04\x00\x00\x00\xbb\x93\x95\x16\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\ +HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\ +\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x14\x1d\x00\xb0\ +\xd55\xa3\x00\x00\x00*IDAT\x08\xd7c`\xc0\ +\x06\xfe\x9fg``B0\xa1\x1c\x08\x93\x81\x81\x09\xc1\ +d``b``4D\xe2 s\x19\x90\x8d@\x02\ +\x00d@\x09u\x86\xb3\xad\x9c\x00\x00\x00\x00IEN\ +D\xaeB`\x82\ +\x00\x00\x00\x9e\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x09\x00\x00\x00\x06\x08\x04\x00\x00\x00\xbb\xce|N\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\ +HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\ +\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x08\x15\x0f\xfd\ +\x8f\xf8.\x00\x00\x00\x22IDAT\x08\xd7c`\xc0\ +\x0d\xfe\x9f\x87\xb1\x18\x91\x05\x18\x0d\xe1BH*\x0c\x19\ +\x18\x18\x91\x05\x10*\xd1\x00\x00\xca\xb5\x07\xd2v\xbb\xb2\ +\xc5\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x07\xdd\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x07\x00\x00\x00\x0a\x08\x06\x00\x00\x00x\xccD\x0d\ +\x00\x00\x05RiTXtXML:com.\ +adobe.xmp\x00\x00\x00\x00\x00\x0a\x0a \x0a \x0a \x0a \ +\x0a branch_close<\ +/rdf:li>\x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a \x0a <\ +/rdf:RDF>\x0a\x0aX\xad\xf2\x80\x00\x00\ +\x01\x83iCCPsRGB IEC61\ +966-2.1\x00\x00(\x91u\x91\xcf+D\ +Q\x14\xc7?fh\xfc\x18\x8dba1e\x12\x16B\ +\x83\x12\x1b\x8b\x99\x18\x0a\x8b\x99Q~mf\x9ey3\ +j\xdex\xbd7\xd2d\xabl\xa7(\xb1\xf1k\xc1_\ +\xc0VY+E\xa4d\xa7\xac\x89\x0dz\xce\x9bQ#\ +\x99s;\xf7|\xee\xf7\xdes\xba\xf7\x5cpD\xd3\x8a\ +fV\xfaA\xcbd\x8dp(\xe0\x9b\x99\x9d\xf3\xb9\x9e\ +\xa8\xa2\x85\x1a:\xf1\xc6\x14S\x9f\x8c\x8cF)k\xef\ +\xb7T\xd8\xf1\xba\xdb\xaeU\xfe\xdc\xbfV\xb7\x980\x15\ +\xa8\xa8\x16\x1eVt#+<&<\xb1\x9a\xd5m\xde\ +\x12nRR\xb1E\xe1\x13\xe1.C.(|c\xeb\ +\xf1\x22?\xdb\x9c,\xf2\xa7\xcdF4\x1c\x04G\x83\xb0\ +/\xf9\x8b\xe3\xbfXI\x19\x9a\xb0\xbc\x9c6-\xbd\xa2\ +\xfc\xdc\xc7~\x89;\x91\x99\x8eHl\x15\xf7b\x12&\ +D\x00\x1f\xe3\x8c\x10d\x80^\x86d\x1e\xa0\x9b>z\ +dE\x99|\x7f!\x7f\x8ae\xc9Ud\xd6\xc9a\xb0\ +D\x92\x14Y\xbaD]\x91\xea\x09\x89\xaa\xe8\x09\x19i\ +rv\xff\xff\xf6\xd5T\xfb\xfb\x8a\xd5\xdd\x01\xa8z\xb4\ +\xac\xd7vpm\xc2W\xde\xb2>\x0e,\xeb\xeb\x10\x9c\ +\x0fp\x9e)\xe5/\xef\xc3\xe0\x9b\xe8\xf9\x92\xd6\xb6\x07\ +\x9eu8\xbd(i\xf1m8\xdb\x80\xe6{=f\xc4\ +\x0a\x92S\xdc\xa1\xaa\xf0r\x0c\xf5\xb3\xd0x\x05\xb5\xf3\ +\xc5\x9e\xfd\xecst\x07\xd15\xf9\xaaK\xd8\xd9\x85\x0e\ +9\xefY\xf8\x06\x8e\xfdg\xf8\xfd\x8a\x18\x97\x00\x00\x00\ +\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\ +\x9c\x18\x00\x00\x00\xa2IDAT\x18\x95U\xcf\xb1J\ +\xc31\x00\xc4\xe1/\xff\xb9\x93\xa3\x93\xb8\xa5\x8b\x0f \ +UD\x10\x5c:\x84,\x1d\x5c|\x0f\xb7\x8e>J\x88\ +\xa3\xb8\x08m\x05\xbbw\xc8\xea\xe2\x0bto\xe9\xd2B\ +zpp\xf0\xe3\x0e.\xa4\xd2\xae\xf0\x8a\xf7\x9a\xe3V\ +\xa7\x01\xd7x\xc32\x95vy\x06k\x8e\xdfx\xc1\x18\ +\xbf\xa9\xb4\xf1\x09\x86SH\xa5=\xe2\x03;Lk\x8e\ +\xab\xd0\xcf\xa4\xd2n\xf0\x89\x0b\xdc\x0f\xce\xb5?: \ +\x0c]\xeb\x01?\x18\xe1\xa9\xe6\xb8\x1e\x8e`\x86/l\ +q[s\x5c@H\xa5\xdda\x81\x0d\x9ek\x8e\xff\xfd\ +\xcf?\xcc1\xe9\x01\x1c\x00sR-q\xe4J\x1bi\ +\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x03\xfb\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x000\x00\x00\x000\x08\x06\x00\x00\x00W\x02\xf9\x87\ +\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\ +\x01\x00\x9a\x9c\x18\x00\x00\x03\xadIDATh\x81\xed\ +\x9aO\xa8\x15U\x1c\xc7?\xf7\xbe\xab\xf2 \xa3\x22m\ +\xa1|!\x09*K(\xdaD\xb9\x88RL\xccjQ\ +\xf9\xa4\x85\xf1\xa0\x16Q\xe4.\x10tQD\x8b\x16\x15\ +%\x81\xb5(\x04\xad\xc0\xe2ad\xf6\x97\xe0E\x10\xb4\ +\xa9'DE\xc4\x17\xa2\x125\x0a*\xff<\xab\xc5\x99\ +[\xd7y3s\xce\xdcko\xee\x05?\xbb9\xf3;\ +\xbf\xf3\xfb\x9d3\xe7\x9c\xef\x9c\x99\x16\x19\xb6/\x06v\ +\x00\xab\x81\xab\x81\x05\x0c\x17\xa7\x80\x19`\x1axL\xd2\ +\x11\x80\x16\x80\xed\x9b\x81\xbd\xc0\xd2\xc6\xc2\xab\xc7a`\ +\xb3\xa4\x0f[Y\xcf\x1fbt\x82\xefr\x18\xb8\xaaM\ +xlF-x\x081o\xef\x10\x9e\xf9<\xa7\xe79\ +\x98T\xc6r\xd7\xab;\x84\x09\xdb\xcbiI\x9dy\x0a\ +\xa8\x16\xb6g93\x89Um\x86o\xb5\xa9\xc3\x82v\ +\xd3\x11\x0c\xca\xb9\x04\x9a\xe6\x5c\x02\xff\x07\xb6\xef\xb6\xbd\ +\xd7\xf6\xda\x98\xed\xd0%`\xfb\x11\xe0u`\x028h\ +{\xa7\xed\xd2\x95r\xa8\x12\xb0=\x01<\xddS\xd4\x02\ +\x1e\x04^,\xab34\x1b\x96\xed5\xc0+d\x023\ +\xc7\x16\xdb_\x16\xd5\x1b\x8a\x11\xb0}\x1d\xf0\x06\xb0\xb0\ +\xc2l}Qa\xe3\x09\xd8\xbe\x0cx\x1bX\x1c1}\ +\xa9\xa8\xb0\xd1\x04l_\x02\x1c$\xae\x86\x9f\x92\xf4j\ +\xd1\x8d\xc6\x12\xb0\xbd\x188\x00\xac\x88\x98\xee\x06\x1e-\ +\xbb\xd9H\x02\xb6\x17\x02o\x02\xd7FL\xdf\x01&%\ +\xfd]f0\xef\x09\xd8n\x13z\xf5\x96\x88\xe9g\xc0\ +]\x92f\xab\x8c\x9a\x18\x81g\x80{\x226_\x03\x1b\ +$\xfd\x1es\x96\x94\x80\xedV\xd6s\x03a{\x1b\xf0\ +p\xc4\xecG`]\xf7\xd4!FePY\xe0[\x81\ +?\x81\x19\xdb\xab\x92\x22-\xf65\x09<\x111\xfb\x15\ +\xb8U\xd2\xf7\xa9~K\x13\xc8\xf4\xc7\x14ak_\x04\ +\x5c\x09\xbco\xfb\xf2T\xe7=\xbe6\x02\xbb\x22f'\ +\x80;$}Q\xc7w\xd5\x08<\x0fl\xcc\x95-\x05\ +>\xb0}ij\x03\xb6o\x00^c\xee\x0by/\x7f\ +\x01\xf7J\xfa8\xd5o\x97\xc2\x04\xb2\x1e{\xa0\xa4\xce\ +2\xc2H,\x8b9\xb7\xbd\x12\xd8\x0f\x8cGL\x1f\x92\ +\xb4/\xe6\xaf\x88\xb2\x11\xb8(Ro\x05!\x89%e\ +\x06\xb6\x97\x13v\xd9\x98\xaf\xc7%\xbd\x10\xb1)\xa5,\ +\x81=\x84u\xb8\x8a+\x80wm_\x90\xbfa\xfbB\ +B\xf0\xcb#>vI\xda\x11\x8d\xb2\x82\xc2\x04$\x9d\ +\x22\xa8\xbfB\x09\xdb\xc35\xc0\x01\xdb\xe7u\x0bl\x8f\ +\x13\x1e\x9b\x95\x91\xbaS\x04\xad?\x10\xa5\x93X\xd21\ +`-\xf0M\xc4\xc7\xf5\xc0~\xdb\xe3\xb6\xc7\x08\x13\xf6\ +\xc6H\x9di`B\xd2\xc0'\x80\x95\xfb\x80\xa4\x9f\x81\ +5\x80#~n\x02\xf6\x11\x96\xca\xfc\xca\x95g\x06\xb8\ +]\xd2\xf1\xc4\x18+\x89\xee\xae\x92L\xd0-?EL\ +\xd7\x03\x93\x11\x1b\x136\xaa_\xd2\xc2\x8b\x93$\x0f$\ +}K\x18\x89\xa3\x03\xb4u\x8c \x11~\x18\xc0\xc7\x1c\ +\x92\xf5\x8d\xa4C\xc0:\xe0\xb7>\xda\xf9\x03\xb8M\xd2\ +W}\xd4\xad\xa4\x96@\x93\xf49\xb0!\x0b(\x95Y\ +`\x93\xa4O\xeb\xb4\x95Jm\x85)i\x1a\xb8\x93\xa0\ +]R\xb8_\xd2[u\xdbI\xa5/\x89,\xe9=`\ +\x13\xa1w\xab\xd8&\xe9\xe5~\xdaH\xa5o\x8d/i\ +\x0a\xd8B\x10bE<'\xe9\xc9~\xfd\xa72\xd0K\ +\x8a\xa4=\xc0}\xcc\x9d\x13;\x81\xad\x83\xf8Ne\xe0\ +\x939I\xbbm\x7fB\xd8\xc0\xce\x07>\xca\xe6\xc9\xbc\ +pV\x8e\x16%}\x07<{6|\xd5\xa5M\xf8\x02\ +>\xaa\x9c\xec\x10\xb4I\xef\xf9\xccX\xf65p\x18\xc9\ +\xbf\xd5\xcdt\x08\xca0\x7f\xc0T\xf5\xfa7LL\x8f\ +\xfe\xaf\x06\xd9\xf9\xcb\xe6\xac`T\xe8\xfe\xecq\xe4\xdf\ +\x8f\x09\xd9Hl\xe7\xbf\xdfm\xaa\xce\xea\x9b\xe0$g\ +\xfens\x14\xe0\x1f\x0aC\x12kO\xfd?\x13\x00\x00\ +\x00\x00IEND\xaeB`\x82\ +\x00\x00\x00\xa0\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x00\x06\x00\x00\x00\x09\x08\x04\x00\x00\x00\xbb\x93\x95\x16\ +\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\ +\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09p\ +HYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\ +\x00\x00\x00\x07tIME\x07\xdc\x08\x17\x14\x1f\x0d\xfc\ +R+\x9c\x00\x00\x00$IDAT\x08\xd7c`@\ +\x05s>\xc0XL\xc8\x5c&dY&d\xc5pN\ +\x8a\x00\x9c\x93\x22\x80a\x1a\x0a\x00\x00)\x95\x08\xaf\x88\ +\xac\xba4\x00\x00\x00\x00IEND\xaeB`\x82\ +\x00\x00\x01\xe1\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x000\x00\x00\x000\x08\x06\x00\x00\x00W\x02\xf9\x87\ +\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\ +\x01\x00\x9a\x9c\x18\x00\x00\x01\x93IDATh\x81\xed\ +\x9a;N\xc3@\x10\x86\xbfq\x1c*\xe8\x10\xe56\x94\ +\xd0\xa4\xa1\x8a(\x22\x0a\x0aD\x9f\x9e\x0bp\x80Pp\ +\x01.\xc0\x15h\x80\x13\xa0\x1c!P\x91f\xbbD\xa1\ +B4yh(l\x1e\xb1\xfcH\x08\xc9\xda\xd2~\x9d\ +w\x5c\xfc\x9f\xb3\x1e9\xda\x11bTu\x17\xb8\x02\x9a\ +\xc0!P\xa7\x5cL\x80\x1e\xd0\x05\xaeEd\xf4]Q\ +\xd5\x96\xaa\x0e\xb4:\x0cT\xb5\x05 \x1a=\xf9g`\ +\xcf\xc5c]\x81!p\x10\x10m\x9b\xaa\x85\x87(s\ +'$\xda\xf3If\x1b\x0e\xb3(\xb5\xc4uSTu\ +\xcc\xfc\x0b;\x13\x91p\x83\xa1\x16FU\xa7\xccKL\ +\x02\xca\xd7m\x96\xa1\x1e\xb8N\xb0*^\xc05^\xc0\ +5^\xc05^\xc05^\xc05^\xc05\x85\x9f\xcd\ +\xd6\xda\x13\xe0\x08\xd8^\x7f\x9c9\xa6\xc0\x0b\xf0`\x8c\ +\xf9\xc8\xbaITU\x13k3\x11\x09\xad\xb5!p\x07\ +\x9c\xaf1\xe4\x22\xf4\x81Sc\xcck\xca\xff\x81\xdc-\ +t\x89\xfb\xf0\x00\xfb\xc0mV1O\xe0\xec\xff\xb3\xfc\ +\x99ck\xedNZ\xa1\xf2/q\x9e\xc0\xe3\xc6R\x14\ +\xf3d\x8cyO+\xe4\x09\xdc\x00\xf7\xeb\xc9\xb3\x14}\ +\xe0\x22\xab\x98\xd9\x85\xbe.\xca\xd4F\xd3\xbaP\xa1@\ +\x99X\xb6\x8dV\x02/\xe0\x1a/\xe0\x1a/\xe0\x1a/\ +\xe0\x1a/\xe0\x1a/\xe0\x9a\x80\xe8\x04\xbc\xaa\x8cC\xa2\ +\xe3\xfb\xc6\xaf\xc5Z\xfc\xd9ZF\x92\xc7\xac\xbd\x90h\ +\xf6\xa0QpcY\xe9V\x7f\xd4 \x9e\xfah\xc7\x0b\ +Ua\x08\xb4Ed$_+\xf1/\xd1\xe1g\xdcf\ +\xcbQ\xb8,\xc6\xcc\x8f\xdb\xbc\x01|\x02mw#\xb3\ +\xd4\x95Sv\x00\x00\x00\x00IEND\xaeB`\x82\ +\ +\x00\x00\x01W\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x000\x00\x00\x000\x08\x06\x00\x00\x00W\x02\xf9\x87\ +\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\ +\x01\x00\x9a\x9c\x18\x00\x00\x01\x09IDATh\x81\xed\ +\xda\xcdm\xc2@\x14E\xe1\xf3\x8cI\x05Q\x9aH6\ +\xecY\xd1\x05\xc5\x90Ej\xa3\x04R\x04\x884`\x82\ +n\x163\xf9\xb1\xa5(DH\x5c[z\xdf\x8e\xc1\x8b\ +w\x8c\xcdf&\xa8$\xdd\x03\xcf\xc0\x12x\x02\xe6\x8c\ +\xcb\x09\xd8\x01[\xe0%\x22\x8e_\xdfHZI\xdak\ +:\xf6\x92V\x00\xa1r\xe7_\x81\x07\xc7m\xbd\xc2\x01\ +xl(\x8f\xcd\xd4\x86\x872\xf3\xa6\xa5<\xf3C\xe7\ +\x1b\x0fs\xa9\xd9\xe0\xf32$u\xf4_\xd8sD\xb4\ +7\x1c\xeab\x92\xde\xe9G\x9c\x1a\xc6\xf7o\xf3\x1f\xf3\ +\xc6=\xc1\xb52\xc0-\x03\xdc2\xc0-\x03\xdc2\xc0\ +-\x03\xdc2\xc0-\x03\xdc2\xc0-\x03\xdc2\xc0-\ +\x03\xdc2\xc0-\x03\xdc2\xc0-\x03\xdc2\xc0\xad\xa1\ +\xec\x80OU\xd7R\xb6\xef\x17?\x16gu7p\x8c\ +\x86\xdb\xac\xbb\x96r\xf6`\xf1\xc7\x85c\xb5\x9d\xfeQ\ +\x83z\xeac]\x17\xa6\xe2\x00\xac#\xe2\x18\x9f+\xf5\ +\x97\xd8\xf0}\xdc\xe6\xce4\xdco:\xfa\xc7m\xde\x00\ +>\x00G\xd7\xea\xb1\xadi\xe1\xd6\x00\x00\x00\x00IE\ +ND\xaeB`\x82\ +\x00\x00\x01v\ +\x89\ +PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ +\x00\x000\x00\x00\x000\x08\x06\x00\x00\x00W\x02\xf9\x87\ +\x00\x00\x00\x09pHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\ +\x01\x00\x9a\x9c\x18\x00\x00\x01(IDATh\x81\xed\ +\xda\xb1J\xc3P\x14\x87\xf1/7\xb7\xe0\xae\xf8\x00\x82\ +Su\xe8\xde\xc9ly\x80@\x1fF\x87\xfa\x22nB\ +\xdc\xb3\xc5\xa9/ \xb4]:t\x0f}\x82j\xc1\xe1\ +\xa6P\xb3h\x10\xfa\xcf\x85\xf3\xdbR:\x9c\xaf\xdcf\ +97\xa1\x95\xe5\xc5\x15\xf0\x04L\x81;`\xc4\xb0|\ +\x02K`\x01\xcc\xeb\xaa\xdc\x01$\x00Y^<\x00\xaf\ +\xc0\xb5l\xbc~\x1a`VW\xe5{\xd2\xfe\xf2+\xe2\ +\x19\xfe\xa8\x01\xc6\x8eplb\x1b\x1e\xc2\xcc\x8f\x9ep\ +\xe6\xbb\x0eg\x1e\xe6\xaf\xd2\xce\xf3\xd4\x13\xfe\xb0\xa7\x0e\ +uU\xfa3\x0d\xd4K\x96\x17_\xfc\x8c\xb8w\x0c\xef\ +m\xd3\xc7\xc8\xa9'\xf8/\x0bP\xb3\x005\x0bP\xb3\ +\x005\x0bP\xb3\x005\x0bP\xb3\x005\x0bP\xb3\x00\ +5\x0bP\xb3\x005\x0bP\xb3\x005\x0bP\xb3\x005\ +\x0bPs\x84\x0dx\xac\xf6\x9e\xb0\xbe\x9f\x9c|\x98\xb6\ +\xdb\xc0!\xea\xaeY\x97\x9ep\xf7`\xf2\xcb\x17\x87j\ +\xe1\x809am\x1f\x9b\x06xv\xed\xad\x8f\x19qE\ +\x1c/{\xecR\x80\xedf\xb5\xbd\xb9\x1d\xbf\x00\x17\x84\ +\xc5\xf7%\xc3;F{\xe0\x03x\x03\x8a\xba*\xd7\x00\ +\xdf\xa4\xb56\xa2\xca\x99tG\x00\x00\x00\x00IEN\ +D\xaeB`\x82\ +" + +qt_resource_name = b"\ +\x00\x08\ +\x06\xc5\x8e\xa5\ +\x00o\ +\x00p\x00e\x00n\x00p\x00y\x00p\x00e\ +\x00\x06\ +\x07\x03}\xc3\ +\x00i\ +\x00m\x00a\x00g\x00e\x00s\ +\x00\x17\ +\x0ce\xce\x07\ +\x00l\ +\x00e\x00f\x00t\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00d\x00i\x00s\x00a\x00b\x00l\ +\x00e\x00d\x00.\x00p\x00n\x00g\ +\x00\x1a\ +\x03\x0e\xe4\x87\ +\x00c\ +\x00h\x00e\x00c\x00k\x00b\x00o\x00x\x00_\x00c\x00h\x00e\x00c\x00k\x00e\x00d\x00_\ +\x00h\x00o\x00v\x00e\x00r\x00.\x00p\x00n\x00g\ +\x00 \ +\x0f\xd4\x1b\xc7\ +\x00c\ +\x00h\x00e\x00c\x00k\x00b\x00o\x00x\x00_\x00i\x00n\x00d\x00e\x00t\x00e\x00r\x00m\ +\x00i\x00n\x00a\x00t\x00e\x00_\x00h\x00o\x00v\x00e\x00r\x00.\x00p\x00n\x00g\ +\x00\x15\ +\x03'rg\ +\x00c\ +\x00o\x00m\x00b\x00o\x00b\x00o\x00x\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00o\x00n\ +\x00.\x00p\x00n\x00g\ +\x00\x11\ +\x01\x1f\xc3\x87\ +\x00d\ +\x00o\x00w\x00n\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00o\x00n\x00.\x00p\x00n\x00g\ +\ +\x00\x0e\ +\x04\xa2\xfc\xa7\ +\x00d\ +\x00o\x00w\x00n\x00_\x00a\x00r\x00r\x00o\x00w\x00.\x00p\x00n\x00g\ +\x00\x1b\ +\x03Z2'\ +\x00c\ +\x00o\x00m\x00b\x00o\x00b\x00o\x00x\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00d\x00i\ +\x00s\x00a\x00b\x00l\x00e\x00d\x00.\x00p\x00n\x00g\ +\x00\x0f\ +\x02\x9f\x05\x87\ +\x00r\ +\x00i\x00g\x00h\x00t\x00_\x00a\x00r\x00r\x00o\x00w\x00.\x00p\x00n\x00g\ +\x00\x12\ +\x01.\x03'\ +\x00c\ +\x00o\x00m\x00b\x00o\x00b\x00o\x00x\x00_\x00a\x00r\x00r\x00o\x00w\x00.\x00p\x00n\ +\x00g\ +\x00\x1c\ +\x0e<\xde\x07\ +\x00c\ +\x00h\x00e\x00c\x00k\x00b\x00o\x00x\x00_\x00u\x00n\x00c\x00h\x00e\x00c\x00k\x00e\ +\x00d\x00_\x00h\x00o\x00v\x00e\x00r\x00.\x00p\x00n\x00g\ +\x00\x0f\ +\x06S%\xa7\ +\x00b\ +\x00r\x00a\x00n\x00c\x00h\x00_\x00o\x00p\x00e\x00n\x00.\x00p\x00n\x00g\ +\x00\x0e\ +\x0e\xde\xfa\xc7\ +\x00l\ +\x00e\x00f\x00t\x00_\x00a\x00r\x00r\x00o\x00w\x00.\x00p\x00n\x00g\ +\x00\x11\ +\x0b\xda0\xa7\ +\x00b\ +\x00r\x00a\x00n\x00c\x00h\x00_\x00c\x00l\x00o\x00s\x00e\x00d\x00.\x00p\x00n\x00g\ +\ +\x00\x15\ +\x0f\xf3\xc0\x07\ +\x00u\ +\x00p\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00d\x00i\x00s\x00a\x00b\x00l\x00e\x00d\ +\x00.\x00p\x00n\x00g\ +\x00\x12\ +\x05\x8f\x9d\x07\ +\x00b\ +\x00r\x00a\x00n\x00c\x00h\x00_\x00o\x00p\x00e\x00n\x00_\x00o\x00n\x00.\x00p\x00n\ +\x00g\ +\x00\x1a\ +\x05\x11\xe0\xe7\ +\x00c\ +\x00h\x00e\x00c\x00k\x00b\x00o\x00x\x00_\x00c\x00h\x00e\x00c\x00k\x00e\x00d\x00_\ +\x00f\x00o\x00c\x00u\x00s\x00.\x00p\x00n\x00g\ +\x00\x16\ +\x01u\xcc\x87\ +\x00c\ +\x00h\x00e\x00c\x00k\x00b\x00o\x00x\x00_\x00u\x00n\x00c\x00h\x00e\x00c\x00k\x00e\ +\x00d\x00.\x00p\x00n\x00g\ +\x00\x0f\ +\x0c\xe2hg\ +\x00t\ +\x00r\x00a\x00n\x00s\x00p\x00a\x00r\x00e\x00n\x00t\x00.\x00p\x00n\x00g\ +\x00\x17\ +\x0c\xabQ\x07\ +\x00d\ +\x00o\x00w\x00n\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00d\x00i\x00s\x00a\x00b\x00l\ +\x00e\x00d\x00.\x00p\x00n\x00g\ +\x00\x1d\ +\x09\x07\x81\x07\ +\x00c\ +\x00h\x00e\x00c\x00k\x00b\x00o\x00x\x00_\x00c\x00h\x00e\x00c\x00k\x00e\x00d\x00_\ +\x00d\x00i\x00s\x00a\x00b\x00l\x00e\x00d\x00.\x00p\x00n\x00g\ +\x00\x12\ +\x03\x8d\x04G\ +\x00r\ +\x00i\x00g\x00h\x00t\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00o\x00n\x00.\x00p\x00n\ +\x00g\ +\x00\x1a\ +\x01\x87\xaeg\ +\x00c\ +\x00h\x00e\x00c\x00k\x00b\x00o\x00x\x00_\x00i\x00n\x00d\x00e\x00t\x00e\x00r\x00m\ +\x00i\x00n\x00a\x00t\x00e\x00.\x00p\x00n\x00g\ +\x00#\ +\x06\xf2\x1aG\ +\x00c\ +\x00h\x00e\x00c\x00k\x00b\x00o\x00x\x00_\x00i\x00n\x00d\x00e\x00t\x00e\x00r\x00m\ +\x00i\x00n\x00a\x00t\x00e\x00_\x00d\x00i\x00s\x00a\x00b\x00l\x00e\x00d\x00.\x00p\ +\x00n\x00g\ +\x00\x0c\ +\x06\xe6\xe6g\ +\x00u\ +\x00p\x00_\x00a\x00r\x00r\x00o\x00w\x00.\x00p\x00n\x00g\ +\x00\x11\ +\x00\xb8\x8c\x07\ +\x00l\ +\x00e\x00f\x00t\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00o\x00n\x00.\x00p\x00n\x00g\ +\ +\x00\x0f\ +\x01s\x8b\x07\ +\x00u\ +\x00p\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00o\x00n\x00.\x00p\x00n\x00g\ +\x00\x14\ +\x04^-\xa7\ +\x00b\ +\x00r\x00a\x00n\x00c\x00h\x00_\x00c\x00l\x00o\x00s\x00e\x00d\x00_\x00o\x00n\x00.\ +\x00p\x00n\x00g\ +\x00\x14\ +\x07\xec\xd1\xc7\ +\x00c\ +\x00h\x00e\x00c\x00k\x00b\x00o\x00x\x00_\x00c\x00h\x00e\x00c\x00k\x00e\x00d\x00.\ +\x00p\x00n\x00g\ +\x00\x18\ +\x03\x8e\xdeg\ +\x00r\ +\x00i\x00g\x00h\x00t\x00_\x00a\x00r\x00r\x00o\x00w\x00_\x00d\x00i\x00s\x00a\x00b\ +\x00l\x00e\x00d\x00.\x00p\x00n\x00g\ +\x00 \ +\x09\xd7\x1f\xa7\ +\x00c\ +\x00h\x00e\x00c\x00k\x00b\x00o\x00x\x00_\x00i\x00n\x00d\x00e\x00t\x00e\x00r\x00m\ +\x00i\x00n\x00a\x00t\x00e\x00_\x00f\x00o\x00c\x00u\x00s\x00.\x00p\x00n\x00g\ +\x00\x1c\ +\x08?\xdag\ +\x00c\ +\x00h\x00e\x00c\x00k\x00b\x00o\x00x\x00_\x00u\x00n\x00c\x00h\x00e\x00c\x00k\x00e\ +\x00d\x00_\x00f\x00o\x00c\x00u\x00s\x00.\x00p\x00n\x00g\ +\x00\x1f\ +\x0a\xae'G\ +\x00c\ +\x00h\x00e\x00c\x00k\x00b\x00o\x00x\x00_\x00u\x00n\x00c\x00h\x00e\x00c\x00k\x00e\ +\x00d\x00_\x00d\x00i\x00s\x00a\x00b\x00l\x00e\x00d\x00.\x00p\x00n\x00g\ +" + +qt_resource_struct = b"\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x16\x00\x02\x00\x00\x00 \x00\x00\x00\x03\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x04\xb8\x00\x00\x00\x00\x00\x01\x00\x008:\ +\x00\x00\x01{\xe9xF\xdd\ +\x00\x00\x01\x0c\x00\x00\x00\x00\x00\x01\x00\x00\x07]\ +\x00\x00\x01{\xe9xF\xdb\ +\x00\x00\x01\xb6\x00\x00\x00\x00\x00\x01\x00\x00\x09\xfc\ +\x00\x00\x01{\xe9xF\xd9\ +\x00\x00\x04\xe0\x00\x00\x00\x00\x00\x01\x00\x008\xe4\ +\x00\x00\x01{\xe9xF\xe0\ +\x00\x00\x03 \x00\x00\x00\x00\x00\x01\x00\x00'R\ +\x00\x00\x01}\x0f$Y\x81\ +\x00\x00\x04\x14\x00\x00\x00\x00\x00\x01\x00\x003\xb8\ +\x00\x00\x01}\x0f$Y~\ +\x00\x00\x01\x92\x00\x00\x00\x00\x00\x01\x00\x00\x09X\ +\x00\x00\x01{\xe9xF\xdd\ +\x00\x00\x00\x5c\x00\x00\x00\x00\x00\x01\x00\x00\x00\xaa\ +\x00\x00\x01}\x0f$Y~\ +\x00\x00\x00\xdc\x00\x00\x00\x00\x00\x01\x00\x00\x06\xb3\ +\x00\x00\x01{\xe9xF\xda\ +\x00\x00\x01V\x00\x00\x00\x00\x00\x01\x00\x00\x08\xaf\ +\x00\x00\x01{\xe9xF\xd9\ +\x00\x00\x03\xea\x00\x00\x00\x00\x00\x01\x00\x003\x14\ +\x00\x00\x01{\xe9xF\xde\ +\x00\x00\x05`\x00\x00\x00\x00\x00\x01\x00\x00Ef\ +\x00\x00\x01{\xe9xF\xde\ +\x00\x00\x05\x04\x00\x00\x00\x00\x00\x01\x00\x009\x86\ +\x00\x00\x01{\xe9xF\xd7\ +\x00\x00\x014\x00\x00\x00\x00\x00\x01\x00\x00\x08\x06\ +\x00\x00\x01{\xe9xF\xda\ +\x00\x00\x02\xe6\x00\x00\x00\x00\x00\x01\x00\x00#O\ +\x00\x00\x01}\x0f$Y}\ +\x00\x00\x02\xbc\x00\x00\x00\x00\x00\x01\x00\x00\x1c\x1b\ +\x00\x00\x01{\xe9xF\xd8\ +\x00\x00\x02\x1e\x00\x00\x00\x00\x00\x01\x00\x00\x0c\x13\ +\x00\x00\x01{\xe9xF\xd8\ +\x00\x00\x04\x9a\x00\x00\x00\x00\x00\x01\x00\x007\x98\ +\x00\x00\x01{\xe9xF\xdf\ +\x00\x00\x04N\x00\x00\x00\x00\x00\x01\x00\x005\x98\ +\x00\x00\x01}\x0f$Y\x7f\ +\x00\x00\x052\x00\x00\x00\x00\x00\x01\x00\x00Ag\ +\x00\x00\x01}\x0f$Y|\ +\x00\x00\x05\xdc\x00\x00\x00\x00\x00\x01\x00\x00G\xef\ +\x00\x00\x01}\x0f$Y\x82\ +\x00\x00\x03\xaa\x00\x00\x00\x00\x00\x01\x00\x00.\xdd\ +\x00\x00\x01}\x0f$Y}\ +\x00\x00\x05\x96\x00\x00\x00\x00\x00\x01\x00\x00F\x0a\ +\x00\x00\x01}\x0f$Y\x80\ +\x00\x00\x06\x1a\x00\x00\x00\x00\x00\x01\x00\x00IJ\ +\x00\x00\x01}\x0f$Y\x81\ +\x00\x00\x02d\x00\x00\x00\x00\x00\x01\x00\x00\x13\xc7\ +\x00\x00\x01{\xe9xF\xd7\ +\x00\x00\x00(\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ +\x00\x00\x01{\xe9xF\xdc\ +\x00\x00\x03v\x00\x00\x00\x00\x00\x01\x00\x00.3\ +\x00\x00\x01{\xe9xF\xdb\ +\x00\x00\x03R\x00\x00\x00\x00\x00\x01\x00\x00(\xb1\ +\x00\x00\x01}\x0f$k\xb6\ +\x00\x00\x01\xe0\x00\x00\x00\x00\x00\x01\x00\x00\x0a\xa6\ +\x00\x00\x01}\x0f$Y\x82\ +\x00\x00\x02B\x00\x00\x00\x00\x00\x01\x00\x00\x13\x1d\ +\x00\x00\x01{\xe9xF\xdc\ +\x00\x00\x00\x96\x00\x00\x00\x00\x00\x01\x00\x00\x04\xc0\ +\x00\x00\x01}\x0f$Y\x80\ +\x00\x00\x02\x8c\x00\x00\x00\x00\x00\x01\x00\x00\x1bx\ +\x00\x00\x01{\xe9xF\xdf\ +" + + +def qInitResources(): + QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data) + + +def qCleanupResources(): + QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data) diff --git a/openpype/style/qrc_resources.py b/openpype/style/qrc_resources.py index beacb75f2cd..85f912228d2 100644 --- a/openpype/style/qrc_resources.py +++ b/openpype/style/qrc_resources.py @@ -3,7 +3,9 @@ initialized = False resources = None -if qtpy.API == "pyside2": +if qtpy.API == "pyside6": + from . import pyside6_resources as resources +elif qtpy.API == "pyside2": from . import pyside2_resources as resources elif qtpy.API == "pyqt5": from . import pyqt5_resources as resources From 1fdf261ab74e8e625dbc4e1e38fb891c624c21f8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 15:21:19 +0100 Subject: [PATCH 113/213] added ability to define qtbinding per platform --- pyproject.toml | 14 +- tools/fetch_thirdparty_libs.py | 293 ++++++++++++++++++--------------- 2 files changed, 170 insertions(+), 137 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 72193f732e6..c5a61991717 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -109,12 +109,20 @@ requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" [openpype] - -[openpype.pyside2] # note: in here we can use pip version specifiers as this is installed with pip until # Poetry will support custom location (-t flag for pip) # https://pip.pypa.io/en/stable/cli/pip_install/#requirement-specifiers -version = "==5.15.2" +[openpype.qtbinding.windows] +package = "PySide2" +version = "5.15.2" + +[openpype.qtbinding.darwin] +package = "PySide2" +version = "5.15.2" + +[openpype.qtbinding.linux] +package = "PySide2" +version = "5.15.2" # TODO: we will need to handle different linux flavours here and # also different macos versions too. diff --git a/tools/fetch_thirdparty_libs.py b/tools/fetch_thirdparty_libs.py index 421cc32dbd9..be9c271bf6f 100644 --- a/tools/fetch_thirdparty_libs.py +++ b/tools/fetch_thirdparty_libs.py @@ -64,139 +64,164 @@ def _print(msg: str, message_type: int = 0) -> None: else: header = term.darkolivegreen3("--- ") - print("{}{}".format(header, msg)) - -start_time = time.time_ns() -openpype_root = Path(os.path.dirname(__file__)).parent -pyproject = toml.load(openpype_root / "pyproject.toml") -_print("Handling PySide2 Qt framework ...") -pyside2_version = None -try: - pyside2_version = pyproject["openpype"]["pyside2"]["version"] - _print("We'll install PySide2{}".format(pyside2_version)) -except AttributeError: - _print("No PySide2 version was specified, using latest available.", 2) - -pyside2_arg = "PySide2" if not pyside2_version else "PySide2{}".format(pyside2_version) # noqa: E501 -python_vendor_dir = openpype_root / "vendor" / "python" -try: - subprocess.run( - [sys.executable, "-m", "pip", "install", "--upgrade", - pyside2_arg, "-t", str(python_vendor_dir)], - check=True, stdout=subprocess.DEVNULL) -except subprocess.CalledProcessError as e: - _print("Error during PySide2 installation.", 1) - _print(str(e), 1) - sys.exit(1) - -# Remove libraries for QtSql which don't have available libraries -# by default and Postgre library would require to modify rpath of dependency -platform_name = platform.system().lower() -if platform_name == "darwin": - pyside2_sqldrivers_dir = ( - python_vendor_dir / "PySide2" / "Qt" / "plugins" / "sqldrivers" - ) - for filepath in pyside2_sqldrivers_dir.iterdir(): - os.remove(str(filepath)) - -_print("Processing third-party dependencies ...") - -try: - thirdparty = pyproject["openpype"]["thirdparty"] -except AttributeError: - _print("No third-party libraries specified in pyproject.toml", 1) - sys.exit(1) - -for k, v in thirdparty.items(): - _print(f"processing {k}") - destination_path = openpype_root / "vendor" / "bin" / k - - - if not v.get(platform_name): - _print(("missing definition for current " - f"platform [ {platform_name} ]"), 2) - _print("trying to get universal url for all platforms") - url = v.get("url") - if not url: - _print("cannot get url", 1) - sys.exit(1) - else: - url = v.get(platform_name).get("url") - destination_path = destination_path / platform_name - - parsed_url = urlparse(url) - - # check if file is already extracted in /vendor/bin - if destination_path.exists(): - _print("destination path already exists, deleting ...", 2) - if destination_path.is_dir(): - try: - shutil.rmtree(destination_path) - except OSError as e: - _print("cannot delete folder.", 1) - raise SystemExit(e) - - # download file - _print(f"Downloading {url} ...") - with tempfile.TemporaryDirectory() as temp_dir: - temp_file = Path(temp_dir) / Path(parsed_url.path).name - - r = requests.get(url, stream=True) - content_len = int(r.headers.get('Content-Length', '0')) or None - with manager.counter(color='green', - total=content_len and math.ceil(content_len / 2 ** 20), # noqa: E501 - unit='MiB', leave=False) as counter: - with open(temp_file, 'wb', buffering=2 ** 24) as file_handle: - for chunk in r.iter_content(chunk_size=2 ** 20): - file_handle.write(chunk) - counter.update() - - # get file with checksum - _print("Calculating sha256 ...", 2) - calc_checksum = sha256_sum(temp_file) - - if v.get(platform_name): - item_hash = v.get(platform_name).get("hash") + print(f"{header}{msg}") + + +def install_qtbinding(pyproject, openpype_root, platform_name): + _print("Handling Qt binding framework ...") + qtbinding_def = pyproject["openpype"]["qtbinding"][platform_name] + package = qtbinding_def["package"] + version = qtbinding_def.get("version") + + qtbinding_arg = None + if package and version: + qtbinding_arg = f"{package}=={version}" + elif package: + qtbinding_arg = package + + if not qtbinding_arg: + _print("Didn't find Qt binding to install") + sys.exit(1) + + _print(f"We'll install {qtbinding_arg}") + + python_vendor_dir = openpype_root / "vendor" / "python" + try: + subprocess.run( + [ + sys.executable, + "-m", "pip", "install", "--upgrade", qtbinding_arg, + "-t", str(python_vendor_dir) + ], + check=True, + stdout=subprocess.DEVNULL + ) + except subprocess.CalledProcessError as e: + _print("Error during PySide2 installation.", 1) + _print(str(e), 1) + sys.exit(1) + + # Remove libraries for QtSql which don't have available libraries + # by default and Postgre library would require to modify rpath of + # dependency + if platform_name == "darwin": + sqldrivers_dir = ( + python_vendor_dir / package / "Qt" / "plugins" / "sqldrivers" + ) + for filepath in sqldrivers_dir.iterdir(): + os.remove(str(filepath)) + + +def install_thirdparty(pyproject, openpype_root, platform_name): + _print("Processing third-party dependencies ...") + try: + thirdparty = pyproject["openpype"]["thirdparty"] + except AttributeError: + _print("No third-party libraries specified in pyproject.toml", 1) + sys.exit(1) + + for k, v in thirdparty.items(): + _print(f"processing {k}") + destination_path = openpype_root / "vendor" / "bin" / k + + if not v.get(platform_name): + _print(("missing definition for current " + f"platform [ {platform_name} ]"), 2) + _print("trying to get universal url for all platforms") + url = v.get("url") + if not url: + _print("cannot get url", 1) + sys.exit(1) else: - item_hash = v.get("hash") - - if item_hash != calc_checksum: - _print("Downloaded files checksum invalid.") - sys.exit(1) - - _print("File OK", 3) - if not destination_path.exists(): - destination_path.mkdir(parents=True) - - # extract to destination - archive_type = temp_file.suffix.lstrip(".") - _print(f"Extracting {archive_type} file to {destination_path}") - if archive_type in ['zip']: - zip_file = zipfile.ZipFile(temp_file) - zip_file.extractall(destination_path) - zip_file.close() - - elif archive_type in [ - 'tar', 'tgz', 'tar.gz', 'tar.xz', 'tar.bz2' - ]: - if archive_type == 'tar': - tar_type = 'r:' - elif archive_type.endswith('xz'): - tar_type = 'r:xz' - elif archive_type.endswith('gz'): - tar_type = 'r:gz' - elif archive_type.endswith('bz2'): - tar_type = 'r:bz2' + url = v.get(platform_name).get("url") + destination_path = destination_path / platform_name + + parsed_url = urlparse(url) + + # check if file is already extracted in /vendor/bin + if destination_path.exists(): + _print("destination path already exists, deleting ...", 2) + if destination_path.is_dir(): + try: + shutil.rmtree(destination_path) + except OSError as e: + _print("cannot delete folder.", 1) + raise SystemExit(e) + + # download file + _print(f"Downloading {url} ...") + with tempfile.TemporaryDirectory() as temp_dir: + temp_file = Path(temp_dir) / Path(parsed_url.path).name + + r = requests.get(url, stream=True) + content_len = int(r.headers.get('Content-Length', '0')) or None + with manager.counter( + color='green', + total=content_len and math.ceil(content_len / 2 ** 20), + unit='MiB', leave=False) as counter: + with open(temp_file, 'wb', buffering=2 ** 24) as file_handle: + for chunk in r.iter_content(chunk_size=2 ** 20): + file_handle.write(chunk) + counter.update() + + # get file with checksum + _print("Calculating sha256 ...", 2) + calc_checksum = sha256_sum(temp_file) + + if v.get(platform_name): + item_hash = v.get(platform_name).get("hash") else: - tar_type = 'r:*' - try: - tar_file = tarfile.open(temp_file, tar_type) - except tarfile.ReadError: - raise SystemExit("corrupted archive") - tar_file.extractall(destination_path) - tar_file.close() - _print("Extraction OK", 3) - -end_time = time.time_ns() -total_time = (end_time - start_time) / 1000000000 -_print(f"Downloading and extracting took {total_time} secs.") + item_hash = v.get("hash") + + if item_hash != calc_checksum: + _print("Downloaded files checksum invalid.") + sys.exit(1) + + _print("File OK", 3) + if not destination_path.exists(): + destination_path.mkdir(parents=True) + + # extract to destination + archive_type = temp_file.suffix.lstrip(".") + _print(f"Extracting {archive_type} file to {destination_path}") + if archive_type in ['zip']: + zip_file = zipfile.ZipFile(temp_file) + zip_file.extractall(destination_path) + zip_file.close() + + elif archive_type in [ + 'tar', 'tgz', 'tar.gz', 'tar.xz', 'tar.bz2' + ]: + if archive_type == 'tar': + tar_type = 'r:' + elif archive_type.endswith('xz'): + tar_type = 'r:xz' + elif archive_type.endswith('gz'): + tar_type = 'r:gz' + elif archive_type.endswith('bz2'): + tar_type = 'r:bz2' + else: + tar_type = 'r:*' + try: + tar_file = tarfile.open(temp_file, tar_type) + except tarfile.ReadError: + raise SystemExit("corrupted archive") + tar_file.extractall(destination_path) + tar_file.close() + _print("Extraction OK", 3) + + +def main(): + start_time = time.time_ns() + openpype_root = Path(os.path.dirname(__file__)).parent + pyproject = toml.load(openpype_root / "pyproject.toml") + platform_name = platform.system().lower() + install_qtbinding(pyproject, openpype_root, platform_name) + install_thirdparty(pyproject, openpype_root, platform_name) + end_time = time.time_ns() + total_time = (end_time - start_time) / 1000000000 + _print(f"Downloading and extracting took {total_time} secs.") + + +if __name__ == "__main__": + main() From b5b8119f493fa302a37e280acd2872b529f9f500 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 15:54:46 +0100 Subject: [PATCH 114/213] use class attributes from classes --- openpype/tools/attribute_defs/files_widget.py | 8 ++--- openpype/tools/launcher/widgets.py | 2 +- .../publish_report_viewer/delegates.py | 6 ++-- .../publish_report_viewer/widgets.py | 14 +++++--- .../tools/publisher/widgets/assets_widget.py | 2 +- .../publisher/widgets/border_label_widget.py | 12 +++---- .../publisher/widgets/list_view_widgets.py | 6 ++-- .../publisher/widgets/overview_widget.py | 6 ++-- .../tools/publisher/widgets/publish_frame.py | 2 +- .../publisher/widgets/thumbnail_widget.py | 33 ++++++++++++------- openpype/tools/publisher/widgets/widgets.py | 14 ++++---- openpype/tools/utils/delegates.py | 7 ++-- openpype/tools/utils/tasks_widget.py | 2 +- openpype/tools/utils/widgets.py | 11 ++++--- openpype/vendor/python/common/qargparse.py | 6 ++-- 15 files changed, 75 insertions(+), 56 deletions(-) diff --git a/openpype/tools/attribute_defs/files_widget.py b/openpype/tools/attribute_defs/files_widget.py index 71714fdf161..067866035f1 100644 --- a/openpype/tools/attribute_defs/files_widget.py +++ b/openpype/tools/attribute_defs/files_widget.py @@ -599,14 +599,14 @@ class FilesView(QtWidgets.QListView): def __init__(self, *args, **kwargs): super(FilesView, self).__init__(*args, **kwargs) - self.setEditTriggers(QtWidgets.QListView.NoEditTriggers) + self.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) self.setSelectionMode( QtWidgets.QAbstractItemView.ExtendedSelection ) self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.setAcceptDrops(True) self.setDragEnabled(True) - self.setDragDropMode(self.InternalMove) + self.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove) remove_btn = InViewButton(self) pix_enabled = paint_image_with_color( @@ -616,7 +616,7 @@ def __init__(self, *args, **kwargs): get_image(filename="delete.png"), QtCore.Qt.gray ) icon = QtGui.QIcon(pix_enabled) - icon.addPixmap(pix_disabled, icon.Disabled, icon.Off) + icon.addPixmap(pix_disabled, QtGui.QIcon.Disabled, QtGui.QIcon.Off) remove_btn.setIcon(icon) remove_btn.setEnabled(False) @@ -734,7 +734,7 @@ def __init__(self, single_item, allow_sequences, extensions_label, parent): layout = QtWidgets.QStackedLayout(self) layout.setContentsMargins(0, 0, 0, 0) - layout.setStackingMode(layout.StackAll) + layout.setStackingMode(QtWidgets.QStackedLayout.StackAll) layout.addWidget(empty_widget) layout.addWidget(files_view) layout.setCurrentWidget(empty_widget) diff --git a/openpype/tools/launcher/widgets.py b/openpype/tools/launcher/widgets.py index 3eb641bdb33..81636c2206b 100644 --- a/openpype/tools/launcher/widgets.py +++ b/openpype/tools/launcher/widgets.py @@ -173,7 +173,7 @@ def __init__(self, launcher_model, dbcon, parent=None): view.setResizeMode(QtWidgets.QListView.Adjust) view.setSelectionMode(QtWidgets.QListView.NoSelection) view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) - view.setEditTriggers(QtWidgets.QListView.NoEditTriggers) + view.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) view.setWrapping(True) view.setGridSize(QtCore.QSize(70, 75)) view.setIconSize(QtCore.QSize(30, 30)) diff --git a/openpype/tools/publisher/publish_report_viewer/delegates.py b/openpype/tools/publisher/publish_report_viewer/delegates.py index fc06b23900d..6cd0886e6bc 100644 --- a/openpype/tools/publisher/publish_report_viewer/delegates.py +++ b/openpype/tools/publisher/publish_report_viewer/delegates.py @@ -201,10 +201,10 @@ def item_paint(self, painter, option, index): style = QtWidgets.QApplicaion.style() style.proxy().drawPrimitive( - style.PE_PanelItemViewItem, option, painter, widget + QtWidgets.QStyle.PE_PanelItemViewItem, option, painter, widget ) _rect = style.proxy().subElementRect( - style.SE_ItemViewItemText, option, widget + QtWidgets.QStyle.SE_ItemViewItemText, option, widget ) bg_rect = QtCore.QRectF(option.rect) bg_rect.setY(_rect.y()) @@ -265,7 +265,7 @@ def group_item_paint(self, painter, option, index): else: style = QtWidgets.QApplicaion.style() _rect = style.proxy().subElementRect( - style.SE_ItemViewItemText, option, widget + QtWidgets.QStyle.SE_ItemViewItemText, option, widget ) bg_rect = QtCore.QRectF(option.rect) diff --git a/openpype/tools/publisher/publish_report_viewer/widgets.py b/openpype/tools/publisher/publish_report_viewer/widgets.py index 84af9bb1b86..dc449b6b69f 100644 --- a/openpype/tools/publisher/publish_report_viewer/widgets.py +++ b/openpype/tools/publisher/publish_report_viewer/widgets.py @@ -76,7 +76,7 @@ def __init__(self, parent): super(PluginLoadReportWidget, self).__init__(parent) view = QtWidgets.QTreeView(self) - view.setEditTriggers(view.NoEditTriggers) + view.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) view.setTextElideMode(QtCore.Qt.ElideLeft) view.setHeaderHidden(True) view.setAlternatingRowColors(True) @@ -372,8 +372,10 @@ def __init__(self, parent=None): instances_view.setModel(instances_proxy) instances_view.setIndentation(0) instances_view.setHeaderHidden(True) - instances_view.setEditTriggers(QtWidgets.QTreeView.NoEditTriggers) - instances_view.setSelectionMode(QtWidgets.QTreeView.ExtendedSelection) + instances_view.setEditTriggers( + QtWidgets.QAbstractItemView.NoEditTriggers) + instances_view.setSelectionMode( + QtWidgets.QAbstractItemView.ExtendedSelection) instances_view.setExpandsOnDoubleClick(False) instances_delegate = GroupItemDelegate(instances_view) @@ -393,8 +395,10 @@ def __init__(self, parent=None): plugins_view.setModel(plugins_proxy) plugins_view.setIndentation(0) plugins_view.setHeaderHidden(True) - plugins_view.setSelectionMode(QtWidgets.QTreeView.ExtendedSelection) - plugins_view.setEditTriggers(QtWidgets.QTreeView.NoEditTriggers) + plugins_view.setSelectionMode( + QtWidgets.QAbstractItemView.ExtendedSelection) + plugins_view.setEditTriggers( + QtWidgets.QAbstractItemView.NoEditTriggers) plugins_view.setExpandsOnDoubleClick(False) plugins_delegate = GroupItemDelegate(plugins_view) diff --git a/openpype/tools/publisher/widgets/assets_widget.py b/openpype/tools/publisher/widgets/assets_widget.py index 583e70ad5a9..402f8c2f9f0 100644 --- a/openpype/tools/publisher/widgets/assets_widget.py +++ b/openpype/tools/publisher/widgets/assets_widget.py @@ -193,7 +193,7 @@ def __init__(self, controller, parent): asset_view.setModel(proxy_model) asset_view.setHeaderHidden(True) asset_view.setFrameShape(QtWidgets.QFrame.NoFrame) - asset_view.setEditTriggers(QtWidgets.QTreeView.NoEditTriggers) + asset_view.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) asset_view.setAlternatingRowColors(True) asset_view.setSelectionBehavior(QtWidgets.QTreeView.SelectRows) asset_view.setAllColumnsShowFocus(True) diff --git a/openpype/tools/publisher/widgets/border_label_widget.py b/openpype/tools/publisher/widgets/border_label_widget.py index db575d7b906..5617e159cdc 100644 --- a/openpype/tools/publisher/widgets/border_label_widget.py +++ b/openpype/tools/publisher/widgets/border_label_widget.py @@ -29,8 +29,8 @@ def paintEvent(self, event): pos_x = self.width() painter = QtGui.QPainter(self) painter.setRenderHints( - painter.Antialiasing - | painter.SmoothPixmapTransform + QtGui.QPainter.Antialiasing + | QtGui.QPainter.SmoothPixmapTransform ) if self._color: pen = QtGui.QPen(self._color) @@ -73,8 +73,8 @@ def paintEvent(self, event): ) painter = QtGui.QPainter(self) painter.setRenderHints( - painter.Antialiasing - | painter.SmoothPixmapTransform + QtGui.QPainter.Antialiasing + | QtGui.QPainter.SmoothPixmapTransform ) if self._color: pen = QtGui.QPen(self._color) @@ -131,8 +131,8 @@ def paintEvent(self, event): painter = QtGui.QPainter(self) painter.setRenderHints( - painter.Antialiasing - | painter.SmoothPixmapTransform + QtGui.QPainter.Antialiasing + | QtGui.QPainter.SmoothPixmapTransform ) if self._color: pen = QtGui.QPen(self._color) diff --git a/openpype/tools/publisher/widgets/list_view_widgets.py b/openpype/tools/publisher/widgets/list_view_widgets.py index 28feeee7aa8..e6dad48b67d 100644 --- a/openpype/tools/publisher/widgets/list_view_widgets.py +++ b/openpype/tools/publisher/widgets/list_view_widgets.py @@ -86,9 +86,9 @@ def group_item_paint(self, painter, option, index): painter.save() painter.setRenderHints( - painter.Antialiasing - | painter.SmoothPixmapTransform - | painter.TextAntialiasing + QtGui.QPainter.Antialiasing + | QtGui.QPainter.SmoothPixmapTransform + | QtGui.QPainter.TextAntialiasing ) # Draw backgrounds diff --git a/openpype/tools/publisher/widgets/overview_widget.py b/openpype/tools/publisher/widgets/overview_widget.py index 2566806228c..022de2dc342 100644 --- a/openpype/tools/publisher/widgets/overview_widget.py +++ b/openpype/tools/publisher/widgets/overview_widget.py @@ -172,7 +172,7 @@ def set_state(self, new_state, animate): self._current_state = new_state anim_is_running = ( - self._change_anim.state() == self._change_anim.Running + self._change_anim.state() == QtCore.QAbstractAnimation.Running ) if not animate: self._change_visibility_for_state() @@ -184,9 +184,9 @@ def set_state(self, new_state, animate): self._max_widget_width = self._subset_views_widget.maximumWidth() if new_state == "create": - direction = self._change_anim.Backward + direction = QtCore.QAbstractAnimation.Backward else: - direction = self._change_anim.Forward + direction = QtCore.QAbstractAnimation.Forward self._change_anim.setDirection(direction) if not anim_is_running: diff --git a/openpype/tools/publisher/widgets/publish_frame.py b/openpype/tools/publisher/widgets/publish_frame.py index fe349e52a60..e4e67405329 100644 --- a/openpype/tools/publisher/widgets/publish_frame.py +++ b/openpype/tools/publisher/widgets/publish_frame.py @@ -230,7 +230,7 @@ def set_shrunk_state(self, shrunk): self._shrunken = shrunk anim_is_running = ( - self._shrunk_anim.state() == self._shrunk_anim.Running + self._shrunk_anim.state() == QtCore.QAbstractAnimation.Running ) if not self.isVisible(): if anim_is_running: diff --git a/openpype/tools/publisher/widgets/thumbnail_widget.py b/openpype/tools/publisher/widgets/thumbnail_widget.py index 07423d47648..e234f4cdc13 100644 --- a/openpype/tools/publisher/widgets/thumbnail_widget.py +++ b/openpype/tools/publisher/widgets/thumbnail_widget.py @@ -126,11 +126,14 @@ def _paint_default_pix(self, pix_width, pix_height): new_pix.fill(QtCore.Qt.transparent) pix_painter = QtGui.QPainter() pix_painter.begin(new_pix) - pix_painter.setRenderHints( - pix_painter.Antialiasing - | pix_painter.SmoothPixmapTransform - | pix_painter.HighQualityAntialiasing + render_hints = ( + QtGui.QPainter.Antialiasing + | QtGui.QPainter.SmoothPixmapTransform ) + if hasattr(QtGui.QPainter, "HighQualityAntialiasing"): + render_hints |= QtGui.QPainter.HighQualityAntialiasing + + pix_painter.setRenderHints(render_hints) pix_painter.drawPixmap(pos_x, pos_y, scaled_pix) pix_painter.end() return new_pix @@ -159,11 +162,13 @@ def _draw_thumbnails(self, thumbnails, pix_width, pix_height): new_pix.fill(QtCore.Qt.transparent) pix_painter = QtGui.QPainter() pix_painter.begin(new_pix) - pix_painter.setRenderHints( - pix_painter.Antialiasing - | pix_painter.SmoothPixmapTransform - | pix_painter.HighQualityAntialiasing + render_hints = ( + QtGui.QPainter.Antialiasing + | QtGui.QPainter.SmoothPixmapTransform ) + if hasattr(QtGui.QPainter, "HighQualityAntialiasing"): + render_hints |= QtGui.QPainter.HighQualityAntialiasing + pix_painter.setRenderHints(render_hints) tiled_rect = QtCore.QRectF( pos_x, pos_y, scaled_pix.width(), scaled_pix.height() @@ -239,11 +244,15 @@ def _cache_pix(self): final_painter = QtGui.QPainter() final_painter.begin(final_pix) - final_painter.setRenderHints( - final_painter.Antialiasing - | final_painter.SmoothPixmapTransform - | final_painter.HighQualityAntialiasing + render_hints = ( + QtGui.QPainter.Antialiasing + | QtGui.QPainter.SmoothPixmapTransform ) + if hasattr(QtGui.QPainter, "HighQualityAntialiasing"): + render_hints |= QtGui.QPainter.HighQualityAntialiasing + + final_painter.setRenderHints(render_hints) + final_painter.setBrush(QtGui.QBrush(self.thumbnail_bg_color)) final_painter.setPen(bg_pen) final_painter.drawRect(rect) diff --git a/openpype/tools/publisher/widgets/widgets.py b/openpype/tools/publisher/widgets/widgets.py index f65d0302c53..2e8d0ce37cd 100644 --- a/openpype/tools/publisher/widgets/widgets.py +++ b/openpype/tools/publisher/widgets/widgets.py @@ -143,9 +143,9 @@ def generate_icon(self, pixmap_path, enabled_color, disabled_color): icon = QtGui.QIcon() image = QtGui.QImage(pixmap_path) enabled_pixmap = self.paint_image_with_color(image, enabled_color) - icon.addPixmap(enabled_pixmap, icon.Normal) + icon.addPixmap(enabled_pixmap, QtGui.QIcon.Normal) disabled_pixmap = self.paint_image_with_color(image, disabled_color) - icon.addPixmap(disabled_pixmap, icon.Disabled) + icon.addPixmap(disabled_pixmap, QtGui.QIcon.Disabled) return icon @staticmethod @@ -1779,11 +1779,11 @@ def set_increasing(self, increasing): return self._increasing = increasing if increasing: - self._change_anim.setDirection(self._change_anim.Forward) + self._change_anim.setDirection(QtCore.QAbstractAnimation.Forward) else: - self._change_anim.setDirection(self._change_anim.Backward) + self._change_anim.setDirection(QtCore.QAbstractAnimation.Backward) - if self._change_anim.state() != self._change_anim.Running: + if self._change_anim.state() != QtCore.QAbstractAnimation.Running: self._change_anim.start() def set_visible(self, visible): @@ -1857,8 +1857,8 @@ def paintEvent(self, event): painter.setClipRect(event.rect()) painter.setRenderHints( - painter.Antialiasing - | painter.SmoothPixmapTransform + QtGui.QPainter.Antialiasing + | QtGui.QPainter.SmoothPixmapTransform ) painter.setPen(QtCore.Qt.NoPen) diff --git a/openpype/tools/utils/delegates.py b/openpype/tools/utils/delegates.py index 17479488003..604df689c48 100644 --- a/openpype/tools/utils/delegates.py +++ b/openpype/tools/utils/delegates.py @@ -66,9 +66,12 @@ def paint(self, painter, option, index): pen.setColor(fg_color) painter.setPen(pen) - text_rect = style.subElementRect(style.SE_ItemViewItemText, option) + text_rect = style.subElementRect( + QtWidgets.QStyle.SE_ItemViewItemText, + option + ) text_margin = style.proxy().pixelMetric( - style.PM_FocusFrameHMargin, option, option.widget + QtWidgets.QStyle.PM_FocusFrameHMargin, option, option.widget ) + 1 painter.drawText( diff --git a/openpype/tools/utils/tasks_widget.py b/openpype/tools/utils/tasks_widget.py index b0ddedcb6e1..8c0505223e3 100644 --- a/openpype/tools/utils/tasks_widget.py +++ b/openpype/tools/utils/tasks_widget.py @@ -180,7 +180,7 @@ def __init__(self, dbcon, parent=None): tasks_view = DeselectableTreeView(self) tasks_view.setIndentation(0) tasks_view.setSortingEnabled(True) - tasks_view.setEditTriggers(QtWidgets.QTreeView.NoEditTriggers) + tasks_view.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) header_view = tasks_view.header() header_view.setSortIndicator(0, QtCore.Qt.AscendingOrder) diff --git a/openpype/tools/utils/widgets.py b/openpype/tools/utils/widgets.py index c64b6e3400d..a9d6fa35b26 100644 --- a/openpype/tools/utils/widgets.py +++ b/openpype/tools/utils/widgets.py @@ -283,11 +283,14 @@ def paintEvent(self, event): painter.end() return - painter.setRenderHints( - painter.Antialiasing - | painter.SmoothPixmapTransform - | painter.HighQualityAntialiasing + render_hints = ( + QtGui.QPainter.Antialiasing + | QtGui.QPainter.SmoothPixmapTransform ) + if hasattr(QtGui.QPainter, "HighQualityAntialiasing"): + render_hints |= QtGui.QPainter.HighQualityAntialiasing + + painter.setRenderHints(render_hints) if self._cached_pixmap is None: self._cache_pixmap() diff --git a/openpype/vendor/python/common/qargparse.py b/openpype/vendor/python/common/qargparse.py index 172c594d1dd..17cf493a898 100644 --- a/openpype/vendor/python/common/qargparse.py +++ b/openpype/vendor/python/common/qargparse.py @@ -570,7 +570,7 @@ def data(self, index, role): model = QtCore.QStringListModel(self["default"]) widget = QtWidgets.QListView() widget.setModel(model) - widget.setEditTriggers(widget.NoEditTriggers) + widget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) self._read = lambda: model.stringList() self._write = lambda value: model.setStringList(value) @@ -640,8 +640,8 @@ def reset(items, default=None): model = QtCore.QStringListModel() widget = QtWidgets.QListView() widget.setModel(model) - widget.setEditTriggers(widget.NoEditTriggers) - widget.setSelectionMode(widget.SingleSelection) + widget.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) + widget.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) smodel = widget.selectionModel() smodel.selectionChanged.connect(on_changed) From 940cb35e0d2f56feec5eefd7de81005971456307 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 15:55:05 +0100 Subject: [PATCH 115/213] fix usage of filteregexp --- openpype/tools/sceneinventory/model.py | 9 +++++++-- openpype/tools/settings/settings/search_dialog.py | 9 +++++++-- .../widgets/model_filter_proxy_recursive_sort.py | 11 ++++++----- openpype/tools/utils/models.py | 9 +++++++-- 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/openpype/tools/sceneinventory/model.py b/openpype/tools/sceneinventory/model.py index 9b45ed6cc1b..3398743aec5 100644 --- a/openpype/tools/sceneinventory/model.py +++ b/openpype/tools/sceneinventory/model.py @@ -482,8 +482,13 @@ def filterAcceptsRow(self, row, parent): return True # Filter by regex - if not self.filterRegExp().isEmpty(): - pattern = re.escape(self.filterRegExp().pattern()) + if hasattr(self, "filterRegularExpression"): + regex = self.filterRegularExpression() + else: + regex = self.filterRegExp() + pattern = regex.pattern() + if pattern: + pattern = re.escape(pattern) if not self._matches(row, parent, pattern): return False diff --git a/openpype/tools/settings/settings/search_dialog.py b/openpype/tools/settings/settings/search_dialog.py index 71d258febea..33a4d16e982 100644 --- a/openpype/tools/settings/settings/search_dialog.py +++ b/openpype/tools/settings/settings/search_dialog.py @@ -27,8 +27,13 @@ def filterAcceptsRow(self, row, parent): if not parent.isValid(): return False - regex = self.filterRegExp() - if not regex.isEmpty() and regex.isValid(): + if hasattr(self, "filterRegularExpression"): + regex = self.filterRegularExpression() + else: + regex = self.filterRegExp() + + pattern = regex.pattern() + if pattern and regex.isValid(): pattern = regex.pattern() compiled_regex = re.compile(pattern, re.IGNORECASE) source_model = self.sourceModel() diff --git a/openpype/tools/standalonepublish/widgets/model_filter_proxy_recursive_sort.py b/openpype/tools/standalonepublish/widgets/model_filter_proxy_recursive_sort.py index 727d3a97d70..5c72e2049b9 100644 --- a/openpype/tools/standalonepublish/widgets/model_filter_proxy_recursive_sort.py +++ b/openpype/tools/standalonepublish/widgets/model_filter_proxy_recursive_sort.py @@ -5,14 +5,15 @@ class RecursiveSortFilterProxyModel(QtCore.QSortFilterProxyModel): """Filters to the regex if any of the children matches allow parent""" def filterAcceptsRow(self, row, parent): - - regex = self.filterRegExp() - if not regex.isEmpty(): - pattern = regex.pattern() + if hasattr(self, "filterRegularExpression"): + regex = self.filterRegularExpression() + else: + regex = self.filterRegExp() + pattern = regex.pattern() + if pattern: model = self.sourceModel() source_index = model.index(row, self.filterKeyColumn(), parent) if source_index.isValid(): - # Check current index itself key = model.data(source_index, self.filterRole()) if re.search(pattern, key, re.IGNORECASE): diff --git a/openpype/tools/utils/models.py b/openpype/tools/utils/models.py index b26ec566fee..270e00b2ef8 100644 --- a/openpype/tools/utils/models.py +++ b/openpype/tools/utils/models.py @@ -203,8 +203,13 @@ class RecursiveSortFilterProxyModel(QtCore.QSortFilterProxyModel): the filter string but first checks if any children does. """ def filterAcceptsRow(self, row, parent_index): - regex = self.filterRegExp() - if not regex.isEmpty(): + if hasattr(self, "filterRegularExpression"): + regex = self.filterRegularExpression() + else: + regex = self.filterRegExp() + + pattern = regex.pattern() + if pattern: model = self.sourceModel() source_index = model.index( row, self.filterKeyColumn(), parent_index From 7ce1dfb2c803d861a0f2f2fb60842cb89c2f61f9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 15:55:17 +0100 Subject: [PATCH 116/213] fix center window 'maybe' --- openpype/tools/utils/lib.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/openpype/tools/utils/lib.py b/openpype/tools/utils/lib.py index ae9c5bc96c5..fb5530b100d 100644 --- a/openpype/tools/utils/lib.py +++ b/openpype/tools/utils/lib.py @@ -25,9 +25,15 @@ def center_window(window): """Move window to center of it's screen.""" - desktop = QtWidgets.QApplication.desktop() - screen_idx = desktop.screenNumber(window) - screen_geo = desktop.screenGeometry(screen_idx) + + if hasattr(QtWidgets.QApplication, "desktop"): + desktop = QtWidgets.QApplication.desktop() + screen_idx = desktop.screenNumber(window) + screen_geo = desktop.screenGeometry(screen_idx) + else: + screen = window.screen() + screen_geo = screen.geometry() + geo = window.frameGeometry() geo.moveCenter(screen_geo.center()) if geo.y() < screen_geo.y(): From dd8a7e565343a59d8a5b8479d3fb969fd4177c94 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 15:55:27 +0100 Subject: [PATCH 117/213] fix usage of qtpy in assets widget --- openpype/tools/utils/assets_widget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/utils/assets_widget.py b/openpype/tools/utils/assets_widget.py index f33a412a7bb..8a7511e6c5a 100644 --- a/openpype/tools/utils/assets_widget.py +++ b/openpype/tools/utils/assets_widget.py @@ -136,7 +136,7 @@ def sizeHint(self, option, index): def paint(self, painter, option, index): """Replicate painting of an item and draw color bars if needed.""" # Qt4 compat - if Qt.__binding__ in ("PySide", "PyQt4"): + if qtpy.API in ("pyside", "pyqt4"): option = QStyleOptionViewItemV4(option) painter.save() From fd611bdae7ad639ddb0d3a641cab04bcbde208db Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 21 Dec 2022 16:01:51 +0100 Subject: [PATCH 118/213] changed PySide2 to PySide2 in pyproject toml --- pyproject.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c5a61991717..cdf0a5cd972 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -113,16 +113,16 @@ build-backend = "poetry.core.masonry.api" # Poetry will support custom location (-t flag for pip) # https://pip.pypa.io/en/stable/cli/pip_install/#requirement-specifiers [openpype.qtbinding.windows] -package = "PySide2" -version = "5.15.2" +package = "PySide6" +version = "6.4.1" [openpype.qtbinding.darwin] -package = "PySide2" -version = "5.15.2" +package = "PySide6" +version = "6.4.1" [openpype.qtbinding.linux] -package = "PySide2" -version = "5.15.2" +package = "PySide6" +version = "6.4.1" # TODO: we will need to handle different linux flavours here and # also different macos versions too. From 843f6e8cd036452fd8f98dc88b70abdf6b06fdd3 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Thu, 22 Dec 2022 16:10:07 +0100 Subject: [PATCH 119/213] :art: improve online family functionality --- .../plugins/create/create_online.py | 31 ++++++++++++++++--- .../plugins/publish/collect_online_file.py | 8 ++++- .../plugins/publish/validate_online_file.py | 2 ++ openpype/plugins/publish/extract_thumbnail.py | 2 +- 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/traypublisher/plugins/create/create_online.py b/openpype/hosts/traypublisher/plugins/create/create_online.py index 19f956a50ee..096172d5811 100644 --- a/openpype/hosts/traypublisher/plugins/create/create_online.py +++ b/openpype/hosts/traypublisher/plugins/create/create_online.py @@ -7,13 +7,14 @@ """ from pathlib import Path -from openpype.client import get_subset_by_name, get_asset_by_name -from openpype.lib.attribute_definitions import FileDef +# from openpype.client import get_subset_by_name, get_asset_by_name +from openpype.lib.attribute_definitions import FileDef, BoolDef, UILabelDef from openpype.pipeline import ( CreatedInstance, CreatorError ) from openpype.hosts.traypublisher.api.plugin import TrayPublishCreator +from typing import Union class OnlineCreator(TrayPublishCreator): @@ -23,7 +24,11 @@ class OnlineCreator(TrayPublishCreator): label = "Online" family = "online" description = "Publish file retaining its original file name" - extensions = [".mov", ".mp4", ".mxf", ".m4v", ".mpg"] + extensions = [".mov", ".mp4", ".mxf", ".m4v", ".mpg", ".exr", ".dpx", ".tif", ".png", ".jpg"] + + def __init__(self, *args, **kwargs): + super(OnlineCreator, self).__init__(*args, **kwargs) + self._original_path: Union[str, None] = None def get_detail_description(self): return """# Create file retaining its original file name. @@ -49,13 +54,17 @@ def create(self, subset_name, instance_data, pre_create_data): origin_basename = Path(files[0]).stem + # disable check for existing subset with the same name + """ asset = get_asset_by_name( self.project_name, instance_data["asset"], fields=["_id"]) + if get_subset_by_name( self.project_name, origin_basename, asset["_id"], fields=["_id"]): raise CreatorError(f"subset with {origin_basename} already " "exists in selected asset") + """ instance_data["originalBasename"] = origin_basename subset_name = origin_basename @@ -69,15 +78,29 @@ def create(self, subset_name, instance_data, pre_create_data): instance_data, self) self._store_new_instance(new_instance) + def get_instance_attr_defs(self): + return [ + BoolDef( + "add_review_family", + default=True, + label="Review" + ) + ] + def get_pre_create_attr_defs(self): return [ FileDef( "representation_file", folders=False, extensions=self.extensions, - allow_sequences=False, + allow_sequences=True, single_item=True, label="Representation", + ), + BoolDef( + "add_review_family", + default=True, + label="Review" ) ] diff --git a/openpype/hosts/traypublisher/plugins/publish/collect_online_file.py b/openpype/hosts/traypublisher/plugins/publish/collect_online_file.py index a3f86afa138..05b00e9516e 100644 --- a/openpype/hosts/traypublisher/plugins/publish/collect_online_file.py +++ b/openpype/hosts/traypublisher/plugins/publish/collect_online_file.py @@ -12,12 +12,18 @@ class CollectOnlineFile(pyblish.api.InstancePlugin): def process(self, instance): file = Path(instance.data["creator_attributes"]["path"]) + review = instance.data["creator_attributes"]["add_review_family"] + instance.data["review"] = review + if "review" not in instance.data["families"]: + instance.data["families"].append("review") + self.log.info(f"Adding review: {review}") instance.data["representations"].append( { "name": file.suffix.lstrip("."), "ext": file.suffix.lstrip("."), "files": file.name, - "stagingDir": file.parent.as_posix() + "stagingDir": file.parent.as_posix(), + "tags": ["review"] if review else [] } ) diff --git a/openpype/hosts/traypublisher/plugins/publish/validate_online_file.py b/openpype/hosts/traypublisher/plugins/publish/validate_online_file.py index 12b2e72ced9..2db865ca2bb 100644 --- a/openpype/hosts/traypublisher/plugins/publish/validate_online_file.py +++ b/openpype/hosts/traypublisher/plugins/publish/validate_online_file.py @@ -20,6 +20,8 @@ class ValidateOnlineFile(OptionalPyblishPluginMixin, optional = True def process(self, instance): + if not self.is_active(instance.data): + return project_name = instance.context.data["projectName"] asset_id = instance.data["assetEntity"]["_id"] subset = get_subset_by_name( diff --git a/openpype/plugins/publish/extract_thumbnail.py b/openpype/plugins/publish/extract_thumbnail.py index 14b43beae85..14c6a21ed00 100644 --- a/openpype/plugins/publish/extract_thumbnail.py +++ b/openpype/plugins/publish/extract_thumbnail.py @@ -19,7 +19,7 @@ class ExtractThumbnail(pyblish.api.InstancePlugin): order = pyblish.api.ExtractorOrder families = [ "imagesequence", "render", "render2d", "prerender", - "source", "clip", "take" + "source", "clip", "take", "online" ] hosts = ["shell", "fusion", "resolve", "traypublisher"] enabled = False From 64afc35cd2ea98a2c340a23c6a2a2c0158a2e216 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Thu, 22 Dec 2022 16:13:37 +0100 Subject: [PATCH 120/213] :rotating_light: some hound fixes --- .../hosts/traypublisher/plugins/create/create_online.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/traypublisher/plugins/create/create_online.py b/openpype/hosts/traypublisher/plugins/create/create_online.py index 096172d5811..1a366bcff55 100644 --- a/openpype/hosts/traypublisher/plugins/create/create_online.py +++ b/openpype/hosts/traypublisher/plugins/create/create_online.py @@ -8,7 +8,7 @@ from pathlib import Path # from openpype.client import get_subset_by_name, get_asset_by_name -from openpype.lib.attribute_definitions import FileDef, BoolDef, UILabelDef +from openpype.lib.attribute_definitions import FileDef, BoolDef from openpype.pipeline import ( CreatedInstance, CreatorError @@ -24,7 +24,8 @@ class OnlineCreator(TrayPublishCreator): label = "Online" family = "online" description = "Publish file retaining its original file name" - extensions = [".mov", ".mp4", ".mxf", ".m4v", ".mpg", ".exr", ".dpx", ".tif", ".png", ".jpg"] + extensions = [".mov", ".mp4", ".mxf", ".m4v", ".mpg", ".exr", + ".dpx", ".tif", ".png", ".jpg"] def __init__(self, *args, **kwargs): super(OnlineCreator, self).__init__(*args, **kwargs) @@ -58,7 +59,7 @@ def create(self, subset_name, instance_data, pre_create_data): """ asset = get_asset_by_name( self.project_name, instance_data["asset"], fields=["_id"]) - + if get_subset_by_name( self.project_name, origin_basename, asset["_id"], fields=["_id"]): From c86ebf1e93257ad29647797de57111bb1544fe08 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 23 Dec 2022 10:05:51 +0800 Subject: [PATCH 121/213] only allows the loaded reference to be imported reference --- openpype/hosts/maya/plugins/publish/extract_import_reference.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index 8e0257dafb1..b77740ae134 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -82,7 +82,7 @@ def process(self, instance): print(">>> Processing references") all_reference = cmds.file(q=True, reference=True) or [] for ref in all_reference: - if cmds.referenceQuery(ref, f=True): + if cmds.referenceQuery(ref, f=True, il=True): cmds.file(ref, importReference=True) nested_ref = cmds.file(q=True, reference=True) From 139706a62e7043626f16776be0bf8b20608793dd Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 28 Dec 2022 10:27:40 +0100 Subject: [PATCH 122/213] initial commit of window --- .../republisher_dialog/__init__.py | 0 .../republisher/republisher_dialog/window.py | 428 ++++++++++++++++++ .../republisher_processor/__init__.py | 0 .../republisher_processor/control.py | 24 + 4 files changed, 452 insertions(+) create mode 100644 openpype/tools/republisher/republisher_dialog/__init__.py create mode 100644 openpype/tools/republisher/republisher_dialog/window.py create mode 100644 openpype/tools/republisher/republisher_processor/__init__.py create mode 100644 openpype/tools/republisher/republisher_processor/control.py diff --git a/openpype/tools/republisher/republisher_dialog/__init__.py b/openpype/tools/republisher/republisher_dialog/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openpype/tools/republisher/republisher_dialog/window.py b/openpype/tools/republisher/republisher_dialog/window.py new file mode 100644 index 00000000000..e268e1f6fb9 --- /dev/null +++ b/openpype/tools/republisher/republisher_dialog/window.py @@ -0,0 +1,428 @@ +import collections + +from qtpy import QtWidgets, QtGui, QtCore + +from openpype.client import get_projects, get_assets +from openpype.lib.events import EventSystem + + +PROJECT_NAME_ROLE = QtCore.Qt.UserRole + 1 +ASSET_NAME_ROLE = QtCore.Qt.UserRole + 2 +ASSET_ICON_ROLE = QtCore.Qt.UserRole + 3 +ASSET_ID_ROLE = QtCore.Qt.UserRole + 4 + + +class AssetItem: + def __init__(self, entity_id, name, icon, parent_id): + self.id = entity_id + self.name = name + self.icon = icon + self.parent_id = parent_id + + @classmethod + def from_doc(cls, asset_doc): + parent_id = asset_doc["data"].get("visualParent") + if parent_id is not None: + parent_id = str(parent_id) + return cls( + str(asset_doc["_id"]), + asset_doc["name"], + asset_doc["data"].get("icon"), + parent_id + ) + + + +class EntitiesModel: + def __init__(self, event_system): + self._event_system = event_system + self._projects = None + self._assets_by_project = {} + self._tasks_by_asset_id = collections.defaultdict(dict) + + def has_cached_projects(self): + return self._projects is None + + def has_cached_assets(self, project_name): + if not project_name: + return True + return project_name in self._assets_by_project + + def has_cached_tasks(self, project_name): + if not project_name: + return True + return project_name in self._assets_by_project + + def get_projects(self): + if self._projects is not None: + return list(self._projects) + + self.refresh_projects() + + def get_assets(self, project_name): + if project_name in self._assets_by_project: + return dict(self._assets_by_project[project_name]) + self.refresh_assets(project_name) + return [] + + def get_tasks(self, project_name, asset_id): + output = [] + if not project_name or not asset_id: + return output + + if project_name not in self._assets_by_project: + self.refresh_assets(project_name) + return output + + asset_docs = self._assets_by_project[project_name] + asset_doc = asset_docs.get(asset_id) + if not asset_doc: + return output + + for task, _task_info in asset_doc["data"]["tasks"].items(): + output.append(task) + return output + + def refresh_projects(self): + self._projects = None + self._event_system.emit( + "projects.refresh.started", {}, "entities.model" + ) + self._projects = [project["name"] for project in get_projects()] + self._event_system.emit( + "projects.refresh.finished", {}, "entities.model" + ) + + def refresh_assets(self, project_name): + self._event_system.emit( + "assets.refresh.started", + {"project_name": project_name}, + "entities.model" + ) + asset_docs = [] + if project_name: + asset_docs = get_assets(project_name) + asset_items_by_id = {} + for asset_doc in asset_docs: + asset_item = AssetItem.from_doc(asset_doc) + asset_items_by_id[asset_item.id] = asset_item + self._assets_by_project[project_name] = asset_items_by_id + self._event_system.emit( + "assets.refresh.finished", + {"project_name": project_name}, + "entities.model" + ) + + +class SelectionModel: + def __init__(self, event_system): + self._event_system = event_system + + self.project_name = None + self.asset_id = None + self.task_name = None + + def select_project(self, project_name): + if self.project_name == project_name: + return + + self.project_name = project_name + self.asset_id = None + self.task_name = None + self._event_system.emit( + "project.changed", + {"project_name": project_name}, + "selection.model" + ) + + def select_asset(self, asset_id): + if self.asset_id == asset_id: + return + self.asset_id = asset_id + self.task_name = None + self._event_system.emit( + "asset.changed", + { + "project_name": self.project_name, + "asset_id": asset_id + }, + "selection.model" + ) + + def select_task(self, task_name): + if self.task_name == task_name: + return + self.task_name = task_name + self._event_system.emit( + "task.changed", + { + "project_name": self.project_name, + "asset_id": self.asset_id, + "task_name": task_name + }, + "selection.model" + ) + + +class RepublisherDialogController: + def __init__(self): + event_system = EventSystem() + entities_model = EntitiesModel(event_system) + selection_model = SelectionModel(event_system) + + self._event_system = event_system + self._entities_model = entities_model + self._selection_model = selection_model + + self.dst_project_name = None + self.dst_asset_id = None + self.dst_task_name = None + + @property + def event_system(self): + return self._event_system + + @property + def model(self): + return self._entities_model + + @property + def selection_model(self): + return self._selection_model + + +class ProjectsModel(QtGui.QStandardItemModel): + empty_text = "< Empty >" + refreshing_text = "< Refreshing >" + select_project_text = "< Select Project >" + + def __init__(self, controller): + super().__init__() + self._controller = controller + + self.event_system.add_callback( + "projects.refresh.finished", self._on_refresh_finish + ) + + placeholder_item = QtGui.QStandardItem(self.empty_text) + + root_item = self.invisibleRootItem() + root_item.appendRows([placeholder_item]) + items = {None: placeholder_item} + + self._placeholder_item = placeholder_item + self._items = items + + @property + def event_system(self): + return self._controller.event_system + + def _on_refresh_finish(self): + root_item = self.invisibleRootItem() + project_names = self._controller.model.get_projects() + + if not project_names: + placeholder_text = self.empty_text + else: + placeholder_text = self.select_project_text + self._placeholder_item.setData(placeholder_text, QtCore.Qt.DisplayRole) + + new_items = [] + if None not in self._items: + new_items.append(self._placeholder_item) + + current_project_names = set(self._items.keys()) + for project_name in current_project_names - set(project_names): + if project_name is None: + continue + item = self._items.pop(project_name) + root_item.removeRow(item.row()) + + for project_name in project_names: + if project_name in self._items: + continue + item = QtGui.QStandardItem(project_name) + item.setData(project_name, PROJECT_NAME_ROLE) + new_items.append(item) + + if new_items: + root_item.appendRows(new_items) + + +class AssetsModel(QtGui.QStandardItemModel): + empty_text = "< Empty >" + + def __init__(self, controller): + super().__init__() + self._controller = controller + + items = {} + + placeholder_item = QtGui.QStandardItem(self.empty_text) + + root_item = self.invisibleRootItem() + root_item.appendRows([placeholder_item]) + items[None] = placeholder_item + + self.event_system.add_callback( + "project.changed", self._on_project_change + ) + self.event_system.add_callback( + "assets.refresh.started", self._on_refresh_start + ) + self.event_system.add_callback( + "assets.refresh.finished", self._on_refresh_finish + ) + + self._items = {} + + self._placeholder_item = placeholder_item + + @property + def event_system(self): + return self._controller.event_system + + def _clear(self): + placeholder_in = False + root_item = self.invisibleRootItem() + for row in reversed(range(root_item.rowCount())): + item = root_item.child(row) + asset_id = item.data(ASSET_ID_ROLE) + if asset_id is None: + placeholder_in = True + continue + root_item.removeRow(item.row()) + + for key in tuple(self._items.keys()): + if key is not None: + self._items.pop(key) + + if not placeholder_in: + root_item.appendRows([self._placeholder_item]) + self._items[None] = self._placeholder_item + + def _on_project_change(self, event): + self._clear() + + def _on_refresh_start(self, event): + pass + + def _on_refresh_finish(self, event): + event_project_name = event["project_name"] + project_name = self._controller.selection_model.project_name + print("finished", event_project_name, project_name) + if event_project_name != project_name: + return + + if project_name is None: + if None not in self._items: + self._clear() + return + + asset_items_by_id = self._controller.model.get_assets(project_name) + if not asset_items_by_id: + self._clear() + return + + assets_by_parent_id = collections.defaultdict(list) + for asset_item in asset_items_by_id.values(): + assets_by_parent_id[asset_item.parent_id].append(asset_item) + + root_item = self.invisibleRootItem() + if None in self._items: + self._items.pop(None) + root_item.takeRow(self._placeholder_item.row()) + + items_to_remove = set(self._items) - set(asset_items_by_id.keys()) + + hierarchy_queue = collections.deque() + hierarchy_queue.append((None, root_item)) + while hierarchy_queue: + parent_id, parent_item = hierarchy_queue.popleft() + new_items = [] + for asset_item in assets_by_parent_id[parent_id]: + item = self._items.get(asset_item.id) + if item is None: + item = QtGui.QStandardItem() + new_items.append(item) + self._items[asset_item.id] = item + + elif item.parent() is not parent_item: + new_items.append(item) + + item.setData(asset_item.name, QtCore.Qt.DisplayRole) + item.setData(asset_item.id, ASSET_ID_ROLE) + item.setData(asset_item.icon, ASSET_ICON_ROLE) + + if new_items: + parent_item.appendRows(new_items) + + for item_id in items_to_remove: + item = self._items.pop(item_id, None) + if item is None: + continue + parent = item.parent() + if parent is not None: + parent.removeRow(item.row()) + + +class RepublisherDialogWindow(QtWidgets.QWidget): + def __init__(self, controller=None): + super().__init__() + if controller is None: + controller = RepublisherDialogController() + self._controller = controller + + left_widget = QtWidgets.QWidget(self) + + project_combobox = QtWidgets.QComboBox(left_widget) + project_model = ProjectsModel(controller) + project_delegate = QtWidgets.QStyledItemDelegate() + project_combobox.setItemDelegate(project_delegate) + project_combobox.setModel(project_model) + + asset_view = QtWidgets.QTreeView(self) + asset_model = AssetsModel(controller) + asset_view.setModel(asset_model) + + left_layout = QtWidgets.QVBoxLayout(left_widget) + left_layout.addWidget(project_combobox, 0) + left_layout.addWidget(asset_view, 1) + + main_layout = QtWidgets.QHBoxLayout(self) + main_layout.addWidget(left_widget) + + self._project_combobox = project_combobox + self._project_model = project_model + self._project_delegate = project_delegate + + self._asset_view = asset_view + self._asset_model = asset_model + + self._first_show = True + + def showEvent(self, event): + super().showEvent(event) + if self._first_show: + self._first_show = False + self._controller.model.refresh_projects() + + +def main(): + app = QtWidgets.QApplication.instance() + if not app: + app = QtWidgets.QApplication([]) + + # TODO find way how to get these + project_name = None + representation_id = None + + # Show window dialog + window = RepublisherDialogWindow() + window.show() + + app.exec_() + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/openpype/tools/republisher/republisher_processor/__init__.py b/openpype/tools/republisher/republisher_processor/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openpype/tools/republisher/republisher_processor/control.py b/openpype/tools/republisher/republisher_processor/control.py new file mode 100644 index 00000000000..dbaffe2ef7b --- /dev/null +++ b/openpype/tools/republisher/republisher_processor/control.py @@ -0,0 +1,24 @@ +import requests + + +class PublishItem: + def __init__( + self, + src_project_name, + src_representation_id, + dst_project_name, + dst_asset_id, + dst_task_name + ): + self.src_project_name = src_project_name + self.src_representation_id = src_representation_id + self.dst_project_name = dst_project_name + self.dst_asset_id = dst_asset_id + self.dst_task_name = dst_task_name + + +class RepublisherController: + def __init__(self): + pass + + From 77d26dd0232a65fc62853661d64f423797c53221 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 30 Dec 2022 14:52:48 +0100 Subject: [PATCH 123/213] separated controller and UI --- .../republisher/republisher_dialog/control.py | 225 +++++++++ .../republisher/republisher_dialog/window.py | 443 ++++++++++-------- 2 files changed, 473 insertions(+), 195 deletions(-) create mode 100644 openpype/tools/republisher/republisher_dialog/control.py diff --git a/openpype/tools/republisher/republisher_dialog/control.py b/openpype/tools/republisher/republisher_dialog/control.py new file mode 100644 index 00000000000..8c4e0313fe1 --- /dev/null +++ b/openpype/tools/republisher/republisher_dialog/control.py @@ -0,0 +1,225 @@ +import collections +from openpype.client import get_projects, get_assets +from openpype.lib.events import EventSystem + + +class AssetItem: + def __init__(self, entity_id, name, icon, parent_id): + self.id = entity_id + self.name = name + self.icon = icon + self.parent_id = parent_id + + @classmethod + def from_doc(cls, asset_doc): + parent_id = asset_doc["data"].get("visualParent") + if parent_id is not None: + parent_id = str(parent_id) + return cls( + str(asset_doc["_id"]), + asset_doc["name"], + asset_doc["data"].get("icon"), + parent_id + ) + + +class TaskItem: + def __init__(self, asset_id, name, task_type, short_name): + self.asset_id = asset_id + self.name = name + self.task_type = task_type + self.short_name = short_name + + @classmethod + def from_asset_doc(cls, asset_doc, project_doc): + asset_tasks = asset_doc["data"].get("tasks") or {} + project_task_types = project_doc["config"]["tasks"] + output = [] + for task_name, task_info in asset_tasks.items(): + task_type = task_info.get("type") + task_type_info = project_task_types.get(task_type) or {} + output.append(cls( + asset_doc["_id"], + task_name, + task_type, + task_type_info.get("short_name") + )) + return output + + +class EntitiesModel: + def __init__(self, event_system): + self._event_system = event_system + self._project_names = None + self._project_docs_by_name = {} + self._assets_by_project = {} + self._tasks_by_asset_id = collections.defaultdict(dict) + + def has_cached_projects(self): + return self._project_names is None + + def has_cached_assets(self, project_name): + if not project_name: + return True + return project_name in self._assets_by_project + + def has_cached_tasks(self, project_name): + return self.has_cached_assets(project_name) + + def get_projects(self): + if self._project_names is None: + self.refresh_projects() + return list(self._project_names) + + def get_assets(self, project_name): + if project_name not in self._assets_by_project: + self.refresh_assets(project_name) + return dict(self._assets_by_project[project_name]) + + def get_tasks(self, project_name, asset_id): + if not project_name or not asset_id: + return [] + + if project_name not in self._tasks_by_asset_id: + self.refresh_assets(project_name) + + all_task_items = self._tasks_by_asset_id[project_name] + asset_task_items = all_task_items.get(asset_id) + return list(asset_task_items) + + def refresh_projects(self, force=False): + self._event_system.emit( + "projects.refresh.started", {}, "entities.model" + ) + if force or self._project_names is None: + project_names = [] + project_docs_by_name = {} + for project_doc in get_projects(): + project_name = project_doc["name"] + project_names.append(project_name) + project_docs_by_name[project_name] = project_doc + self._project_names = project_names + self._project_docs_by_name = project_docs_by_name + self._event_system.emit( + "projects.refresh.finished", {}, "entities.model" + ) + + def _refresh_assets(self, project_name): + asset_items_by_id = {} + task_items_by_asset_id = {} + self._assets_by_project[project_name] = asset_items_by_id + self._tasks_by_asset_id[project_name] = task_items_by_asset_id + if not project_name: + return + + project_doc = self._project_docs_by_name[project_name] + for asset_doc in get_assets(project_name): + asset_item = AssetItem.from_doc(asset_doc) + asset_items_by_id[asset_item.id] = asset_item + task_items_by_asset_id[asset_item.id] = ( + TaskItem.from_asset_doc(asset_doc, project_doc) + ) + + def refresh_assets(self, project_name, force=False): + self._event_system.emit( + "assets.refresh.started", + {"project_name": project_name}, + "entities.model" + ) + + if force or project_name not in self._assets_by_project: + self._refresh_assets(project_name) + + self._event_system.emit( + "assets.refresh.finished", + {"project_name": project_name}, + "entities.model" + ) + + +class SelectionModel: + def __init__(self, event_system): + self._event_system = event_system + + self.project_name = None + self.asset_id = None + self.task_name = None + + def select_project(self, project_name): + if self.project_name == project_name: + return + + self.project_name = project_name + self._event_system.emit( + "project.changed", + {"project_name": project_name}, + "selection.model" + ) + + def select_asset(self, asset_id): + if self.asset_id == asset_id: + return + self.asset_id = asset_id + self._event_system.emit( + "asset.changed", + { + "project_name": self.project_name, + "asset_id": asset_id + }, + "selection.model" + ) + + def select_task(self, task_name): + if self.task_name == task_name: + return + self.task_name = task_name + self._event_system.emit( + "task.changed", + { + "project_name": self.project_name, + "asset_id": self.asset_id, + "task_name": task_name + }, + "selection.model" + ) + + +class RepublisherDialogController: + def __init__(self): + event_system = EventSystem() + entities_model = EntitiesModel(event_system) + selection_model = SelectionModel(event_system) + + self._event_system = event_system + self._entities_model = entities_model + self._selection_model = selection_model + + self.dst_project_name = None + self.dst_asset_id = None + self.dst_task_name = None + + event_system.add_callback("project.changed", self._on_project_change) + + @property + def event_system(self): + return self._event_system + + @property + def model(self): + return self._entities_model + + @property + def selection_model(self): + return self._selection_model + + def _on_project_change(self, event): + project_name = event["project_name"] + self.model.refresh_assets(project_name) + + def submit(self): + project_name = self.selection_model.project_name + asset_id = self.selection_model.asset_id + task_name = self.selection_model.task_name + self.dst_project_name = project_name + self.dst_asset_id = asset_id + self.dst_task_name = task_name diff --git a/openpype/tools/republisher/republisher_dialog/window.py b/openpype/tools/republisher/republisher_dialog/window.py index e268e1f6fb9..593a95be176 100644 --- a/openpype/tools/republisher/republisher_dialog/window.py +++ b/openpype/tools/republisher/republisher_dialog/window.py @@ -2,193 +2,16 @@ from qtpy import QtWidgets, QtGui, QtCore -from openpype.client import get_projects, get_assets -from openpype.lib.events import EventSystem +from openpype.style import load_stylesheet +from .control import RepublisherDialogController PROJECT_NAME_ROLE = QtCore.Qt.UserRole + 1 ASSET_NAME_ROLE = QtCore.Qt.UserRole + 2 ASSET_ICON_ROLE = QtCore.Qt.UserRole + 3 ASSET_ID_ROLE = QtCore.Qt.UserRole + 4 - - -class AssetItem: - def __init__(self, entity_id, name, icon, parent_id): - self.id = entity_id - self.name = name - self.icon = icon - self.parent_id = parent_id - - @classmethod - def from_doc(cls, asset_doc): - parent_id = asset_doc["data"].get("visualParent") - if parent_id is not None: - parent_id = str(parent_id) - return cls( - str(asset_doc["_id"]), - asset_doc["name"], - asset_doc["data"].get("icon"), - parent_id - ) - - - -class EntitiesModel: - def __init__(self, event_system): - self._event_system = event_system - self._projects = None - self._assets_by_project = {} - self._tasks_by_asset_id = collections.defaultdict(dict) - - def has_cached_projects(self): - return self._projects is None - - def has_cached_assets(self, project_name): - if not project_name: - return True - return project_name in self._assets_by_project - - def has_cached_tasks(self, project_name): - if not project_name: - return True - return project_name in self._assets_by_project - - def get_projects(self): - if self._projects is not None: - return list(self._projects) - - self.refresh_projects() - - def get_assets(self, project_name): - if project_name in self._assets_by_project: - return dict(self._assets_by_project[project_name]) - self.refresh_assets(project_name) - return [] - - def get_tasks(self, project_name, asset_id): - output = [] - if not project_name or not asset_id: - return output - - if project_name not in self._assets_by_project: - self.refresh_assets(project_name) - return output - - asset_docs = self._assets_by_project[project_name] - asset_doc = asset_docs.get(asset_id) - if not asset_doc: - return output - - for task, _task_info in asset_doc["data"]["tasks"].items(): - output.append(task) - return output - - def refresh_projects(self): - self._projects = None - self._event_system.emit( - "projects.refresh.started", {}, "entities.model" - ) - self._projects = [project["name"] for project in get_projects()] - self._event_system.emit( - "projects.refresh.finished", {}, "entities.model" - ) - - def refresh_assets(self, project_name): - self._event_system.emit( - "assets.refresh.started", - {"project_name": project_name}, - "entities.model" - ) - asset_docs = [] - if project_name: - asset_docs = get_assets(project_name) - asset_items_by_id = {} - for asset_doc in asset_docs: - asset_item = AssetItem.from_doc(asset_doc) - asset_items_by_id[asset_item.id] = asset_item - self._assets_by_project[project_name] = asset_items_by_id - self._event_system.emit( - "assets.refresh.finished", - {"project_name": project_name}, - "entities.model" - ) - - -class SelectionModel: - def __init__(self, event_system): - self._event_system = event_system - - self.project_name = None - self.asset_id = None - self.task_name = None - - def select_project(self, project_name): - if self.project_name == project_name: - return - - self.project_name = project_name - self.asset_id = None - self.task_name = None - self._event_system.emit( - "project.changed", - {"project_name": project_name}, - "selection.model" - ) - - def select_asset(self, asset_id): - if self.asset_id == asset_id: - return - self.asset_id = asset_id - self.task_name = None - self._event_system.emit( - "asset.changed", - { - "project_name": self.project_name, - "asset_id": asset_id - }, - "selection.model" - ) - - def select_task(self, task_name): - if self.task_name == task_name: - return - self.task_name = task_name - self._event_system.emit( - "task.changed", - { - "project_name": self.project_name, - "asset_id": self.asset_id, - "task_name": task_name - }, - "selection.model" - ) - - -class RepublisherDialogController: - def __init__(self): - event_system = EventSystem() - entities_model = EntitiesModel(event_system) - selection_model = SelectionModel(event_system) - - self._event_system = event_system - self._entities_model = entities_model - self._selection_model = selection_model - - self.dst_project_name = None - self.dst_asset_id = None - self.dst_task_name = None - - @property - def event_system(self): - return self._event_system - - @property - def model(self): - return self._entities_model - - @property - def selection_model(self): - return self._selection_model +TASK_NAME_ROLE = QtCore.Qt.UserRole + 5 +TASK_TYPE_ROLE = QtCore.Qt.UserRole + 6 class ProjectsModel(QtGui.QStandardItemModel): @@ -197,7 +20,7 @@ class ProjectsModel(QtGui.QStandardItemModel): select_project_text = "< Select Project >" def __init__(self, controller): - super().__init__() + super(ProjectsModel, self).__init__() self._controller = controller self.event_system.add_callback( @@ -249,20 +72,39 @@ def _on_refresh_finish(self): root_item.appendRows(new_items) +class ProjectProxyModel(QtCore.QSortFilterProxyModel): + def __init__(self): + super(ProjectProxyModel, self).__init__() + self._filter_empty_projects = False + + def set_filter_empty_project(self, filter_empty_projects): + if filter_empty_projects == self._filter_empty_projects: + return + self._filter_empty_projects = filter_empty_projects + self.invalidate() + + def filterAcceptsRow(self, row, parent): + if not self._filter_empty_projects: + return True + model = self.sourceModel() + source_index = model.index(row, self.filterKeyColumn(), parent) + if model.data(source_index, PROJECT_NAME_ROLE) is None: + return False + return True + + class AssetsModel(QtGui.QStandardItemModel): empty_text = "< Empty >" def __init__(self, controller): - super().__init__() + super(AssetsModel, self).__init__() self._controller = controller - items = {} - placeholder_item = QtGui.QStandardItem(self.empty_text) + placeholder_item.setFlags(QtCore.Qt.ItemIsEnabled) root_item = self.invisibleRootItem() root_item.appendRows([placeholder_item]) - items[None] = placeholder_item self.event_system.add_callback( "project.changed", self._on_project_change @@ -274,9 +116,10 @@ def __init__(self, controller): "assets.refresh.finished", self._on_refresh_finish ) - self._items = {} + self._items = {None: placeholder_item} self._placeholder_item = placeholder_item + self._last_project = None @property def event_system(self): @@ -302,6 +145,11 @@ def _clear(self): self._items[None] = self._placeholder_item def _on_project_change(self, event): + project_name = event["project_name"] + if project_name == self._last_project: + return + + self._last_project = project_name self._clear() def _on_refresh_start(self, event): @@ -310,10 +158,10 @@ def _on_refresh_start(self, event): def _on_refresh_finish(self, event): event_project_name = event["project_name"] project_name = self._controller.selection_model.project_name - print("finished", event_project_name, project_name) if event_project_name != project_name: return + self._last_project = event["project_name"] if project_name is None: if None not in self._items: self._clear() @@ -334,7 +182,6 @@ def _on_refresh_finish(self, event): root_item.takeRow(self._placeholder_item.row()) items_to_remove = set(self._items) - set(asset_items_by_id.keys()) - hierarchy_queue = collections.deque() hierarchy_queue.append((None, root_item)) while hierarchy_queue: @@ -344,6 +191,10 @@ def _on_refresh_finish(self, event): item = self._items.get(asset_item.id) if item is None: item = QtGui.QStandardItem() + item.setFlags( + QtCore.Qt.ItemIsSelectable + | QtCore.Qt.ItemIsEnabled + ) new_items.append(item) self._items[asset_item.id] = item @@ -354,6 +205,8 @@ def _on_refresh_finish(self, event): item.setData(asset_item.id, ASSET_ID_ROLE) item.setData(asset_item.icon, ASSET_ICON_ROLE) + hierarchy_queue.append((asset_item.id, item)) + if new_items: parent_item.appendRows(new_items) @@ -366,46 +219,246 @@ def _on_refresh_finish(self, event): parent.removeRow(item.row()) +class TasksModel(QtGui.QStandardItemModel): + empty_text = "< Empty >" + + def __init__(self, controller): + super(TasksModel, self).__init__() + self._controller = controller + + placeholder_item = QtGui.QStandardItem(self.empty_text) + placeholder_item.setFlags(QtCore.Qt.ItemIsEnabled) + + root_item = self.invisibleRootItem() + root_item.appendRows([placeholder_item]) + + self.event_system.add_callback( + "project.changed", self._on_project_change + ) + self.event_system.add_callback( + "assets.refresh.finished", self._on_asset_refresh_finish + ) + self.event_system.add_callback( + "asset.changed", self._on_asset_change + ) + + self._items = {None: placeholder_item} + + self._placeholder_item = placeholder_item + self._last_project = None + + @property + def event_system(self): + return self._controller.event_system + + def _clear(self): + placeholder_in = False + root_item = self.invisibleRootItem() + for row in reversed(range(root_item.rowCount())): + item = root_item.child(row) + task_name = item.data(TASK_NAME_ROLE) + if task_name is None: + placeholder_in = True + continue + root_item.removeRow(item.row()) + + for key in tuple(self._items.keys()): + if key is not None: + self._items.pop(key) + + if not placeholder_in: + root_item.appendRows([self._placeholder_item]) + self._items[None] = self._placeholder_item + + def _on_project_change(self, event): + project_name = event["project_name"] + if project_name == self._last_project: + return + + self._last_project = project_name + self._clear() + + def _on_asset_refresh_finish(self, event): + self._refresh(event["project_name"]) + + def _on_asset_change(self, event): + self._refresh(event["project_name"]) + + def _refresh(self, new_project_name): + project_name = self._controller.selection_model.project_name + if new_project_name != project_name: + return + + self._last_project = project_name + if project_name is None: + if None not in self._items: + self._clear() + return + + asset_id = self._controller.selection_model.asset_id + task_items = self._controller.model.get_tasks( + project_name, asset_id + ) + if not task_items: + self._clear() + return + + root_item = self.invisibleRootItem() + if None in self._items: + self._items.pop(None) + root_item.takeRow(self._placeholder_item.row()) + + new_items = [] + task_names = set() + for task_item in task_items: + task_name = task_item.name + item = self._items.get(task_name) + if item is None: + item = QtGui.QStandardItem() + item.setFlags( + QtCore.Qt.ItemIsSelectable + | QtCore.Qt.ItemIsEnabled + ) + new_items.append(item) + self._items[task_name] = item + + item.setData(task_name, QtCore.Qt.DisplayRole) + item.setData(task_name, TASK_NAME_ROLE) + item.setData(task_item.task_type, TASK_TYPE_ROLE) + + if new_items: + root_item.appendRows(new_items) + + items_to_remove = set(self._items) - task_names + for item_id in items_to_remove: + item = self._items.pop(item_id, None) + if item is None: + continue + parent = item.parent() + if parent is not None: + parent.removeRow(item.row()) + + class RepublisherDialogWindow(QtWidgets.QWidget): def __init__(self, controller=None): - super().__init__() + super(RepublisherDialogWindow, self).__init__() if controller is None: controller = RepublisherDialogController() self._controller = controller - left_widget = QtWidgets.QWidget(self) + main_splitter = QtWidgets.QSplitter(self) + + left_widget = QtWidgets.QWidget(main_splitter) project_combobox = QtWidgets.QComboBox(left_widget) project_model = ProjectsModel(controller) + project_proxy = ProjectProxyModel() + project_proxy.setSourceModel(project_model) project_delegate = QtWidgets.QStyledItemDelegate() project_combobox.setItemDelegate(project_delegate) - project_combobox.setModel(project_model) + project_combobox.setModel(project_proxy) - asset_view = QtWidgets.QTreeView(self) + asset_view = QtWidgets.QTreeView(left_widget) + asset_view.setHeaderHidden(True) asset_model = AssetsModel(controller) asset_view.setModel(asset_model) left_layout = QtWidgets.QVBoxLayout(left_widget) + left_layout.setContentsMargins(0, 0, 0, 0) left_layout.addWidget(project_combobox, 0) left_layout.addWidget(asset_view, 1) + right_widget = QtWidgets.QWidget(main_splitter) + + task_view = QtWidgets.QListView(right_widget) + task_proxy = QtCore.QSortFilterProxyModel() + task_model = TasksModel(controller) + task_proxy.setSourceModel(task_model) + task_view.setModel(task_proxy) + + right_layout = QtWidgets.QVBoxLayout(right_widget) + right_layout.setContentsMargins(0, 0, 0, 0) + right_layout.addWidget(task_view, 1) + + main_splitter.addWidget(left_widget) + main_splitter.addWidget(right_widget) + + btns_widget = QtWidgets.QWidget(self) + close_btn = QtWidgets.QPushButton("Close", btns_widget) + select_btn = QtWidgets.QPushButton("Select", btns_widget) + + btns_layout = QtWidgets.QHBoxLayout(btns_widget) + btns_layout.setContentsMargins(0, 0, 0, 0) + btns_layout.addStretch(1) + btns_layout.addWidget(close_btn, 0) + btns_layout.addWidget(select_btn, 0) + main_layout = QtWidgets.QHBoxLayout(self) - main_layout.addWidget(left_widget) + main_layout.addWidget(main_splitter, 1) + main_layout.addWidget(btns_widget, 0) + + project_combobox.currentIndexChanged.connect(self._on_project_change) + asset_view.selectionModel().selectionChanged.connect( + self._on_asset_change + ) + task_view.selectionModel().selectionChanged.connect( + self._on_task_change + ) + select_btn.clicked.connect(self._on_select_click) + close_btn.clicked.connect(self._on_close_click) self._project_combobox = project_combobox self._project_model = project_model + self._project_proxy = project_proxy self._project_delegate = project_delegate self._asset_view = asset_view self._asset_model = asset_model + self._task_view = task_view + self._first_show = True def showEvent(self, event): - super().showEvent(event) + super(RepublisherDialogWindow, self).showEvent(event) if self._first_show: self._first_show = False self._controller.model.refresh_projects() + self.setStyleSheet(load_stylesheet()) + + def _on_project_change(self): + idx = self._project_combobox.currentIndex() + if idx < 0: + self._project_proxy.set_filter_empty_project(False) + return + + project_name = self._project_combobox.itemData(idx, PROJECT_NAME_ROLE) + self._project_proxy.set_filter_empty_project(project_name is not None) + self._controller.selection_model.select_project(project_name) + + def _on_asset_change(self): + indexes = self._asset_view.selectedIndexes() + index = next(iter(indexes), None) + asset_id = None + if index is not None: + model = self._asset_view.model() + asset_id = model.data(index, ASSET_ID_ROLE) + self._controller.selection_model.select_asset(asset_id) + + def _on_task_change(self): + indexes = self._task_view.selectedIndexes() + index = next(iter(indexes), None) + task_name = None + if index is not None: + model = self._task_view.model() + task_name = model.data(index, TASK_NAME_ROLE) + self._controller.selection_model.select_task(task_name) + + def _on_close_click(self): + self.close() + + def _on_select_click(self): + self._controller.submit() def main(): From ac67affcd3e0dcf5f99f8b43a2ea365f62f5a543 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 30 Dec 2022 14:53:52 +0100 Subject: [PATCH 124/213] initial commit of republisher logic --- .../republisher_processor/control.py | 410 +++++++++++++++++- 1 file changed, 407 insertions(+), 3 deletions(-) diff --git a/openpype/tools/republisher/republisher_processor/control.py b/openpype/tools/republisher/republisher_processor/control.py index dbaffe2ef7b..8a235561d79 100644 --- a/openpype/tools/republisher/republisher_processor/control.py +++ b/openpype/tools/republisher/republisher_processor/control.py @@ -1,24 +1,428 @@ +import os +import re +import copy import requests +from openpype.client import ( + get_project, + get_asset_by_id, + get_subset_by_name, + get_representation_by_id, + get_representation_parents, +) +from openpype.lib import StringTemplate +from openpype.settings import get_project_settings +from openpype.pipeline.publish import get_publish_template_name +from openpype.pipeline.create import get_subset_name -class PublishItem: +class RepublishError(Exception): + pass + + +class RepublishItem: def __init__( self, src_project_name, src_representation_id, dst_project_name, dst_asset_id, - dst_task_name + dst_task_name, + dst_version=None ): self.src_project_name = src_project_name self.src_representation_id = src_representation_id self.dst_project_name = dst_project_name self.dst_asset_id = dst_asset_id self.dst_task_name = dst_task_name + self.dst_version = dst_version + self._id = "|".join([ + src_project_name, + src_representation_id, + dst_project_name, + dst_asset_id, + dst_task_name + ]) + + @property + def id(self): + return self._id + + def __repr__(self): + return "{} - {} -> {}/{}/{}".format( + self.src_project_name, + self.src_representation_id, + self.dst_project_name, + self.dst_asset_id, + self.dst_task_name + ) + + +class RepublishItemStatus: + def __init__( + self, + item, + failed=False, + finished=False, + error=None + ): + self._item = item + self._failed = failed + self._finished = finished + self._error = error + self._progress_messages = [] + self._last_message = None + + def get_failed(self): + return self._failed + + def set_failed(self, failed): + if failed == self._failed: + return + self._failed = failed + + def get_finished(self): + return self._finished + + def set_finished(self, finished): + if finished == self._finished: + return + self._finished = finished + + def get_error(self): + return self._error + + def set_error(self, error, failed=None): + if error == self._error: + return + self._error = error + if failed is None: + failed = error is not None + + if failed: + self.failed = failed + + failed = property(get_failed, set_failed) + finished = property(get_finished, set_finished) + error = property(get_error, set_error) + + def add_progress_message(self, message): + self._progress_messages.append(message) + self._last_message = message + print(message) + + @property + def last_message(self): + return self._last_message class RepublisherController: def __init__(self): - pass + self._items = {} + + def add_item(self, item): + if item.id in self._items: + raise RepublishError(f"Item is already in queue {item}") + self._items[item.id] = item + + def remote_item(self, item_id): + self._items.pop(item_id, None) + + def get_items(self): + return dict(self._items) + + +class SourceFile: + def __init__(self, path, frame=None, udim=None): + self.path = path + self.frame = frame + self.udim = udim + + def __repr__(self): + subparts = [self.__class__.__name__] + if self.frame is not None: + subparts.append("frame: {}".format(self.frame)) + if self.udim is not None: + subparts.append("UDIM: {}".format(self.udim)) + + return "<{}> '{}'".format(" - ".join(subparts), self.path) + + +class ResourceFile: + def __init__(self, path, relative_path): + self.path = path + self.relative_path = relative_path + + def __repr__(self): + return "<{}> '{}'".format(self.__class__.__name__, self.relative_path) + + +def get_source_files_with_frames(src_representation): + frame_placeholder = "__frame__" + udim_placeholder = "__udim__" + src_files = [] + resource_files = [] + template = src_representation["data"]["template"] + repre_context = src_representation["context"] + fill_repre_context = copy.deepcopy(repre_context) + if "frame" in fill_repre_context: + fill_repre_context["frame"] = frame_placeholder + if "udim" in fill_repre_context: + fill_repre_context["udim"] = udim_placeholder + fill_roots = fill_repre_context["root"] + for root_name in tuple(fill_roots.keys()): + fill_roots[root_name] = "{{root[{}]}}".format(root_name) + repre_path = StringTemplate.format_template(template, fill_repre_context) + repre_path = repre_path.replace("\\", "/") + src_dirpath, src_basename = os.path.split(repre_path) + src_basename = ( + re.escape(src_basename) + .replace(frame_placeholder, "(?P[0-9]+)") + .replace(udim_placeholder, "(?P[0-9]+)") + ) + src_basename_regex = re.compile("^{}$".format(src_basename)) + for file_info in src_representation["files"]: + filepath = file_info["path"].replace("\\", "/") + dirpath, basename = os.path.split(filepath) + if dirpath != src_dirpath or not src_basename_regex.match(basename): + relative_dir = dirpath.replace(src_dirpath, "") + if relative_dir: + relative_path = "/".join([relative_dir, basename]) + else: + relative_path = basename + resource_files.append(ResourceFile(filepath, relative_path)) + continue + + frame = None + udim = None + for item in src_basename_regex.finditer(basename): + group_name = item.lastgroup + value = item.group(group_name) + if group_name == "frame": + frame = int(value) + elif group_name == "udim": + udim = value + + src_files.append(SourceFile(filepath, frame, udim)) + + return src_files, resource_files + + +def get_source_files(src_representation): + repre_context = src_representation["context"] + if "frame" in repre_context or "udim" in repre_context: + return get_source_files_with_frames(src_representation) + + src_files = [] + resource_files = [] + template = src_representation["data"]["template"] + fill_repre_context = copy.deepcopy(repre_context) + fill_roots = fill_repre_context["root"] + for root_name in tuple(fill_roots.keys()): + fill_roots[root_name] = "{{root[{}]}}".format(root_name) + repre_path = StringTemplate.format_template(template, fill_repre_context) + repre_path = repre_path.replace("\\", "/") + src_dirpath = os.path.dirname(repre_path) + for file_info in src_representation["files"]: + filepath = file_info["path"] + if filepath == repre_path: + src_files.append(SourceFile(filepath)) + else: + dirpath, basename = os.path.split(filepath) + relative_dir = dirpath.replace(src_dirpath, "") + if relative_dir: + relative_path = "/".join([relative_dir, basename]) + else: + relative_path = basename + resource_files.append(ResourceFile(filepath, relative_path)) + return src_files, resource_files + + +def _republish_to( + item, + item_process, + src_representation, + src_representation_parents, + dst_asset_doc, + dst_task_info +): + """ + + Args: + item (RepublishItem): Item to process. + item_process (RepublishItemStatus): Item process information. + src_representation (Dict[str, Any]): Representation document. + src_representation_parents (Tuple[Any, Any, Any, Any]): Representation + parent documents. + dst_asset_doc (Dict[str, Any]): Asset document as destination of + publishing. + dst_task_info (Dict[str, str]): Task information with prepared + infromation from project config. + """ + + src_subset_doc = src_representation_parents[1] + family = src_subset_doc["data"].get("family") + if not family: + families = src_subset_doc["data"]["families"] + family = families[0] + + item_process.add_progress_message( + f"Republishing family '{family}' (Based on source subset)" + ) + # TODO how to define 'variant'? + variant = "Main" + # TODO where to get host? + host_name = "republisher" + project_settings = get_project_settings(item.dst_project_name) + + subset_name = get_subset_name( + family, + variant, + dst_task_info["name"], + dst_asset_doc, + project_name=item.dst_project_name, + host_name=host_name, + project_settings=project_settings + ) + item_process.add_progress_message(f"Final subset name is '{subset_name}'") + + template_name = get_publish_template_name( + item.dst_project_name, + host_name, + family, + dst_task_info["name"], + dst_task_info["type"], + project_settings=project_settings + ) + item_process.add_progress_message( + f"Using template '{template_name}' for integration" + ) + + src_files, resource_files = get_source_files(src_representation) + + +def _process_item(item, item_process): + # Query all entities source and destination + # - all of them are required for processing to exist + # --- Source entities --- + # Project - we just need validation of existence + src_project_name = item.src_project_name + src_project_doc = get_project(src_project_name, fields=["name"]) + if not src_project_doc: + item_process.error = ( + f"Source project '{src_project_name}' was not found" + ) + return + item_process.add_progress_message(f"Project '{src_project_name}' found") + + # Representation - contains information of source files and template data + src_representation_id = item.src_representation_id + src_representation = get_representation_by_id( + src_project_name, src_representation_id + ) + if not src_representation: + item_process.error = ( + f"Representation with id '{src_representation_id}'" + f" was not found in project '{src_project_name}'" + ) + return + item_process.add_progress_message( + f"Representation with id '{src_representation_id}' found" + f" in project '{src_project_name}'" + ) + + # --- Destination entities --- + dst_project_name = item.dst_project_name + dst_asset_id = item.dst_asset_id + dst_task_name = item.dst_task_name + + # Validate project existence + dst_project_doc = get_project(dst_project_name, fields=["name", "config"]) + if not dst_project_doc: + item_process.error = ( + f"Destination project '{dst_project_name}' was not found" + ) + return + item_process.add_progress_message(f"Project '{dst_project_name}' found") + + # Get asset document + dst_asset_doc = get_asset_by_id( + dst_project_name, + dst_asset_id + ) + if not dst_asset_doc: + item_process.error = ( + f"Destination asset with id '{dst_asset_id}'" + f" was not found in project '{dst_project_name}'" + ) + return + item_process.add_progress_message(( + f"Asset with id '{dst_asset_id}'" + f" found in project '{dst_project_name}'" + )) + + # Get task information from asset document + asset_tasks = dst_asset_doc.get("data", {}).get("tasks") or {} + task_info = asset_tasks.get(dst_task_name) + if not task_info: + item_process.error = ( + f"Destination task '{dst_task_name}'" + f" was not found on asset with id '{dst_asset_id}'" + f" in project '{dst_project_name}'" + ) + return + + item_process.add_progress_message(( + f"Task with name '{dst_task_name}'" + f" found on asset with id '{dst_asset_id}'" + f" in project '{dst_project_name}'" + )) + # Create copy of task info to avoid changing data in asset document + dst_task_info = copy.deepcopy(task_info) + dst_task_info["name"] = dst_task_name + # Fill rest of task information based on task type + task_type = dst_task_info["type"] + task_type_info = dst_project_doc["config"]["tasks"].get(task_type) + dst_task_info.update(task_type_info) + + src_representation_parents = get_representation_parents( + src_project_name, src_representation + ) + _republish_to( + item, + item_process, + src_representation, + src_representation_parents, + dst_asset_doc, + dst_task_info + ) + + +def fake_process(controller): + items = controller.get_items() + for item in items.values(): + item_process = RepublishItemStatus(item) + _process_item(item, item_process) + if item_process.failed: + print("Process failed") + else: + print("Process Finished") + + +def main(): + # NOTE For development purposes + controller = RepublisherController() + project_name = "" + representation_id = "" + dst_project_name = "" + dst_asset_id = "" + dst_task_name = "" + controller.add_item(RepublishItem( + project_name, + representation_id, + dst_project_name, + dst_asset_id, + dst_task_name + )) + fake_process(controller) \ No newline at end of file From df62f315cc60f72ff401992b2853b8fc47e1cd31 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 3 Jan 2023 23:45:51 +0800 Subject: [PATCH 125/213] drop out the force flag in referenceQuery --- openpype/hosts/maya/plugins/publish/extract_import_reference.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index b77740ae134..ce284d16fb2 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -82,7 +82,7 @@ def process(self, instance): print(">>> Processing references") all_reference = cmds.file(q=True, reference=True) or [] for ref in all_reference: - if cmds.referenceQuery(ref, f=True, il=True): + if cmds.referenceQuery(ref, il=True): cmds.file(ref, importReference=True) nested_ref = cmds.file(q=True, reference=True) From 0fd51e8a5b5f2068ea2e2970617e4f79fd58b5b0 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 4 Jan 2023 14:21:58 +0100 Subject: [PATCH 126/213] traypublisher: adding single file subset name condition --- .../hosts/traypublisher/plugins/create/create_editorial.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/traypublisher/plugins/create/create_editorial.py b/openpype/hosts/traypublisher/plugins/create/create_editorial.py index 205403d33e2..614cf9dbcab 100644 --- a/openpype/hosts/traypublisher/plugins/create/create_editorial.py +++ b/openpype/hosts/traypublisher/plugins/create/create_editorial.py @@ -264,9 +264,14 @@ def create(self, subset_name, instance_data, pre_create_data): ) + # alter subset name if multiple files + subset_name_edit = subset_name + if len(sequence_paths) > 1: + subset_name_edit = subset_name + str(index) + # create otio editorial instance self._create_otio_instance( - subset_name + str(index), + subset_name_edit, instance_data, seq_path, media_path, otio_timeline From ddb6ae8a5a38e2a408a701c47bc5484c485dbbd7 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 5 Jan 2023 12:25:55 +0800 Subject: [PATCH 127/213] import reference during publish --- .../plugins/publish/extract_import_reference.py | 16 ++++++---------- .../modules/deadline/abstract_submit_deadline.py | 4 ++-- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_import_reference.py b/openpype/hosts/maya/plugins/publish/extract_import_reference.py index ce284d16fb2..51c82dde924 100644 --- a/openpype/hosts/maya/plugins/publish/extract_import_reference.py +++ b/openpype/hosts/maya/plugins/publish/extract_import_reference.py @@ -105,17 +105,13 @@ def process(self, instance): # can't use TemporaryNamedFile as that can't be opened in another # process until handles are closed by context manager. with tempfile.TemporaryDirectory() as tmp_dir_name: - tmp_file_name = os.path.join(tmp_dir_name, "import_ref.py") - tmp = open(tmp_file_name, "w+t") - subprocess_args = [ - mayapy_exe, - tmp_file_name - ] - self.log.info("Using temp file: {}".format(tmp.name)) - try: + tmp_script_path = os.path.join(tmp_dir_name, "import_ref.py") + self.log.info("Using script file: {}".format(tmp_script_path)) + with open(tmp_script_path, "wt") as tmp: tmp.write(script) - tmp.close() - run_subprocess(subprocess_args) + + try: + run_subprocess([mayapy_exe, tmp_script_path]) except Exception: self.log.error("Import reference failed", exc_info=True) raise diff --git a/openpype/modules/deadline/abstract_submit_deadline.py b/openpype/modules/deadline/abstract_submit_deadline.py index 909a5871e3e..155a647ff60 100644 --- a/openpype/modules/deadline/abstract_submit_deadline.py +++ b/openpype/modules/deadline/abstract_submit_deadline.py @@ -525,9 +525,9 @@ def from_published_scene(self, replace_in_path=True): # determine published path from Anatomy. template_data = workfile_instance.data.get("anatomyData") if self.import_reference: - rep = workfile_instance.data.get("representations")[1] + rep = workfile_instance.data["representations"][1] else: - rep = workfile_instance.data.get("representations")[0] + rep = workfile_instance.data["representations"][0] template_data["representation"] = rep.get("name") template_data["ext"] = rep.get("ext") template_data["comment"] = None From 5d4be5750402f4657b5100a6de3f4438f68bbd14 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 5 Jan 2023 10:15:20 +0100 Subject: [PATCH 128/213] implemented new function 'get_subset_name_template' --- openpype/pipeline/create/__init__.py | 2 + openpype/pipeline/create/subset_name.py | 87 +++++++++++++++++-------- 2 files changed, 63 insertions(+), 26 deletions(-) diff --git a/openpype/pipeline/create/__init__.py b/openpype/pipeline/create/__init__.py index b0877d0a291..c89fb04c428 100644 --- a/openpype/pipeline/create/__init__.py +++ b/openpype/pipeline/create/__init__.py @@ -6,6 +6,7 @@ from .subset_name import ( TaskNotSetError, + get_subset_name_template, get_subset_name, ) @@ -46,6 +47,7 @@ "PRE_CREATE_THUMBNAIL_KEY", "TaskNotSetError", + "get_subset_name_template", "get_subset_name", "CreatorError", diff --git a/openpype/pipeline/create/subset_name.py b/openpype/pipeline/create/subset_name.py index f508263708e..ed05dd6083f 100644 --- a/openpype/pipeline/create/subset_name.py +++ b/openpype/pipeline/create/subset_name.py @@ -14,6 +14,53 @@ def __init__(self, msg=None): super(TaskNotSetError, self).__init__(msg) +def get_subset_name_template( + project_name, + family, + task_name, + task_type, + host_name, + default_template=None, + project_settings=None +): + """Get subset name template based on passed context. + + Args: + project_name (str): Project on which the context lives. + family (str): Family (subset type) for which the subset name is + calculated. + host_name (str): Name of host in which the subset name is calculated. + task_name (str): Name of task in which context the subset is created. + task_type (str): Type of task in which context the subset is created. + default_template (Union[str, None]): Default template which is used if + settings won't find any matching possitibility. Constant + 'DEFAULT_SUBSET_TEMPLATE' is used if not defined. + project_settings (Union[Dict[str, Any], None]): Prepared settings for + project. Settings are queried if not passed. + """ + + if project_settings is None: + project_settings = get_project_settings(project_name) + tools_settings = project_settings["global"]["tools"] + profiles = tools_settings["creator"]["subset_name_profiles"] + filtering_criteria = { + "families": family, + "hosts": host_name, + "tasks": task_name, + "task_types": task_type + } + + matching_profile = filter_profiles(profiles, filtering_criteria) + template = None + if matching_profile: + template = matching_profile["template"] + + # Make sure template is set (matching may have empty string) + if not template: + template = default_template or DEFAULT_SUBSET_TEMPLATE + return template + + def get_subset_name( family, variant, @@ -37,9 +84,9 @@ def get_subset_name( Args: family (str): Instance family. - variant (str): In most of cases it is user input during creation. + variant (str): In most of the cases it is user input during creation. task_name (str): Task name on which context is instance created. - asset_doc (dict): Queried asset document with it's tasks in data. + asset_doc (dict): Queried asset document with its tasks in data. Used to get task type. project_name (str): Name of project on which is instance created. Important for project settings that are loaded. @@ -50,15 +97,15 @@ def get_subset_name( is not passed. dynamic_data (dict): Dynamic data specific for a creator which creates instance. - dbcon (AvalonMongoDB): Mongo connection to be able query asset document - if 'asset_doc' is not passed. + project_settings (Union[Dict[str, Any], None]): Prepared settings for + project. Settings are queried if not passed. """ if not family: return "" if not host_name: - host_name = os.environ["AVALON_APP"] + host_name = os.environ.get("AVALON_APP") # Use only last part of class family value split by dot (`.`) family = family.rsplit(".", 1)[-1] @@ -70,27 +117,15 @@ def get_subset_name( task_info = asset_tasks.get(task_name) or {} task_type = task_info.get("type") - # Get settings - if not project_settings: - project_settings = get_project_settings(project_name) - tools_settings = project_settings["global"]["tools"] - profiles = tools_settings["creator"]["subset_name_profiles"] - filtering_criteria = { - "families": family, - "hosts": host_name, - "tasks": task_name, - "task_types": task_type - } - - matching_profile = filter_profiles(profiles, filtering_criteria) - template = None - if matching_profile: - template = matching_profile["template"] - - # Make sure template is set (matching may have empty string) - if not template: - template = default_template or DEFAULT_SUBSET_TEMPLATE - + template = get_subset_name_template( + project_name, + family, + task_name, + task_type, + host_name, + default_template=default_template, + project_settings=project_settings + ) # Simple check of task name existence for template with {task} in # - missing task should be possible only in Standalone publisher if not task_name and "{task" in template.lower(): From 4c43acde9dc37247e1bb05d795cd139961a9cef6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 5 Jan 2023 10:18:06 +0100 Subject: [PATCH 129/213] changed select context dialog --- .../republisher/republisher_dialog/control.py | 427 +++++++++++++++++- .../republisher/republisher_dialog/window.py | 347 ++++++++++++-- 2 files changed, 719 insertions(+), 55 deletions(-) diff --git a/openpype/tools/republisher/republisher_dialog/control.py b/openpype/tools/republisher/republisher_dialog/control.py index 8c4e0313fe1..242ca475e74 100644 --- a/openpype/tools/republisher/republisher_dialog/control.py +++ b/openpype/tools/republisher/republisher_dialog/control.py @@ -1,17 +1,42 @@ +import re import collections -from openpype.client import get_projects, get_assets + +from openpype.client import ( + get_projects, + get_assets, + get_asset_by_id, + get_subset_by_id, + get_version_by_id, + get_representations, +) +from openpype.settings import get_project_settings +from openpype.lib import prepare_template_data from openpype.lib.events import EventSystem +from openpype.pipeline.create import ( + SUBSET_NAME_ALLOWED_SYMBOLS, + get_subset_name_template, +) class AssetItem: - def __init__(self, entity_id, name, icon, parent_id): + def __init__( + self, + entity_id, + name, + icon_name, + icon_color, + parent_id, + has_children + ): self.id = entity_id self.name = name - self.icon = icon + self.icon_name = icon_name + self.icon_color = icon_color self.parent_id = parent_id + self.has_children = has_children @classmethod - def from_doc(cls, asset_doc): + def from_doc(cls, asset_doc, has_children=True): parent_id = asset_doc["data"].get("visualParent") if parent_id is not None: parent_id = str(parent_id) @@ -19,7 +44,9 @@ def from_doc(cls, asset_doc): str(asset_doc["_id"]), asset_doc["name"], asset_doc["data"].get("icon"), - parent_id + asset_doc["data"].get("color"), + parent_id, + has_children ) @@ -76,6 +103,9 @@ def get_assets(self, project_name): self.refresh_assets(project_name) return dict(self._assets_by_project[project_name]) + def get_asset_by_id(self, project_name, asset_id): + return self._assets_by_project[project_name].get(asset_id) + def get_tasks(self, project_name, asset_id): if not project_name or not asset_id: return [] @@ -85,6 +115,8 @@ def get_tasks(self, project_name, asset_id): all_task_items = self._tasks_by_asset_id[project_name] asset_task_items = all_task_items.get(asset_id) + if not asset_task_items: + return [] return list(asset_task_items) def refresh_projects(self, force=False): @@ -113,12 +145,25 @@ def _refresh_assets(self, project_name): return project_doc = self._project_docs_by_name[project_name] + asset_docs_by_parent_id = collections.defaultdict(list) for asset_doc in get_assets(project_name): - asset_item = AssetItem.from_doc(asset_doc) + parent_id = asset_doc["data"].get("visualParent") + asset_docs_by_parent_id[parent_id].append(asset_doc) + + hierarchy_queue = collections.deque() + for asset_doc in asset_docs_by_parent_id[None]: + hierarchy_queue.append(asset_doc) + + while hierarchy_queue: + asset_doc = hierarchy_queue.popleft() + children = asset_docs_by_parent_id[asset_doc["_id"]] + asset_item = AssetItem.from_doc(asset_doc, len(children) > 0) asset_items_by_id[asset_item.id] = asset_item task_items_by_asset_id[asset_item.id] = ( TaskItem.from_asset_doc(asset_doc, project_doc) ) + for child in children: + hierarchy_queue.append(child) def refresh_assets(self, project_name, force=False): self._event_system.emit( @@ -184,21 +229,330 @@ def select_task(self, task_name): ) -class RepublisherDialogController: - def __init__(self): +class UserPublishValues: + """Helper object to validate values required for push to different project. + + Args: + event_system (EventSystem): Event system to catch and emit events. + new_asset_name (str): Name of new asset name. + variant (str): Variant for new subset name in new project. + """ + + asset_name_regex = re.compile("^[a-zA-Z0-9_.]+$") + variant_regex = re.compile("^[{}]+$".format(SUBSET_NAME_ALLOWED_SYMBOLS)) + + def __init__(self, event_system): + self._event_system = event_system + self._new_asset_name = None + self._variant = None + self._comment = None + self._is_variant_valid = False + self._is_new_asset_name_valid = False + + self.set_new_asset("") + self.set_variant("") + self.set_comment("") + + @property + def new_asset_name(self): + return self._new_asset_name + + @property + def variant(self): + return self._variant + + @property + def comment(self): + return self._comment + + @property + def is_variant_valid(self): + return self._is_variant_valid + + @property + def is_new_asset_name_valid(self): + return self._is_new_asset_name_valid + + @property + def is_valid(self): + return self.is_variant_valid and self.is_new_asset_name_valid + + def set_variant(self, variant): + if variant == self._variant: + return + + old_variant = self._variant + old_is_valid = self._is_variant_valid + + self._variant = variant + is_valid = False + if variant: + is_valid = self.variant_regex.match(variant) is not None + self._is_variant_valid = is_valid + + changes = { + key: {"new": new, "old": old} + for key, old, new in ( + ("variant", old_variant, variant), + ("is_valid", old_is_valid, is_valid) + ) + } + + self._event_system.emit( + "variant.changed", + { + "variant": variant, + "is_valid": self._is_variant_valid, + "changes": changes + }, + "user_values" + ) + + def set_new_asset(self, asset_name): + if self._new_asset_name == asset_name: + return + old_asset_name = self._new_asset_name + old_is_valid = self._is_new_asset_name_valid + self._new_asset_name = asset_name + is_valid = True + if asset_name: + is_valid = ( + self.asset_name_regex.match(asset_name) is not None + ) + self._is_new_asset_name_valid = is_valid + changes = { + key: {"new": new, "old": old} + for key, old, new in ( + ("new_asset_name", old_asset_name, asset_name), + ("is_valid", old_is_valid, is_valid) + ) + } + + self._event_system.emit( + "new_asset_name.changed", + { + "new_asset_name": self._new_asset_name, + "is_valid": self._is_new_asset_name_valid, + "changes": changes + }, + "user_values" + ) + + def set_comment(self, comment): + if comment == self._comment: + return + old_comment = self._comment + self._comment = comment + self._event_system.emit( + "comment.changed", + { + "new_asset_name": comment, + "changes": { + "comment": {"new": comment, "old": old_comment} + } + }, + "user_values" + ) + + +class PushToContextController: + def __init__(self, project_name=None, version_id=None): + self._src_project_name = None + self._src_version_id = None + self._src_asset_doc = None + self._src_subset_doc = None + self._src_version_doc = None + event_system = EventSystem() entities_model = EntitiesModel(event_system) selection_model = SelectionModel(event_system) + user_values = UserPublishValues(event_system) self._event_system = event_system self._entities_model = entities_model self._selection_model = selection_model - - self.dst_project_name = None - self.dst_asset_id = None - self.dst_task_name = None + self._user_values = user_values event_system.add_callback("project.changed", self._on_project_change) + event_system.add_callback("asset.changed", self._invalidate) + event_system.add_callback("variant.changed", self._invalidate) + event_system.add_callback("new_asset_name.changed", self._invalidate) + + self._submission_enabled = False + + self.set_source(project_name, version_id) + + def _get_task_info_from_repre_docs(self, asset_doc, repre_docs): + asset_tasks = asset_doc["data"].get("tasks") or {} + found_comb = [] + for repre_doc in repre_docs: + context = repre_doc["context"] + task_info = context.get("task") + if task_info is None: + continue + + task_name = None + task_type = None + if isinstance(task_info, str): + task_name = task_info + asset_task_info = asset_tasks.get(task_info) or {} + task_type = asset_task_info.get("type") + + elif isinstance(task_info, dict): + task_name = task_info.get("name") + task_type = task_info.get("type") + + if task_name and task_type: + return task_name, task_type + + if task_name: + found_comb.append((task_name, task_type)) + + for task_name, task_type in found_comb: + return task_name, task_type + return None, None + + def _get_src_variant(self): + project_name = self._src_project_name + version_doc = self._src_version_doc + asset_doc = self._src_asset_doc + repre_docs = get_representations( + project_name, version_ids=[version_doc["_id"]] + ) + task_name, task_type = self._get_task_info_from_repre_docs( + asset_doc, repre_docs + ) + + project_settings = get_project_settings(project_name) + subset_doc = self.src_subset_doc + family = subset_doc["data"].get("family") + if not family: + family = subset_doc["data"]["families"][0] + template = get_subset_name_template( + self._src_project_name, + family, + task_name, + task_type, + None, + project_settings=project_settings + ) + template_low = template.lower() + variant_placeholder = "{variant}" + if ( + variant_placeholder not in template_low + or (not task_name and "{task" in template_low) + ): + return "" + + idx = template_low.index(variant_placeholder) + template_s = template[:idx] + template_e = template[idx + len(variant_placeholder):] + fill_data = prepare_template_data({ + "family": family, + "task": task_name + }) + try: + subset_s = template_s.format(**fill_data) + subset_e = template_e.format(**fill_data) + except Exception as exc: + print("Failed format", exc) + return "" + + subset_name = self.src_subset_doc["name"] + if ( + (subset_s and not subset_name.startswith(subset_s)) + or (subset_e and not subset_name.endswith(subset_e)) + ): + return "" + + if subset_s: + subset_name = subset_name[len(subset_s):] + if subset_e: + subset_name = subset_name[:len(subset_e)] + return subset_name + + def set_source(self, project_name, version_id): + if ( + project_name == self._src_project_name + and version_id == self._src_version_id + ): + return + + self._src_project_name = project_name + self._src_version_id = version_id + asset_doc = None + subset_doc = None + version_doc = None + if project_name and version_id: + version_doc = get_version_by_id(project_name, version_id) + + if version_doc: + subset_doc = get_subset_by_id(project_name, version_doc["parent"]) + + if subset_doc: + asset_doc = get_asset_by_id(project_name, subset_doc["parent"]) + + self._src_asset_doc = asset_doc + self._src_subset_doc = subset_doc + self._src_version_doc = version_doc + if asset_doc: + self.user_values.set_new_asset(asset_doc["name"]) + variant = self._get_src_variant() + if variant: + self.user_values.set_variant(variant) + + comment = version_doc["data"].get("comment") + if comment: + self.user_values.set_comment(comment) + + self._event_system.emit( + "source.changed", { + "project_name": project_name, + "version_id": version_id + }, + "controller" + ) + + @property + def src_project_name(self): + return self._src_project_name + + @property + def src_version_id(self): + return self._src_version_id + + @property + def src_label(self): + if not self._src_project_name or not self._src_version_id: + return "Source is not defined" + + asset_doc = self.src_asset_doc + if not asset_doc: + return "Source is invalid" + + asset_path_parts = list(asset_doc["data"]["parents"]) + asset_path_parts.append(asset_doc["name"]) + asset_path = "/".join(asset_path_parts) + subset_doc = self.src_subset_doc + version_doc = self.src_version_doc + return "Source: {}/{}/{}/v{:0>3}".format( + self._src_project_name, + asset_path, + subset_doc["name"], + version_doc["name"] + ) + + @property + def src_version_doc(self): + return self._src_version_doc + + @property + def src_subset_doc(self): + return self._src_subset_doc + + @property + def src_asset_doc(self): + return self._src_asset_doc @property def event_system(self): @@ -212,11 +566,60 @@ def model(self): def selection_model(self): return self._selection_model + @property + def user_values(self): + return self._user_values + + @property + def submission_enabled(self): + return self._submission_enabled + def _on_project_change(self, event): project_name = event["project_name"] self.model.refresh_assets(project_name) + self._invalidate() + + def _invalidate(self): + submission_enabled = self._check_submit_validations() + if submission_enabled == self._submission_enabled: + return + self._submission_enabled = submission_enabled + self._event_system.emit( + "submission.enabled.changed", + {"enabled": submission_enabled}, + "controller" + ) + + def _check_submit_validations(self): + if not self._user_values.is_valid: + return False + + if not self.selection_model.project_name: + return False + + if ( + not self._user_values.new_asset_name + and not self.selection_model.asset_id + ): + return False + + return True + + def get_selected_asset_name(self): + project_name = self._selection_model.project_name + asset_id = self._selection_model.asset_id + if not project_name or not asset_id: + return None + asset_item = self._entities_model.get_asset_by_id( + project_name, asset_id + ) + if asset_item: + return asset_item.name + return None def submit(self): + if not self.submission_enabled: + return project_name = self.selection_model.project_name asset_id = self.selection_model.asset_id task_name = self.selection_model.task_name diff --git a/openpype/tools/republisher/republisher_dialog/window.py b/openpype/tools/republisher/republisher_dialog/window.py index 593a95be176..cfc236ad273 100644 --- a/openpype/tools/republisher/republisher_dialog/window.py +++ b/openpype/tools/republisher/republisher_dialog/window.py @@ -1,17 +1,23 @@ +import re import collections from qtpy import QtWidgets, QtGui, QtCore -from openpype.style import load_stylesheet +from openpype.style import load_stylesheet, get_app_icon_path +from openpype.tools.utils import ( + PlaceholderLineEdit, + SeparatorWidget, + get_asset_icon_by_name, + set_style_property, +) -from .control import RepublisherDialogController +from .control import PushToContextController PROJECT_NAME_ROLE = QtCore.Qt.UserRole + 1 ASSET_NAME_ROLE = QtCore.Qt.UserRole + 2 -ASSET_ICON_ROLE = QtCore.Qt.UserRole + 3 -ASSET_ID_ROLE = QtCore.Qt.UserRole + 4 -TASK_NAME_ROLE = QtCore.Qt.UserRole + 5 -TASK_TYPE_ROLE = QtCore.Qt.UserRole + 6 +ASSET_ID_ROLE = QtCore.Qt.UserRole + 3 +TASK_NAME_ROLE = QtCore.Qt.UserRole + 4 +TASK_TYPE_ROLE = QtCore.Qt.UserRole + 5 class ProjectsModel(QtGui.QStandardItemModel): @@ -19,6 +25,8 @@ class ProjectsModel(QtGui.QStandardItemModel): refreshing_text = "< Refreshing >" select_project_text = "< Select Project >" + refreshed = QtCore.Signal() + def __init__(self, controller): super(ProjectsModel, self).__init__() self._controller = controller @@ -59,7 +67,7 @@ def _on_refresh_finish(self): if project_name is None: continue item = self._items.pop(project_name) - root_item.removeRow(item.row()) + root_item.takeRow(item.row()) for project_name in project_names: if project_name in self._items: @@ -70,6 +78,7 @@ def _on_refresh_finish(self): if new_items: root_item.appendRows(new_items) + self.refreshed.emit() class ProjectProxyModel(QtCore.QSortFilterProxyModel): @@ -94,6 +103,7 @@ def filterAcceptsRow(self, row, parent): class AssetsModel(QtGui.QStandardItemModel): + items_changed = QtCore.Signal() empty_text = "< Empty >" def __init__(self, controller): @@ -151,6 +161,7 @@ def _on_project_change(self, event): self._last_project = project_name self._clear() + self.items_changed.emit() def _on_refresh_start(self, event): pass @@ -165,11 +176,13 @@ def _on_refresh_finish(self, event): if project_name is None: if None not in self._items: self._clear() + self.items_changed.emit() return asset_items_by_id = self._controller.model.get_assets(project_name) if not asset_items_by_id: self._clear() + self.items_changed.emit() return assets_by_parent_id = collections.defaultdict(list) @@ -201,9 +214,12 @@ def _on_refresh_finish(self, event): elif item.parent() is not parent_item: new_items.append(item) + icon = get_asset_icon_by_name( + asset_item.icon_name, asset_item.icon_color + ) item.setData(asset_item.name, QtCore.Qt.DisplayRole) + item.setData(icon, QtCore.Qt.DecorationRole) item.setData(asset_item.id, ASSET_ID_ROLE) - item.setData(asset_item.icon, ASSET_ICON_ROLE) hierarchy_queue.append((asset_item.id, item)) @@ -216,10 +232,13 @@ def _on_refresh_finish(self, event): continue parent = item.parent() if parent is not None: - parent.removeRow(item.row()) + parent.takeRow(item.row()) + + self.items_changed.emit() class TasksModel(QtGui.QStandardItemModel): + items_changed = QtCore.Signal() empty_text = "< Empty >" def __init__(self, controller): @@ -277,6 +296,7 @@ def _on_project_change(self, event): self._last_project = project_name self._clear() + self.items_changed.emit() def _on_asset_refresh_finish(self, event): self._refresh(event["project_name"]) @@ -293,6 +313,7 @@ def _refresh(self, new_project_name): if project_name is None: if None not in self._items: self._clear() + self.items_changed.emit() return asset_id = self._controller.selection_model.asset_id @@ -301,6 +322,7 @@ def _refresh(self, new_project_name): ) if not task_items: self._clear() + self.items_changed.emit() return root_item = self.invisibleRootItem() @@ -338,74 +360,153 @@ def _refresh(self, new_project_name): if parent is not None: parent.removeRow(item.row()) + self.items_changed.emit() + -class RepublisherDialogWindow(QtWidgets.QWidget): +class PushToContextSelectWindow(QtWidgets.QWidget): def __init__(self, controller=None): - super(RepublisherDialogWindow, self).__init__() + super(PushToContextSelectWindow, self).__init__() if controller is None: - controller = RepublisherDialogController() + controller = PushToContextController() self._controller = controller - main_splitter = QtWidgets.QSplitter(self) + self.setWindowTitle("Push to project (select context)") + self.setWindowIcon(QtGui.QIcon(get_app_icon_path())) - left_widget = QtWidgets.QWidget(main_splitter) + header_widget = QtWidgets.QWidget(self) - project_combobox = QtWidgets.QComboBox(left_widget) + header_label = QtWidgets.QLabel(controller.src_label, header_widget) + + header_layout = QtWidgets.QHBoxLayout(header_widget) + header_layout.setContentsMargins(0, 0, 0, 0) + header_layout.addWidget(header_label) + + main_splitter = QtWidgets.QSplitter(QtCore.Qt.Horizontal, self) + + context_widget = QtWidgets.QWidget(main_splitter) + + project_combobox = QtWidgets.QComboBox(context_widget) project_model = ProjectsModel(controller) project_proxy = ProjectProxyModel() project_proxy.setSourceModel(project_model) + project_proxy.setDynamicSortFilter(True) project_delegate = QtWidgets.QStyledItemDelegate() project_combobox.setItemDelegate(project_delegate) project_combobox.setModel(project_proxy) - asset_view = QtWidgets.QTreeView(left_widget) + asset_task_splitter = QtWidgets.QSplitter( + QtCore.Qt.Vertical, context_widget + ) + + asset_view = QtWidgets.QTreeView(asset_task_splitter) asset_view.setHeaderHidden(True) asset_model = AssetsModel(controller) - asset_view.setModel(asset_model) + asset_proxy = QtCore.QSortFilterProxyModel() + asset_proxy.setSourceModel(asset_model) + asset_proxy.setDynamicSortFilter(True) + asset_view.setModel(asset_proxy) - left_layout = QtWidgets.QVBoxLayout(left_widget) - left_layout.setContentsMargins(0, 0, 0, 0) - left_layout.addWidget(project_combobox, 0) - left_layout.addWidget(asset_view, 1) - - right_widget = QtWidgets.QWidget(main_splitter) - - task_view = QtWidgets.QListView(right_widget) + task_view = QtWidgets.QListView(asset_task_splitter) task_proxy = QtCore.QSortFilterProxyModel() task_model = TasksModel(controller) task_proxy.setSourceModel(task_model) + task_proxy.setDynamicSortFilter(True) task_view.setModel(task_proxy) - right_layout = QtWidgets.QVBoxLayout(right_widget) - right_layout.setContentsMargins(0, 0, 0, 0) - right_layout.addWidget(task_view, 1) + asset_task_splitter.addWidget(asset_view) + asset_task_splitter.addWidget(task_view) + + context_layout = QtWidgets.QVBoxLayout(context_widget) + context_layout.setContentsMargins(0, 0, 0, 0) + context_layout.addWidget(project_combobox, 0) + context_layout.addWidget(asset_task_splitter, 1) + + # --- Inputs widget --- + inputs_widget = QtWidgets.QWidget(main_splitter) + + asset_name_input = PlaceholderLineEdit(inputs_widget) + asset_name_input.setPlaceholderText("< Name of new asset >") + asset_name_input.setObjectName("ValidatedLineEdit") + + variant_input = PlaceholderLineEdit(inputs_widget) + variant_input.setPlaceholderText("< Variant >") + variant_input.setObjectName("ValidatedLineEdit") - main_splitter.addWidget(left_widget) - main_splitter.addWidget(right_widget) + comment_input = PlaceholderLineEdit(inputs_widget) + comment_input.setPlaceholderText("< Publish comment >") + inputs_layout = QtWidgets.QFormLayout(inputs_widget) + inputs_layout.setContentsMargins(0, 0, 0, 0) + inputs_layout.addRow("New asset name", asset_name_input) + inputs_layout.addRow("Variant", variant_input) + inputs_layout.addRow("Comment", comment_input) + + main_splitter.addWidget(context_widget) + main_splitter.addWidget(inputs_widget) + + # --- Buttons widget --- btns_widget = QtWidgets.QWidget(self) - close_btn = QtWidgets.QPushButton("Close", btns_widget) - select_btn = QtWidgets.QPushButton("Select", btns_widget) + cancel_btn = QtWidgets.QPushButton("Cancel", btns_widget) + publish_btn = QtWidgets.QPushButton("Publish", btns_widget) btns_layout = QtWidgets.QHBoxLayout(btns_widget) btns_layout.setContentsMargins(0, 0, 0, 0) btns_layout.addStretch(1) - btns_layout.addWidget(close_btn, 0) - btns_layout.addWidget(select_btn, 0) - - main_layout = QtWidgets.QHBoxLayout(self) + btns_layout.addWidget(cancel_btn, 0) + btns_layout.addWidget(publish_btn, 0) + + sep_1 = SeparatorWidget(parent=self) + sep_2 = SeparatorWidget(parent=self) + main_layout = QtWidgets.QVBoxLayout(self) + main_layout.addWidget(header_widget, 0) + main_layout.addWidget(sep_1, 0) main_layout.addWidget(main_splitter, 1) + main_layout.addWidget(sep_2, 0) main_layout.addWidget(btns_widget, 0) + show_timer = QtCore.QTimer() + show_timer.setInterval(1) + + user_input_changed_timer = QtCore.QTimer() + user_input_changed_timer.setInterval(200) + user_input_changed_timer.setSingleShot(True) + + show_timer.timeout.connect(self._on_show_timer) + user_input_changed_timer.timeout.connect(self._on_user_input_timer) + asset_name_input.textChanged.connect(self._on_new_asset_change) + variant_input.textChanged.connect(self._on_variant_change) + comment_input.textChanged.connect(self._on_comment_change) + project_model.refreshed.connect(self._on_projects_refresh) project_combobox.currentIndexChanged.connect(self._on_project_change) asset_view.selectionModel().selectionChanged.connect( self._on_asset_change ) + asset_model.items_changed.connect(self._on_asset_model_change) task_view.selectionModel().selectionChanged.connect( self._on_task_change ) - select_btn.clicked.connect(self._on_select_click) - close_btn.clicked.connect(self._on_close_click) + task_model.items_changed.connect(self._on_task_model_change) + publish_btn.clicked.connect(self._on_select_click) + cancel_btn.clicked.connect(self._on_close_click) + + controller.event_system.add_callback( + "new_asset_name.changed", self._on_controller_new_asset_change + ) + controller.event_system.add_callback( + "variant.changed", self._on_controller_variant_change + ) + controller.event_system.add_callback( + "comment.changed", self._on_controller_comment_change + ) + controller.event_system.add_callback( + "submission.enabled.changed", self._on_submission_change + ) + controller.event_system.add_callback( + "source.changed", self._on_controller_source_change + ) + + self._header_label = header_label + self._main_splitter = main_splitter self._project_combobox = project_combobox self._project_model = project_model @@ -414,17 +515,153 @@ def __init__(self, controller=None): self._asset_view = asset_view self._asset_model = asset_model + self._asset_proxy_model = asset_proxy self._task_view = task_view - + self._task_proxy_model = task_proxy + + self._variant_input = variant_input + self._asset_name_input = asset_name_input + self._comment_input = comment_input + + self._publish_btn = publish_btn + + self._user_input_changed_timer = user_input_changed_timer + # Store current value on input text change + # The value is unset when is passed to controller + # The goal is to have controll over changes happened during user change + # in UI and controller auto-changes + self._variant_input_text = None + self._new_asset_name_input_text = None + self._comment_input_text = None + self._show_timer = show_timer + self._show_counter = 2 self._first_show = True + publish_btn.setEnabled(False) + + if controller.user_values.new_asset_name: + asset_name_input.setText(controller.user_values.new_asset_name) + if controller.user_values.variant: + variant_input.setText(controller.user_values.variant) + self._invalidate_variant() + self._invalidate_new_asset_name() + + @property + def controller(self): + return self._controller + def showEvent(self, event): - super(RepublisherDialogWindow, self).showEvent(event) + super(PushToContextSelectWindow, self).showEvent(event) if self._first_show: self._first_show = False - self._controller.model.refresh_projects() self.setStyleSheet(load_stylesheet()) + self._invalidate_variant() + self._show_timer.start() + + def _on_show_timer(self): + if self._show_counter == 0: + self._show_timer.stop() + return + + self._show_counter -= 1 + if self._show_counter == 1: + width = 740 + height = 640 + inputs_width = 360 + self.resize(width, height) + self._main_splitter.setSizes([width - inputs_width, inputs_width]) + + if self._show_counter > 0: + return + + self._controller.model.refresh_projects() + + def _on_new_asset_change(self, text): + self._new_asset_name_input_text = text + self._user_input_changed_timer.start() + + def _on_variant_change(self, text): + self._variant_input_text = text + self._user_input_changed_timer.start() + + def _on_comment_change(self, text): + self._comment_input_text = text + self._user_input_changed_timer.start() + + def _on_user_input_timer(self): + asset_name = self._new_asset_name_input_text + if asset_name is not None: + self._new_asset_name_input_text = None + self._controller.user_values.set_new_asset(asset_name) + + variant = self._variant_input_text + if variant is not None: + self._variant_input_text = None + self._controller.user_values.set_variant(variant) + + comment = self._comment_input_text + if comment is not None: + self._comment_input_text = None + self._controller.user_values.set_comment(comment) + + def _on_controller_new_asset_change(self, event): + asset_name = event["changes"]["new_asset_name"]["new"] + if ( + self._new_asset_name_input_text is None + and asset_name != self._asset_name_input.text() + ): + self._asset_name_input.setText(asset_name) + + self._invalidate_new_asset_name() + + def _on_controller_variant_change(self, event): + is_valid_changes = event["changes"]["is_valid"] + variant = event["changes"]["variant"]["new"] + if ( + self._variant_input_text is None + and variant != self._variant_input.text() + ): + self._variant_input.setText(variant) + + if is_valid_changes["old"] != is_valid_changes["new"]: + self._invalidate_variant() + + def _on_controller_comment_change(self, event): + comment = event["comment"] + if ( + self._comment_input_text is None + and comment != self._comment_input.text() + ): + self._comment_input.setText(comment) + + def _on_controller_source_change(self): + self._header_label.setText(self._controller.src_label) + + def _invalidate_new_asset_name(self): + asset_name = self._controller.user_values.new_asset_name + self._task_view.setVisible(not asset_name) + + valid = None + if asset_name: + valid = self._controller.user_values.is_new_asset_name_valid + + state = "" + if valid is True: + state = "valid" + elif valid is False: + state = "invalid" + set_style_property(self._asset_name_input, "state", state) + + def _invalidate_variant(self): + valid = self._controller.user_values.is_variant_valid + state = "invalid" + if valid is True: + state = "valid" + set_style_property(self._variant_input, "state", state) + + def _on_projects_refresh(self): + self._project_proxy.sort(0, QtCore.Qt.AscendingOrder) def _on_project_change(self): idx = self._project_combobox.currentIndex() @@ -445,6 +682,12 @@ def _on_asset_change(self): asset_id = model.data(index, ASSET_ID_ROLE) self._controller.selection_model.select_asset(asset_id) + def _on_asset_model_change(self): + self._asset_proxy_model.sort(0, QtCore.Qt.AscendingOrder) + + def _on_task_model_change(self): + self._task_proxy_model.sort(0, QtCore.Qt.AscendingOrder) + def _on_task_change(self): indexes = self._task_view.selectedIndexes() index = next(iter(indexes), None) @@ -454,6 +697,9 @@ def _on_task_change(self): task_name = model.data(index, TASK_NAME_ROLE) self._controller.selection_model.select_task(task_name) + def _on_submission_change(self, event): + self._publish_btn.setEnabled(event["enabled"]) + def _on_close_click(self): self.close() @@ -464,14 +710,29 @@ def _on_select_click(self): def main(): app = QtWidgets.QApplication.instance() if not app: + # 'AA_EnableHighDpiScaling' must be set before app instance creation + high_dpi_scale_attr = getattr( + QtCore.Qt, "AA_EnableHighDpiScaling", None + ) + if high_dpi_scale_attr is not None: + QtWidgets.QApplication.setAttribute(high_dpi_scale_attr) + app = QtWidgets.QApplication([]) + for attr_name in ( + "AA_UseHighDpiPixmaps", + ): + attr = getattr(QtCore.Qt, attr_name, None) + if attr is not None: + app.setAttribute(attr) + # TODO find way how to get these project_name = None - representation_id = None + version_id = None # Show window dialog - window = RepublisherDialogWindow() + window = PushToContextSelectWindow() + window.controller.set_source(project_name, version_id) window.show() app.exec_() From f1d7ce1d11b683984a322bed25848d70da5e4a31 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 5 Jan 2023 10:22:05 +0100 Subject: [PATCH 130/213] modified processing logic of republishing --- .../republisher_processor/control.py | 817 +++++++++++++----- 1 file changed, 622 insertions(+), 195 deletions(-) diff --git a/openpype/tools/republisher/republisher_processor/control.py b/openpype/tools/republisher/republisher_processor/control.py index 8a235561d79..36e78bfdc6c 100644 --- a/openpype/tools/republisher/republisher_processor/control.py +++ b/openpype/tools/republisher/republisher_processor/control.py @@ -1,43 +1,63 @@ import os import re import copy -import requests +import socket +import itertools + from openpype.client import ( get_project, get_asset_by_id, + get_subset_by_id, get_subset_by_name, - get_representation_by_id, - get_representation_parents, + get_version_by_id, + get_last_version_by_subset_id, + get_version_by_name, + get_representations, + get_representation_by_name, +) +from openpype.client.operations import ( + OperationsSession, + new_subset_document, + new_version_doc, + prepare_version_update_data, +) +from openpype.lib import ( + StringTemplate, + get_openpype_username, + get_formatted_current_time, ) -from openpype.lib import StringTemplate from openpype.settings import get_project_settings +from openpype.pipeline import Anatomy +from openpype.pipeline.template_data import get_template_data from openpype.pipeline.publish import get_publish_template_name from openpype.pipeline.create import get_subset_name +UNKNOWN = object() + class RepublishError(Exception): pass -class RepublishItem: +class ProjectPushItem: def __init__( self, src_project_name, - src_representation_id, + src_version_id, dst_project_name, dst_asset_id, dst_task_name, dst_version=None ): self.src_project_name = src_project_name - self.src_representation_id = src_representation_id + self.src_version_id = src_version_id self.dst_project_name = dst_project_name self.dst_asset_id = dst_asset_id self.dst_task_name = dst_task_name self.dst_version = dst_version self._id = "|".join([ src_project_name, - src_representation_id, + src_version_id, dst_project_name, dst_asset_id, dst_task_name @@ -50,14 +70,391 @@ def id(self): def __repr__(self): return "{} - {} -> {}/{}/{}".format( self.src_project_name, - self.src_representation_id, + self.src_version_id, self.dst_project_name, self.dst_asset_id, self.dst_task_name ) -class RepublishItemStatus: +class ProjectPushRepreItem: + """Representation item. + + Args: + repre_doc (Dict[str, Ant]): Representation document. + roots (Dict[str, str]): Project roots (based on project anatomy). + """ + + def __init__(self, repre_doc, roots): + self._repre_doc = repre_doc + self._roots = roots + self._src_files = None + self._resource_files = None + + @property + def repre_doc(self): + return self._repre_doc + + @property + def src_files(self): + if self._src_files is None: + self.get_source_files() + return self._src_files + + @property + def resource_files(self): + if self._resource_files is None: + self.get_source_files() + return self._resource_files + + @staticmethod + def validate_source_files(src_files, resource_files): + if not src_files: + raise AssertionError(( + "Couldn't figure out source files from representation." + " Found resource files {}" + ).format(", ".join(str(i) for i in resource_files))) + + invalid_items = [ + item + for item in itertools.chain(src_files, resource_files) + if not item.is_valid_file + ] + if invalid_items: + raise AssertionError(( + "Source files that were not found on disk: {}" + ).format(", ".join(str(i) for i in invalid_items))) + + def get_source_files(self): + if self._src_files is not None: + return self._src_files, self._resource_files + + repre_context = self._repre_doc["context"] + if "frame" in repre_context or "udim" in repre_context: + src_files, resource_files = self._get_source_files_with_frames() + else: + src_files, resource_files = self._get_source_files() + + self.validate_source_files(src_files, resource_files) + + self._src_files = src_files + self._resource_files = resource_files + return self._src_files, self._resource_files + + def _get_source_files_with_frames(self): + frame_placeholder = "__frame__" + udim_placeholder = "__udim__" + src_files = [] + resource_files = [] + template = self._repre_doc["data"]["template"] + repre_context = self._repre_doc["context"] + fill_repre_context = copy.deepcopy(repre_context) + if "frame" in fill_repre_context: + fill_repre_context["frame"] = frame_placeholder + + if "udim" in fill_repre_context: + fill_repre_context["udim"] = udim_placeholder + + fill_roots = fill_repre_context["root"] + for root_name in tuple(fill_roots.keys()): + fill_roots[root_name] = "{{root[{}]}}".format(root_name) + repre_path = StringTemplate.format_template(template, + fill_repre_context) + repre_path = repre_path.replace("\\", "/") + src_dirpath, src_basename = os.path.split(repre_path) + src_basename = ( + re.escape(src_basename) + .replace(frame_placeholder, "(?P[0-9]+)") + .replace(udim_placeholder, "(?P[0-9]+)") + ) + src_basename_regex = re.compile("^{}$".format(src_basename)) + for file_info in self._repre_doc["files"]: + filepath_template = file_info["path"].replace("\\", "/") + filepath = filepath_template.format(root=self._roots) + dirpath, basename = os.path.split(filepath_template) + if dirpath != src_dirpath or not src_basename_regex.match(basename): + relative_dir = dirpath.replace(src_dirpath, "") + if relative_dir: + relative_path = "/".join([relative_dir, basename]) + else: + relative_path = basename + resource_files.append(ResourceFile(filepath, relative_path)) + continue + + frame = None + udim = None + for item in src_basename_regex.finditer(basename): + group_name = item.lastgroup + value = item.group(group_name) + if group_name == "frame": + frame = int(value) + elif group_name == "udim": + udim = value + + src_files.append(SourceFile(filepath, frame, udim)) + + return src_files, resource_files + + def _get_source_files(self): + src_files = [] + resource_files = [] + template = self._repre_doc["data"]["template"] + repre_context = self._repre_doc["context"] + fill_repre_context = copy.deepcopy(repre_context) + fill_roots = fill_repre_context["root"] + for root_name in tuple(fill_roots.keys()): + fill_roots[root_name] = "{{root[{}]}}".format(root_name) + repre_path = StringTemplate.format_template(template, + fill_repre_context) + repre_path = repre_path.replace("\\", "/") + src_dirpath = os.path.dirname(repre_path) + for file_info in self._repre_doc["files"]: + filepath_template = file_info["path"].replace("\\", "/") + filepath = filepath_template.format(root=self._roots) + if filepath_template == repre_path: + src_files.append(SourceFile(filepath)) + else: + dirpath, basename = os.path.split(filepath_template) + relative_dir = dirpath.replace(src_dirpath, "") + if relative_dir: + relative_path = "/".join([relative_dir, basename]) + else: + relative_path = basename + + resource_files.append( + ResourceFile(filepath, relative_path) + ) + return src_files, resource_files + + +class ProjectPushItemProcess: + """ + Args: + item (ProjectPushItem): Item which is being processed. + """ + + # TODO how to define 'variant'? - ask user + variant = "Main" + # TODO where to get host?!!! + host_name = "republisher" + + def __init__(self, item): + self._item = item + self._src_project_doc = UNKNOWN + self._src_asset_doc = UNKNOWN + self._src_subset_doc = UNKNOWN + self._src_version_doc = UNKNOWN + self._src_repre_items = UNKNOWN + self._src_anatomy = None + + self._project_doc = UNKNOWN + self._anatomy = None + self._asset_doc = UNKNOWN + self._task_info = UNKNOWN + self._subset_doc = None + self._version_doc = None + + self._family = UNKNOWN + self._subset_name = UNKNOWN + + self._project_settings = UNKNOWN + self._template_name = UNKNOWN + + self._src_files = UNKNOWN + self._src_resource_files = UNKNOWN + + def get_src_project_doc(self): + if self._src_project_doc is UNKNOWN: + self._src_project_doc = get_project(self._item.src_project_name) + return self._src_project_doc + + def get_src_anatomy(self): + if self._src_anatomy is None: + self._src_anatomy = Anatomy(self._item.src_project_name) + return self._src_anatomy + + def get_src_asset_doc(self): + if self._src_asset_doc is UNKNOWN: + asset_doc = None + subset_doc = self.get_src_subset_doc() + if subset_doc: + asset_doc = get_asset_by_id( + self._item.src_project_name, + subset_doc["parent"] + ) + self._src_asset_doc = asset_doc + return self._src_asset_doc + + def get_src_subset_doc(self): + if self._src_subset_doc is UNKNOWN: + version_doc = self.get_src_version_doc() + subset_doc = None + if version_doc: + subset_doc = get_subset_by_id( + self._item.src_project_name, + version_doc["parent"] + ) + self._src_subset_doc = subset_doc + return self._src_subset_doc + + def get_src_version_doc(self): + if self._src_version_doc is UNKNOWN: + self._src_version_doc = get_version_by_id( + self._item.src_project_name, self._item.src_version_id + ) + return self._src_version_doc + + def get_src_repre_items(self): + if self._src_repre_items is UNKNOWN: + repre_items = None + version_doc = self.get_src_version_doc() + if version_doc: + repre_docs = get_representations( + self._item.src_project_name, + version_ids=[version_doc["_id"]] + ) + repre_items = [ + ProjectPushRepreItem(repre_doc, self.src_anatomy.roots) + for repre_doc in repre_docs + ] + self._src_repre_items = repre_items + return self._src_repre_items + + src_project_doc = property(get_src_project_doc) + src_anatomy = property(get_src_anatomy) + src_asset_doc = property(get_src_asset_doc) + src_subset_doc = property(get_src_subset_doc) + src_version_doc = property(get_src_version_doc) + src_repre_items = property(get_src_repre_items) + + def get_project_doc(self): + if self._project_doc is UNKNOWN: + self._project_doc = get_project(self._item.dst_project_name) + return self._project_doc + + def get_anatomy(self): + if self._anatomy is None: + self._anatomy = Anatomy(self._item.dst_project_name) + return self._anatomy + + def get_asset_doc(self): + if self._asset_doc is UNKNOWN: + self._asset_doc = get_asset_by_id( + self._item.dst_project_name, self._item.dst_asset_id + ) + return self._asset_doc + + def get_task_info(self): + if self._task_info is UNKNOWN: + task_name = self._item.dst_task_name + if not task_name: + self._task_info = {} + return self._task_info + + project_doc = self.get_project_doc() + asset_doc = self.get_asset_doc() + if not project_doc or not asset_doc: + self._task_info = None + return self._task_info + + asset_tasks = asset_doc.get("data", {}).get("tasks") or {} + task_info = asset_tasks.get(task_name) + if not task_info: + self._task_info = None + return self._task_info + + # Create copy of task info to avoid changing data in asset document + task_info = copy.deepcopy(task_info) + task_info["name"] = task_name + # Fill rest of task information based on task type + task_type = task_info["type"] + task_type_info = project_doc["config"]["tasks"].get(task_type, {}) + task_info.update(task_type_info) + self._task_info = task_info + + return self._task_info + + def get_subset_doc(self): + return self._subset_doc + + def set_subset_doc(self, subset_doc): + self._subset_doc = subset_doc + + def get_version_doc(self): + return self._version_doc + + def set_version_doc(self, version_doc): + self._version_doc = version_doc + + project_doc = property(get_project_doc) + anatomy = property(get_anatomy) + asset_doc = property(get_asset_doc) + task_info = property(get_task_info) + subset_doc = property(get_subset_doc) + version_doc = property(get_version_doc, set_version_doc) + + def get_project_settings(self): + if self._project_settings is UNKNOWN: + self._project_settings = get_project_settings( + self._item.dst_project_name + ) + return self._project_settings + + project_settings = property(get_project_settings) + + @property + def family(self): + if self._family is UNKNOWN: + family = None + subset_doc = self.src_subset_doc + if subset_doc: + family = subset_doc["data"].get("family") + families = subset_doc["data"].get("families") + if not family and families: + family = families[0] + self._family = family + return self._family + + @property + def subset_name(self): + if self._subset_name is UNKNOWN: + subset_name = None + family = self.family + asset_doc = self.asset_doc + task_info = self.task_info + if family and asset_doc and task_info: + subset_name = get_subset_name( + family, + self.variant, + task_info["name"], + asset_doc, + project_name=self._item.dst_project_name, + host_name=self.host_name, + project_settings=self.project_settings + ) + self._subset_name = subset_name + return self._subset_name + + @property + def template_name(self): + if self._template_name is UNKNOWN: + task_info = self.task_info + family = self.family + template_name = None + if family and task_info: + template_name = get_publish_template_name( + self._item.dst_project_name, + self.host_name, + self.family, + task_info["name"], + task_info["type"], + project_settings=self.project_settings + ) + self._template_name = template_name + return self._template_name + + +class ProjectPushItemStatus: def __init__( self, item, @@ -131,9 +528,18 @@ def get_items(self): return dict(self._items) -class SourceFile: - def __init__(self, path, frame=None, udim=None): +class FileItem(object): + def __init__(self, path): self.path = path + + @property + def is_valid_file(self): + return os.path.exists(self.path) and os.path.isfile(self.path) + + +class SourceFile(FileItem): + def __init__(self, path, frame=None, udim=None): + super(SourceFile, self).__init__(path) self.frame = frame self.udim = udim @@ -147,189 +553,231 @@ def __repr__(self): return "<{}> '{}'".format(" - ".join(subparts), self.path) -class ResourceFile: +class ResourceFile(FileItem): def __init__(self, path, relative_path): - self.path = path + super(ResourceFile, self).__init__(path) self.relative_path = relative_path def __repr__(self): return "<{}> '{}'".format(self.__class__.__name__, self.relative_path) -def get_source_files_with_frames(src_representation): - frame_placeholder = "__frame__" - udim_placeholder = "__udim__" - src_files = [] - resource_files = [] - template = src_representation["data"]["template"] - repre_context = src_representation["context"] - fill_repre_context = copy.deepcopy(repre_context) - if "frame" in fill_repre_context: - fill_repre_context["frame"] = frame_placeholder - - if "udim" in fill_repre_context: - fill_repre_context["udim"] = udim_placeholder - - fill_roots = fill_repre_context["root"] - for root_name in tuple(fill_roots.keys()): - fill_roots[root_name] = "{{root[{}]}}".format(root_name) - repre_path = StringTemplate.format_template(template, fill_repre_context) - repre_path = repre_path.replace("\\", "/") - src_dirpath, src_basename = os.path.split(repre_path) - src_basename = ( - re.escape(src_basename) - .replace(frame_placeholder, "(?P[0-9]+)") - .replace(udim_placeholder, "(?P[0-9]+)") +def _make_sure_subset_exists(item_process, project_name, operations): + dst_asset_doc = item_process.asset_doc + subset_name = item_process.subset_name + family = item_process.family + asset_id = dst_asset_doc["_id"] + subset_doc = get_subset_by_name(project_name, subset_name, asset_id) + if subset_doc: + return subset_doc + + data = { + "families": [family] + } + subset_doc = new_subset_document( + subset_name, family, asset_id, data ) - src_basename_regex = re.compile("^{}$".format(src_basename)) - for file_info in src_representation["files"]: - filepath = file_info["path"].replace("\\", "/") - dirpath, basename = os.path.split(filepath) - if dirpath != src_dirpath or not src_basename_regex.match(basename): - relative_dir = dirpath.replace(src_dirpath, "") - if relative_dir: - relative_path = "/".join([relative_dir, basename]) - else: - relative_path = basename - resource_files.append(ResourceFile(filepath, relative_path)) - continue - - frame = None - udim = None - for item in src_basename_regex.finditer(basename): - group_name = item.lastgroup - value = item.group(group_name) - if group_name == "frame": - frame = int(value) - elif group_name == "udim": - udim = value - - src_files.append(SourceFile(filepath, frame, udim)) - - return src_files, resource_files - - -def get_source_files(src_representation): - repre_context = src_representation["context"] - if "frame" in repre_context or "udim" in repre_context: - return get_source_files_with_frames(src_representation) - - src_files = [] - resource_files = [] - template = src_representation["data"]["template"] - fill_repre_context = copy.deepcopy(repre_context) - fill_roots = fill_repre_context["root"] - for root_name in tuple(fill_roots.keys()): - fill_roots[root_name] = "{{root[{}]}}".format(root_name) - repre_path = StringTemplate.format_template(template, fill_repre_context) - repre_path = repre_path.replace("\\", "/") - src_dirpath = os.path.dirname(repre_path) - for file_info in src_representation["files"]: - filepath = file_info["path"] - if filepath == repre_path: - src_files.append(SourceFile(filepath)) - else: - dirpath, basename = os.path.split(filepath) - relative_dir = dirpath.replace(src_dirpath, "") - if relative_dir: - relative_path = "/".join([relative_dir, basename]) - else: - relative_path = basename - resource_files.append(ResourceFile(filepath, relative_path)) - return src_files, resource_files + operations.create_entity(project_name, "subset", subset_doc) + item_process.set_subset_doc(subset_doc) -def _republish_to( - item, +def _make_sure_version_exists( item_process, - src_representation, - src_representation_parents, - dst_asset_doc, - dst_task_info + project_name, + version, + operations ): + """Make sure version document exits in database. + + Args: + item_process (ProjectPushItemProcess): Item handling process. + project_name (str): Name of project where version should live. + version (Union[int, None]): Number of version. Latest is used when + 'None' is passed. + operations (OperationsSession): Session which handler creation and + update of entities. + + Returns: + Tuple[Dict[str, Any], bool]: New version document and boolean if version + already existed in database. + """ + + src_version_doc = item_process.src_version_doc + subset_doc = item_process.subset_doc + subset_id = subset_doc["_id"] + src_data = src_version_doc["data"] + families = subset_doc["data"].get("families") + if not families: + families = [subset_doc["data"]["family"]] + + version_data = { + "families": list(families), + "fps": src_data.get("fps"), + "source": src_data.get("source"), + "machine": socket.gethostname(), + "comment": "", + "author": get_openpype_username(), + "time": get_formatted_current_time(), + } + if version is None: + last_version_doc = get_last_version_by_subset_id( + project_name, subset_id + ) + version = 1 + if last_version_doc: + version += int(last_version_doc["name"]) + + existing_version_doc = get_version_by_name( + project_name, version, subset_id + ) + # Update existing version + if existing_version_doc: + version_doc = new_version_doc( + version, subset_id, version_data, existing_version_doc["_id"] + ) + update_data = prepare_version_update_data( + existing_version_doc, version_doc + ) + if update_data: + operations.update_entity( + project_name, + "version", + existing_version_doc["_id"], + update_data + ) + item_process.set_version_doc(version_doc) + + return + + if version is None: + last_version_doc = get_last_version_by_subset_id( + project_name, subset_id + ) + version = 1 + if last_version_doc: + version += int(last_version_doc["name"]) + + version_doc = new_version_doc( + version, subset_id, version_data + ) + operations.create_entity(project_name, "version", version_doc) + + item_process.set_version_doc(version_doc) + + +def _integrate_representations(item, item_process, item_status, operations): """ Args: - item (RepublishItem): Item to process. - item_process (RepublishItemStatus): Item process information. - src_representation (Dict[str, Any]): Representation document. - src_representation_parents (Tuple[Any, Any, Any, Any]): Representation - parent documents. - dst_asset_doc (Dict[str, Any]): Asset document as destination of - publishing. - dst_task_info (Dict[str, str]): Task information with prepared - infromation from project config. + item (ProjectPushItem): Item to be pushed to different project. + item_process (ProjectPushItemProcess): Process of push item. """ - src_subset_doc = src_representation_parents[1] - family = src_subset_doc["data"].get("family") - if not family: - families = src_subset_doc["data"]["families"] - family = families[0] + version_id = item_process.version_doc["_id"] + repre_names = { + repre_item.repre_doc["name"] + for repre_item in item_process.src_repre_items + } + existing_repres = get_representations( + item.dst_project_name, + representation_names=repre_names, + version_ids=[version_id] + ) + existing_repres_by_name = { + repre_doc["name"] : repre_doc + for repre_doc in existing_repres + } + anatomy = item_process.anatomy + formatting_data = get_template_data( + item_process.project_doc, + item_process.asset_doc, + item.dst_task_name, + item_process.host_name + ) + + +def _republish_to(item, item_process, item_status): + """ - item_process.add_progress_message( + Args: + item (ProjectPushItem): Item to process. + item_process (ProjectPushItemProcess): Item process information. + item_status (ProjectPushItemStatus): Item status information. + """ + + family = item_process.family + item_status.add_progress_message( f"Republishing family '{family}' (Based on source subset)" ) - # TODO how to define 'variant'? - variant = "Main" - # TODO where to get host? - host_name = "republisher" - project_settings = get_project_settings(item.dst_project_name) - - subset_name = get_subset_name( - family, - variant, - dst_task_info["name"], - dst_asset_doc, - project_name=item.dst_project_name, - host_name=host_name, - project_settings=project_settings + + subset_name = item_process.subset_name + item_status.add_progress_message(f"Final subset name is '{subset_name}'") + + template_name = item_process.template_name + item_status.add_progress_message( + f"Using template '{template_name}' for integration" + ) + + repre_items = item_process.src_repre_items + file_count = sum( + len(repre_item.src_files) + len(repre_item.resource_files) + for repre_item in repre_items + ) + item_status.add_progress_message( + f"Representation has {file_count} files to integrate" ) - item_process.add_progress_message(f"Final subset name is '{subset_name}'") - template_name = get_publish_template_name( + operations = OperationsSession() + item_status.add_progress_message( + f"Integration to {item.dst_project_name} begins." + ) + _make_sure_subset_exists( + item_process, item.dst_project_name, - host_name, - family, - dst_task_info["name"], - dst_task_info["type"], - project_settings=project_settings + operations ) - item_process.add_progress_message( - f"Using template '{template_name}' for integration" + _make_sure_version_exists( + item_process, + item.dst_project_name, + item.dst_version, + operations ) + _integrate_representations(item, item_process, item_status, operations) + - src_files, resource_files = get_source_files(src_representation) +def _process_item(item, item_process, item_status): + """ + Args: + item (ProjectPushItem): Item defying the source and destination. + item_process (ProjectPushItemProcess): Process item. + item_status (ProjectPushItemStatus): Status of process item. + """ -def _process_item(item, item_process): # Query all entities source and destination # - all of them are required for processing to exist # --- Source entities --- # Project - we just need validation of existence src_project_name = item.src_project_name - src_project_doc = get_project(src_project_name, fields=["name"]) + src_project_doc = item_process.get_src_project_doc() if not src_project_doc: - item_process.error = ( + item_status.error = ( f"Source project '{src_project_name}' was not found" ) return - item_process.add_progress_message(f"Project '{src_project_name}' found") + item_status.add_progress_message(f"Project '{src_project_name}' found") # Representation - contains information of source files and template data - src_representation_id = item.src_representation_id - src_representation = get_representation_by_id( - src_project_name, src_representation_id - ) - if not src_representation: - item_process.error = ( - f"Representation with id '{src_representation_id}'" - f" was not found in project '{src_project_name}'" + repre_items = item_process.get_src_repre_items() + if not repre_items: + item_status.error = ( + f"Version {item.src_version_id} does not have any representations" ) return - item_process.add_progress_message( - f"Representation with id '{src_representation_id}' found" - f" in project '{src_project_name}'" + + item_status.add_progress_message( + f"Found {len(repre_items)} representations on" + f" version {item.src_version_id} in project '{src_project_name}'" ) # --- Destination entities --- @@ -338,73 +786,51 @@ def _process_item(item, item_process): dst_task_name = item.dst_task_name # Validate project existence - dst_project_doc = get_project(dst_project_name, fields=["name", "config"]) + dst_project_doc = item_process.get_project_doc() if not dst_project_doc: - item_process.error = ( + item_status.error = ( f"Destination project '{dst_project_name}' was not found" ) return - item_process.add_progress_message(f"Project '{dst_project_name}' found") + item_status.add_progress_message(f"Project '{dst_project_name}' found") # Get asset document - dst_asset_doc = get_asset_by_id( - dst_project_name, - dst_asset_id - ) - if not dst_asset_doc: - item_process.error = ( + if not item_process.asset_doc: + item_status.error = ( f"Destination asset with id '{dst_asset_id}'" f" was not found in project '{dst_project_name}'" ) return - item_process.add_progress_message(( + item_status.add_progress_message(( f"Asset with id '{dst_asset_id}'" f" found in project '{dst_project_name}'" )) # Get task information from asset document - asset_tasks = dst_asset_doc.get("data", {}).get("tasks") or {} - task_info = asset_tasks.get(dst_task_name) - if not task_info: - item_process.error = ( + if not item_process.task_info: + item_status.error = ( f"Destination task '{dst_task_name}'" f" was not found on asset with id '{dst_asset_id}'" f" in project '{dst_project_name}'" ) return - item_process.add_progress_message(( + item_status.add_progress_message(( f"Task with name '{dst_task_name}'" f" found on asset with id '{dst_asset_id}'" f" in project '{dst_project_name}'" )) - # Create copy of task info to avoid changing data in asset document - dst_task_info = copy.deepcopy(task_info) - dst_task_info["name"] = dst_task_name - # Fill rest of task information based on task type - task_type = dst_task_info["type"] - task_type_info = dst_project_doc["config"]["tasks"].get(task_type) - dst_task_info.update(task_type_info) - - src_representation_parents = get_representation_parents( - src_project_name, src_representation - ) - _republish_to( - item, - item_process, - src_representation, - src_representation_parents, - dst_asset_doc, - dst_task_info - ) + + _republish_to(item, item_process, item_status) def fake_process(controller): items = controller.get_items() for item in items.values(): - item_process = RepublishItemStatus(item) - _process_item(item, item_process) - if item_process.failed: + item_process = ProjectPushItemProcess(item) + item_status = ProjectPushItemStatus(item) + _process_item(item, item_process, item_status) + if item_status.failed: print("Process failed") else: print("Process Finished") @@ -414,15 +840,16 @@ def main(): # NOTE For development purposes controller = RepublisherController() project_name = "" - representation_id = "" + verssion_id = "" dst_project_name = "" dst_asset_id = "" dst_task_name = "" - controller.add_item(RepublishItem( + controller.add_item(ProjectPushItem( project_name, - representation_id, + version_id, dst_project_name, dst_asset_id, - dst_task_name + dst_task_name, + dst_version=1 )) fake_process(controller) \ No newline at end of file From 6f3059050f55d3110f6946f54af2d7ac096b872a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 5 Jan 2023 10:25:57 +0100 Subject: [PATCH 131/213] moved content to different subfolder --- .../republisher_dialog => push_to_project}/__init__.py | 0 .../control.py => push_to_project/control_context.py} | 0 .../control.py => push_to_project/control_integrate.py} | 0 .../republisher_dialog => push_to_project}/window.py | 2 +- openpype/tools/republisher/republisher_processor/__init__.py | 0 5 files changed, 1 insertion(+), 1 deletion(-) rename openpype/tools/{republisher/republisher_dialog => push_to_project}/__init__.py (100%) rename openpype/tools/{republisher/republisher_dialog/control.py => push_to_project/control_context.py} (100%) rename openpype/tools/{republisher/republisher_processor/control.py => push_to_project/control_integrate.py} (100%) rename openpype/tools/{republisher/republisher_dialog => push_to_project}/window.py (99%) delete mode 100644 openpype/tools/republisher/republisher_processor/__init__.py diff --git a/openpype/tools/republisher/republisher_dialog/__init__.py b/openpype/tools/push_to_project/__init__.py similarity index 100% rename from openpype/tools/republisher/republisher_dialog/__init__.py rename to openpype/tools/push_to_project/__init__.py diff --git a/openpype/tools/republisher/republisher_dialog/control.py b/openpype/tools/push_to_project/control_context.py similarity index 100% rename from openpype/tools/republisher/republisher_dialog/control.py rename to openpype/tools/push_to_project/control_context.py diff --git a/openpype/tools/republisher/republisher_processor/control.py b/openpype/tools/push_to_project/control_integrate.py similarity index 100% rename from openpype/tools/republisher/republisher_processor/control.py rename to openpype/tools/push_to_project/control_integrate.py diff --git a/openpype/tools/republisher/republisher_dialog/window.py b/openpype/tools/push_to_project/window.py similarity index 99% rename from openpype/tools/republisher/republisher_dialog/window.py rename to openpype/tools/push_to_project/window.py index cfc236ad273..99a77caa9e5 100644 --- a/openpype/tools/republisher/republisher_dialog/window.py +++ b/openpype/tools/push_to_project/window.py @@ -11,7 +11,7 @@ set_style_property, ) -from .control import PushToContextController +from .control_context import PushToContextController PROJECT_NAME_ROLE = QtCore.Qt.UserRole + 1 ASSET_NAME_ROLE = QtCore.Qt.UserRole + 2 diff --git a/openpype/tools/republisher/republisher_processor/__init__.py b/openpype/tools/republisher/republisher_processor/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 From 4762ff30e30cfe9b797178c15fa618709fe584c8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 5 Jan 2023 15:42:39 +0100 Subject: [PATCH 132/213] implemented rest of integration logic --- .../push_to_project/control_integrate.py | 1254 ++++++++++------- 1 file changed, 720 insertions(+), 534 deletions(-) diff --git a/openpype/tools/push_to_project/control_integrate.py b/openpype/tools/push_to_project/control_integrate.py index 36e78bfdc6c..97de03ee1f4 100644 --- a/openpype/tools/push_to_project/control_integrate.py +++ b/openpype/tools/push_to_project/control_integrate.py @@ -3,9 +3,13 @@ import copy import socket import itertools +import datetime + +from bson.objectid import ObjectId from openpype.client import ( get_project, + get_assets, get_asset_by_id, get_subset_by_id, get_subset_by_name, @@ -13,19 +17,25 @@ get_last_version_by_subset_id, get_version_by_name, get_representations, - get_representation_by_name, ) from openpype.client.operations import ( OperationsSession, + new_asset_document, new_subset_document, new_version_doc, + new_representation_doc, prepare_version_update_data, + prepare_representation_update_data, ) +from openpype.modules import ModulesManager from openpype.lib import ( StringTemplate, get_openpype_username, get_formatted_current_time, + source_hash, ) + +from openpype.lib.file_transaction import FileTransaction from openpype.settings import get_project_settings from openpype.pipeline import Anatomy from openpype.pipeline.template_data import get_template_data @@ -35,10 +45,44 @@ UNKNOWN = object() -class RepublishError(Exception): +class PushToProjectError(Exception): pass +class FileItem(object): + def __init__(self, path): + self.path = path + + @property + def is_valid_file(self): + return os.path.exists(self.path) and os.path.isfile(self.path) + + +class SourceFile(FileItem): + def __init__(self, path, frame=None, udim=None): + super(SourceFile, self).__init__(path) + self.frame = frame + self.udim = udim + + def __repr__(self): + subparts = [self.__class__.__name__] + if self.frame is not None: + subparts.append("frame: {}".format(self.frame)) + if self.udim is not None: + subparts.append("UDIM: {}".format(self.udim)) + + return "<{}> '{}'".format(" - ".join(subparts), self.path) + + +class ResourceFile(FileItem): + def __init__(self, path, relative_path): + super(ResourceFile, self).__init__(path) + self.relative_path = relative_path + + def __repr__(self): + return "<{}> '{}'".format(self.__class__.__name__, self.relative_path) + + class ProjectPushItem: def __init__( self, @@ -47,6 +91,9 @@ def __init__( dst_project_name, dst_asset_id, dst_task_name, + variant, + comment=None, + new_asset_name=None, dst_version=None ): self.src_project_name = src_project_name @@ -55,12 +102,17 @@ def __init__( self.dst_asset_id = dst_asset_id self.dst_task_name = dst_task_name self.dst_version = dst_version + self.variant = variant + self.new_asset_name = new_asset_name + self.comment = comment or "" self._id = "|".join([ src_project_name, src_version_id, dst_project_name, - dst_asset_id, - dst_task_name + str(dst_asset_id), + str(new_asset_name), + str(dst_task_name), + str(dst_version) ]) @property @@ -68,13 +120,62 @@ def id(self): return self._id def __repr__(self): - return "{} - {} -> {}/{}/{}".format( - self.src_project_name, - self.src_version_id, - self.dst_project_name, - self.dst_asset_id, - self.dst_task_name - ) + return "<{} - {}>".format(self.__class__.__name__, self.id) + + +class ProjectPushItemStatus: + def __init__( + self, failed=False, finished=False, error=None, messages=None + ): + if messages is None: + messages = [] + self._failed = failed + self._finished = finished + self._error = error + self._progress_messages = messages + self._last_message = None + + def get_failed(self): + return self._failed + + def set_failed(self, failed): + if failed == self._failed: + return + self._failed = failed + + def get_finished(self): + return self._finished + + def set_finished(self, finished): + if finished == self._finished: + return + self._finished = finished + + def get_error(self): + return self._error + + def set_error(self, error, failed=None): + if error == self._error: + return + self._error = error + if failed is None: + failed = error is not None + + if failed: + self.failed = failed + + failed = property(get_failed, set_failed) + finished = property(get_finished, set_finished) + error = property(get_error, set_error) + + def add_progress_message(self, message): + self._progress_messages.append(message) + self._last_message = message + print(message) + + @property + def last_message(self): + return self._last_message class ProjectPushRepreItem: @@ -90,6 +191,7 @@ def __init__(self, repre_doc, roots): self._roots = roots self._src_files = None self._resource_files = None + self._frame = UNKNOWN @property def repre_doc(self): @@ -107,6 +209,20 @@ def resource_files(self): self.get_source_files() return self._resource_files + @property + def frame(self): + if self._frame is UNKNOWN: + frame = None + for src_file in self.src_files: + src_frame = src_file.frame + if ( + src_frame is not None + and (frame is None or src_frame < frame) + ): + frame = src_frame + self._frame = frame + return self._frame + @staticmethod def validate_source_files(src_files, resource_files): if not src_files: @@ -233,623 +349,693 @@ class ProjectPushItemProcess: item (ProjectPushItem): Item which is being processed. """ - # TODO how to define 'variant'? - ask user - variant = "Main" # TODO where to get host?!!! host_name = "republisher" def __init__(self, item): self._item = item - self._src_project_doc = UNKNOWN - self._src_asset_doc = UNKNOWN - self._src_subset_doc = UNKNOWN - self._src_version_doc = UNKNOWN - self._src_repre_items = UNKNOWN + + self._src_project_doc = None + self._src_asset_doc = None + self._src_subset_doc = None + self._src_version_doc = None + self._src_repre_items = None self._src_anatomy = None - self._project_doc = UNKNOWN + self._project_doc = None self._anatomy = None - self._asset_doc = UNKNOWN - self._task_info = UNKNOWN + self._asset_doc = None + self._created_asset_doc = None + self._task_info = None self._subset_doc = None self._version_doc = None - self._family = UNKNOWN - self._subset_name = UNKNOWN + self._family = None + self._subset_name = None - self._project_settings = UNKNOWN - self._template_name = UNKNOWN + self._project_settings = None + self._template_name = None - self._src_files = UNKNOWN - self._src_resource_files = UNKNOWN + self._status = ProjectPushItemStatus() + self._operations = OperationsSession() + self._file_transaction = FileTransaction() - def get_src_project_doc(self): - if self._src_project_doc is UNKNOWN: - self._src_project_doc = get_project(self._item.src_project_name) + @property + def src_project_doc(self): return self._src_project_doc - def get_src_anatomy(self): - if self._src_anatomy is None: - self._src_anatomy = Anatomy(self._item.src_project_name) + @property + def src_anatomy(self): return self._src_anatomy - def get_src_asset_doc(self): - if self._src_asset_doc is UNKNOWN: - asset_doc = None - subset_doc = self.get_src_subset_doc() - if subset_doc: - asset_doc = get_asset_by_id( - self._item.src_project_name, - subset_doc["parent"] - ) - self._src_asset_doc = asset_doc + @property + def src_asset_doc(self): return self._src_asset_doc - def get_src_subset_doc(self): - if self._src_subset_doc is UNKNOWN: - version_doc = self.get_src_version_doc() - subset_doc = None - if version_doc: - subset_doc = get_subset_by_id( - self._item.src_project_name, - version_doc["parent"] - ) - self._src_subset_doc = subset_doc + @property + def src_subset_doc(self): return self._src_subset_doc - def get_src_version_doc(self): - if self._src_version_doc is UNKNOWN: - self._src_version_doc = get_version_by_id( - self._item.src_project_name, self._item.src_version_id - ) + @property + def src_version_doc(self): return self._src_version_doc - def get_src_repre_items(self): - if self._src_repre_items is UNKNOWN: - repre_items = None - version_doc = self.get_src_version_doc() - if version_doc: - repre_docs = get_representations( - self._item.src_project_name, - version_ids=[version_doc["_id"]] - ) - repre_items = [ - ProjectPushRepreItem(repre_doc, self.src_anatomy.roots) - for repre_doc in repre_docs - ] - self._src_repre_items = repre_items + @property + def src_repre_items(self): return self._src_repre_items - src_project_doc = property(get_src_project_doc) - src_anatomy = property(get_src_anatomy) - src_asset_doc = property(get_src_asset_doc) - src_subset_doc = property(get_src_subset_doc) - src_version_doc = property(get_src_version_doc) - src_repre_items = property(get_src_repre_items) - - def get_project_doc(self): - if self._project_doc is UNKNOWN: - self._project_doc = get_project(self._item.dst_project_name) + @property + def project_doc(self): return self._project_doc - def get_anatomy(self): - if self._anatomy is None: - self._anatomy = Anatomy(self._item.dst_project_name) + @property + def anatomy(self): return self._anatomy - def get_asset_doc(self): - if self._asset_doc is UNKNOWN: - self._asset_doc = get_asset_by_id( - self._item.dst_project_name, self._item.dst_asset_id - ) - return self._asset_doc + @property + def project_settings(self): + return self._project_settings - def get_task_info(self): - if self._task_info is UNKNOWN: - task_name = self._item.dst_task_name - if not task_name: - self._task_info = {} - return self._task_info - - project_doc = self.get_project_doc() - asset_doc = self.get_asset_doc() - if not project_doc or not asset_doc: - self._task_info = None - return self._task_info - - asset_tasks = asset_doc.get("data", {}).get("tasks") or {} - task_info = asset_tasks.get(task_name) - if not task_info: - self._task_info = None - return self._task_info - - # Create copy of task info to avoid changing data in asset document - task_info = copy.deepcopy(task_info) - task_info["name"] = task_name - # Fill rest of task information based on task type - task_type = task_info["type"] - task_type_info = project_doc["config"]["tasks"].get(task_type, {}) - task_info.update(task_type_info) - self._task_info = task_info + @property + def asset_doc(self): + return self._asset_doc + @property + def task_info(self): return self._task_info - def get_subset_doc(self): + @property + def subset_doc(self): return self._subset_doc - def set_subset_doc(self, subset_doc): - self._subset_doc = subset_doc - - def get_version_doc(self): + @property + def version_doc(self): return self._version_doc - def set_version_doc(self, version_doc): - self._version_doc = version_doc - - project_doc = property(get_project_doc) - anatomy = property(get_anatomy) - asset_doc = property(get_asset_doc) - task_info = property(get_task_info) - subset_doc = property(get_subset_doc) - version_doc = property(get_version_doc, set_version_doc) - - def get_project_settings(self): - if self._project_settings is UNKNOWN: - self._project_settings = get_project_settings( - self._item.dst_project_name - ) - return self._project_settings - - project_settings = property(get_project_settings) + @property + def variant(self): + return self._item.variant @property def family(self): - if self._family is UNKNOWN: - family = None - subset_doc = self.src_subset_doc - if subset_doc: - family = subset_doc["data"].get("family") - families = subset_doc["data"].get("families") - if not family and families: - family = families[0] - self._family = family return self._family @property def subset_name(self): - if self._subset_name is UNKNOWN: - subset_name = None - family = self.family - asset_doc = self.asset_doc - task_info = self.task_info - if family and asset_doc and task_info: - subset_name = get_subset_name( - family, - self.variant, - task_info["name"], - asset_doc, - project_name=self._item.dst_project_name, - host_name=self.host_name, - project_settings=self.project_settings - ) - self._subset_name = subset_name return self._subset_name @property def template_name(self): - if self._template_name is UNKNOWN: - task_info = self.task_info - family = self.family - template_name = None - if family and task_info: - template_name = get_publish_template_name( - self._item.dst_project_name, - self.host_name, - self.family, - task_info["name"], - task_info["type"], - project_settings=self.project_settings - ) - self._template_name = template_name return self._template_name + def fill_source_variables(self): + src_project_name = self._item.src_project_name + src_version_id = self._item.src_version_id -class ProjectPushItemStatus: - def __init__( - self, - item, - failed=False, - finished=False, - error=None - ): - self._item = item - self._failed = failed - self._finished = finished - self._error = error - self._progress_messages = [] - self._last_message = None - - def get_failed(self): - return self._failed - - def set_failed(self, failed): - if failed == self._failed: - return - self._failed = failed - - def get_finished(self): - return self._finished - - def set_finished(self, finished): - if finished == self._finished: - return - self._finished = finished - - def get_error(self): - return self._error - - def set_error(self, error, failed=None): - if error == self._error: - return - self._error = error - if failed is None: - failed = error is not None - - if failed: - self.failed = failed - - failed = property(get_failed, set_failed) - finished = property(get_finished, set_finished) - error = property(get_error, set_error) - - def add_progress_message(self, message): - self._progress_messages.append(message) - self._last_message = message - print(message) - - @property - def last_message(self): - return self._last_message - - -class RepublisherController: - def __init__(self): - self._items = {} - - def add_item(self, item): - if item.id in self._items: - raise RepublishError(f"Item is already in queue {item}") - self._items[item.id] = item - - def remote_item(self, item_id): - self._items.pop(item_id, None) - - def get_items(self): - return dict(self._items) - - -class FileItem(object): - def __init__(self, path): - self.path = path - - @property - def is_valid_file(self): - return os.path.exists(self.path) and os.path.isfile(self.path) - - -class SourceFile(FileItem): - def __init__(self, path, frame=None, udim=None): - super(SourceFile, self).__init__(path) - self.frame = frame - self.udim = udim - - def __repr__(self): - subparts = [self.__class__.__name__] - if self.frame is not None: - subparts.append("frame: {}".format(self.frame)) - if self.udim is not None: - subparts.append("UDIM: {}".format(self.udim)) - - return "<{}> '{}'".format(" - ".join(subparts), self.path) - - -class ResourceFile(FileItem): - def __init__(self, path, relative_path): - super(ResourceFile, self).__init__(path) - self.relative_path = relative_path - - def __repr__(self): - return "<{}> '{}'".format(self.__class__.__name__, self.relative_path) - - -def _make_sure_subset_exists(item_process, project_name, operations): - dst_asset_doc = item_process.asset_doc - subset_name = item_process.subset_name - family = item_process.family - asset_id = dst_asset_doc["_id"] - subset_doc = get_subset_by_name(project_name, subset_name, asset_id) - if subset_doc: - return subset_doc - - data = { - "families": [family] - } - subset_doc = new_subset_document( - subset_name, family, asset_id, data - ) - operations.create_entity(project_name, "subset", subset_doc) - item_process.set_subset_doc(subset_doc) + project_doc = get_project(src_project_name) + if not project_doc: + self._status.error = ( + f"Source project \"{src_project_name}\" was not found" + ) + raise PushToProjectError(self._status.error) + self._status.add_progress_message( + f"Project '{src_project_name}' found" + ) -def _make_sure_version_exists( - item_process, - project_name, - version, - operations -): - """Make sure version document exits in database. + version_doc = get_version_by_id(src_project_name, src_version_id) + if not version_doc: + self._status.error = ( + f"Source version with id \"{src_version_id}\"" + f" was not found in project \"{src_project_name}\"" + ) + raise PushToProjectError(self._status.error) + + subset_id = version_doc["parent"] + subset_doc = get_subset_by_id(src_project_name, subset_id) + if not subset_doc: + self._status.error = ( + f"Could find subset with id \"{subset_id}\"" + f" in project \"{src_project_name}\"" + ) + raise PushToProjectError(self._status.error) + + asset_id = subset_doc["parent"] + asset_doc = get_asset_by_id(src_project_name, asset_id) + if not asset_doc: + self._status.error = ( + f"Could find asset with id \"{asset_id}\"" + f" in project \"{src_project_name}\"" + ) + raise PushToProjectError(self._status.error) - Args: - item_process (ProjectPushItemProcess): Item handling process. - project_name (str): Name of project where version should live. - version (Union[int, None]): Number of version. Latest is used when - 'None' is passed. - operations (OperationsSession): Session which handler creation and - update of entities. - - Returns: - Tuple[Dict[str, Any], bool]: New version document and boolean if version - already existed in database. - """ + anatomy = Anatomy(src_project_name) - src_version_doc = item_process.src_version_doc - subset_doc = item_process.subset_doc - subset_id = subset_doc["_id"] - src_data = src_version_doc["data"] - families = subset_doc["data"].get("families") - if not families: - families = [subset_doc["data"]["family"]] - - version_data = { - "families": list(families), - "fps": src_data.get("fps"), - "source": src_data.get("source"), - "machine": socket.gethostname(), - "comment": "", - "author": get_openpype_username(), - "time": get_formatted_current_time(), - } - if version is None: - last_version_doc = get_last_version_by_subset_id( - project_name, subset_id + repre_docs = get_representations( + src_project_name, + version_ids=[src_version_id] ) - version = 1 - if last_version_doc: - version += int(last_version_doc["name"]) + repre_items = [ + ProjectPushRepreItem(repre_doc, anatomy.roots) + for repre_doc in repre_docs + ] + self._status.add_progress_message(( + f"Found {len(repre_items)} representations on" + f" version {src_version_id} in project '{src_project_name}'" + )) + + self._src_anatomy = anatomy + self._src_project_doc = project_doc + self._src_asset_doc = asset_doc + self._src_subset_doc = subset_doc + self._src_version_doc = version_doc + self._src_repre_items = repre_items + + def fill_destination_project(self): + # --- Destination entities --- + dst_project_name = self._item.dst_project_name + # Validate project existence + dst_project_doc = get_project(dst_project_name) + if not dst_project_doc: + self._status.error = ( + f"Destination project '{dst_project_name}' was not found" + ) + raise PushToProjectError(self._status.error) - existing_version_doc = get_version_by_name( - project_name, version, subset_id - ) - # Update existing version - if existing_version_doc: - version_doc = new_version_doc( - version, subset_id, version_data, existing_version_doc["_id"] + self._status.add_progress_message( + f"Destination project '{dst_project_name}' found" ) - update_data = prepare_version_update_data( - existing_version_doc, version_doc + self._project_doc = dst_project_doc + self._anatomy = Anatomy(dst_project_name) + self._project_settings = get_project_settings( + self._item.dst_project_name ) - if update_data: - operations.update_entity( - project_name, - "version", - existing_version_doc["_id"], - update_data - ) - item_process.set_version_doc(version_doc) - return - - if version is None: - last_version_doc = get_last_version_by_subset_id( - project_name, subset_id + def _create_asset( + self, + src_asset_doc, + project_doc, + parent_asset_doc, + asset_name + ): + parent_id = None + parents = [] + tools = [] + if parent_asset_doc: + parent_id = parent_asset_doc["_id"] + parents = list(parent_asset_doc["data"]["parents"]) + parents.append(parent_asset_doc["name"]) + _tools = parent_asset_doc["data"].get("tools_env") + if _tools: + tools = list(_tools) + + asset_name_low = asset_name.lower() + other_asset_docs = get_assets( + project_doc["name"], parent_ids=[parent_id], fields=["name"] ) - version = 1 - if last_version_doc: - version += int(last_version_doc["name"]) - - version_doc = new_version_doc( - version, subset_id, version_data - ) - operations.create_entity(project_name, "version", version_doc) - - item_process.set_version_doc(version_doc) - - -def _integrate_representations(item, item_process, item_status, operations): - """ - - Args: - item (ProjectPushItem): Item to be pushed to different project. - item_process (ProjectPushItemProcess): Process of push item. - """ - - version_id = item_process.version_doc["_id"] - repre_names = { - repre_item.repre_doc["name"] - for repre_item in item_process.src_repre_items - } - existing_repres = get_representations( - item.dst_project_name, - representation_names=repre_names, - version_ids=[version_id] - ) - existing_repres_by_name = { - repre_doc["name"] : repre_doc - for repre_doc in existing_repres - } - anatomy = item_process.anatomy - formatting_data = get_template_data( - item_process.project_doc, - item_process.asset_doc, - item.dst_task_name, - item_process.host_name - ) - + for other_asset_doc in other_asset_docs: + other_name = other_asset_doc["name"] + if other_name.lower() == asset_name_low: + self._status.add_progress_message( + f"Found already existing asset with name \"{other_name}\"" + f" which match requested name \"{asset_name}\"" + ) + return other_asset_doc + + data_keys = ( + "clipIn", + "clipOut", + "frameStart", + "frameEnd", + "handleStart", + "handleEnd", + "resolutionWidth", + "resolutionHeight", + "fps", + "pixelAspect", + ) + asset_data = { + "visualParent": parent_id, + "parents": parents, + "tasks": {}, + "tools_env": tools + } + src_asset_data = src_asset_doc["data"] + for key in data_keys: + if key in src_asset_data: + asset_data[key] = src_asset_data[key] + + asset_doc = new_asset_document( + asset_name, + project_doc["_id"], + parent_id, + parents, + data=asset_data + ) + self._operations.create_entity( + project_doc["name"], + asset_doc["type"], + asset_doc + ) + self._status.add_progress_message( + f"Creating new asset with name \"{asset_name}\"" + ) + self._created_asset_doc = asset_doc + return asset_doc + + def fill_or_create_destination_asset(self): + dst_project_name = self._item.dst_project_name + dst_asset_id = self._item.dst_asset_id + dst_task_name = self._item.dst_task_name + new_asset_name = self._item.new_asset_name + if not dst_asset_id and not new_asset_name: + self._status.error = ( + "Push item does not have defined destination asset" + ) + raise PushToProjectError(self._status.error) -def _republish_to(item, item_process, item_status): - """ + # Get asset document + parent_asset_doc = None + if dst_asset_id: + parent_asset_doc = get_asset_by_id( + self._item.dst_project_name, self._item.dst_asset_id + ) + if not parent_asset_doc: + self._status.error = ( + f"Could find asset with id \"{dst_asset_id}\"" + f" in project \"{dst_project_name}\"" + ) + raise PushToProjectError(self._status.error) - Args: - item (ProjectPushItem): Item to process. - item_process (ProjectPushItemProcess): Item process information. - item_status (ProjectPushItemStatus): Item status information. - """ + if not new_asset_name: + asset_doc = parent_asset_doc + else: + asset_doc = self._create_asset( + self.src_asset_doc, + self.project_doc, + parent_asset_doc, + new_asset_name + ) + self._asset_doc = asset_doc + if not dst_task_name: + self._task_info = {} + return - family = item_process.family - item_status.add_progress_message( - f"Republishing family '{family}' (Based on source subset)" - ) + asset_path_parts = list(asset_doc["data"]["parents"]) + asset_path_parts.append(asset_doc["name"]) + asset_path = "/".join(asset_path_parts) + asset_tasks = asset_doc.get("data", {}).get("tasks") or {} + task_info = asset_tasks.get(dst_task_name) + if not task_info: + self._status.error = ( + f"Could find task with name \"{dst_task_name}\"" + f" on asset \"{asset_path}\"" + f" in project \"{dst_project_name}\"" + ) + raise PushToProjectError(self._status.error) + + # Create copy of task info to avoid changing data in asset document + task_info = copy.deepcopy(task_info) + task_info["name"] = dst_task_name + # Fill rest of task information based on task type + task_type = task_info["type"] + task_type_info = self.project_doc["config"]["tasks"].get(task_type, {}) + task_info.update(task_type_info) + self._task_info = task_info + + def determine_family(self): + subset_doc = self.src_subset_doc + family = subset_doc["data"].get("family") + families = subset_doc["data"].get("families") + if not family and families: + family = families[0] + + if not family: + self._status.error = ( + "Couldn't figure out family from source subset" + ) + raise PushToProjectError(self._status.error) - subset_name = item_process.subset_name - item_status.add_progress_message(f"Final subset name is '{subset_name}'") + self._status.add_progress_message( + f"Publishing family is '{family}' (Based on source subset)" + ) + self._family = family + + def determine_publish_template_name(self): + template_name = get_publish_template_name( + self._item.dst_project_name, + self.host_name, + self.family, + self.task_info.get("name"), + self.task_info.get("type"), + project_settings=self.project_settings + ) + self._status.add_progress_message( + f"Using template '{template_name}' for integration" + ) + self._template_name = template_name + + def determine_subset_name(self): + family = self.family + asset_doc = self.asset_doc + task_info = self.task_info + subset_name = get_subset_name( + family, + self.variant, + task_info.get("name"), + asset_doc, + project_name=self._item.dst_project_name, + host_name=self.host_name, + project_settings=self.project_settings + ) + self._status.add_progress_message( + f"Push will be integrating to subet with name '{subset_name}'" + ) + self._subset_name = subset_name + + def make_sure_subset_exists(self): + project_name = self._item.dst_project_name + asset_id = self.asset_doc["_id"] + subset_name = self.subset_name + family = self.family + subset_doc = get_subset_by_name(project_name, subset_name, asset_id) + if subset_doc: + self._subset_doc = subset_doc + return subset_doc + + data = { + "families": [family] + } + subset_doc = new_subset_document( + subset_name, family, asset_id, data + ) + self._operations.create_entity(project_name, "subset", subset_doc) + self._subset_doc = subset_doc - template_name = item_process.template_name - item_status.add_progress_message( - f"Using template '{template_name}' for integration" - ) + def make_sure_version_exists(self): + """Make sure version document exits in database. + + Args: + item_process (ProjectPushItemProcess): Item handling process. + project_name (str): Name of project where version should live. + version (Union[int, None]): Number of version. Latest is used when + 'None' is passed. + operations (OperationsSession): Session which handler creation and + update of entities. + + Returns: + Tuple[Dict[str, Any], bool]: New version document and boolean if version + already existed in database. + """ + + project_name = self._item.dst_project_name + version = self._item.dst_version + src_version_doc = self.src_version_doc + subset_doc = self.subset_doc + subset_id = subset_doc["_id"] + src_data = src_version_doc["data"] + families = subset_doc["data"].get("families") + if not families: + families = [subset_doc["data"]["family"]] + + version_data = { + "families": list(families), + "fps": src_data.get("fps"), + "source": src_data.get("source"), + "machine": socket.gethostname(), + "comment": "", + "author": get_openpype_username(), + "time": get_formatted_current_time(), + } + if version is None: + last_version_doc = get_last_version_by_subset_id( + project_name, subset_id + ) + version = 1 + if last_version_doc: + version += int(last_version_doc["name"]) - repre_items = item_process.src_repre_items - file_count = sum( - len(repre_item.src_files) + len(repre_item.resource_files) - for repre_item in repre_items - ) - item_status.add_progress_message( - f"Representation has {file_count} files to integrate" - ) + existing_version_doc = get_version_by_name( + project_name, version, subset_id + ) + # Update existing version + if existing_version_doc: + version_doc = new_version_doc( + version, subset_id, version_data, existing_version_doc["_id"] + ) + update_data = prepare_version_update_data( + existing_version_doc, version_doc + ) + if update_data: + self._operations.update_entity( + project_name, + "version", + existing_version_doc["_id"], + update_data + ) + self._version_doc = version_doc - operations = OperationsSession() - item_status.add_progress_message( - f"Integration to {item.dst_project_name} begins." - ) - _make_sure_subset_exists( - item_process, - item.dst_project_name, - operations - ) - _make_sure_version_exists( - item_process, - item.dst_project_name, - item.dst_version, - operations - ) - _integrate_representations(item, item_process, item_status, operations) + return + if version is None: + last_version_doc = get_last_version_by_subset_id( + project_name, subset_id + ) + version = 1 + if last_version_doc: + version += int(last_version_doc["name"]) -def _process_item(item, item_process, item_status): - """ + version_doc = new_version_doc( + version, subset_id, version_data + ) + self._operations.create_entity(project_name, "version", version_doc) - Args: - item (ProjectPushItem): Item defying the source and destination. - item_process (ProjectPushItemProcess): Process item. - item_status (ProjectPushItemStatus): Status of process item. - """ + self._version_doc = version_doc - # Query all entities source and destination - # - all of them are required for processing to exist - # --- Source entities --- - # Project - we just need validation of existence - src_project_name = item.src_project_name - src_project_doc = item_process.get_src_project_doc() - if not src_project_doc: - item_status.error = ( - f"Source project '{src_project_name}' was not found" + def integrate_representations(self): + try: + self._integrate_representations() + except Exception: + self._operations.clear() + self._file_transaction.rollback() + raise + + def _integrate_representations(self): + version_doc = self.version_doc + version_id = version_doc["_id"] + existing_repres = get_representations( + self._item.dst_project_name, + version_ids=[version_id] ) - return - item_status.add_progress_message(f"Project '{src_project_name}' found") - - # Representation - contains information of source files and template data - repre_items = item_process.get_src_repre_items() - if not repre_items: - item_status.error = ( - f"Version {item.src_version_id} does not have any representations" + existing_repres_by_low_name = { + repre_doc["name"].lower(): repre_doc + for repre_doc in existing_repres + } + template_name = self.template_name + anatomy = self.anatomy + formatting_data = get_template_data( + self.project_doc, + self.asset_doc, + self.task_info.get("name"), + self.host_name ) - return - - item_status.add_progress_message( - f"Found {len(repre_items)} representations on" - f" version {item.src_version_id} in project '{src_project_name}'" - ) - - # --- Destination entities --- - dst_project_name = item.dst_project_name - dst_asset_id = item.dst_asset_id - dst_task_name = item.dst_task_name - - # Validate project existence - dst_project_doc = item_process.get_project_doc() - if not dst_project_doc: - item_status.error = ( - f"Destination project '{dst_project_name}' was not found" + formatting_data.update({ + "subset": self.subset_name, + "family": self.family, + "version": version_doc["name"] + }) + + path_template = anatomy.templates[template_name]["path"].replace( + "\\", "/" + ) + file_template = StringTemplate( + anatomy.templates[template_name]["file"] ) - return - item_status.add_progress_message(f"Project '{dst_project_name}' found") - - # Get asset document - if not item_process.asset_doc: - item_status.error = ( - f"Destination asset with id '{dst_asset_id}'" - f" was not found in project '{dst_project_name}'" + processed_repre_items = self._prepare_file_transactions( + anatomy, template_name, formatting_data, file_template ) - return - item_status.add_progress_message(( - f"Asset with id '{dst_asset_id}'" - f" found in project '{dst_project_name}'" - )) - - # Get task information from asset document - if not item_process.task_info: - item_status.error = ( - f"Destination task '{dst_task_name}'" - f" was not found on asset with id '{dst_asset_id}'" - f" in project '{dst_project_name}'" + self._file_transaction.process() + self._prepare_database_operations( + version_id, + processed_repre_items, + path_template, + existing_repres_by_low_name ) - return + self._operations.commit() + self._file_transaction.finalize() - item_status.add_progress_message(( - f"Task with name '{dst_task_name}'" - f" found on asset with id '{dst_asset_id}'" - f" in project '{dst_project_name}'" - )) + def _prepare_file_transactions( + self, anatomy, template_name, formatting_data, file_template + ): + processed_repre_items = [] + for repre_item in self.src_repre_items: + repre_doc = repre_item.repre_doc + repre_name = repre_doc["name"] + repre_format_data = copy.deepcopy(formatting_data) + repre_format_data["representation"] = repre_name + for src_file in repre_item.src_files: + ext = os.path.splitext(src_file.path)[-1] + repre_format_data["ext"] = ext[1:] + break + + tmp_result = anatomy.format(formatting_data) + folder_path = tmp_result[template_name]["folder"] + repre_context = folder_path.used_values + folder_path_rootless = folder_path.rootless + repre_filepaths = [] + published_path = None + for src_file in repre_item.src_files: + file_data = copy.deepcopy(repre_format_data) + frame = src_file.frame + if frame is not None: + file_data["frame"] = frame + + udim = src_file.udim + if udim is not None: + file_data["udim"] = udim + + filename = file_template.format_strict(file_data) + dst_filepath = os.path.normpath( + os.path.join(folder_path, filename) + ) + dst_rootless_path = os.path.normpath( + os.path.join(folder_path_rootless, filename) + ) + if published_path is None or frame == repre_item.frame: + published_path = dst_filepath + repre_context.update(filename.used_values) - _republish_to(item, item_process, item_status) + repre_filepaths.append((dst_filepath, dst_rootless_path)) + self._file_transaction.add(src_file.path, dst_filepath) + for resource_file in repre_item.resource_files: + dst_filepath = os.path.normpath( + os.path.join(folder_path, resource_file.relative_path) + ) + dst_rootless_path = os.path.normpath( + os.path.join( + folder_path_rootless, resource_file.relative_path + ) + ) + repre_filepaths.append((dst_filepath, dst_rootless_path)) + self._file_transaction.add(resource_file.path, dst_filepath) + processed_repre_items.append( + (repre_item, repre_filepaths, repre_context, published_path) + ) + return processed_repre_items -def fake_process(controller): - items = controller.get_items() - for item in items.values(): - item_process = ProjectPushItemProcess(item) - item_status = ProjectPushItemStatus(item) - _process_item(item, item_process, item_status) - if item_status.failed: - print("Process failed") + def _prepare_database_operations( + self, + version_id, + processed_repre_items, + path_template, + existing_repres_by_low_name + ): + modules_manager = ModulesManager() + sync_server_module = modules_manager.get("sync_server") + if sync_server_module is None or not sync_server_module.enabled: + sites = [{ + "name": "studio", + "created_dt": datetime.datetime.now() + }] else: - print("Process Finished") + sites = sync_server_module.compute_resource_sync_sites( + project_name=self._item.dst_project_name + ) + + added_repre_names = set() + for item in processed_repre_items: + (repre_item, repre_filepaths, repre_context, published_path) = item + repre_name = repre_item.repre_doc["name"] + added_repre_names.add(repre_name.lower()) + new_repre_data = { + "path": published_path, + "template": path_template + } + new_repre_files = [] + for (path, rootless_path) in repre_filepaths: + new_repre_files.append({ + "_id": ObjectId(), + "path": rootless_path, + "size": os.path.getsize(path), + "hash": source_hash(path), + "sites": sites + }) + + existing_repre = existing_repres_by_low_name.get( + repre_name.lower() + ) + entity_id = None + if existing_repre: + entity_id = existing_repre["_id"] + new_repre_doc = new_representation_doc( + repre_name, + version_id, + repre_context, + data=new_repre_data, + entity_id=entity_id + ) + new_repre_doc["files"] = new_repre_files + if not existing_repre: + self._operations.create_entity( + self._item.dst_project_name, + new_repre_doc["type"], + new_repre_doc + ) + else: + update_data = prepare_representation_update_data( + existing_repre, new_repre_doc + ) + if update_data: + self._operations.update_entity( + self._item.dst_project_name, + new_repre_doc["type"], + new_repre_doc["_id"], + update_data + ) + + existing_repre_names = set(existing_repres_by_low_name.keys()) + for repre_name in (existing_repre_names - added_repre_names): + repre_doc = existing_repres_by_low_name[repre_name] + self._operations.update_entity( + self._item.dst_project_name, + repre_doc["type"], + repre_doc["_id"], + {"type": "archived_representation"} + ) + + def process(self): + item_process.fill_source_variables() + item_process.fill_destination_project() + item_process.fill_or_create_destination_asset() + item_process.determine_family() + item_process.determine_publish_template_name() + item_process.determine_subset_name() + item_process.make_sure_subset_exists() + item_process.make_sure_version_exists() + item_process.integrate_representations() def main(): # NOTE For development purposes - controller = RepublisherController() project_name = "" - verssion_id = "" + version_id = "" dst_project_name = "" dst_asset_id = "" dst_task_name = "" - controller.add_item(ProjectPushItem( + version = None + variant = "" + comment = "" + + item = ProjectPushItem( project_name, version_id, dst_project_name, dst_asset_id, dst_task_name, + variant, + version, dst_version=1 - )) - fake_process(controller) \ No newline at end of file + ) + item_process = ProjectPushItemProcess(item) + item_process.process() From 854bf3a585e1a7baa7525d9c94daca67ddada851 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 5 Jan 2023 16:19:26 +0100 Subject: [PATCH 133/213] added more detailed helper functions to receive asset icon name and color --- openpype/tools/utils/__init__.py | 6 +++++ openpype/tools/utils/lib.py | 42 ++++++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/openpype/tools/utils/__init__.py b/openpype/tools/utils/__init__.py index 31c8232f47c..d51ebb57444 100644 --- a/openpype/tools/utils/__init__.py +++ b/openpype/tools/utils/__init__.py @@ -20,6 +20,9 @@ DynamicQThread, qt_app_context, get_asset_icon, + get_asset_icon_by_name, + get_asset_icon_name_from_doc, + get_asset_icon_color_from_doc, ) from .models import ( @@ -53,6 +56,9 @@ "DynamicQThread", "qt_app_context", "get_asset_icon", + "get_asset_icon_by_name", + "get_asset_icon_name_from_doc", + "get_asset_icon_color_from_doc", "RecursiveSortFilterProxyModel", diff --git a/openpype/tools/utils/lib.py b/openpype/tools/utils/lib.py index 5302946c28b..04ab2d028f2 100644 --- a/openpype/tools/utils/lib.py +++ b/openpype/tools/utils/lib.py @@ -168,20 +168,52 @@ def get_project_icon(project_doc): def get_asset_icon_name(asset_doc, has_children=True): - icon_name = asset_doc["data"].get("icon") + icon_name = get_asset_icon_name_from_doc(asset_doc) if icon_name: return icon_name + return get_default_asset_icon_name(has_children) + +def get_asset_icon_color(asset_doc): + icon_color = get_asset_icon_color_from_doc(asset_doc) + if icon_color: + return icon_color + return get_default_entity_icon_color() + + +def get_default_asset_icon_name(has_children): if has_children: return "fa.folder" return "fa.folder-o" -def get_asset_icon_color(asset_doc): - icon_color = asset_doc["data"].get("color") +def get_asset_icon_name_from_doc(asset_doc): + if asset_doc: + return asset_doc["data"].get("icon") + return None + + +def get_asset_icon_color_from_doc(asset_doc): + if asset_doc: + return asset_doc["data"].get("color") + return None + + +def get_asset_icon_by_name(icon_name, icon_color, has_children=False): + if not icon_name: + icon_name = get_default_asset_icon_name(has_children) + if icon_color: - return icon_color - return get_default_entity_icon_color() + icon_color = QtGui.QColor(icon_color) + else: + icon_color = get_default_entity_icon_color() + icon = get_qta_icon_by_name_and_color(icon_name, icon_color) + if icon is not None: + return icon + return get_qta_icon_by_name_and_color( + get_default_asset_icon_name(has_children), + icon_color + ) def get_asset_icon(asset_doc, has_children=False): From 3650ac489aabc4277ac2cef73893515888a28428 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 5 Jan 2023 17:07:20 +0100 Subject: [PATCH 134/213] small cleanup --- .../push_to_project/control_integrate.py | 299 ++++++++++++------ 1 file changed, 210 insertions(+), 89 deletions(-) diff --git a/openpype/tools/push_to_project/control_integrate.py b/openpype/tools/push_to_project/control_integrate.py index 97de03ee1f4..e4ae9decd34 100644 --- a/openpype/tools/push_to_project/control_integrate.py +++ b/openpype/tools/push_to_project/control_integrate.py @@ -123,64 +123,150 @@ def __repr__(self): return "<{} - {}>".format(self.__class__.__name__, self.id) +class StatusMessage: + def __init__(self, message, level): + self.message = message + self.level = level + + def __str__(self): + return "{}: {}".format(self.level.upper(), self.message) + + def __repr__(self): + return "<{} - {}> {}".format( + self.__class__.__name__, self.level.upper, self.message + ) + + class ProjectPushItemStatus: def __init__( - self, failed=False, finished=False, error=None, messages=None + self, + failed=False, + finished=False, + fail_reason=None, + messages=None ): if messages is None: messages = [] self._failed = failed self._finished = finished - self._error = error - self._progress_messages = messages - self._last_message = None + self._fail_reason = fail_reason + self._messages = messages + + def get_finished(self): + """Processing of push to project finished. + + Returns: + bool: Finished. + """ + + return self._finished + + def set_finished(self, finished=True): + """Mark status as finished. + + Args: + finished (bool): Processing finished (failed or not). + """ + + if finished != self._finished: + self._finished = finished def get_failed(self): + """Processing failed. + + Returns: + bool: Processing failed. + """ + return self._failed - def set_failed(self, failed): - if failed == self._failed: - return + def set_failed(self, failed, fail_reason=UNKNOWN): + """Set status as failed. + + Attribute 'fail_reason' can change automatically based on passed value. + Reason is unset if 'failed' is 'False' and is set do default reason if + is set to 'True' and reason is not set. + + Args: + failed (bool): Push to project failed. + fail_reason (str): Reason why failed. + """ + self._failed = failed + if fail_reason is not UNKNOWN and self._fail_reason != fail_reason: + self.set_fail_reason(fail_reason) - def get_finished(self): - return self._finished + if failed and self._fail_reason is None: + self.set_fail_reason("Failed without specified reason") - def set_finished(self, finished): - if finished == self._finished: - return - self._finished = finished + elif not failed and self._fail_reason: + self.set_fail_reason(None) + + def get_fail_reason(self): + """Reason why push to process failed. + + Returns: + Union[str, None]: Reason why push failed or None. + """ - def get_error(self): - return self._error + return self._fail_reason - def set_error(self, error, failed=None): - if error == self._error: + def set_fail_reason(self, reason): + """Mark process status as failed. + + Status is also set to failed if 'reason' is not None. + + Args: + reason (str): Reason why push to project failed. + """ + + if self._fail_reason == reason: return - self._error = error - if failed is None: - failed = error is not None + self._fail_reason = reason + if reason and not self._failed: + self.set_failed(True) - if failed: - self.failed = failed + if reason: + print(f"Integration failed: {reason}") - failed = property(get_failed, set_failed) finished = property(get_finished, set_finished) - error = property(get_error, set_error) + failed = property(get_failed, set_failed) + fail_reason = property(get_fail_reason, set_fail_reason) - def add_progress_message(self, message): - self._progress_messages.append(message) - self._last_message = message - print(message) + # Loggin helpers + # TODO better logging + def add_message(self, message, level): + message_obj = StatusMessage(message, level) + self._messages.append(message_obj) + print(message_obj) + return message_obj - @property - def last_message(self): - return self._last_message + def debug(self, message): + return self.add_message(message, "debug") + + def info(self, message): + return self.add_message(message, "info") + + def warning(self, message): + return self.add_message(message, "warning") + + def error(self, message): + return self.add_message(message, "error") + + def critical(self, message): + return self.add_message(message, "critical") class ProjectPushRepreItem: """Representation item. + Representation item based on representation document and project roots. + + Representation document may have reference to: + - source files: Files defined with publish template + - resource files: Files that should be in publish directory + but filenames are not template based. + Args: repre_doc (Dict[str, Ant]): Representation document. roots (Dict[str, str]): Project roots (based on project anatomy). @@ -211,6 +297,15 @@ def resource_files(self): @property def frame(self): + """First frame of representation files. + + This value will be in representation document context if is sequence. + + Returns: + Union[int, None]: First frame in representation files based on + source files or None if frame is not part of filename. + """ + if self._frame is UNKNOWN: frame = None for src_file in self.src_files: @@ -263,6 +358,13 @@ def _get_source_files_with_frames(self): src_files = [] resource_files = [] template = self._repre_doc["data"]["template"] + # Remove padding from 'udim' and 'frame' formatting keys + # - "{frame:0>4}" -> "{frame}" + for key in ("udim", "frame"): + sub_part = "{" + key + "[^}]*}" + replacement = "{{{}}}".format(key) + template = re.sub(sub_part, replacement, template) + repre_context = self._repre_doc["context"] fill_repre_context = copy.deepcopy(repre_context) if "frame" in fill_repre_context: @@ -347,12 +449,13 @@ class ProjectPushItemProcess: """ Args: item (ProjectPushItem): Item which is being processed. + item_status (ProjectPushItemStatus): Object to store status. """ # TODO where to get host?!!! host_name = "republisher" - def __init__(self, item): + def __init__(self, item, item_status=None): self._item = item self._src_project_doc = None @@ -376,10 +479,16 @@ def __init__(self, item): self._project_settings = None self._template_name = None - self._status = ProjectPushItemStatus() + if item_status is None: + item_status = ProjectPushItemStatus() + self._status = item_status self._operations = OperationsSession() self._file_transaction = FileTransaction() + @property + def status(self): + return self._status + @property def src_project_doc(self): return self._src_project_doc @@ -454,40 +563,38 @@ def fill_source_variables(self): project_doc = get_project(src_project_name) if not project_doc: - self._status.error = ( + self._status.set_fail_reason( f"Source project \"{src_project_name}\" was not found" ) - raise PushToProjectError(self._status.error) + raise PushToProjectError(self._status.fail_reason) - self._status.add_progress_message( - f"Project '{src_project_name}' found" - ) + self._status.debug(f"Project '{src_project_name}' found") version_doc = get_version_by_id(src_project_name, src_version_id) if not version_doc: - self._status.error = ( + self._status.set_fail_reason(( f"Source version with id \"{src_version_id}\"" f" was not found in project \"{src_project_name}\"" - ) - raise PushToProjectError(self._status.error) + )) + raise PushToProjectError(self._status.fail_reason) subset_id = version_doc["parent"] subset_doc = get_subset_by_id(src_project_name, subset_id) if not subset_doc: - self._status.error = ( + self._status.set_fail_reason(( f"Could find subset with id \"{subset_id}\"" f" in project \"{src_project_name}\"" - ) - raise PushToProjectError(self._status.error) + )) + raise PushToProjectError(self._status.fail_reason) asset_id = subset_doc["parent"] asset_doc = get_asset_by_id(src_project_name, asset_id) if not asset_doc: - self._status.error = ( + self._status.set_fail_reason(( f"Could find asset with id \"{asset_id}\"" f" in project \"{src_project_name}\"" - ) - raise PushToProjectError(self._status.error) + )) + raise PushToProjectError(self._status.fail_reason) anatomy = Anatomy(src_project_name) @@ -499,10 +606,16 @@ def fill_source_variables(self): ProjectPushRepreItem(repre_doc, anatomy.roots) for repre_doc in repre_docs ] - self._status.add_progress_message(( + self._status.debug(( f"Found {len(repre_items)} representations on" f" version {src_version_id} in project '{src_project_name}'" )) + if not repre_items: + self._status.set_fail_reason( + "Source version does not have representations" + f" (Version id: {src_version_id})" + ) + raise PushToProjectError(self._status.fail_reason) self._src_anatomy = anatomy self._src_project_doc = project_doc @@ -517,12 +630,12 @@ def fill_destination_project(self): # Validate project existence dst_project_doc = get_project(dst_project_name) if not dst_project_doc: - self._status.error = ( + self._status.set_fail_reason( f"Destination project '{dst_project_name}' was not found" ) - raise PushToProjectError(self._status.error) + raise PushToProjectError(self._status.fail_reason) - self._status.add_progress_message( + self._status.debug( f"Destination project '{dst_project_name}' found" ) self._project_doc = dst_project_doc @@ -556,10 +669,10 @@ def _create_asset( for other_asset_doc in other_asset_docs: other_name = other_asset_doc["name"] if other_name.lower() == asset_name_low: - self._status.add_progress_message( + self._status.debug(( f"Found already existing asset with name \"{other_name}\"" f" which match requested name \"{asset_name}\"" - ) + )) return other_asset_doc data_keys = ( @@ -597,7 +710,7 @@ def _create_asset( asset_doc["type"], asset_doc ) - self._status.add_progress_message( + self._status.info( f"Creating new asset with name \"{asset_name}\"" ) self._created_asset_doc = asset_doc @@ -609,10 +722,10 @@ def fill_or_create_destination_asset(self): dst_task_name = self._item.dst_task_name new_asset_name = self._item.new_asset_name if not dst_asset_id and not new_asset_name: - self._status.error = ( + self._status.set_fail_reason( "Push item does not have defined destination asset" ) - raise PushToProjectError(self._status.error) + raise PushToProjectError(self._status.fail_reason) # Get asset document parent_asset_doc = None @@ -621,11 +734,11 @@ def fill_or_create_destination_asset(self): self._item.dst_project_name, self._item.dst_asset_id ) if not parent_asset_doc: - self._status.error = ( + self._status.set_fail_reason( f"Could find asset with id \"{dst_asset_id}\"" f" in project \"{dst_project_name}\"" ) - raise PushToProjectError(self._status.error) + raise PushToProjectError(self._status.fail_reason) if not new_asset_name: asset_doc = parent_asset_doc @@ -647,12 +760,12 @@ def fill_or_create_destination_asset(self): asset_tasks = asset_doc.get("data", {}).get("tasks") or {} task_info = asset_tasks.get(dst_task_name) if not task_info: - self._status.error = ( + self._status.set_fail_reason( f"Could find task with name \"{dst_task_name}\"" f" on asset \"{asset_path}\"" f" in project \"{dst_project_name}\"" ) - raise PushToProjectError(self._status.error) + raise PushToProjectError(self._status.fail_reason) # Create copy of task info to avoid changing data in asset document task_info = copy.deepcopy(task_info) @@ -671,12 +784,12 @@ def determine_family(self): family = families[0] if not family: - self._status.error = ( + self._status.set_fail_reason( "Couldn't figure out family from source subset" ) - raise PushToProjectError(self._status.error) + raise PushToProjectError(self._status.fail_reason) - self._status.add_progress_message( + self._status.debug( f"Publishing family is '{family}' (Based on source subset)" ) self._family = family @@ -690,7 +803,7 @@ def determine_publish_template_name(self): self.task_info.get("type"), project_settings=self.project_settings ) - self._status.add_progress_message( + self._status.debug( f"Using template '{template_name}' for integration" ) self._template_name = template_name @@ -708,8 +821,8 @@ def determine_subset_name(self): host_name=self.host_name, project_settings=self.project_settings ) - self._status.add_progress_message( - f"Push will be integrating to subet with name '{subset_name}'" + self._status.info( + f"Push will be integrating to subset with name '{subset_name}'" ) self._subset_name = subset_name @@ -733,20 +846,7 @@ def make_sure_subset_exists(self): self._subset_doc = subset_doc def make_sure_version_exists(self): - """Make sure version document exits in database. - - Args: - item_process (ProjectPushItemProcess): Item handling process. - project_name (str): Name of project where version should live. - version (Union[int, None]): Number of version. Latest is used when - 'None' is passed. - operations (OperationsSession): Session which handler creation and - update of entities. - - Returns: - Tuple[Dict[str, Any], bool]: New version document and boolean if version - already existed in database. - """ + """Make sure version document exits in database.""" project_name = self._item.dst_project_name version = self._item.dst_version @@ -851,16 +951,19 @@ def _integrate_representations(self): file_template = StringTemplate( anatomy.templates[template_name]["file"] ) + self._status.info("Preparing files to transfer") processed_repre_items = self._prepare_file_transactions( anatomy, template_name, formatting_data, file_template ) self._file_transaction.process() + self._status.info("Preparing database changes") self._prepare_database_operations( version_id, processed_repre_items, path_template, existing_repres_by_low_name ) + self._status.info("Finalization") self._operations.commit() self._file_transaction.finalize() @@ -1005,15 +1108,33 @@ def _prepare_database_operations( ) def process(self): - item_process.fill_source_variables() - item_process.fill_destination_project() - item_process.fill_or_create_destination_asset() - item_process.determine_family() - item_process.determine_publish_template_name() - item_process.determine_subset_name() - item_process.make_sure_subset_exists() - item_process.make_sure_version_exists() - item_process.integrate_representations() + try: + self._status.info("Process started") + self.fill_source_variables() + self._status.info("Source entities were found") + self.fill_destination_project() + self._status.info("Destination project was found") + self.fill_or_create_destination_asset() + self._status.info("Destination asset was determined") + self.determine_family() + self.determine_publish_template_name() + self.determine_subset_name() + self.make_sure_subset_exists() + self.make_sure_version_exists() + self._status.info("Prerequirements were prepared") + self.integrate_representations() + self._status.info("Integration finished") + + except PushToProjectError: + pass + + except Exception as exc: + self._status.set_fail_reason( + "Unhandled error happened: {}".format(str(exc)) + ) + + finally: + self._status.set_finished() def main(): From 3eed7b8de0b8a0963d1eba13b704cece21e5429b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 5 Jan 2023 18:09:00 +0100 Subject: [PATCH 135/213] added app definition for push to project --- openpype/tools/push_to_project/app.py | 41 +++++++++++++++++++ .../push_to_project/control_integrate.py | 25 ----------- openpype/tools/push_to_project/window.py | 35 ---------------- 3 files changed, 41 insertions(+), 60 deletions(-) create mode 100644 openpype/tools/push_to_project/app.py diff --git a/openpype/tools/push_to_project/app.py b/openpype/tools/push_to_project/app.py new file mode 100644 index 00000000000..9ca5fd83e9a --- /dev/null +++ b/openpype/tools/push_to_project/app.py @@ -0,0 +1,41 @@ +import click +from qtpy import QtWidgets, QtCore + +from openpype.tools.push_to_project.window import PushToContextSelectWindow + + +@click.command() +@click.option("--project", help="Source project name") +@click.option("--version", help="Source version id") +def main(project, version): + """Run PushToProject tool to integrate version in different project. + + Args: + project (str): Source project name. + version (str): Version id. + """ + + app = QtWidgets.QApplication.instance() + if not app: + # 'AA_EnableHighDpiScaling' must be set before app instance creation + high_dpi_scale_attr = getattr( + QtCore.Qt, "AA_EnableHighDpiScaling", None + ) + if high_dpi_scale_attr is not None: + QtWidgets.QApplication.setAttribute(high_dpi_scale_attr) + + app = QtWidgets.QApplication([]) + + attr = getattr(QtCore.Qt, "AA_UseHighDpiPixmaps", None) + if attr is not None: + app.setAttribute(attr) + + window = PushToContextSelectWindow() + window.show() + window.controller.set_source(project, version) + + app.exec_() + + +if __name__ == "__main__": + main() diff --git a/openpype/tools/push_to_project/control_integrate.py b/openpype/tools/push_to_project/control_integrate.py index e4ae9decd34..fec9b9ddd29 100644 --- a/openpype/tools/push_to_project/control_integrate.py +++ b/openpype/tools/push_to_project/control_integrate.py @@ -1135,28 +1135,3 @@ def process(self): finally: self._status.set_finished() - - -def main(): - # NOTE For development purposes - project_name = "" - version_id = "" - dst_project_name = "" - dst_asset_id = "" - dst_task_name = "" - version = None - variant = "" - comment = "" - - item = ProjectPushItem( - project_name, - version_id, - dst_project_name, - dst_asset_id, - dst_task_name, - variant, - version, - dst_version=1 - ) - item_process = ProjectPushItemProcess(item) - item_process.process() diff --git a/openpype/tools/push_to_project/window.py b/openpype/tools/push_to_project/window.py index 99a77caa9e5..a68f0f53408 100644 --- a/openpype/tools/push_to_project/window.py +++ b/openpype/tools/push_to_project/window.py @@ -705,38 +705,3 @@ def _on_close_click(self): def _on_select_click(self): self._controller.submit() - - -def main(): - app = QtWidgets.QApplication.instance() - if not app: - # 'AA_EnableHighDpiScaling' must be set before app instance creation - high_dpi_scale_attr = getattr( - QtCore.Qt, "AA_EnableHighDpiScaling", None - ) - if high_dpi_scale_attr is not None: - QtWidgets.QApplication.setAttribute(high_dpi_scale_attr) - - app = QtWidgets.QApplication([]) - - for attr_name in ( - "AA_UseHighDpiPixmaps", - ): - attr = getattr(QtCore.Qt, attr_name, None) - if attr is not None: - app.setAttribute(attr) - - # TODO find way how to get these - project_name = None - version_id = None - - # Show window dialog - window = PushToContextSelectWindow() - window.controller.set_source(project_name, version_id) - window.show() - - app.exec_() - - -if __name__ == "__main__": - main() \ No newline at end of file From e2f3aadb45e076560c1f2a6b1d2d3db3524eee8f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 5 Jan 2023 18:09:16 +0100 Subject: [PATCH 136/213] added 'ValidatedLineEdit' to style --- openpype/style/style.css | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/openpype/style/style.css b/openpype/style/style.css index a7a48cdb9d7..c96239dbd57 100644 --- a/openpype/style/style.css +++ b/openpype/style/style.css @@ -1416,6 +1416,13 @@ CreateNextPageOverlay { } /* Globally used names */ +#ValidatedLineEdit[state="valid"], #ValidatedLineEdit[state="valid"]:focus, #ValidatedLineEdit[state="valid"]:hover { + border-color: {color:publisher:success}; +} +#ValidatedLineEdit[state="invalid"], #ValidatedLineEdit[state="invalid"]:focus, #ValidatedLineEdit[state="invalid"]:hover { + border-color: {color:publisher:error}; +} + #Separator { background: {color:bg-menu-separator}; } From 74984afc7be1e0d707b0742fa594bc75396a8eee Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 5 Jan 2023 18:11:49 +0100 Subject: [PATCH 137/213] added new 'LoadError' to avoid showing traceback --- openpype/pipeline/load/__init__.py | 1 + openpype/pipeline/load/utils.py | 10 ++++++++ openpype/tools/loader/widgets.py | 38 ++++++++++++++++++------------ 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/openpype/pipeline/load/__init__.py b/openpype/pipeline/load/__init__.py index e96f64f2a44..8bd09876bf5 100644 --- a/openpype/pipeline/load/__init__.py +++ b/openpype/pipeline/load/__init__.py @@ -1,6 +1,7 @@ from .utils import ( HeroVersionType, + LoadError, IncompatibleLoaderError, InvalidRepresentationContext, diff --git a/openpype/pipeline/load/utils.py b/openpype/pipeline/load/utils.py index 784d4628f3a..e2b3675115a 100644 --- a/openpype/pipeline/load/utils.py +++ b/openpype/pipeline/load/utils.py @@ -60,6 +60,16 @@ def __format__(self, format_spec): return self.version.__format__(format_spec) +class LoadError(Exception): + """Known error that happened during loading. + + A message is shown to user (without traceback). Make sure an artist can + understand the problem. + """ + + pass + + class IncompatibleLoaderError(ValueError): """Error when Loader is incompatible with a representation.""" pass diff --git a/openpype/tools/loader/widgets.py b/openpype/tools/loader/widgets.py index 826c7110da8..b0b43c1e40c 100644 --- a/openpype/tools/loader/widgets.py +++ b/openpype/tools/loader/widgets.py @@ -29,6 +29,7 @@ load_with_repre_context, load_with_subset_context, load_with_subset_contexts, + LoadError, IncompatibleLoaderError, ) from openpype.tools.utils import ( @@ -1577,6 +1578,7 @@ def _load_representations_by_loader(loader, repre_contexts, repre_context, options=options ) + except IncompatibleLoaderError as exc: print(exc) error_info.append(( @@ -1588,10 +1590,13 @@ def _load_representations_by_loader(loader, repre_contexts, )) except Exception as exc: - exc_type, exc_value, exc_traceback = sys.exc_info() - formatted_traceback = "".join(traceback.format_exception( - exc_type, exc_value, exc_traceback - )) + formatted_traceback = None + if not isinstance(exc, LoadError): + exc_type, exc_value, exc_traceback = sys.exc_info() + formatted_traceback = "".join(traceback.format_exception( + exc_type, exc_value, exc_traceback + )) + error_info.append(( str(exc), formatted_traceback, @@ -1616,7 +1621,7 @@ def _load_subsets_by_loader(loader, subset_contexts, options, error_info = [] if options is None: # not load when cancelled - return + return error_info if loader.is_multiple_contexts_compatible: subset_names = [] @@ -1631,13 +1636,14 @@ def _load_subsets_by_loader(loader, subset_contexts, options, subset_contexts, options=options ) + except Exception as exc: - exc_type, exc_value, exc_traceback = sys.exc_info() - formatted_traceback = "".join( - traceback.format_exception( + formatted_traceback = None + if not isinstance(exc, LoadError): + exc_type, exc_value, exc_traceback = sys.exc_info() + formatted_traceback = "".join(traceback.format_exception( exc_type, exc_value, exc_traceback - ) - ) + )) error_info.append(( str(exc), formatted_traceback, @@ -1657,13 +1663,15 @@ def _load_subsets_by_loader(loader, subset_contexts, options, subset_context, options=options ) + except Exception as exc: - exc_type, exc_value, exc_traceback = sys.exc_info() - formatted_traceback = "\n".join( - traceback.format_exception( + formatted_traceback = None + if not isinstance(exc, LoadError): + exc_type, exc_value, exc_traceback = sys.exc_info() + formatted_traceback = "".join(traceback.format_exception( exc_type, exc_value, exc_traceback - ) - ) + )) + error_info.append(( str(exc), formatted_traceback, From 23d2f78a3829c1095bfeeaf82fab4e1dafb44c3c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 5 Jan 2023 18:13:44 +0100 Subject: [PATCH 138/213] added push to library loader --- openpype/plugins/load/push_to_library.py | 52 ++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 openpype/plugins/load/push_to_library.py diff --git a/openpype/plugins/load/push_to_library.py b/openpype/plugins/load/push_to_library.py new file mode 100644 index 00000000000..dd7291e686f --- /dev/null +++ b/openpype/plugins/load/push_to_library.py @@ -0,0 +1,52 @@ +import os + +from openpype import PACKAGE_DIR +from openpype.lib import get_openpype_execute_args, run_detached_process +from openpype.pipeline import load +from openpype.pipeline.load import LoadError + + +class PushToLibraryProject(load.SubsetLoaderPlugin): + """Export selected versions to folder structure from Template""" + + is_multiple_contexts_compatible = True + + representations = ["*"] + families = ["*"] + + label = "Push to Library project" + order = 35 + icon = "send" + color = "#d8d8d8" + + def load(self, contexts, name=None, namespace=None, options=None): + filtered_contexts = [ + context + for context in contexts + if context.get("project") and context.get("version") + ] + if not filtered_contexts: + raise LoadError("Nothing to push for your selection") + + if len(filtered_contexts) > 1: + raise LoadError("Please select only one item") + + context = tuple(filtered_contexts)[0] + push_tool_script_path = os.path.join( + PACKAGE_DIR, + "tools", + "push_to_project", + "app.py" + ) + project_doc = context["project"] + version_doc = context["version"] + project_name = project_doc["name"] + version_id = str(version_doc["_id"]) + + args = get_openpype_execute_args( + "run", + push_tool_script_path, + "--project", project_name, + "--version", version_id + ) + run_detached_process(args) From ae52289e13c52ca4e6646c470c5ed257683d2a6f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 5 Jan 2023 18:15:24 +0100 Subject: [PATCH 139/213] filter library projects --- openpype/tools/push_to_project/control_context.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openpype/tools/push_to_project/control_context.py b/openpype/tools/push_to_project/control_context.py index 242ca475e74..bfa6042504e 100644 --- a/openpype/tools/push_to_project/control_context.py +++ b/openpype/tools/push_to_project/control_context.py @@ -127,6 +127,9 @@ def refresh_projects(self, force=False): project_names = [] project_docs_by_name = {} for project_doc in get_projects(): + library_project = project_doc["data"].get("library_project") + if not library_project: + continue project_name = project_doc["name"] project_names.append(project_name) project_docs_by_name[project_name] = project_doc From c74a969ae4e8f97bd7f597f62bb0dc188777f791 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 5 Jan 2023 19:26:31 +0100 Subject: [PATCH 140/213] added ability to submit integration --- .../tools/push_to_project/control_context.py | 55 ++++++++++++++++--- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/openpype/tools/push_to_project/control_context.py b/openpype/tools/push_to_project/control_context.py index bfa6042504e..aebdd5a0d01 100644 --- a/openpype/tools/push_to_project/control_context.py +++ b/openpype/tools/push_to_project/control_context.py @@ -1,5 +1,7 @@ import re +import time import collections +import threading from openpype.client import ( get_projects, @@ -17,6 +19,12 @@ get_subset_name_template, ) +from .control_integrate import ( + ProjectPushItem, + ProjectPushItemProcess, + ProjectPushItemStatus, +) + class AssetItem: def __init__( @@ -382,6 +390,7 @@ def __init__(self, project_name=None, version_id=None): event_system.add_callback("new_asset_name.changed", self._invalidate) self._submission_enabled = False + self._process_thread = None self.set_source(project_name, version_id) @@ -620,12 +629,44 @@ def get_selected_asset_name(self): return asset_item.name return None - def submit(self): + def submit(self, wait=True): if not self.submission_enabled: return - project_name = self.selection_model.project_name - asset_id = self.selection_model.asset_id - task_name = self.selection_model.task_name - self.dst_project_name = project_name - self.dst_asset_id = asset_id - self.dst_task_name = task_name + + if self._process_thread is not None: + return + + self._event_system.emit("submit.started", {}, "controller") + thread = threading.Thread(target=self._submit_callback) + thread.start() + if wait: + while thread.is_alive(): + time.sleep(0.1) + thread.join() + self._event_system.emit("submit.finished", {}, "controller") + return + self._process_thread = thread + + def wait_for_process_thread(self): + if self._process_thread is None: + return + self._process_thread.join() + self._process_thread = None + + def _submit_callback(self): + item = ProjectPushItem( + self.src_project_name, + self.src_version_id, + self.selection_model.project_name, + self.selection_model.asset_id, + self.selection_model.task_name, + self.user_values.variant, + comment=self.user_values.comment, + new_asset_name=self.user_values.new_asset_name, + dst_version=1 + ) + + status_item = ProjectPushItemStatus(event_system=self._event_system) + process_item = ProjectPushItemProcess(item, status_item) + process_item.process() + self._event_system.emit("submit.finished", {}, "controller") From ad99133cf06d2a0a344a53ddcb9429f020ecf942 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 5 Jan 2023 19:36:49 +0100 Subject: [PATCH 141/213] added base of pushing --- .../push_to_project/control_integrate.py | 51 +++++-- openpype/tools/push_to_project/window.py | 131 ++++++++++++++++-- 2 files changed, 162 insertions(+), 20 deletions(-) diff --git a/openpype/tools/push_to_project/control_integrate.py b/openpype/tools/push_to_project/control_integrate.py index fec9b9ddd29..7ad2e1e8748 100644 --- a/openpype/tools/push_to_project/control_integrate.py +++ b/openpype/tools/push_to_project/control_integrate.py @@ -4,6 +4,8 @@ import socket import itertools import datetime +import sys +import traceback from bson.objectid import ObjectId @@ -143,7 +145,8 @@ def __init__( failed=False, finished=False, fail_reason=None, - messages=None + messages=None, + event_system=None ): if messages is None: messages = [] @@ -151,6 +154,13 @@ def __init__( self._finished = finished self._fail_reason = fail_reason self._messages = messages + self._event_system = event_system + + def emit_event(self, topic, data=None): + if self._event_system is None: + return + + self._event_system.emit(topic, data or {}, "push.status") def get_finished(self): """Processing of push to project finished. @@ -170,6 +180,7 @@ def set_finished(self, finished=True): if finished != self._finished: self._finished = finished + self.emit_event("push.finished.changed", {"finished": finished}) def get_failed(self): """Processing failed. @@ -192,7 +203,10 @@ def set_failed(self, failed, fail_reason=UNKNOWN): fail_reason (str): Reason why failed. """ - self._failed = failed + if self._failed != failed: + self._failed = failed + self.emit_event("push.failed.changed", {"failed": failed}) + if fail_reason is not UNKNOWN and self._fail_reason != fail_reason: self.set_fail_reason(fail_reason) @@ -223,6 +237,7 @@ def set_fail_reason(self, reason): if self._fail_reason == reason: return self._fail_reason = reason + self.emit_event("push.fail_reason.changed", {"fail_reason": reason}) if reason and not self._failed: self.set_failed(True) @@ -238,6 +253,10 @@ def set_fail_reason(self, reason): def add_message(self, message, level): message_obj = StatusMessage(message, level) self._messages.append(message_obj) + self.emit_event( + "push.message.added", + {"message": message, "level": level} + ) print(message_obj) return message_obj @@ -664,16 +683,26 @@ def _create_asset( asset_name_low = asset_name.lower() other_asset_docs = get_assets( - project_doc["name"], parent_ids=[parent_id], fields=["name"] + project_doc["name"], fields=["_id", "name", "data.visualParent"] ) for other_asset_doc in other_asset_docs: other_name = other_asset_doc["name"] - if other_name.lower() == asset_name_low: - self._status.debug(( - f"Found already existing asset with name \"{other_name}\"" - f" which match requested name \"{asset_name}\"" + other_parent_id = other_asset_doc["data"].get("visualParent") + if other_name.lower() != asset_name_low: + continue + + if other_parent_id != parent_id: + self._status.set_fail_reason(( + f"Asset with name \"{other_name}\" already" + " exists in different hierarchy." )) - return other_asset_doc + raise PushToProjectError(self._status.fail_reason) + + self._status.debug(( + f"Found already existing asset with name \"{other_name}\"" + f" which match requested name \"{asset_name}\"" + )) + return get_asset_by_id(project_doc["name"], other_asset_doc["_id"]) data_keys = ( "clipIn", @@ -1129,8 +1158,12 @@ def process(self): pass except Exception as exc: + _exc, _value, _tb = sys.exc_info() self._status.set_fail_reason( - "Unhandled error happened: {}".format(str(exc)) + "Unhandled error happened: {}\n{}".format( + str(exc), + "".join(traceback.format_exception(_exc, _value, _tb)) + ) ) finally: diff --git a/openpype/tools/push_to_project/window.py b/openpype/tools/push_to_project/window.py index a68f0f53408..5dea0401cb6 100644 --- a/openpype/tools/push_to_project/window.py +++ b/openpype/tools/push_to_project/window.py @@ -373,7 +373,9 @@ def __init__(self, controller=None): self.setWindowTitle("Push to project (select context)") self.setWindowIcon(QtGui.QIcon(get_app_icon_path())) - header_widget = QtWidgets.QWidget(self) + main_context_widget = QtWidgets.QWidget(self) + + header_widget = QtWidgets.QWidget(main_context_widget) header_label = QtWidgets.QLabel(controller.src_label, header_widget) @@ -381,7 +383,9 @@ def __init__(self, controller=None): header_layout.setContentsMargins(0, 0, 0, 0) header_layout.addWidget(header_label) - main_splitter = QtWidgets.QSplitter(QtCore.Qt.Horizontal, self) + main_splitter = QtWidgets.QSplitter( + QtCore.Qt.Horizontal, main_context_widget + ) context_widget = QtWidgets.QWidget(main_splitter) @@ -455,22 +459,57 @@ def __init__(self, controller=None): btns_layout.addWidget(cancel_btn, 0) btns_layout.addWidget(publish_btn, 0) - sep_1 = SeparatorWidget(parent=self) - sep_2 = SeparatorWidget(parent=self) - main_layout = QtWidgets.QVBoxLayout(self) - main_layout.addWidget(header_widget, 0) - main_layout.addWidget(sep_1, 0) - main_layout.addWidget(main_splitter, 1) - main_layout.addWidget(sep_2, 0) - main_layout.addWidget(btns_widget, 0) + sep_1 = SeparatorWidget(parent=main_context_widget) + sep_2 = SeparatorWidget(parent=main_context_widget) + main_context_layout = QtWidgets.QVBoxLayout(main_context_widget) + main_context_layout.addWidget(header_widget, 0) + main_context_layout.addWidget(sep_1, 0) + main_context_layout.addWidget(main_splitter, 1) + main_context_layout.addWidget(sep_2, 0) + main_context_layout.addWidget(btns_widget, 0) + + # NOTE This was added in hurry + # - should be reorganized and changed styles + overlay_widget = QtWidgets.QFrame(self) + overlay_widget.setObjectName("OverlayFrame") + + overlay_label = QtWidgets.QLabel(overlay_widget) + overlay_label.setAlignment(QtCore.Qt.AlignCenter) + + overlay_btns_widget = QtWidgets.QWidget(overlay_widget) + overlay_btns_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) + + # Add try again button (requires changes in controller) + overlay_close_btn = QtWidgets.QPushButton("Close", overlay_btns_widget) + + overlay_btns_layout = QtWidgets.QHBoxLayout(overlay_btns_widget) + overlay_btns_layout.addStretch(1) + overlay_btns_layout.addWidget(overlay_close_btn, 0) + overlay_btns_layout.addStretch(1) + + overlay_layout = QtWidgets.QVBoxLayout(overlay_widget) + overlay_layout.addWidget(overlay_label, 0) + overlay_layout.addWidget(overlay_btns_widget, 0) + overlay_layout.setAlignment(QtCore.Qt.AlignCenter) + + main_layout = QtWidgets.QStackedLayout(self) + main_layout.setContentsMargins(0, 0, 0, 0) + main_layout.addWidget(main_context_widget) + main_layout.addWidget(overlay_widget) + main_layout.setStackingMode(QtWidgets.QStackedLayout.StackAll) + main_layout.setCurrentWidget(main_context_widget) show_timer = QtCore.QTimer() show_timer.setInterval(1) + main_thread_timer = QtCore.QTimer() + main_thread_timer.setInterval(10) + user_input_changed_timer = QtCore.QTimer() user_input_changed_timer.setInterval(200) user_input_changed_timer.setSingleShot(True) + main_thread_timer.timeout.connect(self._on_main_thread_timer) show_timer.timeout.connect(self._on_show_timer) user_input_changed_timer.timeout.connect(self._on_user_input_timer) asset_name_input.textChanged.connect(self._on_new_asset_change) @@ -488,6 +527,7 @@ def __init__(self, controller=None): task_model.items_changed.connect(self._on_task_model_change) publish_btn.clicked.connect(self._on_select_click) cancel_btn.clicked.connect(self._on_close_click) + overlay_close_btn.clicked.connect(self._on_close_click) controller.event_system.add_callback( "new_asset_name.changed", self._on_controller_new_asset_change @@ -504,6 +544,25 @@ def __init__(self, controller=None): controller.event_system.add_callback( "source.changed", self._on_controller_source_change ) + controller.event_system.add_callback( + "submit.started", self._on_controller_submit_start + ) + controller.event_system.add_callback( + "submit.finished", self._on_controller_submit_end + ) + controller.event_system.add_callback( + "push.failed.changed", self._on_push_failed + ) + controller.event_system.add_callback( + "push.fail_reason.changed", self._on_push_failed_reason + ) + controller.event_system.add_callback( + "push.message.added", self._on_push_message + ) + + self._main_layout = main_layout + + self._main_context_widget = main_context_widget self._header_label = header_label self._main_splitter = main_splitter @@ -526,6 +585,10 @@ def __init__(self, controller=None): self._publish_btn = publish_btn + self._overlay_widget = overlay_widget + self._overlay_close_btn = overlay_close_btn + self._overlay_label = overlay_label + self._user_input_changed_timer = user_input_changed_timer # Store current value on input text change # The value is unset when is passed to controller @@ -538,7 +601,15 @@ def __init__(self, controller=None): self._show_counter = 2 self._first_show = True + self._main_thread_timer = main_thread_timer + self._main_thread_timer_can_stop = True + self._submit_messages = [] + self._last_submit_message = None + self._push_fail_reason = "Unknown" + self._push_failed = False + publish_btn.setEnabled(False) + overlay_close_btn.setVisible(False) if controller.user_values.new_asset_name: asset_name_input.setText(controller.user_values.new_asset_name) @@ -704,4 +775,42 @@ def _on_close_click(self): self.close() def _on_select_click(self): - self._controller.submit() + self._controller.submit(wait=False) + + def _on_main_thread_timer(self): + if self._last_submit_message: + self._overlay_label.setText(self._last_submit_message) + self._last_submit_message = None + + if self._main_thread_timer_can_stop: + self._main_thread_timer.stop() + self._overlay_close_btn.setVisible(True) + + if self._push_failed: + self._overlay_label.setText( + "Push Failed\n{}".format(self._push_fail_reason) + ) + set_style_property( + self._overlay_close_btn, + "state", + "error" + ) + + def _on_controller_submit_start(self): + self._main_thread_timer_can_stop = False + self._main_thread_timer.start() + self._main_layout.setCurrentWidget(self._overlay_widget) + self._overlay_label.setText("Submittion started") + + def _on_controller_submit_end(self): + self._main_thread_timer_can_stop = True + + def _on_push_message(self, event): + self._submit_messages.append(event["message"]) + self._last_submit_message = event["message"] + + def _on_push_failed_reason(self, event): + self._push_fail_reason = event["fail_reason"] + + def _on_push_failed(self, event): + self._push_failed = event["failed"] From 75fefd6ae2eb8af58d02f5e4a7201ae5a154cb15 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 5 Jan 2023 19:37:12 +0100 Subject: [PATCH 142/213] added errored button --- openpype/style/style.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openpype/style/style.css b/openpype/style/style.css index c96239dbd57..da477eeefab 100644 --- a/openpype/style/style.css +++ b/openpype/style/style.css @@ -148,6 +148,10 @@ QPushButton::menu-indicator { padding-right: 5px; } +QPushButton[state="error"] { + background: {color:publisher:error}; +} + QToolButton { border: 0px solid transparent; background: {color:bg-buttons}; From 23817bdc4cf1394a78af62a2532d0a8c62d0dbb5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 6 Jan 2023 12:17:46 +0100 Subject: [PATCH 143/213] formatting changes --- openpype/tools/push_to_project/control_integrate.py | 5 ++++- openpype/tools/push_to_project/window.py | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/tools/push_to_project/control_integrate.py b/openpype/tools/push_to_project/control_integrate.py index 7ad2e1e8748..522d0f7a317 100644 --- a/openpype/tools/push_to_project/control_integrate.py +++ b/openpype/tools/push_to_project/control_integrate.py @@ -409,7 +409,10 @@ def _get_source_files_with_frames(self): filepath_template = file_info["path"].replace("\\", "/") filepath = filepath_template.format(root=self._roots) dirpath, basename = os.path.split(filepath_template) - if dirpath != src_dirpath or not src_basename_regex.match(basename): + if ( + dirpath != src_dirpath + or not src_basename_regex.match(basename) + ): relative_dir = dirpath.replace(src_dirpath, "") if relative_dir: relative_path = "/".join([relative_dir, basename]) diff --git a/openpype/tools/push_to_project/window.py b/openpype/tools/push_to_project/window.py index 5dea0401cb6..c43ecad1605 100644 --- a/openpype/tools/push_to_project/window.py +++ b/openpype/tools/push_to_project/window.py @@ -1,4 +1,3 @@ -import re import collections from qtpy import QtWidgets, QtGui, QtCore From c1e5daa35934e5cd1588a7db14259b117792c617 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 6 Jan 2023 12:18:00 +0100 Subject: [PATCH 144/213] use 'DeselectableTreeView' for asset view --- openpype/tools/push_to_project/window.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/tools/push_to_project/window.py b/openpype/tools/push_to_project/window.py index c43ecad1605..963e519b12f 100644 --- a/openpype/tools/push_to_project/window.py +++ b/openpype/tools/push_to_project/window.py @@ -9,6 +9,7 @@ get_asset_icon_by_name, set_style_property, ) +from openpype.tools.utils.views import DeselectableTreeView from .control_context import PushToContextController @@ -401,7 +402,7 @@ def __init__(self, controller=None): QtCore.Qt.Vertical, context_widget ) - asset_view = QtWidgets.QTreeView(asset_task_splitter) + asset_view = DeselectableTreeView(asset_task_splitter) asset_view.setHeaderHidden(True) asset_model = AssetsModel(controller) asset_proxy = QtCore.QSortFilterProxyModel() From 6266877bc660725f30e7f1dab0544f94e5d34760 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 6 Jan 2023 13:38:02 +0100 Subject: [PATCH 145/213] cleanup the processing a little bit --- .../tools/push_to_project/control_context.py | 43 ++++--- .../push_to_project/control_integrate.py | 121 ++++++++++-------- openpype/tools/push_to_project/window.py | 64 +++++---- 3 files changed, 128 insertions(+), 100 deletions(-) diff --git a/openpype/tools/push_to_project/control_context.py b/openpype/tools/push_to_project/control_context.py index aebdd5a0d01..a45fab618ad 100644 --- a/openpype/tools/push_to_project/control_context.py +++ b/openpype/tools/push_to_project/control_context.py @@ -391,6 +391,7 @@ def __init__(self, project_name=None, version_id=None): self._submission_enabled = False self._process_thread = None + self._process_item = None self.set_source(project_name, version_id) @@ -636,24 +637,6 @@ def submit(self, wait=True): if self._process_thread is not None: return - self._event_system.emit("submit.started", {}, "controller") - thread = threading.Thread(target=self._submit_callback) - thread.start() - if wait: - while thread.is_alive(): - time.sleep(0.1) - thread.join() - self._event_system.emit("submit.finished", {}, "controller") - return - self._process_thread = thread - - def wait_for_process_thread(self): - if self._process_thread is None: - return - self._process_thread.join() - self._process_thread = None - - def _submit_callback(self): item = ProjectPushItem( self.src_project_name, self.src_version_id, @@ -668,5 +651,29 @@ def _submit_callback(self): status_item = ProjectPushItemStatus(event_system=self._event_system) process_item = ProjectPushItemProcess(item, status_item) + self._process_item = process_item + self._event_system.emit("submit.started", {}, "controller") + if wait: + self._submit_callback() + self._process_item = None + return process_item + + thread = threading.Thread(target=self._submit_callback) + self._process_thread = thread + thread.start() + return process_item + + def wait_for_process_thread(self): + if self._process_thread is None: + return + self._process_thread.join() + self._process_thread = None + + def _submit_callback(self): + process_item = self._process_item + if process_item is None: + return process_item.process() self._event_system.emit("submit.finished", {}, "controller") + if process_item is self._process_item: + self._process_item = None diff --git a/openpype/tools/push_to_project/control_integrate.py b/openpype/tools/push_to_project/control_integrate.py index 522d0f7a317..704ed7ba515 100644 --- a/openpype/tools/push_to_project/control_integrate.py +++ b/openpype/tools/push_to_project/control_integrate.py @@ -145,6 +145,7 @@ def __init__( failed=False, finished=False, fail_reason=None, + formatted_traceback=None, messages=None, event_system=None ): @@ -153,6 +154,7 @@ def __init__( self._failed = failed self._finished = finished self._fail_reason = fail_reason + self._traceback = formatted_traceback self._messages = messages self._event_system = event_system @@ -182,16 +184,9 @@ def set_finished(self, finished=True): self._finished = finished self.emit_event("push.finished.changed", {"finished": finished}) - def get_failed(self): - """Processing failed. - - Returns: - bool: Processing failed. - """ - - return self._failed + finished = property(get_finished, set_finished) - def set_failed(self, failed, fail_reason=UNKNOWN): + def set_failed(self, fail_reason, exc_info=None): """Set status as failed. Attribute 'fail_reason' can change automatically based on passed value. @@ -203,20 +198,48 @@ def set_failed(self, failed, fail_reason=UNKNOWN): fail_reason (str): Reason why failed. """ - if self._failed != failed: - self._failed = failed - self.emit_event("push.failed.changed", {"failed": failed}) + failed = True + if not fail_reason and not exc_info: + failed = False + + full_traceback = None + if exc_info is not None: + full_traceback = "".join(traceback.format_exception(*exc_info)) + if not fail_reason: + fail_reason = "Failed without specified reason" + + if ( + self._failed == failed + and self._traceback == full_traceback + and self._fail_reason == fail_reason + ): + return + + self._failed = failed + self._fail_reason = fail_reason or None + self._traceback = full_traceback + + self.emit_event( + "push.failed.changed", + { + "failed": failed, + "reason": fail_reason, + "traceback": full_traceback + } + ) - if fail_reason is not UNKNOWN and self._fail_reason != fail_reason: - self.set_fail_reason(fail_reason) + @property + def failed(self): + """Processing failed. - if failed and self._fail_reason is None: - self.set_fail_reason("Failed without specified reason") + Returns: + bool: Processing failed. + """ - elif not failed and self._fail_reason: - self.set_fail_reason(None) + return self._failed - def get_fail_reason(self): + @property + def fail_reason(self): """Reason why push to process failed. Returns: @@ -225,28 +248,17 @@ def get_fail_reason(self): return self._fail_reason - def set_fail_reason(self, reason): - """Mark process status as failed. + @property + def traceback(self): + """Traceback of failed process. - Status is also set to failed if 'reason' is not None. + Traceback is available only if unhandled exception happened. - Args: - reason (str): Reason why push to project failed. + Returns: + Union[str, None]: Formatted traceback. """ - if self._fail_reason == reason: - return - self._fail_reason = reason - self.emit_event("push.fail_reason.changed", {"fail_reason": reason}) - if reason and not self._failed: - self.set_failed(True) - - if reason: - print(f"Integration failed: {reason}") - - finished = property(get_finished, set_finished) - failed = property(get_failed, set_failed) - fail_reason = property(get_fail_reason, set_fail_reason) + return self._traceback # Loggin helpers # TODO better logging @@ -585,7 +597,7 @@ def fill_source_variables(self): project_doc = get_project(src_project_name) if not project_doc: - self._status.set_fail_reason( + self._status.set_failed( f"Source project \"{src_project_name}\" was not found" ) raise PushToProjectError(self._status.fail_reason) @@ -594,7 +606,7 @@ def fill_source_variables(self): version_doc = get_version_by_id(src_project_name, src_version_id) if not version_doc: - self._status.set_fail_reason(( + self._status.set_failed(( f"Source version with id \"{src_version_id}\"" f" was not found in project \"{src_project_name}\"" )) @@ -603,7 +615,7 @@ def fill_source_variables(self): subset_id = version_doc["parent"] subset_doc = get_subset_by_id(src_project_name, subset_id) if not subset_doc: - self._status.set_fail_reason(( + self._status.set_failed(( f"Could find subset with id \"{subset_id}\"" f" in project \"{src_project_name}\"" )) @@ -612,7 +624,7 @@ def fill_source_variables(self): asset_id = subset_doc["parent"] asset_doc = get_asset_by_id(src_project_name, asset_id) if not asset_doc: - self._status.set_fail_reason(( + self._status.set_failed(( f"Could find asset with id \"{asset_id}\"" f" in project \"{src_project_name}\"" )) @@ -633,7 +645,7 @@ def fill_source_variables(self): f" version {src_version_id} in project '{src_project_name}'" )) if not repre_items: - self._status.set_fail_reason( + self._status.set_failed( "Source version does not have representations" f" (Version id: {src_version_id})" ) @@ -652,7 +664,7 @@ def fill_destination_project(self): # Validate project existence dst_project_doc = get_project(dst_project_name) if not dst_project_doc: - self._status.set_fail_reason( + self._status.set_failed( f"Destination project '{dst_project_name}' was not found" ) raise PushToProjectError(self._status.fail_reason) @@ -695,7 +707,7 @@ def _create_asset( continue if other_parent_id != parent_id: - self._status.set_fail_reason(( + self._status.set_failed(( f"Asset with name \"{other_name}\" already" " exists in different hierarchy." )) @@ -754,7 +766,7 @@ def fill_or_create_destination_asset(self): dst_task_name = self._item.dst_task_name new_asset_name = self._item.new_asset_name if not dst_asset_id and not new_asset_name: - self._status.set_fail_reason( + self._status.set_failed( "Push item does not have defined destination asset" ) raise PushToProjectError(self._status.fail_reason) @@ -766,7 +778,7 @@ def fill_or_create_destination_asset(self): self._item.dst_project_name, self._item.dst_asset_id ) if not parent_asset_doc: - self._status.set_fail_reason( + self._status.set_failed( f"Could find asset with id \"{dst_asset_id}\"" f" in project \"{dst_project_name}\"" ) @@ -792,7 +804,7 @@ def fill_or_create_destination_asset(self): asset_tasks = asset_doc.get("data", {}).get("tasks") or {} task_info = asset_tasks.get(dst_task_name) if not task_info: - self._status.set_fail_reason( + self._status.set_failed( f"Could find task with name \"{dst_task_name}\"" f" on asset \"{asset_path}\"" f" in project \"{dst_project_name}\"" @@ -816,7 +828,7 @@ def determine_family(self): family = families[0] if not family: - self._status.set_fail_reason( + self._status.set_failed( "Couldn't figure out family from source subset" ) raise PushToProjectError(self._status.fail_reason) @@ -1157,16 +1169,15 @@ def process(self): self.integrate_representations() self._status.info("Integration finished") - except PushToProjectError: - pass + except PushToProjectError as exc: + if not self._status.failed: + self._status.set_failed(str(exc)) except Exception as exc: _exc, _value, _tb = sys.exc_info() - self._status.set_fail_reason( - "Unhandled error happened: {}\n{}".format( - str(exc), - "".join(traceback.format_exception(_exc, _value, _tb)) - ) + self._status.set_failed( + "Unhandled error happened: {}".format(str(exc)), + (_exc, _value, _tb) ) finally: diff --git a/openpype/tools/push_to_project/window.py b/openpype/tools/push_to_project/window.py index 963e519b12f..e62650ec534 100644 --- a/openpype/tools/push_to_project/window.py +++ b/openpype/tools/push_to_project/window.py @@ -480,10 +480,16 @@ def __init__(self, controller=None): overlay_btns_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) # Add try again button (requires changes in controller) - overlay_close_btn = QtWidgets.QPushButton("Close", overlay_btns_widget) + overlay_try_btn = QtWidgets.QPushButton( + "Try again", overlay_btns_widget + ) + overlay_close_btn = QtWidgets.QPushButton( + "Close", overlay_btns_widget + ) overlay_btns_layout = QtWidgets.QHBoxLayout(overlay_btns_widget) overlay_btns_layout.addStretch(1) + overlay_btns_layout.addWidget(overlay_try_btn, 0) overlay_btns_layout.addWidget(overlay_close_btn, 0) overlay_btns_layout.addStretch(1) @@ -528,6 +534,7 @@ def __init__(self, controller=None): publish_btn.clicked.connect(self._on_select_click) cancel_btn.clicked.connect(self._on_close_click) overlay_close_btn.clicked.connect(self._on_close_click) + overlay_try_btn.clicked.connect(self._on_try_again_click) controller.event_system.add_callback( "new_asset_name.changed", self._on_controller_new_asset_change @@ -550,12 +557,6 @@ def __init__(self, controller=None): controller.event_system.add_callback( "submit.finished", self._on_controller_submit_end ) - controller.event_system.add_callback( - "push.failed.changed", self._on_push_failed - ) - controller.event_system.add_callback( - "push.fail_reason.changed", self._on_push_failed_reason - ) controller.event_system.add_callback( "push.message.added", self._on_push_message ) @@ -587,6 +588,7 @@ def __init__(self, controller=None): self._overlay_widget = overlay_widget self._overlay_close_btn = overlay_close_btn + self._overlay_try_btn = overlay_try_btn self._overlay_label = overlay_label self._user_input_changed_timer = user_input_changed_timer @@ -603,13 +605,12 @@ def __init__(self, controller=None): self._main_thread_timer = main_thread_timer self._main_thread_timer_can_stop = True - self._submit_messages = [] self._last_submit_message = None - self._push_fail_reason = "Unknown" - self._push_failed = False + self._process_item = None publish_btn.setEnabled(False) overlay_close_btn.setVisible(False) + overlay_try_btn.setVisible(False) if controller.user_values.new_asset_name: asset_name_input.setText(controller.user_values.new_asset_name) @@ -775,26 +776,42 @@ def _on_close_click(self): self.close() def _on_select_click(self): - self._controller.submit(wait=False) + self._process_item = self._controller.submit(wait=False) + + def _on_try_again_click(self): + self._process_item = None + self._last_submit_message = None + + self._overlay_close_btn.setVisible(False) + self._overlay_try_btn.setVisible(False) + self._main_layout.setCurrentWidget(self._main_context_widget) def _on_main_thread_timer(self): if self._last_submit_message: self._overlay_label.setText(self._last_submit_message) self._last_submit_message = None + process_status = self._process_item.status + push_failed = process_status.failed + fail_traceback = process_status.traceback if self._main_thread_timer_can_stop: self._main_thread_timer.stop() self._overlay_close_btn.setVisible(True) + if push_failed and not fail_traceback: + self._overlay_try_btn.setVisible(True) + + if push_failed: + message = "Push Failed:\n{}".format(process_status.fail_reason) + if fail_traceback: + message += "\n{}".format(fail_traceback) + self._overlay_label.setText(message) + set_style_property(self._overlay_close_btn, "state", "error") - if self._push_failed: - self._overlay_label.setText( - "Push Failed\n{}".format(self._push_fail_reason) - ) - set_style_property( - self._overlay_close_btn, - "state", - "error" - ) + if self._main_thread_timer_can_stop: + # Join thread in controller + self._controller.wait_for_process_thread() + # Reset process item to None + self._process_item = None def _on_controller_submit_start(self): self._main_thread_timer_can_stop = False @@ -806,11 +823,4 @@ def _on_controller_submit_end(self): self._main_thread_timer_can_stop = True def _on_push_message(self, event): - self._submit_messages.append(event["message"]) self._last_submit_message = event["message"] - - def _on_push_failed_reason(self, event): - self._push_fail_reason = event["fail_reason"] - - def _on_push_failed(self, event): - self._push_failed = event["failed"] From 2742a366175f5ef7f996c546ecf566652ff1c095 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 6 Jan 2023 13:39:28 +0100 Subject: [PATCH 146/213] remove unused import --- openpype/tools/push_to_project/control_context.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/tools/push_to_project/control_context.py b/openpype/tools/push_to_project/control_context.py index a45fab618ad..02f1da6733a 100644 --- a/openpype/tools/push_to_project/control_context.py +++ b/openpype/tools/push_to_project/control_context.py @@ -1,5 +1,4 @@ import re -import time import collections import threading From 0aa011184b7a07d71225ba2898c4412a37d7af43 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 6 Jan 2023 17:38:06 +0100 Subject: [PATCH 147/213] fix checked int for pyside6 --- openpype/tools/loader/widgets.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/tools/loader/widgets.py b/openpype/tools/loader/widgets.py index 8d58e822307..f222b3e9822 100644 --- a/openpype/tools/loader/widgets.py +++ b/openpype/tools/loader/widgets.py @@ -67,6 +67,8 @@ REMOTE_AVAILABILITY_ROLE ) +CHECKED_INT = getattr(QtCore.Qt.Checked, "value", 2) + class OverlayFrame(QtWidgets.QFrame): def __init__(self, label, parent): @@ -1066,7 +1068,9 @@ def get_enabled_families(self): checked_families = [] for row in range(model.rowCount()): index = model.index(row, 0) - if index.data(QtCore.Qt.CheckStateRole) == QtCore.Qt.Checked: + if index.data(QtCore.Qt.CheckStateRole) in ( + QtCore.Qt.Checked, CHECKED_INT + ): family = index.data(QtCore.Qt.DisplayRole) checked_families.append(family) From 679becc3a85b2f30e87a8a745c33fcbaa6dd8f95 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 6 Jan 2023 18:51:51 +0100 Subject: [PATCH 148/213] fix work with checkstate constants --- openpype/tools/loader/widgets.py | 30 ++++++++++--------- .../multiselection_combobox.py | 14 +++++++-- .../publisher/widgets/list_view_widgets.py | 7 +++-- .../settings/multiselection_combobox.py | 13 ++++++-- openpype/tools/utils/constants.py | 4 +++ openpype/tools/utils/lib.py | 24 +++++++++++++++ openpype/widgets/nice_checkbox.py | 22 +++++++++++++- 7 files changed, 91 insertions(+), 23 deletions(-) diff --git a/openpype/tools/loader/widgets.py b/openpype/tools/loader/widgets.py index f222b3e9822..6181fd63f01 100644 --- a/openpype/tools/loader/widgets.py +++ b/openpype/tools/loader/widgets.py @@ -35,6 +35,7 @@ ErrorMessageBox, lib as tools_lib ) +from openpype.tools.utils.lib import checkstate_int_to_enum from openpype.tools.utils.delegates import ( VersionDelegate, PrettyTimeDelegate @@ -47,6 +48,13 @@ TreeViewSpinner, DeselectableTreeView ) +from openpype.tools.utils.constants import ( + LOCAL_PROVIDER_ROLE, + REMOTE_PROVIDER_ROLE, + LOCAL_AVAILABILITY_ROLE, + REMOTE_AVAILABILITY_ROLE, + CHECKED_INT, +) from openpype.tools.assetlinks.widgets import SimpleLinkView from .model import ( @@ -60,15 +68,6 @@ from . import lib from .delegates import LoadedInSceneDelegate -from openpype.tools.utils.constants import ( - LOCAL_PROVIDER_ROLE, - REMOTE_PROVIDER_ROLE, - LOCAL_AVAILABILITY_ROLE, - REMOTE_AVAILABILITY_ROLE -) - -CHECKED_INT = getattr(QtCore.Qt.Checked, "value", 2) - class OverlayFrame(QtWidgets.QFrame): def __init__(self, label, parent): @@ -1068,9 +1067,10 @@ def get_enabled_families(self): checked_families = [] for row in range(model.rowCount()): index = model.index(row, 0) - if index.data(QtCore.Qt.CheckStateRole) in ( - QtCore.Qt.Checked, CHECKED_INT - ): + checked = checkstate_int_to_enum( + index.data(QtCore.Qt.CheckStateRole) + ) + if checked == QtCore.Qt.Checked: family = index.data(QtCore.Qt.DisplayRole) checked_families.append(family) @@ -1104,13 +1104,15 @@ def _set_checkstates(self, checked, indexes): self.blockSignals(True) for index in indexes: - index_state = index.data(QtCore.Qt.CheckStateRole) + index_state = checkstate_int_to_enum( + index.data(QtCore.Qt.CheckStateRole) + ) if index_state == state: continue new_state = state if new_state is None: - if index_state == QtCore.Qt.Checked: + if index_state in QtCore.Qt.Checked: new_state = QtCore.Qt.Unchecked else: new_state = QtCore.Qt.Checked diff --git a/openpype/tools/project_manager/project_manager/multiselection_combobox.py b/openpype/tools/project_manager/project_manager/multiselection_combobox.py index f12f402d1ad..4b5d4689829 100644 --- a/openpype/tools/project_manager/project_manager/multiselection_combobox.py +++ b/openpype/tools/project_manager/project_manager/multiselection_combobox.py @@ -1,5 +1,7 @@ from qtpy import QtCore, QtWidgets +from openpype.tools.utils.lib import checkstate_int_to_enum + class ComboItemDelegate(QtWidgets.QStyledItemDelegate): """ @@ -87,7 +89,9 @@ def _event_popup_shown(self, obj, event): return index_flags = current_index.flags() - state = current_index.data(QtCore.Qt.CheckStateRole) + state = checkstate_int_to_enum( + current_index.data(QtCore.Qt.CheckStateRole) + ) new_state = None if event.type() == QtCore.QEvent.MouseButtonRelease: @@ -184,7 +188,9 @@ def set_value(self, values): def value(self): items = list() for idx in range(self.count()): - state = self.itemData(idx, role=QtCore.Qt.CheckStateRole) + state = checkstate_int_to_enum( + self.itemData(idx, role=QtCore.Qt.CheckStateRole) + ) if state == QtCore.Qt.Checked: items.append( self.itemData(idx, role=QtCore.Qt.UserRole) @@ -194,7 +200,9 @@ def value(self): def checked_items_text(self): items = list() for idx in range(self.count()): - state = self.itemData(idx, role=QtCore.Qt.CheckStateRole) + state = checkstate_int_to_enum( + self.itemData(idx, role=QtCore.Qt.CheckStateRole) + ) if state == QtCore.Qt.Checked: items.append(self.itemText(idx)) return items diff --git a/openpype/tools/publisher/widgets/list_view_widgets.py b/openpype/tools/publisher/widgets/list_view_widgets.py index e6dad48b67d..172563d15cc 100644 --- a/openpype/tools/publisher/widgets/list_view_widgets.py +++ b/openpype/tools/publisher/widgets/list_view_widgets.py @@ -28,7 +28,7 @@ from openpype.style import get_objected_colors from openpype.widgets.nice_checkbox import NiceCheckbox -from openpype.tools.utils.lib import html_escape +from openpype.tools.utils.lib import html_escape, checkstate_int_to_enum from .widgets import AbstractInstanceView from ..constants import ( INSTANCE_ID_ROLE, @@ -272,6 +272,7 @@ def set_checkstate(self, state): state(QtCore.Qt.CheckState): Checkstate of checkbox. Have 3 variants Unchecked, Checked and PartiallyChecked. """ + if self.checkstate() == state: return self._ignore_state_change = True @@ -279,7 +280,8 @@ def set_checkstate(self, state): self._ignore_state_change = False def checkstate(self): - """CUrrent checkstate of "active" checkbox.""" + """Current checkstate of "active" checkbox.""" + return self.toggle_checkbox.checkState() def _on_checkbox_change(self, state): @@ -887,6 +889,7 @@ def _on_convertor_group_expand_request(self, _, expanded): self._instance_view.setExpanded(proxy_index, expanded) def _on_group_toggle_request(self, group_name, state): + state = checkstate_int_to_enum(state) if state == QtCore.Qt.PartiallyChecked: return diff --git a/openpype/tools/settings/settings/multiselection_combobox.py b/openpype/tools/settings/settings/multiselection_combobox.py index 4cc81ff56e7..896be3c06ce 100644 --- a/openpype/tools/settings/settings/multiselection_combobox.py +++ b/openpype/tools/settings/settings/multiselection_combobox.py @@ -1,4 +1,5 @@ from qtpy import QtCore, QtGui, QtWidgets +from openpype.tools.utils.lib import checkstate_int_to_enum class ComboItemDelegate(QtWidgets.QStyledItemDelegate): @@ -108,7 +109,9 @@ def _event_popup_shown(self, obj, event): return index_flags = current_index.flags() - state = current_index.data(QtCore.Qt.CheckStateRole) + state = checkstate_int_to_enum( + current_index.data(QtCore.Qt.CheckStateRole) + ) new_state = None if event.type() == QtCore.QEvent.MouseButtonRelease: @@ -311,7 +314,9 @@ def set_value(self, values): def value(self): items = list() for idx in range(self.count()): - state = self.itemData(idx, role=QtCore.Qt.CheckStateRole) + state = checkstate_int_to_enum( + self.itemData(idx, role=QtCore.Qt.CheckStateRole) + ) if state == QtCore.Qt.Checked: items.append( self.itemData(idx, role=QtCore.Qt.UserRole) @@ -321,7 +326,9 @@ def value(self): def checked_items_text(self): items = list() for idx in range(self.count()): - state = self.itemData(idx, role=QtCore.Qt.CheckStateRole) + state = checkstate_int_to_enum( + self.itemData(idx, role=QtCore.Qt.CheckStateRole) + ) if state == QtCore.Qt.Checked: items.append(self.itemText(idx)) return items diff --git a/openpype/tools/utils/constants.py b/openpype/tools/utils/constants.py index a3b1557f264..99f2602ee3b 100644 --- a/openpype/tools/utils/constants.py +++ b/openpype/tools/utils/constants.py @@ -1,6 +1,10 @@ from qtpy import QtCore +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) + DEFAULT_PROJECT_LABEL = "< Default >" PROJECT_NAME_ROLE = QtCore.Qt.UserRole + 101 PROJECT_IS_ACTIVE_ROLE = QtCore.Qt.UserRole + 102 diff --git a/openpype/tools/utils/lib.py b/openpype/tools/utils/lib.py index fb5530b100d..3f7293d4702 100644 --- a/openpype/tools/utils/lib.py +++ b/openpype/tools/utils/lib.py @@ -20,9 +20,33 @@ from openpype.settings import get_project_settings from openpype.pipeline import registered_host +from .constants import CHECKED_INT, UNCHECKED_INT + log = Logger.get_logger(__name__) +def checkstate_int_to_enum(state): + if not isinstance(state, int): + return state + if state == CHECKED_INT: + return QtCore.Qt.Checked + + if state == UNCHECKED_INT: + return QtCore.Qt.Unchecked + return QtCore.Qt.PartiallyChecked + + +def checkstate_enum_to_int(state): + if isinstance(state, int): + return state + if state == QtCore.Qt.Checked: + return 0 + if state == QtCore.Qt.PartiallyChecked: + return 1 + return 2 + + + def center_window(window): """Move window to center of it's screen.""" diff --git a/openpype/widgets/nice_checkbox.py b/openpype/widgets/nice_checkbox.py index d28a09a5dc2..651187a8ab0 100644 --- a/openpype/widgets/nice_checkbox.py +++ b/openpype/widgets/nice_checkbox.py @@ -166,7 +166,27 @@ def checkState(self): def isChecked(self): return self._checked + def _checkstate_int_to_enum(self, state): + if not isinstance(state, int): + return state + + if state == 2: + return QtCore.Qt.Checked + if state == 1: + return QtCore.Qt.PartiallyChecked + return QtCore.Qt.Unchecked + + def _checkstate_enum_to_int(self, state): + if isinstance(state, int): + return state + if state == QtCore.Qt.Checked: + return 2 + if state == QtCore.Qt.PartiallyChecked: + return 1 + return 0 + def setCheckState(self, state): + state = self._checkstate_int_to_enum(state) if self._checkstate == state: return @@ -176,7 +196,7 @@ def setCheckState(self, state): elif state == QtCore.Qt.Unchecked: self._checked = False - self.stateChanged.emit(self.checkState()) + self.stateChanged.emit(self._checkstate_enum_to_int(self.checkState())) if self._animation_timer.isActive(): self._animation_timer.stop() From 77df80aa8394c9e68573847ae896e5a2fe9dae38 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 6 Jan 2023 18:59:32 +0100 Subject: [PATCH 149/213] removed unused import --- openpype/tools/loader/widgets.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/tools/loader/widgets.py b/openpype/tools/loader/widgets.py index 6181fd63f01..0885a024a3d 100644 --- a/openpype/tools/loader/widgets.py +++ b/openpype/tools/loader/widgets.py @@ -53,7 +53,6 @@ REMOTE_PROVIDER_ROLE, LOCAL_AVAILABILITY_ROLE, REMOTE_AVAILABILITY_ROLE, - CHECKED_INT, ) from openpype.tools.assetlinks.widgets import SimpleLinkView From aaf9be080ad337622c2ee166bf89bb11251e1324 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 9 Jan 2023 11:57:44 +0100 Subject: [PATCH 150/213] keep Pyside6 only for macos --- pyproject.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index cdf0a5cd972..991c2ef7ed3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -113,16 +113,16 @@ build-backend = "poetry.core.masonry.api" # Poetry will support custom location (-t flag for pip) # https://pip.pypa.io/en/stable/cli/pip_install/#requirement-specifiers [openpype.qtbinding.windows] -package = "PySide6" -version = "6.4.1" +package = "PySide2" +version = "5.15.2" [openpype.qtbinding.darwin] package = "PySide6" version = "6.4.1" [openpype.qtbinding.linux] -package = "PySide6" -version = "6.4.1" +package = "PySide2" +version = "5.15.2" # TODO: we will need to handle different linux flavours here and # also different macos versions too. From e37bff0eb5389c99d7e1e9ba04853a398ce73d8b Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Mon, 9 Jan 2023 12:44:21 +0100 Subject: [PATCH 151/213] :recycle: remove unused method --- openpype/hosts/traypublisher/plugins/create/create_online.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/openpype/hosts/traypublisher/plugins/create/create_online.py b/openpype/hosts/traypublisher/plugins/create/create_online.py index 1a366bcff55..c751801340a 100644 --- a/openpype/hosts/traypublisher/plugins/create/create_online.py +++ b/openpype/hosts/traypublisher/plugins/create/create_online.py @@ -27,10 +27,6 @@ class OnlineCreator(TrayPublishCreator): extensions = [".mov", ".mp4", ".mxf", ".m4v", ".mpg", ".exr", ".dpx", ".tif", ".png", ".jpg"] - def __init__(self, *args, **kwargs): - super(OnlineCreator, self).__init__(*args, **kwargs) - self._original_path: Union[str, None] = None - def get_detail_description(self): return """# Create file retaining its original file name. From ba2646580916fca6224c7b72c1a43ee776bf3dd5 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Mon, 9 Jan 2023 12:45:05 +0100 Subject: [PATCH 152/213] :recycle: remove unused import --- openpype/hosts/traypublisher/plugins/create/create_online.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/traypublisher/plugins/create/create_online.py b/openpype/hosts/traypublisher/plugins/create/create_online.py index c751801340a..199fae6d2cd 100644 --- a/openpype/hosts/traypublisher/plugins/create/create_online.py +++ b/openpype/hosts/traypublisher/plugins/create/create_online.py @@ -14,7 +14,6 @@ CreatorError ) from openpype.hosts.traypublisher.api.plugin import TrayPublishCreator -from typing import Union class OnlineCreator(TrayPublishCreator): From 6cf511c76727e5509b538e98eb4f7c391c17c74c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 9 Jan 2023 15:14:05 +0100 Subject: [PATCH 153/213] EnumDef has explicit definitions of items --- openpype/lib/attribute_definitions.py | 72 ++++++++++++++++++++++----- 1 file changed, 59 insertions(+), 13 deletions(-) diff --git a/openpype/lib/attribute_definitions.py b/openpype/lib/attribute_definitions.py index 0df7b16e642..a688962b5a3 100644 --- a/openpype/lib/attribute_definitions.py +++ b/openpype/lib/attribute_definitions.py @@ -3,6 +3,7 @@ import collections import uuid import json +import copy from abc import ABCMeta, abstractmethod, abstractproperty import six @@ -433,38 +434,83 @@ def __init__(self, key, items, default=None, **kwargs): " defined values on initialization." ).format(self.__class__.__name__)) - items = collections.OrderedDict(items) - if default not in items: - for _key in items.keys(): - default = _key + items = self.prepare_enum_items(items) + item_values = [item["value"] for item in items] + if default not in item_values: + for value in item_values: + default = value break super(EnumDef, self).__init__(key, default=default, **kwargs) self.items = items + self._item_values = set(item_values) def __eq__(self, other): if not super(EnumDef, self).__eq__(other): return False - if set(self.items.keys()) != set(other.items.keys()): - return False - - for key, label in self.items.items(): - if other.items[key] != label: - return False - return True + return self.items == other.items def convert_value(self, value): - if value in self.items: + if value in self._item_values: return value return self.default def serialize(self): data = super(TextDef, self).serialize() - data["items"] = list(self.items) + data["items"] = copy.deepcopy(self.items) return data + @staticmethod + def prepare_enum_items(items): + """Convert items to unified structure. + + Output is a list where each item is dictionary with 'value' + and 'label'. + + ```python + # Example output + [ + {"label": "Option 1", "value": 1}, + {"label": "Option 2", "value": 2}, + {"label": "Option 3", "value": 3} + ] + ``` + + Args: + items (Union[Dict[str, Any], List[Any], List[Dict[str, Any]]): The + items to convert. + + Returns: + List[Dict[str, Any]]: Unified structure of items. + """ + + output = [] + if isinstance(items, dict): + for key, value in items.items(): + output.append({"label": key, "value": value}) + + elif isinstance(items, (tuple, list, set)): + for item in items: + if isinstance(item, dict): + # Test if value is available + item["value"] + if "label" not in item: + item["label"] = str(item["value"]) + elif isinstance(item, (list, tuple)) and len(item) == 2: + value, label = item + item = {"label": label, "value": value} + else: + item = {"label": str(item), "value": item} + output.append(item) + + else: + raise TypeError( + "Unknown type for enum items '{}'".format(type(items)) + ) + + return output class BoolDef(AbtractAttrDef): """Boolean representation. From 9772a6dcc54f783c9cafcba5c7ba0a6797ad7576 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 9 Jan 2023 15:14:33 +0100 Subject: [PATCH 154/213] enum widget is using attr def items correctly --- openpype/tools/attribute_defs/widgets.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/openpype/tools/attribute_defs/widgets.py b/openpype/tools/attribute_defs/widgets.py index 1ffb3d37994..353a424b001 100644 --- a/openpype/tools/attribute_defs/widgets.py +++ b/openpype/tools/attribute_defs/widgets.py @@ -401,9 +401,8 @@ def _ui_init(self): if self.attr_def.tooltip: input_widget.setToolTip(self.attr_def.tooltip) - items = self.attr_def.items - for key, label in items.items(): - input_widget.addItem(label, key) + for item in self.attr_def.items: + input_widget.addItem(item["label"], item["value"]) idx = input_widget.findData(self.attr_def.default) if idx >= 0: From 15fd19337a18f48184ec0581e0bfcbdea61c6632 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 9 Jan 2023 15:15:25 +0100 Subject: [PATCH 155/213] fix usages of 'EnumDef' in codebase --- .../plugins/create/create_editorial.py | 16 ++++++++-------- .../workfile/workfile_template_builder.py | 10 +++++----- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/openpype/hosts/traypublisher/plugins/create/create_editorial.py b/openpype/hosts/traypublisher/plugins/create/create_editorial.py index 28a115629ed..98a0b8320a7 100644 --- a/openpype/hosts/traypublisher/plugins/create/create_editorial.py +++ b/openpype/hosts/traypublisher/plugins/create/create_editorial.py @@ -33,14 +33,14 @@ CLIP_ATTR_DEFS = [ EnumDef( "fps", - items={ - "from_selection": "From selection", - 23.997: "23.976", - 24: "24", - 25: "25", - 29.97: "29.97", - 30: "30" - }, + items=[ + {"value": "from_selection", "label": "From selection"}, + {"value": 23.997, "label" : "23.976"}, + {"value": 24, "label": "24"}, + {"value": 25, "label": "25"}, + {"value": 29.97, "label": "29.97"}, + {"value": 30, "label": "30"} + ], label="FPS" ), NumberDef( diff --git a/openpype/pipeline/workfile/workfile_template_builder.py b/openpype/pipeline/workfile/workfile_template_builder.py index e3821bb4d7a..c6324543667 100644 --- a/openpype/pipeline/workfile/workfile_template_builder.py +++ b/openpype/pipeline/workfile/workfile_template_builder.py @@ -1147,11 +1147,11 @@ def get_load_plugin_options(self, options=None): loaders_by_name = self.builder.get_loaders_by_name() loader_items = [ - (loader_name, loader.label or loader_name) + {"value": loader_name, "label": loader.label or loader_name} for loader_name, loader in loaders_by_name.items() ] - loader_items = list(sorted(loader_items, key=lambda i: i[1])) + loader_items = list(sorted(loader_items, key=lambda i: i["label"])) options = options or {} return [ attribute_definitions.UISeparatorDef(), @@ -1163,9 +1163,9 @@ def get_load_plugin_options(self, options=None): label="Asset Builder Type", default=options.get("builder_type"), items=[ - ("context_asset", "Current asset"), - ("linked_asset", "Linked assets"), - ("all_assets", "All assets") + {"label": "Current asset", "value": "context_asset"}, + {"label": "Linked assets", "value": "linked_asset"}, + {"label": "All assets", "value": "all_assets"}, ], tooltip=( "Asset Builder Type\n" From ead387c5ff50b217da2603b45e4d9bb71767aaa6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 9 Jan 2023 15:24:29 +0100 Subject: [PATCH 156/213] added more validations --- openpype/lib/attribute_definitions.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/openpype/lib/attribute_definitions.py b/openpype/lib/attribute_definitions.py index a688962b5a3..e9597f41fb8 100644 --- a/openpype/lib/attribute_definitions.py +++ b/openpype/lib/attribute_definitions.py @@ -498,8 +498,18 @@ def prepare_enum_items(items): item["value"] if "label" not in item: item["label"] = str(item["value"]) - elif isinstance(item, (list, tuple)) and len(item) == 2: - value, label = item + elif isinstance(item, (list, tuple)): + if len(item) == 2: + value, label = item + elif len(item) == 1: + value = item[0] + label = str(value) + else: + raise ValueError(( + "Invalid items count {}." + " Expected 1 or 2. Value: {}" + ).format(len(item), str(item))) + item = {"label": label, "value": value} else: item = {"label": str(item), "value": item} From 15ee2a79bcd69cd00ec81c1f1a5d29349e5f710e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 9 Jan 2023 15:25:59 +0100 Subject: [PATCH 157/213] keep backwards compatibility --- openpype/lib/attribute_definitions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/lib/attribute_definitions.py b/openpype/lib/attribute_definitions.py index e9597f41fb8..f6c34cebf3e 100644 --- a/openpype/lib/attribute_definitions.py +++ b/openpype/lib/attribute_definitions.py @@ -488,8 +488,8 @@ def prepare_enum_items(items): output = [] if isinstance(items, dict): - for key, value in items.items(): - output.append({"label": key, "value": value}) + for value, label in items.items(): + output.append({"label": label, "value": value}) elif isinstance(items, (tuple, list, set)): for item in items: From 6cfab9bd312ede9aa08cece6fffa0bb0e03c74f3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 9 Jan 2023 17:12:46 +0100 Subject: [PATCH 158/213] OP-4706 - add vn tag for ffmpeg This tag ignores any graphic data from secondary audio input. This solves an issue where audio stream should be used from mp4/mov. Without it ffmpeg uses graphical data from this input too. --- openpype/plugins/publish/extract_review.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index 9310923a9f7..dcb43d7fa2a 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -1038,6 +1038,9 @@ def audio_args(self, instance, temp_data, duration_seconds): # Set audio duration audio_in_args.append("-to {:0.10f}".format(audio_duration)) + # Ignore video data from audio input + audio_in_args.append("-vn") + # Add audio input path audio_in_args.append("-i {}".format( path_to_subprocess_arg(audio["filename"]) From 2cf9b1ee6371d740c178451d1d65c6e7637df03c Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 10 Jan 2023 00:28:10 +0800 Subject: [PATCH 159/213] use current file for the scene rendering if the reference is imported --- openpype/modules/deadline/abstract_submit_deadline.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/modules/deadline/abstract_submit_deadline.py b/openpype/modules/deadline/abstract_submit_deadline.py index 155a647ff60..f6750bc0f2f 100644 --- a/openpype/modules/deadline/abstract_submit_deadline.py +++ b/openpype/modules/deadline/abstract_submit_deadline.py @@ -425,7 +425,11 @@ def process(self, instance): file_path = None if self.use_published: - file_path = self.from_published_scene() + if not self.import_reference: + file_path = self.from_published_scene() + else: + self.log.info("use the scene with imported reference for rendering") # noqa + file_path = context.data["currentFile"] # fallback if nothing was set if not file_path: From 9301bf03fac16278f13eb094bc9116e8916328f4 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 10 Jan 2023 00:30:18 +0800 Subject: [PATCH 160/213] use current file for the scene rendering if the reference is imported --- openpype/modules/deadline/abstract_submit_deadline.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/openpype/modules/deadline/abstract_submit_deadline.py b/openpype/modules/deadline/abstract_submit_deadline.py index f6750bc0f2f..648eb77007b 100644 --- a/openpype/modules/deadline/abstract_submit_deadline.py +++ b/openpype/modules/deadline/abstract_submit_deadline.py @@ -528,10 +528,7 @@ def from_published_scene(self, replace_in_path=True): # determine published path from Anatomy. template_data = workfile_instance.data.get("anatomyData") - if self.import_reference: - rep = workfile_instance.data["representations"][1] - else: - rep = workfile_instance.data["representations"][0] + rep = workfile_instance.data["representations"][0] template_data["representation"] = rep.get("name") template_data["ext"] = rep.get("ext") template_data["comment"] = None From 59f051d06596f63cbb861938b1a7c26d2d992e05 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Mon, 9 Jan 2023 18:30:10 +0100 Subject: [PATCH 161/213] :bug: fix instance collection --- openpype/hosts/unreal/plugins/publish/collect_instances.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/unreal/plugins/publish/collect_instances.py b/openpype/hosts/unreal/plugins/publish/collect_instances.py index 1f25cbde7d6..6696eacb6a6 100644 --- a/openpype/hosts/unreal/plugins/publish/collect_instances.py +++ b/openpype/hosts/unreal/plugins/publish/collect_instances.py @@ -26,8 +26,8 @@ def process(self, context): ar = unreal.AssetRegistryHelpers.get_asset_registry() class_name = ["/Script/OpenPype", - "AssetContainer"] if UNREAL_VERSION.major == 5 and \ - UNREAL_VERSION.minor > 0 else "OpenPypePublishInstance" # noqa + "OpenPypePublishInstance"] if UNREAL_VERSION.major == 5 and \ + UNREAL_VERSION.minor > 0 else "OpenPypePublishInstance" # noqa instance_containers = ar.get_assets_by_class(class_name, True) for container_data in instance_containers: From ea65afdbc176e3daf4ffcf1fb95939d39ed1e0b5 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Mon, 9 Jan 2023 18:36:14 +0100 Subject: [PATCH 162/213] :rotating_light: hound fix --- .../hosts/unreal/plugins/publish/collect_instances.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/unreal/plugins/publish/collect_instances.py b/openpype/hosts/unreal/plugins/publish/collect_instances.py index 6696eacb6a6..27b711cad6e 100644 --- a/openpype/hosts/unreal/plugins/publish/collect_instances.py +++ b/openpype/hosts/unreal/plugins/publish/collect_instances.py @@ -25,9 +25,13 @@ class CollectInstances(pyblish.api.ContextPlugin): def process(self, context): ar = unreal.AssetRegistryHelpers.get_asset_registry() - class_name = ["/Script/OpenPype", - "OpenPypePublishInstance"] if UNREAL_VERSION.major == 5 and \ - UNREAL_VERSION.minor > 0 else "OpenPypePublishInstance" # noqa + class_name = [ + "/Script/OpenPype", + "OpenPypePublishInstance" + ] if ( + UNREAL_VERSION.major == 5 + and UNREAL_VERSION.minor > 0 + ) else "OpenPypePublishInstance" # noqa instance_containers = ar.get_assets_by_class(class_name, True) for container_data in instance_containers: From 51a529a1d401faaa00fcde344c4274b34ec08c3a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 9 Jan 2023 18:54:02 +0100 Subject: [PATCH 163/213] fix formatting --- openpype/hosts/traypublisher/plugins/create/create_editorial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/traypublisher/plugins/create/create_editorial.py b/openpype/hosts/traypublisher/plugins/create/create_editorial.py index 98a0b8320a7..2a9c92bca74 100644 --- a/openpype/hosts/traypublisher/plugins/create/create_editorial.py +++ b/openpype/hosts/traypublisher/plugins/create/create_editorial.py @@ -35,7 +35,7 @@ "fps", items=[ {"value": "from_selection", "label": "From selection"}, - {"value": 23.997, "label" : "23.976"}, + {"value": 23.997, "label": "23.976"}, {"value": 24, "label": "24"}, {"value": 25, "label": "25"}, {"value": 29.97, "label": "29.97"}, From b539ba029990c25318cc98f5776de75eda7ccaf4 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 9 Jan 2023 22:16:16 +0100 Subject: [PATCH 164/213] traypublisher: multiple edl workflow with multilayer otio timeline --- openpype/hosts/traypublisher/api/editorial.py | 5 -- .../plugins/create/create_editorial.py | 69 +++++++------------ .../plugins/publish/collect_shot_instances.py | 5 -- 3 files changed, 24 insertions(+), 55 deletions(-) diff --git a/openpype/hosts/traypublisher/api/editorial.py b/openpype/hosts/traypublisher/api/editorial.py index 7c392ef5085..293db542a98 100644 --- a/openpype/hosts/traypublisher/api/editorial.py +++ b/openpype/hosts/traypublisher/api/editorial.py @@ -171,7 +171,6 @@ def _create_parents_from_settings(self, parents, data): _index == 0 and parents[-1]["entity_name"] == parent_name ): - self.log.debug(f" skipping : {parent_name}") continue # in case first parent is project then start parents from start @@ -179,7 +178,6 @@ def _create_parents_from_settings(self, parents, data): _index == 0 and parent_token_type == "Project" ): - self.log.debug("rebuilding parents from scratch") project_parent = parents[0] parents = [project_parent] continue @@ -189,8 +187,6 @@ def _create_parents_from_settings(self, parents, data): "entity_name": parent_name }) - self.log.debug(f"__ parents: {parents}") - return parents def _create_hierarchy_path(self, parents): @@ -297,7 +293,6 @@ def generate_data(self, clip_name, source_data): Returns: (str, dict): shot name and hierarchy data """ - self.log.info(f"_ source_data: {source_data}") tasks = {} asset_doc = source_data["selected_asset_doc"] diff --git a/openpype/hosts/traypublisher/plugins/create/create_editorial.py b/openpype/hosts/traypublisher/plugins/create/create_editorial.py index 614cf9dbcab..d1086a1ff3f 100644 --- a/openpype/hosts/traypublisher/plugins/create/create_editorial.py +++ b/openpype/hosts/traypublisher/plugins/create/create_editorial.py @@ -1,6 +1,5 @@ import os from copy import deepcopy -from pprint import pformat import opentimelineio as otio from openpype.client import ( get_asset_by_name, @@ -13,9 +12,7 @@ from openpype.hosts.traypublisher.api.editorial import ( ShotMetadataSolver ) - from openpype.pipeline import CreatedInstance - from openpype.lib import ( get_ffprobe_data, convert_ffprobe_fps_value, @@ -70,14 +67,12 @@ class EditorialClipInstanceCreatorBase(HiddenTrayPublishCreator): host_name = "traypublisher" def create(self, instance_data, source_data=None): - self.log.info(f"instance_data: {instance_data}") subset_name = instance_data["subset"] # Create new instance new_instance = CreatedInstance( self.family, subset_name, instance_data, self ) - self.log.info(f"instance_data: {pformat(new_instance.data)}") self._store_new_instance(new_instance) @@ -223,8 +218,6 @@ def create(self, subset_name, instance_data, pre_create_data): asset_name = instance_data["asset"] asset_doc = get_asset_by_name(self.project_name, asset_name) - self.log.info(pre_create_data["fps"]) - if pre_create_data["fps"] == "from_selection": # get asset doc data attributes fps = asset_doc["data"]["fps"] @@ -243,7 +236,8 @@ def create(self, subset_name, instance_data, pre_create_data): sequence_path_data, multi=True) media_path = self._get_path_from_file_data(media_path_data) - for index, seq_path in enumerate(sequence_paths): + first_otio_timeline = None + for seq_path in sequence_paths: # get otio timeline otio_timeline = self._create_otio_timeline( seq_path, fps) @@ -260,22 +254,22 @@ def create(self, subset_name, instance_data, pre_create_data): otio_timeline, media_path, clip_instance_properties, - family_presets=allowed_family_presets - + allowed_family_presets, + os.path.basename(seq_path), + first_otio_timeline ) - # alter subset name if multiple files - subset_name_edit = subset_name - if len(sequence_paths) > 1: - subset_name_edit = subset_name + str(index) - - # create otio editorial instance - self._create_otio_instance( - subset_name_edit, - instance_data, - seq_path, media_path, - otio_timeline - ) + if not first_otio_timeline: + # assing otio timeline for multi file to layer + first_otio_timeline = otio_timeline + + # create otio editorial instance + self._create_otio_instance( + subset_name, + instance_data, + seq_path, media_path, + first_otio_timeline + ) def _create_otio_instance( self, @@ -325,7 +319,6 @@ def _create_otio_timeline(self, sequence_path, fps): kwargs["rate"] = fps kwargs["ignore_timecode_mismatch"] = True - self.log.info(f"kwargs: {kwargs}") return otio.adapters.read_from_file(sequence_path, **kwargs) def _get_path_from_file_data(self, file_path_data, multi=False): @@ -343,15 +336,12 @@ def _get_path_from_file_data(self, file_path_data, multi=False): """ return_path_list = [] - self.log.debug(f"type: {type(file_path_data)}") - self.log.debug(f"file_path_data: {file_path_data}") if isinstance(file_path_data, list): return_path_list = [ os.path.join(f["directory"], f["filenames"][0]) for f in file_path_data ] - self.log.debug(f"return_path_list: {return_path_list}") if not return_path_list: raise FileExistsError( @@ -364,7 +354,9 @@ def _get_clip_instances( otio_timeline, media_path, instance_data, - family_presets + family_presets, + sequence_file_name, + first_otio_timeline=None ): """Helping function fro creating clip instance @@ -384,17 +376,15 @@ def _get_clip_instances( media_data = self._get_media_source_metadata(media_path) for track in tracks: - self.log.debug(f"track.name: {track.name}") + track.name = f"{sequence_file_name} - {otio_timeline.name}" try: track_start_frame = ( abs(track.source_range.start_time.value) ) - self.log.debug(f"track_start_frame: {track_start_frame}") track_start_frame -= self.timeline_frame_start except AttributeError: track_start_frame = 0 - self.log.debug(f"track_start_frame: {track_start_frame}") for clip in track.each_child(): if not self._validate_clip_for_processing(clip): @@ -416,10 +406,6 @@ def _get_clip_instances( "instance_label": None, "instance_id": None } - self.log.info(( - "Creating subsets from presets: \n" - f"{pformat(family_presets)}" - )) for _fpreset in family_presets: # exclude audio family if no audio stream @@ -435,7 +421,10 @@ def _get_clip_instances( deepcopy(base_instance_data), parenting_data ) - self.log.debug(f"{pformat(dict(instance.data))}") + + # add track to first otioTimeline if it is in input args + if first_otio_timeline: + first_otio_timeline.tracks.append(deepcopy(track)) def _restore_otio_source_range(self, otio_clip): """Infusing source range. @@ -476,7 +465,6 @@ def _create_otio_reference( target_url=media_path, available_range=available_range ) - otio_clip.media_reference = media_reference def _get_media_source_metadata(self, path): @@ -497,7 +485,6 @@ def _get_media_source_metadata(self, path): media_data = get_ffprobe_data( path, self.log ) - self.log.debug(f"__ media_data: {pformat(media_data)}") # get video stream data video_stream = media_data["streams"][0] @@ -605,9 +592,6 @@ def _make_subset_naming( # get variant name from preset or from inharitance _variant_name = preset.get("variant") or variant_name - self.log.debug(f"__ family: {family}") - self.log.debug(f"__ preset: {preset}") - # subset name subset_name = "{}{}".format( family, _variant_name.capitalize() @@ -738,17 +722,13 @@ def _get_timing_data( clip_in += track_start_frame clip_out = otio_clip.range_in_parent().end_time_inclusive().value clip_out += track_start_frame - self.log.info(f"clip_in: {clip_in} | clip_out: {clip_out}") # add offset in case there is any - self.log.debug(f"__ timeline_offset: {timeline_offset}") if timeline_offset: clip_in += timeline_offset clip_out += timeline_offset clip_duration = otio_clip.duration().value - self.log.info(f"clip duration: {clip_duration}") - source_in = otio_clip.trimmed_range().start_time.value source_out = source_in + clip_duration @@ -778,7 +758,6 @@ def _get_allowed_family_presets(self, pre_create_data): Returns: list: lit of dict with preset items """ - self.log.debug(f"__ pre_create_data: {pre_create_data}") return [ {"family": "shot"}, *[ diff --git a/openpype/hosts/traypublisher/plugins/publish/collect_shot_instances.py b/openpype/hosts/traypublisher/plugins/publish/collect_shot_instances.py index 716f73022e4..78c1f14e4ea 100644 --- a/openpype/hosts/traypublisher/plugins/publish/collect_shot_instances.py +++ b/openpype/hosts/traypublisher/plugins/publish/collect_shot_instances.py @@ -33,8 +33,6 @@ class CollectShotInstance(pyblish.api.InstancePlugin): ] def process(self, instance): - self.log.debug(pformat(instance.data)) - creator_identifier = instance.data["creator_identifier"] if "editorial" not in creator_identifier: return @@ -82,7 +80,6 @@ def _get_otio_clip(self, instance): ] otio_clip = clips.pop() - self.log.debug(f"__ otioclip.parent: {otio_clip.parent}") return otio_clip @@ -172,7 +169,6 @@ def _solve_hierarchy_context(self, instance): } parents = instance.data.get('parents', []) - self.log.debug(f"parents: {pformat(parents)}") actual = {name: in_info} @@ -190,7 +186,6 @@ def _solve_hierarchy_context(self, instance): # adding hierarchy context to instance context.data["hierarchyContext"] = final_context - self.log.debug(pformat(final_context)) def _update_dict(self, ex_dict, new_dict): """ Recursion function From fb886125393d93b1216ece77e987823998401afa Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 10 Jan 2023 10:44:52 +0100 Subject: [PATCH 165/213] added 'install' method with docstring to 'HostBase' --- openpype/host/host.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/openpype/host/host.py b/openpype/host/host.py index 99f7868727c..94416bb39a7 100644 --- a/openpype/host/host.py +++ b/openpype/host/host.py @@ -76,6 +76,18 @@ def __init__(self): pass + def install(self): + """Install host specific functionality. + + This is where should be added menu with tools, registered callbacks + and other host integration initialization. + + It is called automatically when 'openpype.pipeline.install_host' is + triggered. + """ + + pass + @property def log(self): if self._log is None: From 4d0f954785ed03012247263805bff0715ec8d536 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Tue, 10 Jan 2023 16:29:51 +0100 Subject: [PATCH 166/213] :bug: fix missing maintained_selection call --- openpype/hosts/unreal/api/__init__.py | 2 ++ openpype/hosts/unreal/api/pipeline.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/openpype/hosts/unreal/api/__init__.py b/openpype/hosts/unreal/api/__init__.py index 3f96d8ac6f4..ca9db259e6e 100644 --- a/openpype/hosts/unreal/api/__init__.py +++ b/openpype/hosts/unreal/api/__init__.py @@ -18,6 +18,7 @@ show_tools_popup, instantiate, UnrealHost, + maintained_selection ) __all__ = [ @@ -36,4 +37,5 @@ "show_tools_popup", "instantiate", "UnrealHost", + "maintained_selection" ] diff --git a/openpype/hosts/unreal/api/pipeline.py b/openpype/hosts/unreal/api/pipeline.py index ca5a42cd828..2081c8fd132 100644 --- a/openpype/hosts/unreal/api/pipeline.py +++ b/openpype/hosts/unreal/api/pipeline.py @@ -2,6 +2,7 @@ import os import logging from typing import List +from contextlib import contextmanager import semver import pyblish.api @@ -447,3 +448,16 @@ def get_subsequences(sequence: unreal.LevelSequence): if subscene_track is not None and subscene_track.get_sections(): return subscene_track.get_sections() return [] + + +@contextmanager +def maintained_selection(): + """Stub to be either implemented or replaced. + + This is needed for old publisher implementation, but + it is not supported (yet) in UE. + """ + try: + yield + finally: + pass From 48d43f7fea8817af9bfbf906be451cff2d562b5a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 10 Jan 2023 18:16:31 +0100 Subject: [PATCH 167/213] OP-4679 - check comment on context Check for context must be preserved until old Pyblish is completely eradicated as comment could be filled after collection phase, therefore not caught by collector. --- .../modules/ftrack/plugins/publish/integrate_ftrack_note.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py index 6776509ddae..2aecd975919 100644 --- a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py +++ b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py @@ -45,7 +45,8 @@ def process(self, instance): host_name = context.data["hostName"] app_name = context.data["appName"] app_label = context.data["appLabel"] - comment = instance.data["comment"] + # context comment is fallback until old Pyblish is removed + comment = instance.data["comment"] or context.data.get("comment") if not comment: self.log.info("Comment is not set.") else: From 59e2bd2bdf0c738aca7fcfdd76473753c16c97b0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 10 Jan 2023 18:17:15 +0100 Subject: [PATCH 168/213] OP-4679 - updated logging Printing what is actually missing is more helpful than only full data. --- .../modules/ftrack/plugins/publish/integrate_ftrack_note.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py index 2aecd975919..994df304ad1 100644 --- a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py +++ b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py @@ -130,8 +130,8 @@ def process(self, instance): if not note_text.solved: self.log.warning(( "Note template require more keys then can be provided." - "\nTemplate: {}\nData: {}" - ).format(template, format_data)) + "\nTemplate: {}\nMissing values for keys:{}\nData: {}" + ).format(template, note_text.missing_keys, format_data)) continue if not note_text: From e5b3520d6c0bebc40cb5be8bd23476002b6d14db Mon Sep 17 00:00:00 2001 From: OpenPype Date: Wed, 11 Jan 2023 03:28:47 +0000 Subject: [PATCH 169/213] [Automated] Bump version --- openpype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/version.py b/openpype/version.py index 732682dd60a..67cce01ec07 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.14.10-nightly.7" +__version__ = "3.14.10-nightly.8" From c0cabcc6ecd4a6bf5dac2188c93f090f912c3c72 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 11 Jan 2023 12:46:56 +0100 Subject: [PATCH 170/213] changelog for release 3.14.10 --- CHANGELOG.md | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++++ HISTORY.md | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9820dec456..530622f4915 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,58 @@ # Changelog +## [3.14.10](https://github.com/ynput/OpenPype/tree/HEAD) + +[Full Changelog](https://github.com/ynput/OpenPype/compare/3.14.9...HEAD) + +**🆕 New features** + +- Global | Nuke: Creator placeholders in workfile template builder [\#4266](https://github.com/ynput/OpenPype/pull/4266) +- Slack: Added dynamic message [\#4265](https://github.com/ynput/OpenPype/pull/4265) +- Blender: Workfile Loader [\#4234](https://github.com/ynput/OpenPype/pull/4234) +- Unreal: Publishing and Loading for UAssets [\#4198](https://github.com/ynput/OpenPype/pull/4198) +- Publish: register publishes without copying them [\#4157](https://github.com/ynput/OpenPype/pull/4157) + +**🚀 Enhancements** + +- General: Added install method with docstring to HostBase [\#4298](https://github.com/ynput/OpenPype/pull/4298) +- Traypublisher: simple editorial multiple edl [\#4248](https://github.com/ynput/OpenPype/pull/4248) +- General: Extend 'IPluginPaths' to have more available methods [\#4214](https://github.com/ynput/OpenPype/pull/4214) +- Refactorization of folder coloring [\#4211](https://github.com/ynput/OpenPype/pull/4211) +- Flame - loading multilayer with controlled layer names [\#4204](https://github.com/ynput/OpenPype/pull/4204) + +**🐛 Bug fixes** + +- Unreal: fix missing `maintained_selection` call [\#4300](https://github.com/ynput/OpenPype/pull/4300) +- Ftrack: Fix receive of host ip on MacOs [\#4288](https://github.com/ynput/OpenPype/pull/4288) +- SiteSync: sftp connection failing when shouldnt be tested [\#4278](https://github.com/ynput/OpenPype/pull/4278) +- Deadline: fix default value for passing mongo url [\#4275](https://github.com/ynput/OpenPype/pull/4275) +- Scene Manager: Fix variable name [\#4268](https://github.com/ynput/OpenPype/pull/4268) +- Slack: notification fails because of missing published path [\#4264](https://github.com/ynput/OpenPype/pull/4264) +- hiero: creator gui with min max [\#4257](https://github.com/ynput/OpenPype/pull/4257) +- NiceCheckbox: Fix checker positioning in Python 2 [\#4253](https://github.com/ynput/OpenPype/pull/4253) +- Publisher: Fix 'CreatorType' not equal for Python 2 DCCs [\#4249](https://github.com/ynput/OpenPype/pull/4249) +- Deadline: fix dependencies [\#4242](https://github.com/ynput/OpenPype/pull/4242) +- Houdini: hotfix instance data access [\#4236](https://github.com/ynput/OpenPype/pull/4236) +- bugfix/image plane load error [\#4222](https://github.com/ynput/OpenPype/pull/4222) +- Hiero: thumbnail from multilayer exr [\#4209](https://github.com/ynput/OpenPype/pull/4209) + +**🔀 Refactored code** + +- Resolve: Use qtpy in Resolve [\#4254](https://github.com/ynput/OpenPype/pull/4254) +- Houdini: Use qtpy in Houdini [\#4252](https://github.com/ynput/OpenPype/pull/4252) +- Max: Use qtpy in Max [\#4251](https://github.com/ynput/OpenPype/pull/4251) +- Maya: Use qtpy in Maya [\#4250](https://github.com/ynput/OpenPype/pull/4250) +- Hiero: Use qtpy in Hiero [\#4240](https://github.com/ynput/OpenPype/pull/4240) +- Nuke: Use qtpy in Nuke [\#4239](https://github.com/ynput/OpenPype/pull/4239) +- Flame: Use qtpy in flame [\#4238](https://github.com/ynput/OpenPype/pull/4238) +- General: Legacy io not used in global plugins [\#4134](https://github.com/ynput/OpenPype/pull/4134) + +**Merged pull requests:** + +- Bump json5 from 1.0.1 to 1.0.2 in /website [\#4292](https://github.com/ynput/OpenPype/pull/4292) +- Maya: Fix validate frame range repair + fix create render with deadline disabled [\#4279](https://github.com/ynput/OpenPype/pull/4279) + + ## [3.14.9](https://github.com/pypeclub/OpenPype/tree/3.14.9) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.14.8...3.14.9) diff --git a/HISTORY.md b/HISTORY.md index f24e95b2e1f..88b50c67dd2 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,57 @@ # Changelog +## [3.14.10](https://github.com/ynput/OpenPype/tree/HEAD) + +[Full Changelog](https://github.com/ynput/OpenPype/compare/3.14.9...3.14.10) + +**🆕 New features** + +- Global | Nuke: Creator placeholders in workfile template builder [\#4266](https://github.com/ynput/OpenPype/pull/4266) +- Slack: Added dynamic message [\#4265](https://github.com/ynput/OpenPype/pull/4265) +- Blender: Workfile Loader [\#4234](https://github.com/ynput/OpenPype/pull/4234) +- Unreal: Publishing and Loading for UAssets [\#4198](https://github.com/ynput/OpenPype/pull/4198) +- Publish: register publishes without copying them [\#4157](https://github.com/ynput/OpenPype/pull/4157) + +**🚀 Enhancements** + +- General: Added install method with docstring to HostBase [\#4298](https://github.com/ynput/OpenPype/pull/4298) +- Traypublisher: simple editorial multiple edl [\#4248](https://github.com/ynput/OpenPype/pull/4248) +- General: Extend 'IPluginPaths' to have more available methods [\#4214](https://github.com/ynput/OpenPype/pull/4214) +- Refactorization of folder coloring [\#4211](https://github.com/ynput/OpenPype/pull/4211) +- Flame - loading multilayer with controlled layer names [\#4204](https://github.com/ynput/OpenPype/pull/4204) + +**🐛 Bug fixes** + +- Unreal: fix missing `maintained_selection` call [\#4300](https://github.com/ynput/OpenPype/pull/4300) +- Ftrack: Fix receive of host ip on MacOs [\#4288](https://github.com/ynput/OpenPype/pull/4288) +- SiteSync: sftp connection failing when shouldnt be tested [\#4278](https://github.com/ynput/OpenPype/pull/4278) +- Deadline: fix default value for passing mongo url [\#4275](https://github.com/ynput/OpenPype/pull/4275) +- Scene Manager: Fix variable name [\#4268](https://github.com/ynput/OpenPype/pull/4268) +- Slack: notification fails because of missing published path [\#4264](https://github.com/ynput/OpenPype/pull/4264) +- hiero: creator gui with min max [\#4257](https://github.com/ynput/OpenPype/pull/4257) +- NiceCheckbox: Fix checker positioning in Python 2 [\#4253](https://github.com/ynput/OpenPype/pull/4253) +- Publisher: Fix 'CreatorType' not equal for Python 2 DCCs [\#4249](https://github.com/ynput/OpenPype/pull/4249) +- Deadline: fix dependencies [\#4242](https://github.com/ynput/OpenPype/pull/4242) +- Houdini: hotfix instance data access [\#4236](https://github.com/ynput/OpenPype/pull/4236) +- bugfix/image plane load error [\#4222](https://github.com/ynput/OpenPype/pull/4222) +- Hiero: thumbnail from multilayer exr [\#4209](https://github.com/ynput/OpenPype/pull/4209) + +**🔀 Refactored code** + +- Resolve: Use qtpy in Resolve [\#4254](https://github.com/ynput/OpenPype/pull/4254) +- Houdini: Use qtpy in Houdini [\#4252](https://github.com/ynput/OpenPype/pull/4252) +- Max: Use qtpy in Max [\#4251](https://github.com/ynput/OpenPype/pull/4251) +- Maya: Use qtpy in Maya [\#4250](https://github.com/ynput/OpenPype/pull/4250) +- Hiero: Use qtpy in Hiero [\#4240](https://github.com/ynput/OpenPype/pull/4240) +- Nuke: Use qtpy in Nuke [\#4239](https://github.com/ynput/OpenPype/pull/4239) +- Flame: Use qtpy in flame [\#4238](https://github.com/ynput/OpenPype/pull/4238) +- General: Legacy io not used in global plugins [\#4134](https://github.com/ynput/OpenPype/pull/4134) + +**Merged pull requests:** + +- Bump json5 from 1.0.1 to 1.0.2 in /website [\#4292](https://github.com/ynput/OpenPype/pull/4292) +- Maya: Fix validate frame range repair + fix create render with deadline disabled [\#4279](https://github.com/ynput/OpenPype/pull/4279) + ## [3.14.9](https://github.com/pypeclub/OpenPype/tree/3.14.9) From 56813ee8ddf70c14acad8c19b8b4d2834281eb15 Mon Sep 17 00:00:00 2001 From: OpenPype Date: Wed, 11 Jan 2023 12:18:43 +0000 Subject: [PATCH 171/213] [Automated] Bump version --- openpype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/version.py b/openpype/version.py index 67cce01ec07..c050cdafda5 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.14.10-nightly.8" +__version__ = "3.14.10-nightly.9" From 4720208cfd6ec587d532a8c77c62de8009de85fe Mon Sep 17 00:00:00 2001 From: OpenPype Date: Wed, 11 Jan 2023 12:23:22 +0000 Subject: [PATCH 172/213] [Automated] Release --- openpype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/version.py b/openpype/version.py index c050cdafda5..129f7f684b0 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.14.10-nightly.9" +__version__ = "3.14.10" From 12e29805249bdb31a77c8c9249e2f96c0df56080 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 11 Jan 2023 15:03:55 +0100 Subject: [PATCH 173/213] OP-4679 - push adding comment to instance to Extractor phase Pyblish allows modifying comment after collect phase, eg. collector wouldn't collect it. Should be pushed back to Collect phase after Pyblish is eradicated. --- openpype/plugins/publish/collect_comment.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/plugins/publish/collect_comment.py b/openpype/plugins/publish/collect_comment.py index 12579cd9572..5be04731ace 100644 --- a/openpype/plugins/publish/collect_comment.py +++ b/openpype/plugins/publish/collect_comment.py @@ -73,7 +73,9 @@ class CollectComment( """ label = "Collect Instance Comment" - order = pyblish.api.CollectorOrder + 0.49 + # TODO change to CollectorOrder after Pyblish is purged + # Pyblish allows modifying comment after collect phase + order = pyblish.api.ExtractorOrder - 0.49 def process(self, context): context_comment = self.cleanup_comment(context.data.get("comment")) From f854de46295da4d96c007335421647f78b2e6b36 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 11 Jan 2023 15:06:03 +0100 Subject: [PATCH 174/213] OP-4679 - revert back fallback Availability of comment on instance has been resolved by bumping up order of CollectComment. --- .../modules/ftrack/plugins/publish/integrate_ftrack_note.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py index 994df304ad1..6e82897d891 100644 --- a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py +++ b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py @@ -45,8 +45,7 @@ def process(self, instance): host_name = context.data["hostName"] app_name = context.data["appName"] app_label = context.data["appLabel"] - # context comment is fallback until old Pyblish is removed - comment = instance.data["comment"] or context.data.get("comment") + comment = instance.data["comment"] if not comment: self.log.info("Comment is not set.") else: From 7a32fa0b054b49a41e65713f3827abea0fddb63e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 12 Jan 2023 00:41:11 +0100 Subject: [PATCH 175/213] set enabled attribute of LoaderPlugin --- openpype/pipeline/load/plugins.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/pipeline/load/plugins.py b/openpype/pipeline/load/plugins.py index 8cba8d82179..b5e55834db6 100644 --- a/openpype/pipeline/load/plugins.py +++ b/openpype/pipeline/load/plugins.py @@ -30,6 +30,7 @@ class LoaderPlugin(list): representations = list() order = 0 is_multiple_contexts_compatible = False + enabled = True options = [] @@ -73,11 +74,10 @@ def apply_settings(cls, project_settings, system_settings): print(">>> We have preset for {}".format(plugin_name)) for option, value in plugin_settings.items(): if option == "enabled" and value is False: - setattr(cls, "active", False) print(" - is disabled by preset") else: - setattr(cls, option, value) print(" - setting `{}`: `{}`".format(option, value)) + setattr(cls, option, value) @classmethod def get_representations(cls): From 5f4cad685176e34160a833cb63486c08148e013e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 12 Jan 2023 00:41:50 +0100 Subject: [PATCH 176/213] check if loader is enabled in loader tool --- openpype/tools/loader/widgets.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openpype/tools/loader/widgets.py b/openpype/tools/loader/widgets.py index 826c7110da8..b1619ca02b1 100644 --- a/openpype/tools/loader/widgets.py +++ b/openpype/tools/loader/widgets.py @@ -451,6 +451,8 @@ def on_context_menu(self, point): repre_loaders = [] subset_loaders = [] for loader in available_loaders: + if not loader.enabled: + continue # Skip if its a SubsetLoader. if issubclass(loader, SubsetLoaderPlugin): subset_loaders.append(loader) @@ -1352,6 +1354,8 @@ def on_context_menu(self, point): filtered_loaders = [] for loader in available_loaders: + if not loader.enabled: + continue # Skip subset loaders if issubclass(loader, SubsetLoaderPlugin): continue From 7d01590b6476786914cbcc973b4df329b6ed70d4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 12 Jan 2023 11:38:09 +0100 Subject: [PATCH 177/213] creators can be disabled too --- openpype/pipeline/create/legacy_create.py | 4 ++-- openpype/pipeline/workfile/workfile_template_builder.py | 2 ++ openpype/tools/creator/model.py | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/openpype/pipeline/create/legacy_create.py b/openpype/pipeline/create/legacy_create.py index 82e5de7a8f0..7380e9f9c7b 100644 --- a/openpype/pipeline/create/legacy_create.py +++ b/openpype/pipeline/create/legacy_create.py @@ -20,6 +20,7 @@ class LegacyCreator(object): family = None defaults = None maintain_selection = True + enabled = True dynamic_subset_keys = [] @@ -76,11 +77,10 @@ def apply_settings(cls, project_settings, system_settings): print(">>> We have preset for {}".format(plugin_name)) for option, value in plugin_settings.items(): if option == "enabled" and value is False: - setattr(cls, "active", False) print(" - is disabled by preset") else: - setattr(cls, option, value) print(" - setting `{}`: `{}`".format(option, value)) + setattr(cls, option, value) def process(self): pass diff --git a/openpype/pipeline/workfile/workfile_template_builder.py b/openpype/pipeline/workfile/workfile_template_builder.py index e3821bb4d7a..fe9d5dd21b6 100644 --- a/openpype/pipeline/workfile/workfile_template_builder.py +++ b/openpype/pipeline/workfile/workfile_template_builder.py @@ -239,6 +239,8 @@ def get_creators_by_name(self): if self._creators_by_name is None: self._creators_by_name = {} for creator in discover_legacy_creator_plugins(): + if not creator.enabled: + continue creator_name = creator.__name__ if creator_name in self._creators_by_name: raise KeyError( diff --git a/openpype/tools/creator/model.py b/openpype/tools/creator/model.py index 307993103bf..e0570f2bd33 100644 --- a/openpype/tools/creator/model.py +++ b/openpype/tools/creator/model.py @@ -23,6 +23,8 @@ def reset(self): items = [] creators = discover_legacy_creator_plugins() for creator in creators: + if not creator.enabled: + continue item_id = str(uuid.uuid4()) self._creators_by_id[item_id] = creator From 72ff721bd2896261060a967c71569b5a93f79612 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 12 Jan 2023 11:41:03 +0100 Subject: [PATCH 178/213] skip disabled loaders --- openpype/pipeline/workfile/build_workfile.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/pipeline/workfile/build_workfile.py b/openpype/pipeline/workfile/build_workfile.py index 87b9df158f7..26b17fa1515 100644 --- a/openpype/pipeline/workfile/build_workfile.py +++ b/openpype/pipeline/workfile/build_workfile.py @@ -120,6 +120,8 @@ def build_workfile(self): # Prepare available loaders loaders_by_name = {} for loader in discover_loader_plugins(): + if not loader.enabled: + continue loader_name = loader.__name__ if loader_name in loaders_by_name: raise KeyError( From 3752c8dfa6e1c3b4cd43216fcb784502ba7cb965 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Fri, 13 Jan 2023 09:45:32 +0000 Subject: [PATCH 179/213] Fix run_documentation.ps1 --- tools/run_documentation.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/run_documentation.ps1 b/tools/run_documentation.ps1 index a3e3a9b8dd4..d5459f0d2c3 100644 --- a/tools/run_documentation.ps1 +++ b/tools/run_documentation.ps1 @@ -43,4 +43,5 @@ $openpype_root = (Get-Item $script_dir).parent.FullName Set-Location $openpype_root/website -& yarn run start +& yarn install +& yarn start From 1bf9be8c0df08d74d546fe3a02e31d43004d2650 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 13 Jan 2023 14:36:22 +0100 Subject: [PATCH 180/213] Fix: (un)pause_representation doesn't work programmatically. fix #4311 --- openpype/modules/sync_server/sync_server_module.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/modules/sync_server/sync_server_module.py b/openpype/modules/sync_server/sync_server_module.py index 9cd84680f62..68a5346e201 100644 --- a/openpype/modules/sync_server/sync_server_module.py +++ b/openpype/modules/sync_server/sync_server_module.py @@ -1484,7 +1484,8 @@ def get_sync_representations(self, project_name, active_site, remote_site): "$elemMatch": { "name": {"$in": [remote_site]}, "created_dt": {"$exists": False}, - "tries": {"$in": retries_arr} + "tries": {"$in": retries_arr}, + "paused": {"$exists": False} } } }]}, @@ -1494,7 +1495,8 @@ def get_sync_representations(self, project_name, active_site, remote_site): "$elemMatch": { "name": active_site, "created_dt": {"$exists": False}, - "tries": {"$in": retries_arr} + "tries": {"$in": retries_arr}, + "paused": {"$exists": False} } }}, { "files.sites": { From 30e8bc5bb80f15a8fc5527460b54d4d1924e49de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20David?= Date: Fri, 13 Jan 2023 14:55:11 +0100 Subject: [PATCH 181/213] refactor is_representation_paused to check into the database --- .../modules/sync_server/providers/gdrive.py | 18 ++++++--- openpype/modules/sync_server/sync_server.py | 3 -- .../modules/sync_server/sync_server_module.py | 38 +++++++++++++------ 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/openpype/modules/sync_server/providers/gdrive.py b/openpype/modules/sync_server/providers/gdrive.py index 4e24fe41d2f..7d221381233 100644 --- a/openpype/modules/sync_server/providers/gdrive.py +++ b/openpype/modules/sync_server/providers/gdrive.py @@ -328,9 +328,12 @@ def upload_file(self, source_path, path, last_tick = status = response = None status_val = 0 while response is None: - if server.is_representation_paused(representation['_id'], - check_parents=True, - project_name=project_name): + if server.is_representation_paused( + project_name, + representation['_id'], + site, + check_parents=True + ): raise ValueError("Paused during process, please redo.") if status: status_val = float(status.progress()) @@ -415,9 +418,12 @@ def download_file(self, source_path, local_path, last_tick = status = response = None status_val = 0 while response is None: - if server.is_representation_paused(representation['_id'], - check_parents=True, - project_name=project_name): + if server.is_representation_paused( + project_name, + representation['_id'], + site, + check_parents=True + ): raise ValueError("Paused during process, please redo.") if status: status_val = float(status.progress()) diff --git a/openpype/modules/sync_server/sync_server.py b/openpype/modules/sync_server/sync_server.py index 85b0774e90b..aef3623efa9 100644 --- a/openpype/modules/sync_server/sync_server.py +++ b/openpype/modules/sync_server/sync_server.py @@ -284,9 +284,6 @@ async def sync_loop(self): # building folder tree structure in memory # call only if needed, eg. DO_UPLOAD or DO_DOWNLOAD for sync in sync_repres: - if self.module.\ - is_representation_paused(sync['_id']): - continue if limit <= 0: continue files = sync.get("files") or [] diff --git a/openpype/modules/sync_server/sync_server_module.py b/openpype/modules/sync_server/sync_server_module.py index 68a5346e201..93b8fef8217 100644 --- a/openpype/modules/sync_server/sync_server_module.py +++ b/openpype/modules/sync_server/sync_server_module.py @@ -500,27 +500,41 @@ def unpause_representation(self, project_name, self.reset_site_on_representation(project_name, representation_id, site_name=site_name, pause=False) - def is_representation_paused(self, representation_id, - check_parents=False, project_name=None): + def is_representation_paused(self, project_name, representation_id, + site_name, check_parents=False): """ - Returns if 'representation_id' is paused or not. + Returns if 'representation_id' is paused or not for site. Args: - representation_id (string): MongoDB objectId value + project_name (str): project to check if paused + representation_id (str): MongoDB objectId value + site (str): site to check representation is paused for check_parents (bool): check if parent project or server itself are not paused - project_name (string): project to check if paused - if 'check_parents', 'project_name' should be set too Returns: (bool) """ - condition = representation_id in self._paused_representations - if check_parents and project_name: - condition = condition or \ - self.is_project_paused(project_name) or \ - self.is_paused() - return condition + representation = get_representation_by_id(project_name, + representation_id, + fields=["files.sites"]) + if not representation: + return False + + # Check parents are paused + if check_parents and (self.is_project_paused(project_name) or \ + self.is_paused()): + return True + + # Check if representation is paused + for file_info in representation.get("files", []): + for site in file_info.get("sites", []): + if site["name"] != site_name: + continue + + return site.get("paused", False) + + return False def pause_project(self, project_name): """ From 9ca9b0f6f27e55b0082cd44c2f18ed20fa10a7aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20David?= Date: Fri, 13 Jan 2023 15:03:09 +0100 Subject: [PATCH 182/213] clean _paused_representations --- openpype/modules/sync_server/sync_server_module.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/openpype/modules/sync_server/sync_server_module.py b/openpype/modules/sync_server/sync_server_module.py index 93b8fef8217..333746df24f 100644 --- a/openpype/modules/sync_server/sync_server_module.py +++ b/openpype/modules/sync_server/sync_server_module.py @@ -124,7 +124,6 @@ def initialize(self, module_settings): self.action_show_widget = None self._paused = False self._paused_projects = set() - self._paused_representations = set() self._anatomies = {} self._connection = None @@ -475,7 +474,6 @@ def pause_representation(self, project_name, representation_id, site_name): site_name (string): 'gdrive', 'studio' etc. """ self.log.info("Pausing SyncServer for {}".format(representation_id)) - self._paused_representations.add(representation_id) self.reset_site_on_representation(project_name, representation_id, site_name=site_name, pause=True) @@ -492,11 +490,6 @@ def unpause_representation(self, project_name, site_name (string): 'gdrive', 'studio' etc. """ self.log.info("Unpausing SyncServer for {}".format(representation_id)) - try: - self._paused_representations.remove(representation_id) - except KeyError: - pass - # self.paused_representations is not persistent self.reset_site_on_representation(project_name, representation_id, site_name=site_name, pause=False) From b6e849bc73a073d27133b3ae7f3f0d1e20dbd98a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= <33513211+antirotor@users.noreply.github.com> Date: Fri, 13 Jan 2023 15:06:46 +0100 Subject: [PATCH 183/213] :bug: fix animation family contamination --- .../hosts/maya/plugins/publish/extract_multiverse_usd.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py b/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py index 27f676e86c0..0af748f7b62 100644 --- a/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py +++ b/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py @@ -156,6 +156,14 @@ def filter_members(self, members): return members def process(self, instance): + # check if there is "usd" in families because of + # `ExtractMultiverseUsdAnim` that inherits this and should + # run on animation family too. + families = set(instance.data["families"]) + families.add(instance.data["family"]) + if "usd" not in families: + return + # Load plugin first cmds.loadPlugin("MultiverseForMaya", quiet=True) From a119b6236d3847e4196f1c47d5d885e24972c3e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20David?= Date: Fri, 13 Jan 2023 15:11:11 +0100 Subject: [PATCH 184/213] cleaning --- openpype/modules/sync_server/sync_server_module.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/modules/sync_server/sync_server_module.py b/openpype/modules/sync_server/sync_server_module.py index 333746df24f..4b927c5914b 100644 --- a/openpype/modules/sync_server/sync_server_module.py +++ b/openpype/modules/sync_server/sync_server_module.py @@ -515,8 +515,10 @@ def is_representation_paused(self, project_name, representation_id, return False # Check parents are paused - if check_parents and (self.is_project_paused(project_name) or \ - self.is_paused()): + if check_parents and ( + self.is_project_paused(project_name) + or self.is_paused() + ): return True # Check if representation is paused From b4197787f60df8b0127cd8402af6ad24f319d94c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20David?= Date: Fri, 13 Jan 2023 15:21:14 +0100 Subject: [PATCH 185/213] moved parents check before representation check --- openpype/modules/sync_server/sync_server_module.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/openpype/modules/sync_server/sync_server_module.py b/openpype/modules/sync_server/sync_server_module.py index 4b927c5914b..ba0abe7d3b8 100644 --- a/openpype/modules/sync_server/sync_server_module.py +++ b/openpype/modules/sync_server/sync_server_module.py @@ -508,12 +508,6 @@ def is_representation_paused(self, project_name, representation_id, Returns: (bool) """ - representation = get_representation_by_id(project_name, - representation_id, - fields=["files.sites"]) - if not representation: - return False - # Check parents are paused if check_parents and ( self.is_project_paused(project_name) @@ -521,6 +515,13 @@ def is_representation_paused(self, project_name, representation_id, ): return True + # Get representation + representation = get_representation_by_id(project_name, + representation_id, + fields=["files.sites"]) + if not representation: + return False + # Check if representation is paused for file_info in representation.get("files", []): for site in file_info.get("sites", []): From cb3880a8d19c1a6a7e7b33ef0ea7400c0ca3ee9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20David?= Date: Fri, 13 Jan 2023 18:31:53 +0100 Subject: [PATCH 186/213] changes for gdrive optim --- .../modules/sync_server/providers/gdrive.py | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/openpype/modules/sync_server/providers/gdrive.py b/openpype/modules/sync_server/providers/gdrive.py index 7d221381233..3d94e5dff7d 100644 --- a/openpype/modules/sync_server/providers/gdrive.py +++ b/openpype/modules/sync_server/providers/gdrive.py @@ -328,13 +328,6 @@ def upload_file(self, source_path, path, last_tick = status = response = None status_val = 0 while response is None: - if server.is_representation_paused( - project_name, - representation['_id'], - site, - check_parents=True - ): - raise ValueError("Paused during process, please redo.") if status: status_val = float(status.progress()) if not last_tick or \ @@ -349,6 +342,13 @@ def upload_file(self, source_path, path, site=site, progress=status_val ) + if server.is_representation_paused( + project_name, + representation['_id'], + site, + check_parents=True + ): + raise ValueError("Paused during process, please redo.") status, response = request.next_chunk() except errors.HttpError as ex: @@ -418,13 +418,6 @@ def download_file(self, source_path, local_path, last_tick = status = response = None status_val = 0 while response is None: - if server.is_representation_paused( - project_name, - representation['_id'], - site, - check_parents=True - ): - raise ValueError("Paused during process, please redo.") if status: status_val = float(status.progress()) if not last_tick or \ @@ -439,6 +432,13 @@ def download_file(self, source_path, local_path, site=site, progress=status_val ) + if server.is_representation_paused( + project_name, + representation['_id'], + site, + check_parents=True + ): + raise ValueError("Paused during process, please redo.") status, response = downloader.next_chunk() return target_name From a947c4f7a12f1ef90560b642fc7c2d6f1df1fea8 Mon Sep 17 00:00:00 2001 From: OpenPype Date: Sat, 14 Jan 2023 03:27:57 +0000 Subject: [PATCH 187/213] [Automated] Bump version --- openpype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/version.py b/openpype/version.py index 129f7f684b0..ed7da82afb3 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.14.10" +__version__ = "3.14.11-nightly.1" From 8db210fce0b0e34988c386239f51b79d2b949072 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Mon, 16 Jan 2023 10:45:18 +0100 Subject: [PATCH 188/213] :recycle: use pyblish matching --- .../maya/plugins/publish/extract_multiverse_usd.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py b/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py index 0af748f7b62..4399eacda1b 100644 --- a/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py +++ b/openpype/hosts/maya/plugins/publish/extract_multiverse_usd.py @@ -4,6 +4,7 @@ from maya import cmds from maya import mel +import pyblish.api from openpype.pipeline import publish from openpype.hosts.maya.api.lib import maintained_selection @@ -156,13 +157,6 @@ def filter_members(self, members): return members def process(self, instance): - # check if there is "usd" in families because of - # `ExtractMultiverseUsdAnim` that inherits this and should - # run on animation family too. - families = set(instance.data["families"]) - families.add(instance.data["family"]) - if "usd" not in families: - return # Load plugin first cmds.loadPlugin("MultiverseForMaya", quiet=True) @@ -262,7 +256,8 @@ class ExtractMultiverseUsdAnim(ExtractMultiverseUsd): Upon publish a .usd sparse cache will be written. """ label = "Extract Multiverse USD Animation Sparse Cache" - families = ["animation"] + families = ["animation", "usd"] + match = pyblish.api.Subset def get_default_options(self): anim_options = self.default_options From bc61b450db090aa9620648aa95051c27fac13d45 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 16 Jan 2023 11:19:24 +0100 Subject: [PATCH 189/213] use right constants --- openpype/tools/launcher/widgets.py | 2 +- openpype/tools/mayalookassigner/views.py | 2 +- openpype/tools/project_manager/project_manager/view.py | 4 ++-- openpype/tools/publisher/widgets/validations_widget.py | 2 +- openpype/tools/sceneinventory/view.py | 2 +- openpype/tools/utils/assets_widget.py | 4 +++- 6 files changed, 9 insertions(+), 7 deletions(-) diff --git a/openpype/tools/launcher/widgets.py b/openpype/tools/launcher/widgets.py index 81636c2206b..a5bdd616b1b 100644 --- a/openpype/tools/launcher/widgets.py +++ b/openpype/tools/launcher/widgets.py @@ -423,7 +423,7 @@ def show_history(self): return widget = QtWidgets.QListWidget() - widget.setSelectionMode(widget.NoSelection) + widget.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection) widget.setStyleSheet(""" * { font-family: "Courier New"; diff --git a/openpype/tools/mayalookassigner/views.py b/openpype/tools/mayalookassigner/views.py index 87459ad897c..489c194f600 100644 --- a/openpype/tools/mayalookassigner/views.py +++ b/openpype/tools/mayalookassigner/views.py @@ -10,7 +10,7 @@ def __init__(self, parent=None): # view settings self.setAlternatingRowColors(False) self.setSortingEnabled(True) - self.setSelectionMode(self.ExtendedSelection) + self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) def get_indices(self): diff --git a/openpype/tools/project_manager/project_manager/view.py b/openpype/tools/project_manager/project_manager/view.py index 609db30a817..fa08943ea53 100644 --- a/openpype/tools/project_manager/project_manager/view.py +++ b/openpype/tools/project_manager/project_manager/view.py @@ -134,9 +134,9 @@ def __init__(self, dbcon, source_model, parent): main_delegate = QtWidgets.QStyledItemDelegate() self.setItemDelegate(main_delegate) self.setAlternatingRowColors(True) - self.setSelectionMode(HierarchyView.ExtendedSelection) + self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) - self.setEditTriggers(HierarchyView.AllEditTriggers) + self.setEditTriggers(QtWidgets.QAbstractItemView.AllEditTriggers) column_delegates = {} column_key_to_index = {} diff --git a/openpype/tools/publisher/widgets/validations_widget.py b/openpype/tools/publisher/widgets/validations_widget.py index c5b5a00883e..84ec2c067ac 100644 --- a/openpype/tools/publisher/widgets/validations_widget.py +++ b/openpype/tools/publisher/widgets/validations_widget.py @@ -26,7 +26,7 @@ def __init__(self, *args, **kwargs): self.setObjectName("ValidationErrorInstanceList") self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) - self.setSelectionMode(QtWidgets.QListView.ExtendedSelection) + self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) def minimumSizeHint(self): return self.sizeHint() diff --git a/openpype/tools/sceneinventory/view.py b/openpype/tools/sceneinventory/view.py index 183bf2362bb..3c4e03a195e 100644 --- a/openpype/tools/sceneinventory/view.py +++ b/openpype/tools/sceneinventory/view.py @@ -48,7 +48,7 @@ def __init__(self, parent=None): self.setIndentation(12) self.setAlternatingRowColors(True) self.setSortingEnabled(True) - self.setSelectionMode(self.ExtendedSelection) + self.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection) self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.customContextMenuRequested.connect(self._show_right_mouse_menu) self._hierarchy_view = False diff --git a/openpype/tools/utils/assets_widget.py b/openpype/tools/utils/assets_widget.py index 8a7511e6c5a..ffbdd995d6f 100644 --- a/openpype/tools/utils/assets_widget.py +++ b/openpype/tools/utils/assets_widget.py @@ -821,7 +821,9 @@ class MultiSelectAssetsWidget(AssetsWidget): """ def __init__(self, *args, **kwargs): super(MultiSelectAssetsWidget, self).__init__(*args, **kwargs) - self._view.setSelectionMode(QtWidgets.QTreeView.ExtendedSelection) + self._view.setSelectionMode( + QtWidgets.QAbstractItemView.ExtendedSelection + ) delegate = UnderlinesAssetDelegate() self._view.setItemDelegate(delegate) From 681364bbea80b33bf34507cf1934eda42b78e3f5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 16 Jan 2023 11:19:42 +0100 Subject: [PATCH 190/213] fix custom UserType items --- openpype/tools/pyblish_pype/model.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/openpype/tools/pyblish_pype/model.py b/openpype/tools/pyblish_pype/model.py index 774c3054acb..8a07bb447a0 100644 --- a/openpype/tools/pyblish_pype/model.py +++ b/openpype/tools/pyblish_pype/model.py @@ -38,11 +38,14 @@ # ItemTypes -InstanceType = QtGui.QStandardItem.UserType -PluginType = QtGui.QStandardItem.UserType + 1 -GroupType = QtGui.QStandardItem.UserType + 2 -TerminalLabelType = QtGui.QStandardItem.UserType + 3 -TerminalDetailType = QtGui.QStandardItem.UserType + 4 +UserType = QtGui.QStandardItem.UserType +if hasattr(UserType, "value"): + UserType = UserType.value +InstanceType = UserType +PluginType = UserType + 1 +GroupType = UserType + 2 +TerminalLabelType = UserType + 3 +TerminalDetailType = UserType + 4 class QAwesomeTextIconFactory: From 118d1eee16846293cce3d91edc8cb0c1e603cab2 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 16 Jan 2023 11:24:38 +0100 Subject: [PATCH 191/213] Fix 'QRegExpValidator' vs. 'QRegularExpressionValidator' --- openpype/tools/creator/widgets.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/openpype/tools/creator/widgets.py b/openpype/tools/creator/widgets.py index 8ea3cbd03f8..8269cee42c4 100644 --- a/openpype/tools/creator/widgets.py +++ b/openpype/tools/creator/widgets.py @@ -8,6 +8,11 @@ from openpype.pipeline.create import SUBSET_NAME_ALLOWED_SYMBOLS from openpype.tools.utils import ErrorMessageBox +if hasattr(QtGui, "QRegularExpressionValidator"): + RegExpValidatorClass = QtGui.QRegularExpressionValidator +else: + RegExpValidatorClass = QtGui.QRegExpValidator + class CreateErrorMessageBox(ErrorMessageBox): def __init__( @@ -82,7 +87,7 @@ def _create_content(self, content_layout): content_layout.addWidget(tb_widget) -class SubsetNameValidator(QtGui.QRegExpValidator): +class SubsetNameValidator(RegExpValidatorClass): invalid = QtCore.Signal(set) pattern = "^[{}]*$".format(SUBSET_NAME_ALLOWED_SYMBOLS) From 1d2cf742f2a444279074f41b6c246c7b2e8513dc Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 16 Jan 2023 11:27:20 +0100 Subject: [PATCH 192/213] fix also 'QRegExp' vs. 'QRegularExpression' --- openpype/tools/creator/widgets.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/openpype/tools/creator/widgets.py b/openpype/tools/creator/widgets.py index 8269cee42c4..74f75811ffa 100644 --- a/openpype/tools/creator/widgets.py +++ b/openpype/tools/creator/widgets.py @@ -9,9 +9,11 @@ from openpype.tools.utils import ErrorMessageBox if hasattr(QtGui, "QRegularExpressionValidator"): - RegExpValidatorClass = QtGui.QRegularExpressionValidator + RegularExpressionValidatorClass = QtGui.QRegularExpressionValidator + RegularExpressionClass = QtCore.QRegularExpression else: - RegExpValidatorClass = QtGui.QRegExpValidator + RegularExpressionValidatorClass = QtGui.QRegExpValidator + RegularExpressionClass = QtCore.QRegExp class CreateErrorMessageBox(ErrorMessageBox): @@ -87,12 +89,12 @@ def _create_content(self, content_layout): content_layout.addWidget(tb_widget) -class SubsetNameValidator(RegExpValidatorClass): +class SubsetNameValidator(RegularExpressionValidatorClass): invalid = QtCore.Signal(set) pattern = "^[{}]*$".format(SUBSET_NAME_ALLOWED_SYMBOLS) def __init__(self): - reg = QtCore.QRegExp(self.pattern) + reg = RegularExpressionClass(self.pattern) super(SubsetNameValidator, self).__init__(reg) def validate(self, text, pos): From 5ee7c1706728a22521eced4a45c7397ba9537914 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 16 Jan 2023 11:32:41 +0100 Subject: [PATCH 193/213] Slack - get users only if necessary Only trigger when message contains @ sign. --- .../modules/slack/plugins/publish/integrate_slack_api.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/openpype/modules/slack/plugins/publish/integrate_slack_api.py b/openpype/modules/slack/plugins/publish/integrate_slack_api.py index 21069e0b133..bf54abbdd40 100644 --- a/openpype/modules/slack/plugins/publish/integrate_slack_api.py +++ b/openpype/modules/slack/plugins/publish/integrate_slack_api.py @@ -39,6 +39,7 @@ def process(self, instance): token = instance.data["slack_token"] if additional_message: message = "{} \n".format(additional_message) + users = groups = None for message_profile in instance.data["slack_channel_message_profiles"]: message += self._get_filled_message(message_profile["message"], instance, @@ -60,8 +61,10 @@ def process(self, instance): else: client = SlackPython3Operations(token, self.log) - users, groups = client.get_users_and_groups() - message = self._translate_users(message, users, groups) + if "@" in message: + if users is None: + users, groups = client.get_users_and_groups() + message = self._translate_users(message, users, groups) msg_id, file_ids = client.send_message(channel, message, From 0aad4cd224c3441ef5008c7e4222ecd0978e7357 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 16 Jan 2023 11:39:12 +0100 Subject: [PATCH 194/213] ignore 'QT_API' before application launch --- openpype/lib/applications.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/openpype/lib/applications.py b/openpype/lib/applications.py index 317a17796e4..0047b886953 100644 --- a/openpype/lib/applications.py +++ b/openpype/lib/applications.py @@ -908,24 +908,25 @@ def __init__(self, application, executable, env_group=None, **data): self.launch_args.extend(self.data.pop("app_args")) # Handle launch environemtns - env = self.data.pop("env", None) - if env is not None and not isinstance(env, dict): + src_env = self.data.pop("env", None) + if src_env is not None and not isinstance(src_env, dict): self.log.warning(( "Passed `env` kwarg has invalid type: {}. Expected: `dict`." " Using `os.environ` instead." - ).format(str(type(env)))) - env = None + ).format(str(type(src_env)))) + src_env = None - if env is None: - env = os.environ + if src_env is None: + src_env = os.environ - # subprocess.Popen keyword arguments - self.kwargs = { - "env": { - key: str(value) - for key, value in env.items() - } + ignored_env = {"QT_API",} + env = { + key: str(value) + for key, value in src_env.items() + if key not in ignored_env } + # subprocess.Popen keyword arguments + self.kwargs = {"env": env} if platform.system().lower() == "windows": # Detach new process from currently running process on Windows From 2546274c55de23f3cf65409ed0314becc6ad10ed Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Mon, 16 Jan 2023 11:50:20 +0100 Subject: [PATCH 195/213] Fix whitespace --- openpype/lib/applications.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/lib/applications.py b/openpype/lib/applications.py index 0047b886953..7cc296f47bc 100644 --- a/openpype/lib/applications.py +++ b/openpype/lib/applications.py @@ -919,7 +919,7 @@ def __init__(self, application, executable, env_group=None, **data): if src_env is None: src_env = os.environ - ignored_env = {"QT_API",} + ignored_env = {"QT_API", } env = { key: str(value) for key, value in src_env.items() From 1e6c399c30da07a06eebf73e5a6f04cc6a8b7fbe Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 16 Jan 2023 13:09:16 +0100 Subject: [PATCH 196/213] removed line with f string --- openpype/plugins/publish/collect_from_create_context.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/plugins/publish/collect_from_create_context.py b/openpype/plugins/publish/collect_from_create_context.py index 9a740c10cd7..d3398c885ed 100644 --- a/openpype/plugins/publish/collect_from_create_context.py +++ b/openpype/plugins/publish/collect_from_create_context.py @@ -37,7 +37,6 @@ def process(self, context): context.data["projectName"] = project_name for created_instance in create_context.instances: - self.log.info(f"created_instance:: {created_instance}") instance_data = created_instance.data_to_store() if instance_data["active"]: thumbnail_path = thumbnail_paths_by_instance_id.get( From b167b971a2fb4d8af4d4d37858e56e0c0aa83b3d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 16 Jan 2023 13:31:31 +0100 Subject: [PATCH 197/213] Slack - better regex Handles cases like @test\n@john.doe --- openpype/modules/slack/plugins/publish/integrate_slack_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/slack/plugins/publish/integrate_slack_api.py b/openpype/modules/slack/plugins/publish/integrate_slack_api.py index bf54abbdd40..5440817e306 100644 --- a/openpype/modules/slack/plugins/publish/integrate_slack_api.py +++ b/openpype/modules/slack/plugins/publish/integrate_slack_api.py @@ -215,7 +215,7 @@ def _get_group_id(self, groups, group_name): def _translate_users(self, message, users, groups): """Replace all occurences of @mentions with proper <@name> format.""" - matches = re.findall(r"(? Date: Mon, 16 Jan 2023 15:34:25 +0100 Subject: [PATCH 198/213] Slack - introduced caching User and group query is expensive operation, this will speed up publishing of multiple instances --- .../slack/plugins/publish/integrate_slack_api.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/openpype/modules/slack/plugins/publish/integrate_slack_api.py b/openpype/modules/slack/plugins/publish/integrate_slack_api.py index 5440817e306..1e06799f925 100644 --- a/openpype/modules/slack/plugins/publish/integrate_slack_api.py +++ b/openpype/modules/slack/plugins/publish/integrate_slack_api.py @@ -62,8 +62,16 @@ def process(self, instance): client = SlackPython3Operations(token, self.log) if "@" in message: - if users is None: + cache_key = "__cache_ids" + slack_ids = instance.context.data.get(cache_key, None) + if slack_ids is None: users, groups = client.get_users_and_groups() + instance.context.data[cache_key] = {} + instance.context.data[cache_key]["users"] = users + instance.context.data[cache_key]["groups"] = groups + else: + users = slack_ids["users"] + groups = slack_ids["groups"] message = self._translate_users(message, users, groups) msg_id, file_ids = client.send_message(channel, From 0bfaf428dea255241dc6802767df3b044c09d1ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= <33513211+antirotor@users.noreply.github.com> Date: Mon, 16 Jan 2023 17:40:38 +0100 Subject: [PATCH 199/213] :pencil2: fix typos --- .../ftrack/event_handlers_server/event_sync_to_avalon.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/modules/ftrack/event_handlers_server/event_sync_to_avalon.py b/openpype/modules/ftrack/event_handlers_server/event_sync_to_avalon.py index e549de7ed09..0058a428e33 100644 --- a/openpype/modules/ftrack/event_handlers_server/event_sync_to_avalon.py +++ b/openpype/modules/ftrack/event_handlers_server/event_sync_to_avalon.py @@ -973,7 +973,7 @@ def process_removed(self): except Exception: # TODO logging # TODO report - self.process_session.rolback() + self.process_session.rollback() ent_path_items = [self.cur_project["full_name"]] ent_path_items.extend([ par for par in avalon_entity["data"]["parents"] @@ -1016,7 +1016,7 @@ def process_removed(self): except Exception: # TODO logging # TODO report - self.process_session.rolback() + self.process_session.rollback() error_msg = ( "Couldn't update custom attributes after recreation" " of entity in Ftrack" @@ -1338,7 +1338,7 @@ def create_entity_in_avalon(self, ftrack_ent, parent_avalon): try: self.process_session.commit() except Exception: - self.process_session.rolback() + self.process_session.rollback() # TODO logging # TODO report error_msg = ( From 49054a53f84abfa08aa46fc31732131e191436ca Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 16 Jan 2023 17:42:12 +0100 Subject: [PATCH 200/213] Slack - renamed cache key --- openpype/modules/slack/plugins/publish/integrate_slack_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/slack/plugins/publish/integrate_slack_api.py b/openpype/modules/slack/plugins/publish/integrate_slack_api.py index 1e06799f925..612031efac7 100644 --- a/openpype/modules/slack/plugins/publish/integrate_slack_api.py +++ b/openpype/modules/slack/plugins/publish/integrate_slack_api.py @@ -62,7 +62,7 @@ def process(self, instance): client = SlackPython3Operations(token, self.log) if "@" in message: - cache_key = "__cache_ids" + cache_key = "__cache_slack_ids" slack_ids = instance.context.data.get(cache_key, None) if slack_ids is None: users, groups = client.get_users_and_groups() From 459ed76ae2b3edc9b05e609a0f181650b2cc685d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 16 Jan 2023 18:02:20 +0100 Subject: [PATCH 201/213] fix access to 'CE_ItemViewItem' constant --- openpype/tools/utils/delegates.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/tools/utils/delegates.py b/openpype/tools/utils/delegates.py index 604df689c48..d76284afb1a 100644 --- a/openpype/tools/utils/delegates.py +++ b/openpype/tools/utils/delegates.py @@ -54,7 +54,10 @@ def paint(self, painter, option, index): style = QtWidgets.QApplication.style() style.drawControl( - style.CE_ItemViewItem, option, painter, option.widget + QtWidgets.QStyle.CE_ItemViewItem, + option, + painter, + option.widget ) painter.save() From da99fb25cedb3c1483a882affc687b347190744c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 16 Jan 2023 18:20:29 +0100 Subject: [PATCH 202/213] update qtpy --- poetry.lock | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/poetry.lock b/poetry.lock index ca0b3c5c23b..cf780e8dd29 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2705,16 +2705,22 @@ six = "*" [[package]] name = "qtpy" -version = "1.11.3" -description = "Provides an abstraction layer on top of the various Qt bindings (PyQt5, PyQt4 and PySide) and additional custom QWidgets." +version = "2.3.0" +description = "Provides an abstraction layer on top of the various Qt bindings (PyQt5/6 and PySide2/6)." category = "main" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" +python-versions = ">=3.7" files = [ - {file = "QtPy-1.11.3-py2.py3-none-any.whl", hash = "sha256:e121fbee8e95645af29c5a4aceba8d657991551fc1aa3b6b6012faf4725a1d20"}, - {file = "QtPy-1.11.3.tar.gz", hash = "sha256:d427addd37386a8d786db81864a5536700861d95bf085cb31d1bea855d699557"}, + {file = "QtPy-2.3.0-py3-none-any.whl", hash = "sha256:8d6d544fc20facd27360ea189592e6135c614785f0dec0b4f083289de6beb408"}, + {file = "QtPy-2.3.0.tar.gz", hash = "sha256:0603c9c83ccc035a4717a12908bf6bc6cb22509827ea2ec0e94c2da7c9ed57c5"}, ] +[package.dependencies] +packaging = "*" + +[package.extras] +test = ["pytest (>=6,!=7.0.0,!=7.0.1)", "pytest-cov (>=3.0.0)", "pytest-qt"] + [[package]] name = "recommonmark" version = "0.7.1" From d7fdc7d4e77874e946ebababc093d4fbb7cf1ced Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 16 Jan 2023 22:01:00 +0100 Subject: [PATCH 203/213] global: review otio with improved jpg to jpg quality --- .../plugins/publish/extract_otio_review.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_otio_review.py b/openpype/plugins/publish/extract_otio_review.py index 169ff9e136a..9ebcad2af1e 100644 --- a/openpype/plugins/publish/extract_otio_review.py +++ b/openpype/plugins/publish/extract_otio_review.py @@ -350,6 +350,7 @@ def _render_seqment(self, sequence=None, # start command list command = [ffmpeg_path] + input_extension = None if sequence: input_dir, collection = sequence in_frame_start = min(collection.indexes) @@ -357,6 +358,7 @@ def _render_seqment(self, sequence=None, # converting image sequence to image sequence input_file = collection.format("{head}{padding}{tail}") input_path = os.path.join(input_dir, input_file) + input_extension = os.path.splitext(input_path)[-1] # form command for rendering gap files command.extend([ @@ -373,6 +375,7 @@ def _render_seqment(self, sequence=None, sec_duration = frames_to_seconds( frame_duration, input_fps ) + input_extension = os.path.splitext(video_path)[-1] # form command for rendering gap files command.extend([ @@ -397,9 +400,21 @@ def _render_seqment(self, sequence=None, # add output attributes command.extend([ - "-start_number", str(out_frame_start), - output_path + "-start_number", str(out_frame_start) ]) + + # add copying if extensions are matching + if ( + input_extension + and self.output_ext == input_extension + ): + command.extend([ + "-c", "copy" + ]) + + # add output path at the end + command.append(output_path) + # execute self.log.debug("Executing: {}".format(" ".join(command))) output = run_subprocess( From 7066de7af986c6b8850574d01bb1f3a9b413bd41 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 17 Jan 2023 10:12:07 +0100 Subject: [PATCH 204/213] fix tvpaint plugin for TVPaint 11.7 --- .../tvpaint_plugin/plugin_code/library.cpp | 53 +++++++++++------- .../windows_x64/plugin/OpenPypePlugin.dll | Bin 5810688 -> 5811200 bytes .../windows_x86/plugin/OpenPypePlugin.dll | Bin 5572096 -> 5571072 bytes 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/openpype/hosts/tvpaint/tvpaint_plugin/plugin_code/library.cpp b/openpype/hosts/tvpaint/tvpaint_plugin/plugin_code/library.cpp index bb67715cbd3..88106bc7707 100644 --- a/openpype/hosts/tvpaint/tvpaint_plugin/plugin_code/library.cpp +++ b/openpype/hosts/tvpaint/tvpaint_plugin/plugin_code/library.cpp @@ -302,8 +302,9 @@ class Communicator { std::string websocket_url; // Should be avalon plugin available? // - this may change during processing if websocketet url is not set or server is down - bool use_avalon; + bool server_available; public: + Communicator(std::string url); Communicator(); websocket_endpoint endpoint; bool is_connected(); @@ -314,43 +315,45 @@ class Communicator { void call_notification(std::string method_name, nlohmann::json params); }; -Communicator::Communicator() { + +Communicator::Communicator(std::string url) { // URL to websocket server - websocket_url = std::getenv("WEBSOCKET_URL"); + websocket_url = url; // Should be avalon plugin available? // - this may change during processing if websocketet url is not set or server is down - if (websocket_url == "") { - use_avalon = false; + if (url == "") { + server_available = false; } else { - use_avalon = true; + server_available = true; } } + bool Communicator::is_connected(){ return endpoint.connected(); } bool Communicator::is_usable(){ - return use_avalon; + return server_available; } void Communicator::connect() { - if (!use_avalon) { + if (!server_available) { return; } int con_result; con_result = endpoint.connect(websocket_url); if (con_result == -1) { - use_avalon = false; + server_available = false; } else { - use_avalon = true; + server_available = true; } } void Communicator::call_notification(std::string method_name, nlohmann::json params) { - if (!use_avalon || !is_connected()) {return;} + if (!server_available || !is_connected()) {return;} jsonrpcpp::Notification notification = {method_name, params}; endpoint.send_notification(¬ification); @@ -358,7 +361,7 @@ void Communicator::call_notification(std::string method_name, nlohmann::json par jsonrpcpp::Response Communicator::call_method(std::string method_name, nlohmann::json params) { jsonrpcpp::Response response; - if (!use_avalon || !is_connected()) + if (!server_available || !is_connected()) { return response; } @@ -382,7 +385,7 @@ jsonrpcpp::Response Communicator::call_method(std::string method_name, nlohmann: } void Communicator::process_requests() { - if (!use_avalon || !is_connected() || Data.messages.empty()) {return;} + if (!server_available || !is_connected() || Data.messages.empty()) {return;} std::string msg = Data.messages.front(); Data.messages.pop(); @@ -458,7 +461,7 @@ void register_callbacks(){ parser.register_request_callback("execute_george", execute_george); } -Communicator communication; +Communicator* communication = nullptr; //////////////////////////////////////////////////////////////////////////////////////// @@ -484,7 +487,7 @@ static char* GetLocalString( PIFilter* iFilter, int iNum, char* iDefault ) // in the localized file (or the localized file doesn't exist). std::string label_from_evn() { - std::string _plugin_label = "Avalon"; + std::string _plugin_label = "OpenPype"; if (std::getenv("AVALON_LABEL") && std::getenv("AVALON_LABEL") != "") { _plugin_label = std::getenv("AVALON_LABEL"); @@ -540,9 +543,12 @@ int FAR PASCAL PI_Open( PIFilter* iFilter ) { PI_Parameters( iFilter, NULL ); // NULL as iArg means "open the requester" } - - communication.connect(); - register_callbacks(); + char *env_value = std::getenv("WEBSOCKET_URL"); + if (env_value != NULL) { + communication = new Communicator(env_value); + communication->connect(); + register_callbacks(); + } return 1; // OK } @@ -560,7 +566,10 @@ void FAR PASCAL PI_Close( PIFilter* iFilter ) { TVCloseReq( iFilter, Data.mReq ); } - communication.endpoint.close_connection(); + if (communication != nullptr) { + communication->endpoint.close_connection(); + delete communication; + } } @@ -709,7 +718,7 @@ int FAR PASCAL PI_Msg( PIFilter* iFilter, INTPTR iEvent, INTPTR iReq, INTPTR* iA if (Data.menuItemsById.contains(button_up_item_id_str)) { std::string callback_name = Data.menuItemsById[button_up_item_id_str].get(); - communication.call_method(callback_name, nlohmann::json::array()); + communication->call_method(callback_name, nlohmann::json::array()); } TVExecute( iFilter ); break; @@ -737,7 +746,9 @@ int FAR PASCAL PI_Msg( PIFilter* iFilter, INTPTR iEvent, INTPTR iReq, INTPTR* iA { newMenuItemsProcess(iFilter); } - communication.process_requests(); + if (communication != nullptr) { + communication->process_requests(); + } } return 1; diff --git a/openpype/hosts/tvpaint/tvpaint_plugin/plugin_files/windows_x64/plugin/OpenPypePlugin.dll b/openpype/hosts/tvpaint/tvpaint_plugin/plugin_files/windows_x64/plugin/OpenPypePlugin.dll index f7f5119ef34a7f483db8af0994b8c7fb1c1702a6..7081778beee266e9b08d20ba7e48710dd7c4f20c 100644 GIT binary patch delta 422239 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

7tM82!%!NKfH!Ry)#I~ut0<&C509lSa(5s**MX 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

wEMc>6?{e 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

2x153JiTBeAho-M&#H+~kC)V|6))hX3oqloX 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 delta 422235 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 z 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%0ApsDdGIPwtFvXYj74;-QCe_`Ik++&Op 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 E0a~KP(f|Me delta 341783 zcmbrn2V9iL^FMyOcN|AQ;OM>gCKjxKieLc_Q9!X^M{HQI7Zi=)iK3ghcCjUv*rE}; z7z=hWn#2~xM3V<41`}f}iTq}t=NfY6b3XZ9(ae-jrjzKQqB zkaxdQBGJS|Q8k&1d`y}z z6Nz#rnu%POdD)Um6o+5^OEC6Fp(F?gnD!V53?h4pMEyu#91=7E{?hvwClbYlPZ&0O z+z^q7@e!57_sik;r2V^bI+_z_VkYP0)~2F9r!T|5TqXM~(tXZhpX=q?@AKK`8r|nY z_PJX3xr}|T)O{{zpZDrM?`5CMwV!QrE8#(v(Kb~tIYZ5TA}7lw?E{piX7Z92{opCr z4hk2Ykqjt0V}3f<*WXND+Sym8ibs@DDMt*Xmw$j#kpDj&L$wLzJ-v3ZML^60b5(u5a7 zH?v21C{M)xJ6sqEY(v(Fbx+FRNmar>tQ2R%z98eIUihEsWVtjB9UwQP@$y&Gj6}I* z0BiFxM*b)9H4d5+dlw!?nt?P=TBv&`w~T$@P)@Jftkwhdqm+%kb=Iq^WBC0ZyH5dA=%E(;%mt6aoLj9Mj z1>=wbgpzYcj_e^vjrp!xP&vs&$sG_rRCQ_2#K6iKaWnM`5aENWMYV>{94w>;o)B{yak63ws7tp~YVeQHz#&$#C1tYHdz{h-95 zTW*dy^oSfD3B!w9)6Q75SpmAmk2ZA=z0vhhQ`I?~#kl&ABgR2vUQdBO_p|^-$YBpZ zHRENdN`5i4#DYn#kLq!MfiWA(uNFa>gZGRwABWey{FMLtt9-?t2jplUXF&4pSI!2?mf)hVMMz#^{b6)LbTmG~<2 zaD&}UknCY4JYI-Yi+RfE%T-2lc;E{^TrP~Nxo18F`qR13SY1a<$yJ%7?X4Et5mZ#C z$jAguC9h>6-iM}0M5PHeFi2RH%FyG@0YN&6A4g+&L0k(#OO?E^4Xe zk;YV=Og72D@+qWnsDgO@Y)R@(q)p36O?}Y*{lTU^kRnh=UsPwFE`V5M=7A-q^h!G% zLGG9-kR|!uEF5hh?&h)RcQU}d8|p>2o5v#?^3;4VT1rwZzCtwwS%#x95^34ScM}zh zN>k0bCyON_Rn@=&-1yZEH#Q=J3D2Kqt`WQMpCtGG zK*M)fd!TpZqIGBd>?)Dl_!|~aHydyCFBxMKk5b4Un^x9ub+r`LHD{ghw@qij(atso z6_Wy6AKOXGK_CfdU>u5}I|Dh1N=)OQqevU&KPE7EWDNypN|P^ho2Nk6R;Ekrv8QCe zkz%gcCa{&Rd)1w@(%}nqoWwaspi+|WXm7BH<6*ROe@`a~Qe^8GfR3qI|^avaVp z89)*#nT(5HOBfQ+utm2()3|NKYduDWj*}U^`N6BPDVdsbak$zQHa7 z2J{-H;fs~3tK4@TwTk;>oD^mRsDPQfa|tyfE)dG93S^+`O5Rp-^i^XJ6+i+YOfr}@ zb!#aT5frP?VyM-};HYk!v#YYh@-s#v^%TP@d;}Gua|!oET%Vx~+&%b=m8IQ1x!ds! z-94d*gnKepDk|+n(*{dgboKxq8e%C={M!&U+Iu`&Bd*`yYP65$S6Y2~8W?fYfW!Q| zlLWx+h?iRj=%pMtH@vi%>>KJtR=QaRy+33PD!n-uTTIX}Tl{9QdMwM9yIY_JV(IQ^ za3r*L_r}jngn>ZFboWjum7H*oQFz8^HK?dgBP3IgAXHD9dxV8&=NO4f#7sbK|DH6Y zOqE<;+C{E%QF8N6f$1r#lq=I!{p(8;9w}9qxDI5g$3ojAeU9pJ806`Je^=K&oQ&{{ zP^{1|<*DwR9iDAiv3@VHD>hv~Bwk&uPtIpU<0k8p($q*Lmr4eBwe}cxl9zsJK0KG4 zOUxB<(E}x-JaK9AJ?{2MDVdvTN6Jqbk!mlg&3Ro-Y3h17uPC&iID5y7_Z5lx!qqkw zq|mzs$|MKj{~Jl2cPBJvf22#Zp>uZ{+2QD4h*_XphCV}?Kvg*Fo@mkoRYDC^W0nU?#|OXiRz_8DOEv4J zFwzb`*27jUe8}Kb@JA-M9jd5)0)sv0GYe84C^aC9iok14pYEqY`3V2@H8BZUXPv8) zRGJ{wijbuSo@R#bG4RwO?4$v+`fFHcw2y>_w?Qk&h;Sda4 z@wO*^CQL|uzLAMw_LfIis;(-z*(A1202CMj1)>cVaNob7&2q8C><6)kwu_TQ&<7nQ z<|My^R8g)|3s{`1hwGvaVQ4Ek)L|h?CULR$Hr0&^@ynXr*f6>AUm`Z1O1k)#Cd;{! zjl5wz9P5vF-6ip{b{`^J*odsQoG0rO7l@9Md2yXTjL442;!EUjZ^MS5OWeScPD<73 z0oi@%LnCBw-4viKF5K#+KE9w0tfKu+9eW^Cy3Ri6i!Zdo)GkOJTsMfA};4A6If;md!Vcj zk-GDwIHiJAb{^k#l!mqt2F&Hl8LH=1Qns$H>H^yy*q@DR4YPxk4H6k!!3n&JKg7F3 zYX%Ej^1QuN`D$E+H3oUBNVCKMn~s|&laZPv>4%7ZBc5HGOI}?DqWG?ocC5TPDew9W zjU`LF1w(~Ly7}3NzLv^MQ{^gYNusQ@3mDI}Z-E9nAj9XYe|i2Ik6b@w!1yiNuS6gfBP zGl_apf33TpbT3eFH^`gr?a^oOe>c**hc&TR+PF4Ba9g#Y7^>nyoJw=28@#cx_z@RR zj`i>m*nlea@Hg4ImenKU`V)C_b7|p0;O|4aCAY_qHS-?Lt8(PFEz`(sO{$aB^AKR!Opc`XBP)8@ zH0`U~s)T%XMZTCO5{ncMBS!KqQ7L;PSMpF90sps73&);^NqkxeiX>ywg2=(%hIvg@ zi#)(V(Zv8@;L(s4iN=t~p8k@St7Ia&dzUnv6!&a}l}E^_o~_Up^15djIhSEr=7st) z|8W{Nc6!9k?QEbT;I=%y9lA=crTZHvu9U%gdNi@V6+nD?#TyqMg$73NBu6vb8&sVp zwLs5Wcj9}NQ7$Idz5PwUrb5pq&nL^WnoEzt7k};`m$H%#vTUM_Oj+Vd|2`f@j1rV3 zqnXL;6#~msppBknafY3V{5X6$JX5`EV0P&fUQ^m ztYFi}p9w7(vy8;|jy5ho!KCsVnbEs#xTyWd)L=+X8#|EWJ=}5m2{NlsOOqgV6%Rov z9mtJ7UdH(+p%(J&|7R`Z+p}7vJhOXbZl7kRuZhr@eM>=P9^_shM;up4{Ilc2BR{Gw zh73e}JJ#o@lKhz6&NNzGfy+`b4sIl_-$3i*HWE?p&?V-~xe0!yx{G9QzaTVZ|DApl z5t>6%21J0TVfKI_=o0y9z)i(m13<{dfoIW`{bf1+hL$^3gA6TqG6x%a`s?652Awh6 zhlF`c-I_RK#8|thT^XQp#+VH?YwBq6ADV>*kU2vmQGc>`sDH>uZ2e%GXo%!2;`zR_F?e8|F_A=30gtGNa5>QaW3b$(3%?Ti37OM#_h|S@Mt6dya|Q$faRU z!Xx$0q{lY$$FM{!b|x-KE{Ym`NoyQJdBe*PCU1u)DLhZXWY%52s3R)wWRh{NBNB0w zN#=-%aCwK1QTryU((DoE6$y6jmENQ|tFFqJuP0T$D8ZyS(L^(8o=Y$hao-TL+)$GR z3z^Q!xiAu!8?A`@rU|NF$im!+@VJjcwGzyRb_y-)EH(yI&%+I%;_ESpsu^iJGSuYk zCjzQ5B!A?AkaBST+LoLjl|^=0npm5~>M5Fn_qL5|Y47oweoK@}){?jLNT>X+;J17@+MVpo z{~R2IJ;x{FPF8H0N%~9(#b`bGd%U;(=HFo6e1DU(ghWgTMyE;N38ApuTsXno!PZc* zcXgz4LK0FClZmrnTe@&!3sgpqPmD#?icAS1Am8_8qrv!&~9|_wCJ?D3Fee@Nse>v>X z9{2wSoKxqyAYT)37$@K2Jjst!0)sMNf>-~HV`;*cT*ne~5Xt;KY%R|IWvIXno2h!; z_T40ZE_HwS)Q$+5lkw9=pw8s>v@zCG^%1x-1ew$Q%pzS(L{DW!rz9}y7fw&ZpPA6- zBaLjS+(c%Cqt=lZ(>Hmwe88`L!S61#Wt%>y9*k4Pj3{u>*3MXhA3Kq0GgC1Ehl7XA zsa4z>zAdLqleclRp0myUHo*KB<}nN0YV^6Ob$P$@tO*EvEhbB5cR)+XmDybt@;E&$ z(G&}wa}eQzCFG~VAfzJZb4S^?5F-$fe17F5r_wHt%F;wfC3k%}DV8 zl6hj}jM;>I!f*9*ueU4KZ!ygs| z;ve4-^hsyOioQm`_mSGhY1UVSRL>z-oJql9hhGl*PA(r%$kq|vW}IxU#l(9odPs0dI$ zbw`)$7cLxvnvtgq@ zmIT_qd-UFj30Y6Zm&|tSpKL68YF#u0!*imI>b`t4wTab@EZ~WvR1|aS&Oi3JEXI1QT?rGz~36@$)O; zWbR^GI=IhkQvl8!gWx)qGM9Ra5%a|>$U9Yr`T4(jjXA}6fXd{|3xm^`C5vOh z_j`5mIg4W6At7g9ej_8-ZS1TA5-LHxVP-wgT#;#D zQJ%FCgNyu(Dh0N}Ha{BE*kdTfWM_%ckpfda2da8*`C`cm^ni3&8VkmE_R`jNf4$&Q z!Z35u4zp$C^3pIgguGeWiIpwm%a#>eXXzlSxj8HwXYiC`*%CvKW0v1Hz$1VdlpJjHmCm{uM`p5)4B;W&N{*;nOFBab6LbM7cC z7L$v)z3CDW*|*Zxw{or^DEa+noiEG>uLJvUzsq`-zDDb1KZnFzh|~$VT6`_wo@b@06)?2Gar70 z4sK);g|)@{sL;+G4XiCPrU}{sw&^_Qt*O$9JX#;9P+ohl%2~xGw8k1a$n|Gvo`2$o zc>Ae(oO1tBgC9uNZwT~VarHe;rx&U%JGh)>?1vjdo^0?iz7dDPPRYpLvGzgL`Z9_% zs0In<4K1=%B(*$}sc5+eF5g5pmbXKD$o+CBe6}Nbe%uj5ft=5s*{_I=-u6fTWKiU# z$#vY`-fWl0F&6ZBGWI2^jb?boYm&3i(?m1gE|Yy5JrypUwfZ!-u3T*=9&aqc;2F%{ z)DC-|BD*(rwfMRRYz|8M!PLq14I{_51d1cWBqZu495k@=9%f-o7PSB%QI332S!qHo>uL`EGwjp{`!tjMirNRg)K6gt=@cvEE~EQkN?j z8Sv^`+FKve5f#LWpks4b$$~;sx6zTD+BXJH zcOt&>_P-knW*M9#k6_gD-8-u$S5z&PH%ykOuEBH&>tqqPf=v19faKvOa5JA;h#h>d z3}pj-K0naS!JbreTaL2bN41h`dlJKgGaMm#`vV-O8U|5J)@}w6`}6S{R}%S^6X|h4 z=6@@=DU!W6-a`VB2L}$|`{g9^YbW1}gLP(2fU)tg8b-`<0Y(`a^R*NC{E!Vh6I=at zEVk%N?tVQV?IJk`?QjLtK?TiUjEqRxL8;=FK3t6-W5>Z*{BIU{dT>5ox0S6&@CY3l z&3831woTOQU=7)kig4KJ)>bS;YsvUScGzD_Bq8O8q>31WM2C2yLp;%8JG`Qe2B3s` z9yc-$)F+DhuSx0J;TGaRKO^elV&sl4M({lD$XxY3bq-ep>qFh`0TL zpVVsgh^uYO)T%}^ExD6+N3C(g3pOp04S~i%)%pbhgs(qmbMjbb-jaIwa?C4G^k6FWk>hmw1N!>BiAbFCW$o~_C$MICufziowYnU!) z_9V-W`{U|e43Rmvu$uAmUt=ZD(iwk@U+AV=b`+lm}KW#iD~%uKy3*mEKp zck>jC_imy22eg&t`5I_`xvsX}ts2$FHy^*PhhA_a^~a<^D?hsZ5tS{(=iNaOzC!`H zW)CT=RN~9thOCR5pG29S#$g?biWXWFnkn^>zuIg&;03?)O0m7SW#C7nVM1|sV?seS zG-?kDGoRm8(*bR~49FzEM%x2^&9!K>DuK&c$ep4g!WuMIW(gbp&+ zf|=#f6b!k2*32xoStAs@9t7olbk)C~i9{B}^K1aF|LXlZ!`z$J={fO%RsfDs+Nw3( zan@Tii_$$`VkvPsXNEJL^8J!@?yBK=Ex78Yd50y!y!MH=u#v2~h0A%X+lFbLD=E8VW}Tx?WzH)3^3pZ`=PCwK z#BFzP1aTnjXs5!Cc3O9sEr!h`QIU4mEA<)hd*rIibH$GegsB@(>P9wRu?sw}UrGo> zYqAmK7o_*EM2lUljVP@_ezuRbeTHpG=~Z9r zSNg1ZC-XN~JD~s~y|%z%&{F2)hwxkuBnWYa{M5`nB@e5eab^-Zam~j%u)Chvc=A84 z^}^+`B&m9ygZC0c^l$4$vL0HYX+k|#emB-SM)$a=j`u1$e7{N3Hy&1RyT&>h`1)7)dQqJE>s)>6 zeoPfBen^*mwH%44=NdSXZ+FWtWv%Q zEb6rmy9G;>nQ5H=F|Fmg>)gb0$;=p_9024;a_&haha?bfu{RqlFl|d_-;1%1(T6sP zNw(_V4#`fj+F+^LkVQYXXDBcHINkbol0=lU;N_eb(=7kK5Ano7q}Tmn;;<+a8hhTz zO=eQk!&g#k97P`9_Y^;ltyCE7}Efs5xM49j#eBuuZA!Ns@#tv;G zel<}cx%x=dMyjzPC>d5?0-^K2Z6I@NdRpE-0imQUVrq96@H3F{%j|y3rZ)eA9{c*^T@-{;Pd*d)Qe4nVA>P;PhlQQ*RvHd1c0)LxXwc(A>#udo-T&O&(4aj*YvN$vPk)QW z^7Fce3lX-z{GQ+R;qZC`4}YmIZ(4H2^GdlWRjV01&;7&y*o&+qbx+k8170*(XEk}+ z>aUXqo*KV2^z?_9bDEajKjQCLWL?~ZamA}~O`q;>^}1AS@hF%-P%o9V8VE-qUK{Lf zb<{e4{+oXmo1I&)?)kStOmP&|h6*0}cd=O)O~JN7B<)=bgN>N>sS^}%zbv6@O zBDpFW0=^sMr1qT$@+Bq>?)apf1U0n6kl8o5VIjmeK52-;m#jW>L!I2`nIri(@beaa znp+@wy!s~zejl{>dN%5fqLM82MtC0cf0}d6;Tbr0PHT>BXZp$^USB@E49(=G<|4-yzzy})W{SippLY=DRM=_Xs#*BM5VOS6b)-JVj8R!rTxW_M3l{9khv8w z9|-18utMAP*@p^UL;ISc0q9eD*bD_>iG)5fLt}8;sq~m9a;4+Uk+IwNC2YRCr%J8{ z4=;CUoH|nVUuTFfX+w!Q%0>g|@8&4qE(~(906;cF03E;}^gTd7HAj8XYmLVnyAPt&@UioEGTVh1qHg(QIZ&A#1^Z8SV4WSA z-W$5!5hlwW6M%uSxU;Wg|7?cBoUtgNS#q>MY|gHxw#4iF=$&ml^h;x8g1K#G}gOgg$ zi5{>;gYXX()IouELnXN18@kvIx#MwG3%u_o z9qokT-128K7B#Bm%MAt993HHeRFwrtN7S>z3ocBuNaH5|MJG6;llaLC8tsCj(QrD#1=M~CJ?VmGD^hdyRfgBPnKaE69l}5U zLCxHtnMePiVQw(6yV4hKs3U&-oW{7L7HA+H>yAbuJ9^6=V zsV_uzN)UbGfd<2(IN1{gquz9?CouhX2pcj!?qfiKK_yhl_ZkZ7xYTTQb3c_rbFY_I z{OgI{Nl_a8Cjd>vKR&G(ABbcK-*{3nBM6PciX8pg;?yJbO$cgZ-m5KG&xtZHpWA+> zaiPcqU7wxNXI^VdxWtJHMiRBOt+C@ro{rK<)i{)vyi$3!10kEox`Y=UhG}jTzaf8d?{D zT)Y*UukvE}q7v{(M2xGhHrV}R%_0~=X;d@h z-9Hud0HOfm*zx)l`M?3um$_Y!8InjghDY?MAv=RzSru_ka>a(qo&j-q_yhD{OP?~A&b4|Nwb_{a+Kxx?>S_7qH`)ErG zn>ap4J*%glx%i+cmE6?ZG`}TU(PnI> z{jeeiWwws~U0thCY2`;!XZ=9SfE;grMr&ixw)9U~xM3L!H!Op{IDC_-nGk5`#{vxx zu>#s`=*zi{!xOo0Z@ga~!V({~2J1fXTRN@{O2Jm&(vy&Sg%k7X!?wto{uz(PqFuD6 zEt+Od`a(Q!aSkLmkF2J{+MzQ1#aLY_Y*D~IOf|RdqbYXbDmCf=#f-=36&o{AC`K|h z#e%NTt+7yS#3;RDSH`QkWoU|(T%rl_XgNMRQm87PFnJO2pDBxfV3A3mO)9tqWgfwz^DV4S1u>U=nIAn`Bd( zD2G_Bh5|Xay~_$E@}M`nplM+zQ`KmigFVt;19=*PH9ZlHKu76MiKsK0OaqcotXE5j zxdvDTFfFoLA%H{jP%KJUT~i~orwfzNpuhv*LWE*-R0Vu7Xn^;904Hi|Jjq^_P^Y@a zXN7X5RNfV>iW-uvMs@2)4Jw9BP~|v7$nJCnfw|GRZXmFGbZ$2g*rtw+1hyaQ(+O+= zz10m3^Y;+S%v1}k`YvFaRoAwyzPR4T^WvJ<9Zidff-_Bi0rSaS9WVh_jv-jVJ%oPj zA@pMp){k+GU=6va4-5J+ne}6A<9=l6Vg~v#R_MoM=*I@|h48?d-_`(ATN^{Lrl+96 zxUd6_?}i-d%M=uVVySy7YVBdvp%FOyJNn?b_B5yq@}w(LQD?t-?fH@#$wfm}UK7a; zpbe>r`o8$SF{E;RNNPs`J)%Tgp#yW$ke5Z<4Pv-J0sNrp+|m8cSS<4_eRd9`w9i#fkzncfbd7$vp6;PA=lMth0YN5 zGBDP=WdO`X+AX^738Okh(%&tB*HhF!3O#sd4iw1jtds#p)fX~-2u3q6)-06#EBK&qF&FY5&jpOz* z|K2@fJ=cfs=!cxyV&+^wG{E~jYy)7$;tBD{uvmF`gv|<`>S?2>&Xa8myYxpL(}y-H zH%nViK(I|8fv zI{+y>I_p=HEqut}?BS10t~u0Fx!=g`;6OA6p$IxQ2fe`qcGA2-FfTjoq^Ad=Zurs; zYBd-R9+vN*Ee9hN80Bx^(hL+rdqQ>#8d%{z6d5CwS>ZnnR={}UcA7XGw#Boy(^11w zv-W1Em^2(qCGI7Ok^uv=Azhhs$<}Ow`GF-!{Fc7NHLLnN!%+(Ir9mSQwKaz+S_Oek zlOQ;eAD@R$(iYy=Y&}b+C{*(8lq9%$Rl<1{c2xuXo4mbBxb3ZeA)00YJ?mz*=|^Q!0sIi|9kQO2kobKM!~jY`%(IK6!MFDyO~!&R&ro}!SB#umjo-QjC5}JQTCm={&!ptnl>7Z zz&Eq$kaSUmOs@A4nmPtKq2_et7&H){ z=}Rw;K~c6}9Oik1YUQx$Ftr>D+nEO&X@{}MS>8=uKxk~zVS$Saoi`TcqD}PaSQKYF z@^r1C4PCiJ~u$4j%%q*p)JR4U5&P>(%WLQUoZk<)K#qHvyw z_1Fh#VwceEjHc_~nqJ-t6$w4|L81+%wCg+|+O8pbwuKS#J@!GOhQ+jY9!kLdw$R8T z*xflSqWg-Ft=B8a%oLvk4I66v16-X|3m@}DxuT1!+p1pm%;dU$TJg9D%|f`%COUKh znvPFwtf*Ol-XU~?K3Iq*;JZ_3dNEp$+E)Bg40ZvTQSU|QH7;FC-Bielo-0Ev9Ex^R z!G1#X;fV?z(|$Oz7|q3FR@3&S$VpMDk6*Jy%PU2&IHw0nQ8O*on^Lq^`(eitv{q4X zh_vrg)D>?(K))_UQQG29mV&!hgC(yF^;0-z>UFBR+KM}6$Q2v@%9Nn*5WcpYKKvBs z!fnf`>@%1nQhBmbPz`1yX}v(>>%(cjK}3mG~htHg`N=8*%kVO3Onw+l^QiJAot z5=ya!5|~R_QKcu$qq5pExJg0y9LRqzq`OvvS6V)W-dc&m18OGobpe%7D3UKE*AmJ2 z&mtPO3W&-k)2vl62jonq#q4+FWO`^7>eOy-g0Tqvu@bp><=`#r0Gu3^9X7to`tk(*VA0vUgy8zYj{enNi* zA}riv+KlN+m z&&~(0LzCIjeCj6TKp`K{h`wA0OB;7;wH|`6G(m8;ay5tN9TKSZW{9bPLu?_YnLj-b zHDagdokiNy^PnttX3Iy43xN$;T+5CEq;UHCdT5L3G-|d1Ry#vz@CM}C^dOLCZa{Dv zrpq@`yGeiD0Ip0cI<_3PeN*U)a^$H9%+econ(foYa`Y*_x`F0wME)-RprLvlum2~6 ztJ}J_gD{`Db!VUyL^u@bv*U&I)kf&F*7VUv7?xeB?-*oTGe5eQx@Df)a6OW9Z`T$jLoz5-dr!GEnS7Kc+)@qK5I4 zO5<~t+`DpGxfw-S|J(#C-hx7Gey>HMjMC&dO>#&>HjUk)Ma_4#8y?$+%GU$*8g;1O zt)Uv|OF9_pWM3ZpnYUW()6$w?pG;NTp>qdMpkymbF#zw6ttiC0;n&9StZ4Y>@1vU& z(->XtU=6wr?F^w4B>NqIWg9QqMX62DEo{L{7EjEl^S(f-*4_K)DNb`X02w^D4NrM^ zN{#eRnn2aYr2{2=gsE<%blC<2VRuWKvke8ZI7Nug^kdGgiBAv&Ft9sKB&8`zC1=a% zS?CEo5p!R+F`|PuVug{IXLo80=A(nGwj*!Pr~GAa0H5Q^lNtO+y3n9%?n(!mwjD*8 zOm8KOqzQtsQ^wLSwj*#d(+As81irdmP>ucijpNlAR3p^-r^cecw$6ZNxHsozuZedo z&e4dzL(GRoKZ(EkjTe0o#LPAt{UYxBC_(f#qv_8(K{J}71JQ?C*EB}=U=5A<9E7W9 zpxkgovqtDv4bY%VZTVqz3;xooZ5xP~d=K5P5du1&Jo@b}Pz>9?dWylzuGwxFZHFK5 z@RBr&ahOip4HDO&JGLA3z$bD9gP@7e+}&RT&$qcDc*3aa`$7xPDzOPXSNd=djH*{7 z=)Zel3YycOy6r{H@!L|Gz86*wTjta0d%+xzqMP;toVS$TfXB`^i^aUrW0s@nR2(xN zGfU(1m7E#1*@v3J_BCN2^7^P)e?CHuF5QP(iMxbJ= z>jP-H-#&0x3t?IGoP-a{YB&#kqhS@}%3Rlzd|kOQbnn;5N3kji4q`j|2ErVmx&#NV z$_%b0{q<|);WV6mGfKj~_`B`DlWeUHN=s89%`}C297J{w#b%l%1;09yG~`R*W{l;u z`$06!^B=ecmEDUBmx=l+Goil7(jMR)*!t#O13MyjB$eMgG*aw9&B^0I1VMV#wE{Uh zvP25CdN)LKM}C8xgw(hUZV>HJfj$?Tt~VxgyV`lPbo%=T;k$%eO0M7tm^-+t^9ujq zvXUDL4`{!Gs22*Qa}ObVlkc<;%6>2m;o>3Wg`@nbTLp4(WGNIn3{Re_F+@?F=%kpf!@hsS~e zT@0k;2g!9V0q>_X~oXm%5o8ffg zd*&F_Pe>;g(e}qsnDMG?@Gv~?Pp-@C45r-D)yI$zx<*eNgEJ_37Cj}#>U7(RPubj1 z%*_dfi-RED)@Ny7o)ue!YXS!JQ%+eVF%7;4PM9*4s3f6Sl^|!o7jq%>o8w@4JL)kH zS@ns)Jjw;U$+}Ay)cLnx_0iRInZCP+9Gl0&{l2VX*c7~lORR)H`Tq<35~JTQnvL3y z6HsqAPdepC)Wb5gBUb0u)^p|S>GdCxLUJd{SVW@!b|&&U(vM1dgv**?lrBAqnu)VL zBy@In?50V4Os1Zduu2=^qi?|2m>yq=yzTqk)Y`3-mRh^TLiz1zWKSbwdbSc-e3KU)b_)5r_=IE8pH{OIrV2MJ4Y&F};c`jBP5#Q2 z{7nH4*5896&?PyUm&~eRH^4yFq?dSm7^aJyu%E^FFk_KAebU~NK0l2D#dQ{9mOt68 zxHnro^7)f+b;Lbx(3fmruxnnCl6$d7Jvd6g^c~GFeOU%1EPV}SSJJcJ0QhVGU%5Ge zx|~M7$b!b5hRq|N%PFK>I&oaqO#_J4S(vDd9zTr&ECNFrU_KG^ia8G$47c+cXB+V)A&m?a;&9c6TG)0n=&DCbfo0FmmSHE}DM^+{9yO*%@U2Q5k}QK3qG4T8JH; zC3M+f?5B+r#vF`te&&dAH>49DW53nvgin-KCp6g&O8Vnj6pA|uDnV*ED_lE+Hamx! ze^jQRT2s%VKKQ_GUaMAAA&L0gBpI>Z<)_XY#GE(Hu7W+PhpQok-otKa5t>FcLHLCS zKYAQUWv$d;wgnA755^^yrkqFp@VsPZT$0WIT*QWcAveQIuqz%g|yWL z)CD(K&^Z@iTAn1iQ}t|h4>{r zo#c`)-_tIn@>j#jd(`wAP~Wx^sLu-2QKbTPh?+XKHXCxlG~$5YLXUq;Qk}X7t6`jN zvs8~WjZ0y0OU(-Hyp16(w!eBs+ll-Aw4JDDdvmTs_nAG=be}OTyN;wEm1Za4I(Z$X zfjiOqTUb(m(Vj+qtKG(1a6h!rptGb%U>t8VpXVdIDn)m>sECMqORG8WN| zFOV%Ydx-q4v-CMTvh{h#hp?AgLFq#@&*8>QSVAS=Q|H)K!8-%sAJ?Bo{sbO^3oU4m zpTM(R+=RuvpU^Pe5K3!*LUSDF&oIPewl$6V3D&~Xo6|Kv1CB#Yn0)&);E>XQM`)g7 zr|E`x9G43`T1N{&xF$So9s`hZ!F1YV0PNsc4DYX1&AD|f5Q*tfUL6WYz38es zwATEKOg1X=S#E1P8vh&U<~CaW44jz@gXxmr;98W~`W)C*kXjBBTKgN?W|7Tqm@Um^ z915S(xaTMe_wb_EpCJeDkRbhq;fJvtpsjyL>&=aNYq6}RvCmO6EcN6W;l}~I3HdOV zp|qqP?XWz|w}!`ZpTjh*QWgciP=>|0#Gl8)_R;j*3*ij$2m|}pjsEr=sqm+H{FV~( z63SmdY}lSZkg`|NR4~=8qJpQuL@C2WW+^qq_4a@`U5Yw|_!3 zeo?MKWWk916-frm0|-|B1uH*-FOO%vW46IhVdxSndgm_$w@|HkiTdJsBY1UyGtu!c zp}66#^-DO`9>&pruTZjO7gM&i?he;h)pJLN3zfZi%_>vSl)u5;8_IO1d4I$1eD$vs zy#yBB%@~Um{^qOShw=5}?XT(e;jkO-{uG%RHs@_#LA3t2zc3ViaKJF3=7+C@n&Ad= zD_Z{wSvO5MrZ%r3mhIMKn(-RVcd!}8Y{&aK$7|{H*I)=`?$qK9@@<-ROgp}TOrSJ7 z8R=Txh2DRI+B)1AVu;ZdI_Mu}QC(?|w_s75q#x6HZ&62-OV7SV3mi5LHpIeJV6oCg zz|tiDn11ySV0maGU`hEGwRIda$PlCCS2XG^Smr*?^w__sjbf_4?xZLP*K9^mo&e7xXodpBkU!uEzeRKW-zF^y5e;0A9Lr1uQk zE42%d+v`oF!al;#JvKBL~9R5vN8>E>iL_%DMe1JjB&65WyCVT zRQtVvX^9Lt1!NfFw6RX$*P)jIE-w<<r&XqYQ@6K{eSFC@F-0E?+5T6vARz~p7`-rE=U zl3nt_OWY+FT*Im^lhj_~Tg*!=sGA^5PwXe-Z}flcgQ=+-c!tBSXeIj9d3}k#d|#qo zAkprF^!idOQ7;_f8CcxVP}>56fmD5D3+`IY?+Jev&X9;QCUs=eT}7Y!U>6$UjV*m2 z=z6^Kc`T}BcYvtGg66B@V2Qv%ROldY%>2gMu@Wlf-xF|;UiZdf7FRE6yP~K{uPdrv zyx$e;=|CUc>;u3-6+CBr0>GQ;fTwDK!@-dV<`e+#@7n@Ofp)VedFKpc?$`vg37zMQ zeT26Kd@BHd0VYX3#X(NI|3fWWiRfZxJ^;s3lR$Vw z-w^df$wbY!`zwM2u`NOyXonyi3(1(XgK&}!r)xc2sLJP6-wMLvp64>y3gZzR{z0Qf z>}7dp+Ol2pBgk;!`V5lL>Vpzz8W)VEa>v11ofXdWGJWqx{oCKPSSrgP4IV+8IT`tkS8wydf6DgrBztpO5xI}&@~XZI`KL}FL; zzZ`y=MfaUh@r-}mW(r=CV*BH{N?XinE9X@%u8#&V5 z?XhFPD_s*c?*M%dnwI?d{UQ~a`YqQFje>O3t?#3ey-I&yzcdf^R3*u{Ro+s# zWvw2d!mm2^3ql4pQEA8M^(1hsocThy`=1?N*3we#ivak~4oez;9?|%7X;(?@nZ|(xk3C2Cbj2xR_jAT{?k-6L*<)#}{{|poqVCcS zd&>M_lddw(i=hvtMz+$1RvbNR%<{&ZXn8lBfHLVX-EdsUN8D(QO8W?5U}@cPFho|* z><%-NaH}=F-5p{p4^x^p4nQ`FSoXHx11YEK7~CxoYoK>bWKU-f!7{R7qcvUL1N%n) zZxAKP^{W2rTijI9q+hvBsIwBbmg{&}$$SYBc$bNex zM18G`_4S3{()LTi1-SciI$jCa(EOf)?b!;?I2E^M8{3Ylu(3U`3YK-Sv0Y2&rs7z6 zI-~%wST^;>HjGxM;t=awO&FUiVPb@s3MGz~#X%q+s1>Fbso0%fP+~jAy+(;!n%b`v zxW8LUJ=0*{{Pnp;?3dE9X*keYHe5SQit5;1bM%Wem}Uc(3QiW;Icm`p`zo?^#pc$r zAP`nEp(mb+*UX}0dx1Ccc2A*&&wJu-Y-`;<9k$l3RVn_IV5Bb5x0irI8JK8r~u13NL?AQg8C?;$jyE}4Q$ATAU3)f<;H zv{_6(8Pk}2+K{--#x<{YuHr;r9LogJ&=&?kP6cg~jo~V@r2s5>CAX3;$c6!sdJF&! z8!Zd~jtinUvvGj+wk9mub%NBZAMT4c&Zj5);y}8!pP+ol`)RqmQ~Und*R*4)P{o}h z+POcBf@Mb<)iH`Lhf>zJ^y?VPU|01A_HXA2qu}0Q+Ij$PANs%VoM(ncxGs`z8h`^J zdE(*#?5)0delcz~-;iPP3~Z@2EN<{#sz$NTH@S29ycl#rw_%?(!Ek3jk731N97zjjBZuGwy?!mB6L~a1Yy!VUSPJbAtD)83QBFR zLhH5+tqYADhHX0MmGkeI`9@f}2n(10a}$p3aG_38u^GyzyN2Ol%_aJ%6_pOhX*gk) zA^0BI&9v|%AzJ%AorB%ka2$?jw`O2HsK*Fw94Z>A?Q<9*jn{33?9Q_UnX0$>Bo~Oh zhCJ~RxT|${9Ys+cf7biu2pkQzs%8XEgd^R+T-?m_tEu1?u?Keo^F^_jH1TecdM+;k z@h`Ze({r&e>OeQ-VsGJ%sgr3{F3vzFsryLqwVs?pQ%B+szLR&ek*%ISA8|?}ji&+( z7-Y)T$RI?KdKJm+BlJ-*8^xLeXKWdn@b;z`LpQVXFo@w;B!lY~1d-BTu9AXFI1ih` zeQ#~^z*o6pGJkM^=j@`NMDxSR7!vNMayvHB@G&qdg!dtcxi=$e_&9J&tsjGp@!tiIyalkD zB|LYm_WA2PQl?$t3WWdm1+uOaH$}(SeqZg)PdXC|-(E04&;Q z+$a!JNyElrZV`E&E!Fvu~Qv4&>xD`tM6lG zf#(H}s-&KW>8ImyYqW=69S_EG$~0P9fD7aD;il>0txUTo33Fo&HyHeHr3rAh`j4R9 ze?W?-Ch#vyhq0)RU2;n+CV*$}6Z-cA90a!sdQHTAaLE=rb0VzAU(zooLc4bAXoS@b zBYQ9rkHpP~&>pVXS?;Lu`IRQXXbKLaLAh|0uyGQOlC^289;wj20EfHY23n>(LLFf> z3^AQU3t(DYMi&?01)hIwWQ|fEAJlM{Uo~!2KN>X|+gU%=VJoVe$$(`}#%Yq9IWRQa ze<^jKE0<$)dTlb68dn8F-39k(d>#ltZmm?x?n=ft%W2dU?5yZMO0V-Y>zaX6z}p15 zPg5Y|{Ki7o)ev|U0UOu-KBpDcvgK zUJM?`$RcbdWiKRlT1e&7vA;$9B(@04WlO#9bJ$X^bqark61Jg{42)lDWWYOEIr>_g z7|GIZ=7R)anYGhUOFX9G7C7(|)&zG4o|^Bf4j>bCV}2e;Yo@@##oYz}*&5c3_@$fB z5arbW$J%$mMRh!n^LF>>FpydWy-*kVac z>@6{#EsC*V)Tq(eHDXJi5fyuv|LlA38lK7b_xb$sbKdQHyR);iyR);ivoplOjiR4l z#{xMv^9b;mi02`TMw{9bmcoU~hLxrEeEGFidwM=IwnrYQMX-7z?^AOFls+w(NXoOt z(~NyuGLd+yH-^fn%l2XIdPD3~;#~`!o-48^7s&aFAx}l5OPgU9J#5^j44g!&2lhZv z_iFN0rVKwO!IqGG76QQrorPGxFwWj_$p*YIZl?c zpqoh?rMS|6W%1odb+>gRIX$GU&hTDvP*0k5T!xgeI6o{DUp+PwL+;#J&rO|s@}stF zwAGgSlSw4G@g;1Zg!_q{$;836D>4$LdxyiS$;6i|{L+LnXE@S!i4>OMcvqBA4*gO_ z`JWLcl(+VPsX3%st0kpTjw&{yEcf5>r)HFm4ClTjrbecS`>66l95_xTRn>cgm41{g znttmJednP4;bL=-^mMLPwGK8J6QSTHaOT`|Nb+I|^0k;jlS_R0!nJyLF-; zhu*FbgS;6%=^q`2N8Xb!LG4shg=L5@bECR>*$=nbS#S%_K8+mTb=NfEAm&UrCuB zQZS3_{rRTTyVgAO^eD~duA0G`wJ}|0;BoL`1a%Kbr^4pUhpN(Puya0V9A%~BU^kso zD>A#E`b*4C_!)|4$dU^7V-=`36Hig4<6j;0nu*81%?6to#>;-PIwyK=X^!Cu_h*t2 zZS-dr9b;t0m1iOQO587FF<};QcUxG&)F*HH$}E@c|CoQlqgkl#k#Je~v&6kzXAU$W z(+E0blV)BSb4ufq4BuyyAnhbeT#S4|Vo*Q9t87wT{VGuD0f|9%?;~ydolfK^T>zsh zArzZb=Zj0wGA#b-#bxu)yuFp(l9Wo2;QAD~sr`R8HC3W`Z#}kBigJ&R<_is81nyouz$_2PK=;%nm~XIe49KgWnm zMvmbP|u=jdMx3Te}qAc1crLrw8u`SyOjmCU3u`rfxIn=91dlHX|*D zzY*Dmb4jEy6;911^`@|NuLaw7}6IQSPN1L#| z0|__umNfqGI*({bMZ9gr)jh_2V_2cNeHd@V#_1d1d{35d$oE}9Lg}}yWduDwuO}g> zOU{mS72mYy7R*Ifkme<7;zu?wd6^^(u_zY?d{@h*8)io*gl_Zv&6vN^D zLL4q~Ahwy*z8%ZkzDMy|ktYXYgFhFMiaz`76~ym|k5f3}=16Q%50In|Wu*Nx^Z{ZY z_CMSkGpY9G0b16LXen#Xj2Fj3Ka;46SNrF*v88(QSzyGcNSuJaAwD|UflZ4Msnfp3 zes+e!#i$?nZ^SsgERK0L?JWmG&Y*6uIL;cw4a1EXs$1K{&_`wJ>bE_uFyv9w>>+$9 zaUmUiID&!Db188S`yYbGnW?svv?6D{rRF)KH{e3-Q1Sj)c)t{-H93eJ=$7#s5^Q4k zIfk`S24W+OA8tuMoE08<;R*KP|Ba6|vEbD2LS^`QDe;6}%dzSfT4_iq0q%N@u|n=o zp5suwP2}zo6LMR+Ss~}g{l@V-cV$Xz5A`O}JpPu{6k-)ktm5`&*HB|`jfk^Zge&6p*Q)_&p(v0{NH@AsTh+rbYRkW9~ z=pT#Glc_e3p3u-yrqv>==-)JSTUL>Jc#-w?Dx%y0tObo%qe*RhNLelEx4`JGX3=U= zF=(2df{7?L?7}gz(!dRhm$m3cyr_eROdhU9|L;4Ar8rZQ{BrKD$681mbOEe9p z?O!8za*vjJ&*&;=JX-7FnuN@$W>cG;H^7WcnT{r8`0Yfi!Q^5dOcHbYZ@fM=yyiBv zLh#K%BZAu++eGlMcP45r(^(;CIl7$9qbuxxxH)N}**wmhJf+gUoHub322r0XPk;Fm zC2U)Z)Bka_P3)?evAfpZ3OgSYZ~AY1tZ7x6H%)WojVt3QBlmug6>?41BXa$tY$CVh zjfr&a+gTy!C%ILu*2-cU{>Q17W&<9zJgz6R2Kizq%-w)>YZ}_BJ1E&)_q#Txx{ZT; zePh#smSdjxQ4NW*Hx4irY-@|F-+zaLv?f~KyTfK9hcR`n@O0mVc;+=Ijc4Y1QYrA2 zi9}Oce}raOBl##2<3eii9@D{1q;l0hmb?p{yD0}M7H27!^>{*nQms1AzLKM2Hq#j+ zve`!pX)tCp>1;HgS+s>X(iT6fz-@tjSz$1QY(b6Y!ST>*3uz>bfu&neN4sz+?A}6l zXjX2=8zDL&yK}Jiz8g;GT$r*I_pU#t!LqGnBRRSa61I^*r@KQO?1mm2pnn7NKG+9B z?l#h!G#w0&w&6)j;9&6Bj;FJ42SMHKc-qw?75ekifg21EXK;!`)n+(H|z zOLs*_ni(+|#~>r3Guot;M2NZR;PeA&_rDZeXI0Q69kPEQlRt`S_1;k7SnZ zU`AJdBq8*#9?K1Sl7?DjbU$E`@di?SA7-oxfj?u$7f5h4 z(~}{N$hXKibqKuu8S6f^3NrR%-HTGqIrIK>bo6o^V=?_?|JyRwfvM2r09G*qZXCcW zA_kW#*z;2b4-A66`B=~$njXZ07w|@!5jS3=W`|`#9Y0$5`yh!XNdqDH7qUUXXi2}2 zzVupC0>!6^4?B(*ZKR8=(B}{uF}>~&nTK#ZE$I(8`FF$q5PKNEFQaaRe^2NK!V&x~ z(+~O{A#rNaT$^lYwetvWmlpSRfTMrWss`7icpfA8K!XAj?Y{v9^(2g=^v!v^V^Eya zUM;TkAn3b2Nz6@vIR&JKCVZ>~wGue)Q%Bl+S9`7RH7vVVQS{`I9@7uvM!1u>9 zVQO8h5xsG(;he<}AEx?cyP_8LS})l4D+%`UTgG3wqA#Y@{8{|PcfH_md{Ou2(o!!* z^m4HCzChG%@w557tq(LgPR5g?TG(}*Ou<9D1}8{U?MO4FOaW+N_6c$VJ!&SM#FpSa zRs14-n#0d0(Ms;M7d$^n1`*9I@HjBL43Of;<58`cDt**RkL`jmWxlXz@yW|w{m84 zN$;cFLX-92j_DQyT=MZ}_=C(O*P|fjJgH5>I>MOqBt_T&=gyOCRM7XjfV<(!9boDO zQrCM(L#Z3?m({1A#4C|Wx=!MC9TZ$ZLG%Y56kS03gN9J=BB|_==Y*EgWBjYJ)+)+pFI5B!jDYPn5L!cym*b& zu5gE1xm`fv7|$$TM9J+(+v>wm^*RZNS-AP5>YvOlRec~h_fJwwGs}|P@(J;ug!|jf{S%GP zhJoe=sjU91B^PIo;gCcM60|yuzkx>{>%(E}4O}4>!mArN1G+$DAqgf2YC-Qp;y3l{ z1_~KYi9`1)==Lj+&|Pdqcawx}qYV` z!gUfl3oOuiqF2yapr8{N=U^xHgi*IhwpYg=3HnBCfRH1X$Th42x7%cr_p`NF>Zm&U zdHRFiy|PnSkCa2Gc>4M_8j6%ufk(HISeL7S_Z^a{`MAs1-oddP`V>yy!I@d5E+5l6 zVGA_9O9C2B{qCdwy@viUr4_>zNRY4tUjIcJtCv`2$6hwX|BWv-qWN)Fk;DW( z7fA~=M%X7Jli4#0R=ZOlcq1bFz8eCO_ee$0`D%M^cp8I2t8nV*i}`wckMzPjUdQht zPkj6W-rplZWfG5YUb{|gDSclOn@Znm9P|lN2jAi$UfD^4nGXrn2&jQ;N(+p!f&V4&|9a{~XZcx-E@xUWKilDtyXwTJ)uGKJ zl8YVtMSWtp80E82N4*JM3iDo)G9e-UrKkEf zO9XJ6#4m}!p2wuB{k=K3AU1&gPf&6OzmX-*a*f?jNHwZ^y|bzjsr4T9eZkIBT@P~Py3}ge^d4tm z7QA^+ekMmS@WWeD{$DBsurDF!$RWJO!AJPCV{-FmzP76D*g&ayu9v^&W4oA-?e+^3 zsSokh%k@UNE&P)97M3r(Nl%b7*|0^RexIz>A6^JF9=%I8CNz`$$)u|BuIZnI`fI*< zVO8_(e)6n(w$xPf5GFOb&Mdj=F3vER(r~=>8gCM>msK!bn^El9VDn|ptBCGK4KWM5Ie{Wck~JU zZyRUDGutB-txp{<6WBTsg}D6hp@Ti0LayVbX?tpUuZ6oTt?*P5+9Yk~u}5faV(Enc z5Apd8mF)YlzXPqSons#LnMJD*F}`?gaG+HMr-pTrc<6j16RtVZA(c2_ES`+?qrcvantD&L`Z7NJ{v+zTSQBc9}dp*E9J@jkq zw7ztDwe|Z+-&&{Jtg%kJd~cQZy!Upk)q{H*;fX7)rwN~6!G|Pw@D1E>9N-%0M)8Jw zJHFV&(RlP;uxdQ>ff9GBa^&49t|+mGw{CPA@_-C?8c5!^k+asv&6_c+Gs*b?E*^9^ zS>^~6J?KsCYIAj&McZWR!)I!GJoeD=_uMc>C+@)?`b2aG?Wix-Yto`hI&xppYmBe; zL#2HBVh@LMQVP4M;hwmW9WbB4#TTj}U9r_Lh98Hcx1=!+AwKY1Z_~tvP7e?6PJ&!APA#!M3u|JJ_ za~UVJ9l{?U;~s2$?J~qR?iAj5A=1C5!A}|*8MfuEJ>M7W$8<*2b;=$4sKnAONmy=d zN$0n6OX9z^Yzg^;K&&?{`)@=~H4+_ve2n-S<7@pO+lV94eGu_hsS@I(=4*K5!>Qi3 zG_FVRUY0`jR|?fvS>hVn!On{6lJ%N)qG@bq78NRkXY7awE# zeSdiDM}5h&f#6(*M!N4Ai0$f9gw*7sXx{3Y)yc{iFr^Ij@eXaxk-Ve~4Gh_f!0aLR zF-EIu_5w!u)5fIU3fSyV8&=7GkEUoS_{t;V-*I!<+FzmR_x<6R9}RGsVSF&WuY?@x zMwXPL4c)8Z^HR8ZJOg)sTE=@}%hI@gyFkHh>r)9gVKr>>M+Q^lEr|Z~lvZb^RaQ|8 zMV>fbp1$)KZ+;_P_CFs$YtYt}-$P1MyfYCLNS~3jkH9C0_6hyLoK=YnZ!LtU?rMiK zzx5+n5kxPO&mI~kRG@-DhCP5UD$)mJauE!zM7NON?nAj?x|syrhvUJtsET@$sUJ;l z!mI(O4G>-af$NoN0eWToE`%nMCnCHEp>|Z4Cd6m=d&TNeTcTLRdPwl2%O?St4xJy%2QrV#=2wEYpk?5>h zYFSYuE?BkH8^Iu7Am}wn9KP+6=w2_N_(-sXO1b1huvg46aIpnco8$R_Fsuyc9J^DO&2p zjd$!9wIEvP7)IOls@26v1`2#3Ly@bQb=?IbeS@?wXxQF)xqzicYhV z@S#c^jh>+`Nj4vgAj#a4auQv|${{MKRfm?(Yx2@5FGjQvP58p1qZKK-6msLmWw&MI z`c&Y}{(>y1(`o)PzB;cdjD z0y?|FZMG4Dg9Kq+C#4aWq(<0_z8HE%YDD3*e2)c(5SmyI!A!e4R&K}3P*XcxB>xlD zW?MEy2`*>d5GmNH7`lBr}N2!tsvB!IDAsV8MLCbWGsX7*T{iI=EUAmK!OkHTWnEPmqyF|) zP}QTgyT7t*Dc3Y`yg$D3gx*j~McB?xUsDiw1_yzi7GmbwJT`Yh+#jp- zFt;A z2R3m<#Tn7}Tbu?*c{~IXcMCm((0Fa1DW(CIS+ox^;C!uG1WgdS!Huosu_N5NZShJ<*x?cXp~HC1gvP;n6~nCO?Yn|utE>tG^SN(Rt*(2-6w=O z`6HnveHX9DcZ|6r`DU`R$ku2=JG&_6?OX*0u?fg}S2e+!gAT&MCbX5?H`|ab@Rn$|CFuZm$I?)3 zr3@3rGmALRd=x#8rQdKf%O%ZdBi`C4&!sBQeZeFQb{s#&< zvY{u2KPVK_VHRf5W?6zqZIA^1NOQVT0WSq#2;dah)≷X(O*gSAv&-j_ILBa~g_{ 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 Date: Tue, 17 Jan 2023 13:51:08 +0100 Subject: [PATCH 205/213] again fix constant changes --- openpype/tools/sceneinventory/widgets.py | 4 ++-- openpype/tools/settings/settings/tests.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/tools/sceneinventory/widgets.py b/openpype/tools/sceneinventory/widgets.py index 994e6c8108d..49b0dd407dc 100644 --- a/openpype/tools/sceneinventory/widgets.py +++ b/openpype/tools/sceneinventory/widgets.py @@ -8,7 +8,7 @@ def __init__(self, parent=None): self.setObjectName("ButtonWithMenu") - self.setPopupMode(self.MenuButtonPopup) + self.setPopupMode(QtWidgets.QToolButton.MenuButtonPopup) menu = QtWidgets.QMenu(self) self.setMenu(menu) @@ -42,7 +42,7 @@ def __init__(self, parent): super(SearchComboBox, self).__init__(parent) self.setEditable(True) - self.setInsertPolicy(self.NoInsert) + self.setInsertPolicy(QtWidgets.QComboBox.NoInsert) combobox_delegate = QtWidgets.QStyledItemDelegate(self) self.setItemDelegate(combobox_delegate) diff --git a/openpype/tools/settings/settings/tests.py b/openpype/tools/settings/settings/tests.py index 772d4618f72..8353ac1c8f7 100644 --- a/openpype/tools/settings/settings/tests.py +++ b/openpype/tools/settings/settings/tests.py @@ -54,7 +54,7 @@ def __init__(self, placeholder="", parent=None): super(AddibleComboBox, self).__init__(parent) self.setEditable(True) - # self.setInsertPolicy(self.NoInsert) + # self.setInsertPolicy(QtWidgets.QComboBox.NoInsert) self.lineEdit().setPlaceholderText(placeholder) # self.lineEdit().returnPressed.connect(self.on_return_pressed) From 397acd41642b010d128c016a851f6ca1e3c99da6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 17 Jan 2023 13:55:57 +0100 Subject: [PATCH 206/213] better validation of "value" key --- openpype/lib/attribute_definitions.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/openpype/lib/attribute_definitions.py b/openpype/lib/attribute_definitions.py index f6c34cebf3e..bcb2b4a39ce 100644 --- a/openpype/lib/attribute_definitions.py +++ b/openpype/lib/attribute_definitions.py @@ -419,9 +419,8 @@ class EnumDef(AbtractAttrDef): """Enumeration of single item from items. Args: - items: Items definition that can be coverted to - `collections.OrderedDict`. Dictionary represent {value: label} - relation. + items: Items definition that can be coverted using + 'prepare_enum_items'. default: Default value. Must be one key(value) from passed items. """ @@ -495,7 +494,9 @@ def prepare_enum_items(items): for item in items: if isinstance(item, dict): # Test if value is available - item["value"] + if "value" not in item: + raise KeyError("Item does not contain 'value' key.") + if "label" not in item: item["label"] = str(item["value"]) elif isinstance(item, (list, tuple)): From 9741104d09bb8338dbfd2c7422ebbe5ae733a710 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 17 Jan 2023 13:56:33 +0100 Subject: [PATCH 207/213] change comment --- openpype/lib/attribute_definitions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/lib/attribute_definitions.py b/openpype/lib/attribute_definitions.py index bcb2b4a39ce..04db0edc64f 100644 --- a/openpype/lib/attribute_definitions.py +++ b/openpype/lib/attribute_definitions.py @@ -493,7 +493,7 @@ def prepare_enum_items(items): elif isinstance(items, (tuple, list, set)): for item in items: if isinstance(item, dict): - # Test if value is available + # Validate if 'value' is available if "value" not in item: raise KeyError("Item does not contain 'value' key.") From 0994795dc99819044b92b2027f3f5207457dd3b3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 17 Jan 2023 14:09:02 +0100 Subject: [PATCH 208/213] fix another one constant usage --- openpype/modules/log_viewer/tray/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/log_viewer/tray/widgets.py b/openpype/modules/log_viewer/tray/widgets.py index 981152e6e20..399d174fa6e 100644 --- a/openpype/modules/log_viewer/tray/widgets.py +++ b/openpype/modules/log_viewer/tray/widgets.py @@ -11,7 +11,7 @@ def __init__(self, parent=None, placeholder=""): super(SearchComboBox, self).__init__(parent) self.setEditable(True) - self.setInsertPolicy(self.NoInsert) + self.setInsertPolicy(QtWidgets.QComboBox.NoInsert) self.lineEdit().setPlaceholderText(placeholder) # Apply completer settings From bcbf43479a459687ca0fc5178af31bcb310a4141 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 17 Jan 2023 14:16:09 +0100 Subject: [PATCH 209/213] fix key in event data --- openpype/tools/push_to_project/control_context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/push_to_project/control_context.py b/openpype/tools/push_to_project/control_context.py index 02f1da6733a..e4058893d5f 100644 --- a/openpype/tools/push_to_project/control_context.py +++ b/openpype/tools/push_to_project/control_context.py @@ -356,7 +356,7 @@ def set_comment(self, comment): self._event_system.emit( "comment.changed", { - "new_asset_name": comment, + "comment": comment, "changes": { "comment": {"new": comment, "old": old_comment} } From 9b1b3a600ac33effcf01d27ab66877b768c32615 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 17 Jan 2023 14:41:34 +0100 Subject: [PATCH 210/213] use comment when is filled --- openpype/tools/push_to_project/control_integrate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/push_to_project/control_integrate.py b/openpype/tools/push_to_project/control_integrate.py index 704ed7ba515..819724ad4c2 100644 --- a/openpype/tools/push_to_project/control_integrate.py +++ b/openpype/tools/push_to_project/control_integrate.py @@ -907,7 +907,7 @@ def make_sure_version_exists(self): "fps": src_data.get("fps"), "source": src_data.get("source"), "machine": socket.gethostname(), - "comment": "", + "comment": self._item.comment or "", "author": get_openpype_username(), "time": get_formatted_current_time(), } From abe345df56d7fe4f70ec1ebc9a28eaa631ad0b03 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 17 Jan 2023 16:41:45 +0100 Subject: [PATCH 211/213] use constants of QMessageBox properly --- .../publish/validate_assembly_transforms.py | 12 ++++++------ .../project_manager/project_manager/window.py | 13 +++++++------ openpype/tools/workfiles/files_widget.py | 15 +++++++++------ 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_assembly_transforms.py b/openpype/hosts/maya/plugins/publish/validate_assembly_transforms.py index e8087a304f1..d1bca4091b4 100644 --- a/openpype/hosts/maya/plugins/publish/validate_assembly_transforms.py +++ b/openpype/hosts/maya/plugins/publish/validate_assembly_transforms.py @@ -93,12 +93,12 @@ def repair(cls, instance): from openpype.hosts.maya.api import lib # Store namespace in variable, cosmetics thingy - messagebox = QtWidgets.QMessageBox - mode = messagebox.StandardButton.Ok | messagebox.StandardButton.Cancel - choice = messagebox.warning(None, - "Matrix reset", - cls.prompt_message, - mode) + choice = QtWidgets.QMessageBox.warning( + None, + "Matrix reset", + cls.prompt_message, + QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel + ) invalid = cls.get_invalid(instance) if not invalid: diff --git a/openpype/tools/project_manager/project_manager/window.py b/openpype/tools/project_manager/project_manager/window.py index e35922cf36d..942bdaeec31 100644 --- a/openpype/tools/project_manager/project_manager/window.py +++ b/openpype/tools/project_manager/project_manager/window.py @@ -248,12 +248,13 @@ def _on_create_folders(self): if not project_name: return - qm = QtWidgets.QMessageBox - ans = qm.question(self, - "OpenPype Project Manager", - "Confirm to create starting project folders?", - qm.Yes | qm.No) - if ans == qm.Yes: + result = QtWidgets.QMessageBox.question( + self, + "OpenPype Project Manager", + "Confirm to create starting project folders?", + QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No + ) + if result == QtWidgets.QMessageBox.Yes: try: # Invoking OpenPype API to create the project folders create_project_folders(project_name) diff --git a/openpype/tools/workfiles/files_widget.py b/openpype/tools/workfiles/files_widget.py index 52ec348e459..765d32b3d54 100644 --- a/openpype/tools/workfiles/files_widget.py +++ b/openpype/tools/workfiles/files_widget.py @@ -525,22 +525,25 @@ def open_file(self, filepath): def save_changes_prompt(self): self._messagebox = messagebox = QtWidgets.QMessageBox(parent=self) - messagebox.setWindowFlags(messagebox.windowFlags() | - QtCore.Qt.FramelessWindowHint) - messagebox.setIcon(messagebox.Warning) + messagebox.setWindowFlags( + messagebox.windowFlags() | QtCore.Qt.FramelessWindowHint + ) + messagebox.setIcon(QtWidgets.QMessageBox.Warning) messagebox.setWindowTitle("Unsaved Changes!") messagebox.setText( "There are unsaved changes to the current file." "\nDo you want to save the changes?" ) messagebox.setStandardButtons( - messagebox.Yes | messagebox.No | messagebox.Cancel + QtWidgets.QMessageBox.Yes + | QtWidgets.QMessageBox.No + | QtWidgets.QMessageBox.Cancel ) result = messagebox.exec_() - if result == messagebox.Yes: + if result == QtWidgets.QMessageBox.Yes: return True - if result == messagebox.No: + if result == QtWidgets.QMessageBox.No: return False return None From f686f74b7f8bcd9987ad4dd30e727f60628e9b32 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 17 Jan 2023 18:24:18 +0100 Subject: [PATCH 212/213] Fix - Harmony - unable to change workfile It was failing on Mac with OSError 9 Bad file descriptor and 48 Address already in use. --- openpype/hosts/harmony/api/server.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/harmony/api/server.py b/openpype/hosts/harmony/api/server.py index 0de359ec61e..ecf339d91bd 100644 --- a/openpype/hosts/harmony/api/server.py +++ b/openpype/hosts/harmony/api/server.py @@ -40,6 +40,7 @@ def __init__(self, port): # Create a TCP/IP socket self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Bind the socket to the port server_address = ("127.0.0.1", port) @@ -91,7 +92,13 @@ def receive(self): self.log.info("wait ttt") # Receive the data in small chunks and retransmit it request = None - header = self.connection.recv(10) + try: + header = self.connection.recv(10) + except OSError: + # could happen on MacOS + self.log.info("") + break + if len(header) == 0: # null data received, socket is closing. self.log.info(f"[{self.timestamp()}] Connection closing.") From 61c5c62a12f5307060dfe0362228345ef18088d4 Mon Sep 17 00:00:00 2001 From: OpenPype Date: Wed, 18 Jan 2023 03:29:04 +0000 Subject: [PATCH 213/213] [Automated] Bump version --- openpype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/version.py b/openpype/version.py index ed7da82afb3..c6becce4fd4 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.14.11-nightly.1" +__version__ = "3.14.11-nightly.2"