def _get_object_type(self, obj_name): """ Internal function that returns object type given a node :param obj_name: str :return: str """ if not tpDcc.is_maya(): artellapipe.logger.warning('Solve Node Name by type functionality is only supported in Maya!') return None import tpDcc.dccs.maya as maya # TODO: This code is a duplicated version of the one in # tpDcc.dccs.maya.core.name.auto_suffix_object function. Move this code to a DCC specific function obj_type = maya.cmds.objectType(obj_name) if obj_type == 'transform': shape_nodes = maya.cmds.listRelatives(obj_name, shapes=True, fullPath=True) if not shape_nodes: obj_type = 'group' else: obj_type = maya.cmds.objectType(shape_nodes[0]) elif obj_type == 'joint': shape_nodes = maya.cmds.listRelatives(obj_name, shapes=True, fullPath=True) if shape_nodes and maya.cmds.objectType(shape_nodes[0]) == 'nurbsCurve': obj_type = 'controller' if obj_type == 'nurbsCurve': connections = maya.cmds.listConnections('{}.message'.format(obj_name)) if connections: for node in connections: if maya.cmds.nodeType(node) == 'controller': obj_type = 'controller' break return obj_type
def process(self, context, plugin): if not tp.is_maya(): self.log.warning('Remove From Display Layer Action is only available in Maya!') return False import tpDcc.dccs.maya as maya maya.cmds.undoInfo(openChunk=True) try: for instance in context: if not instance.data['publish']: continue node = instance.data.get('node', None) assert node and tp.Dcc.object_exists( node), 'No valid node found in current instance: {}'.format(instance) layer = maya.cmds.listConnections(node, type='displayLayer') if not layer: continue maya.cmds.editDisplayLayerMembers('defaultLayer', node) layer = maya.cmds.listConnections(node, type='displayLayer') if layer and layer != 'defaultLayer': self.log.error('Impossible to remove node from displayLayer: {}'.format(node, layer)) continue except Exception as exc: self.log.error('Error while removing nodes from display layers: {}'.format(exc)) finally: maya.cmds.undoInfo(closeChunk=False) return True
def launch_maya(file_path, maya_version=None): """ :param file_path: str :param maya_version: int :return: """ if not tp.is_maya(): return if maya_version is None: maya_version = tp.Dcc.get_version() spigot = get_artella_client() payload = dict() payload['appName'] = "maya.{0}".format(str(maya_version)) payload['parameter'] = "\"{0}\"".format(file_path) payload['wait'] = "60" payload = json.dumps(payload) rsp = spigot.execute(command_action='do', command_name='launchApp', payload=payload) if isinstance(rsp, (unicode, str)): rsp = json.loads(rsp) return rsp
def init_artella(dev=False, project_type=ArtellaProjectType.ENTERPRISE): if project_type == ArtellaProjectType.ENTERPRISE: # Import all functions in an explicit way from artellapipe.libs.artella.core import artellaenterprise artellaenterprise.init(dev=dev) artellapipe.logger.info('Using Artella Enterprise') else: # Import all functions in an explicit way from artellapipe.libs.artella.core import artellaindie if tp.is_maya(): try: import Artella as art except ImportError: try: artellaindie.update_artella_paths() if not os.environ.get('ENABLE_ARTELLA_PLUGIN', False): if tp.Dcc.is_plugin_loaded('Artella.py'): tp.Dcc.unload_plugin('Artella.py') else: artellaindie.load_artella_maya_plugin() import Artella as art except Exception as exc: artellapipe.logger.error( 'Impossible to load Artella Plugin: {} | {}'.format( exc, traceback.format_exc())) else: artellapipe.logger.info('Using Abstract Artella Class')
def dock_window(project, window_class, min_width=300): """ Utility function to dock Maya window :param project: ArtellaProject :param window_class: cls """ if not tpDcc.is_maya(): return import maya.cmds as cmds import maya.OpenMayaUI as OpenMayaUI try: cmds.deleteUI(window_class.name) except Exception: pass main_control = cmds.workspaceControl(window_class.name, ttc=["AttributeEditor", -1], iw=min_width, mw=True, wp='preferred', label=window_class.title) control_widget = OpenMayaUI.MQtUtil.findControl(window_class.name) control_wrap = qtutils.wrapinstance(int(control_widget), QWidget) control_wrap.setAttribute(Qt.WA_DeleteOnClose) win = window_class(project=project, parent=control_wrap) cmds.evalDeferred( lambda *args: cmds.workspaceControl(main_control, e=True, rs=True)) win.show() return win
def _export_file(self, file_path, *args, **kwargs): exported_shader = None shader_swatch = kwargs.get('shader_swatch', None) if not shader_swatch and tp.is_maya(): shader_swatch = maya_shader.get_shader_swatch(shader_name=self.name) px = QPixmap(QSize(100, 100)) shader_swatch.render(px) temp_file = os.path.join(os.path.dirname(file_path), 'tmp.png') px.save(temp_file) try: exported_shader = shader.ShadingNetwork.write_network( shader_extension=self.extensions[0], shaders_path=os.path.dirname(file_path), shaders=[self.name], icon_path=temp_file) except Exception as exc: LOGGER.error('Error while exporting shader: {} | {}'.format(exc, traceback.format_exc())) finally: if temp_file and os.path.isfile(temp_file): try: os.remove(temp_file) except Exception: LOGGER.warning('Impossible to remove temporary swatch file: {}'.format(temp_file)) return exported_shader
def process(self, context, plugin): if not tp.is_maya(): self.log.warning('Show Geometry Action is only available in Maya!') return False import tpDcc.dccs.maya as maya maya.cmds.undoInfo(openChunk=True) try: for instance in context: if not instance.data['publish']: continue shapes_to_smooth = instance.data.get('nodes_to_make_visible', None) if not shapes_to_smooth: continue for shape in shapes_to_smooth: tp.Dcc.set_attribute_value(shape, 'visibility', True) except Exception as exc: self.log.error( 'Error while disabling smooth preview from shapes: {}'.format( exc)) finally: maya.cmds.undoInfo(openChunk=False) return True
def open_file_in_maya(file_path, maya_version=None): """ Open the given path in the given Maya version :param file_path: str :param maya_version: int """ if not tp.is_maya(): return None if maya_version is None: maya_version = tp.Dcc.get_version() spigot = get_artella_client() # Firt we try to open the app if its not launched launch_maya(file_path=file_path, maya_version=maya_version) # Now we open the file payload = dict() payload['appId'] = "maya.{0}".format(str(maya_version)) payload['message'] = "{\"CommandName\":\"open\",\"" \ "CommandArgs\":{\"path\":\"" + file_path + "\"}}".replace('\\', '/') payload['message'] = payload['message'].replace('\\', '/').replace('//', '/') payload = json.dumps(payload) rsp = spigot.execute(command_action='do', command_name='passToApp', payload=payload) if isinstance(rsp, (unicode, str)): rsp = json.loads(rsp) return rsp
def load_vl_scripts(): """ Loads all vl picker scripts """ if not tp.is_maya(): solstice.logger.warning( 'vl picker scripts are only available for Maya!') return False scripts_path = get_scripts_path() if not scripts_path or not os.path.isdir(scripts_path): solstice.logger.error( 'Impossible to initialize picker related scripts!') return False if scripts_path not in sys.path: sys.path.append(scripts_path) solstice.logger.info('Loading pickers MEL scripts ...') load_script('vlRigIt_getModuleFromControl.mel') load_script('vlRigIt_getControlsFromModuleList.mel') load_script('vlRigIt_selectModuleControls.mel') load_script('vlRigIt_snap_ikFk.mel') load_script('vlRigIt_snapParent.mel') load_script('vlRigIt_snapParentAskSetKey.mel') load_script('vl_resetTransformations.mel') load_script('vl_resetAttributes.mel') load_script('vl_contextualMenuBuilder.mel') return True
def load_script(name): """ Function that loads a given MEL script by its name :param name: str, name of the script to load """ if not tp.is_maya(): return import tpDcc.dccs.maya as maya scripts_path = get_scripts_path() if not scripts_path or not os.path.isdir(scripts_path): return False script_to_load = os.path.join(scripts_path, name) if not os.path.isfile(script_to_load): solstice.logger.error( 'ERROR: Impossible to load {} script'.format(name)) return False try: solstice.logger.debug('Loading MEL script: {}'.format(name)) maya.mel.eval('source "{}"'.format(script_to_load).replace('\\', '/')) solstice.logger.debug( 'MEL script {} loaded successfully!'.format(name)) except Exception as exc: solstice.logger.error( 'ERROR: Impossible to evaluate {} script'.format(name)) solstice.logger.error('-' * 100) solstice.logger.error(str(exc)) return True
def reference_file_in_maya(file_path, maya_version=None): """ Import the given asset path in the given Maya version current scene :param file_path: str :param maya_version: int """ if not tp.is_maya(): return None if maya_version is None: maya_version = tp.Dcc.get_version() spigot = get_artella_client() payload = dict() payload['appId'] = "maya.{0}".format(str(maya_version)) payload['message'] = "{\"CommandName\":\"reference\"," \ "\"CommandArgs\":{\"path\":\"" + file_path + "\"}}".replace('\\', '/') payload['message'] = payload['message'].replace('\\', '/').replace('//', '/') payload = json.dumps(payload) rsp = spigot.execute(command_action='do', command_name='passToApp', payload=payload) if isinstance(rsp, (unicode, str)): rsp = json.loads(rsp) return rsp
def exception_hook(self, exc_type, exc_value, exc_traceback, detail=2): """Function handling uncaught exceptions. It is triggered each time an uncaught exception occurs. """ if issubclass(exc_type, KeyboardInterrupt): # ignore keyboard interrupt to support console applications sys.__excepthook__(exc_type, exc_value, exc_traceback) else: log_msg = '\n'.join([ ''.join(traceback.format_tb(exc_traceback)), '{0}: {1}'.format(exc_type.__name__, exc_value) ]) if 'SKIP_SENTRY_EXCEPTIONS' not in os.environ: if SENTRY_AVAILABLE: capture_sentry_exception(Exception(log_msg)) # trigger message box show skip_exception_box = self._should_skip_exception_box( exc_value, log_msg) if not skip_exception_box: self._exception_caught.emit(exc_value, log_msg) if tp.is_maya(): import tpDcc.dccs.maya as maya return maya.utils._formatGuiException(exc_type, exc_value, exc_traceback, detail)
def _export_file(self, file_path, *args, **kwargs): if not tp.is_maya(): LOGGER.warning('Shaders export is only supported in Maya!') return shaders_to_export = artellapipe.ShadersMgr().get_asset_shaders_to_export( asset=self._asset, return_only_shaders=False) locked_file = False if os.path.isfile(file_path): res = qtutils.show_question( None, 'Exporting Shaders Mapping File', 'Shaders Mapping File "{}" already exists. Do you want to overwrite it?'.format(file_path)) if res == QMessageBox.No: return artellapipe.FilesMgr().lock_file(file_path) locked_file = True try: with open(file_path, 'w') as fp: json.dump(shaders_to_export, fp) except Exception as exc: LOGGER.error('Error while exporting Shaders Mapping File "{}" | {}'.format(file_path, exc)) finally: if locked_file: artellapipe.FilesMgr().unlock_file(file_path) if os.path.isfile(file_path): return file_path
def process(self, context, plugin): if not tp.is_maya(): self.log.warning('Show All Nodes Action is only available in Maya!') transforms = tp.Dcc.list_nodes(node_type='transform') for node in transforms: tp.Dcc.show_node(node) return True
def check_artella_plugin_loaded(): """ Returns True if the Artella plugin is loaded in Maya or False otherwise :return: bool """ if tp.is_maya(): return tp.Dcc.is_plugin_loaded('Artella') return False
def unload_shaders(self): """ Unloads all the shaders of current asset node """ if not tp.is_maya(): LOGGER.warning('Shaders unloading is only supported in Maya!') return return artellapipe.ShadersMgr().unload_asset_shaders(self)
def process(self, context, plugin): if not tp.is_maya(): self.log.warning( 'Clean Garbage Nodes Action is only available in Maya!') from tpDcc.dccs.maya.core import scene scene.delete_garbage() return True
def _setup_host(self): """ Internal function that sets the host of the tool :return: """ # TODO: Should we do this in a central place (not per tool)? if tp.is_maya(): pyblish.api.register_host('maya') elif tp.is_houdini(): pyblish.api.register_host('houdini')
def get_render_layer_token(self): """ Returns render layer token :return: str """ if not tp.is_maya(): return None from tpDcc.dccs.maya.core import layer return layer.get_current_render_layer()
def __init__(self, *args, **kwargs): super(ArtellaExceptionHook, self).__init__(*args, **kwargs) # this registers the exception_hook() function as hook with the Python interpreter sys.excepthook = self.exception_hook if tp.is_maya(): import tpDcc.dccs.maya as maya maya.utils.formatGuiException = self.exception_hook # connect signal to execute the message box function always on main thread self._exception_caught.connect(show_exception_box)
def get_project_rule_token(self, rule_name): """ Returns token based on the given rule :param rule_name: str :return: str """ if not tp.is_maya(): return None from tpDcc.dccs.maya.core import helpers return helpers.get_project_rule(rule_name)
def process(self, context, plugin): if not tp.is_maya(): self.log.warning('Freeze Transforms Action is only available in Maya!') return False for instance in context: if not instance.data['publish']: continue node = instance.data.get('node', None) assert node and tp.Dcc.object_exists(node), 'No valid node found in current instance: {}'.format(instance) tp.Dcc.freeze_transforms(node, clean_history=True)
def get_artella_app_identifier(): """ Returns the installed Artella App identifier :return: variant, str || None """ app_identifier = os.environ.get('ARTELLA_APP_IDENTIFIER', None) if app_identifier is None: app_identifier = tp.Dcc.get_version_name() if tp.is_maya(): app_identifier = 'maya.{}'.format(app_identifier.split()[0]) return app_identifier
def load_shaders(self, status=defines.ArtellaFileStatus.PUBLISHED, apply_shaders=True): """ Loads all the shaders of current asset node """ if not tp.is_maya(): LOGGER.warning('Shaders loading is only supported in Maya!') return return artellapipe.ShadersMgr().load_asset_shaders( self, status=status, apply_shaders=apply_shaders)
def load_shader(self, shader_name, shader_path=None, asset=None, apply=True, status=defines.ArtellaFileStatus.WORKING): """ Loads shader with given name in current DCC :param shader_name: str :param apply: bool """ if not tp.is_maya(): LOGGER.warning('Shaders loading is only supported in Maya!') return from tpDcc.dccs.maya.core import shader as maya_shader if shader_name in maya_shader.get_default_shaders(): return True if apply: all_panels = maya.cmds.getPanel(type='modelPanel') for p in all_panels: maya.cmds.modelEditor(p, edit=True, displayTextures=False) shader_file = self.get_shader_file(shader_name, asset=asset) if shader_file: shader_file.import_file(status=status) else: valid_shaders_paths = list() shader_library_paths = self.get_shaders_paths() for p in shader_library_paths: if not os.path.exists(p): continue valid_shaders_paths.append(p) if not valid_shaders_paths: LOGGER.debug( '{} Shaders Library folder is not synchronized in your PC. Synchronize it please!' .format(artellapipe.project.name.title())) return False shader_file = self.get_shader_file(shader_name, shader_path=shader_path) if not shader_file: LOGGER.warning( 'Impossible to load shader "{}"!'.format(shader_name)) return else: shader_file.import_file() return True
def process(self, context, plugin): if not tp.is_maya(): self.log.warning('Clean History Action is only available in Maya!') return False import tpDcc.dccs.maya as maya for instance in context: if not instance.data['publish']: continue node = instance.data.get('node', None) assert node and tp.Dcc.object_exists( node), 'No valid node found in current instance: {}'.format( instance) tp.Dcc.delete_history(node) shapes = maya.cmds.listRelatives(node, shapes=True, fullPath=True) or list() for shape in shapes: shape_short = tp.Dcc.node_short_name(shape) tp.Dcc.delete_history(shape) # We remove groupdIds and clean connections to memberWireframeColors shape_history = maya.cmds.listHistory(shape) for history_node in shape_history: if maya.cmds.nodeType(history_node) == 'groupId': maya.cmds.delete(history_node) elif maya.cmds.nodeType(history_node) == 'shadingEngine': if maya.cmds.attributeQuery('memberWireframeColor', node=history_node, exists=True): member_wire_color_attr = '{}.memberWireframeColor'.format( history_node) shading_engine_connections = maya.cmds.listConnections( member_wire_color_attr, plugs=True) if shading_engine_connections: for cnt in shading_engine_connections: connected_node = cnt.split('.')[0].split( ':')[-1] if connected_node != shape_short: continue maya.cmds.disconnectAttr( member_wire_color_attr, cnt) # Make sure that shapes do not loose its set maya.cmds.sets(shape, edit=True, forceElement=history_node) return True
def process(self, context): assert tpDcc.is_maya( ), 'Validate Proxy Mesh is only available in Maya!' from tpDcc.dccs.maya.core import node, api root_group_name = artellapipe.NamesMgr().solve_name('root_group') proxy_group_name = artellapipe.NamesMgr().solve_name('proxy_group') geo_group_name = artellapipe.NamesMgr().solve_name('geo_group') proxy_geo = artellapipe.NamesMgr().solve_name('proxy_geo') proxy_geo_parent = '{}|{}|{}'.format(root_group_name, proxy_group_name, geo_group_name) assert proxy_geo and tpDcc.Dcc.object_exists( proxy_geo ), 'Proxy geo "{}" does not exist in current scene!'.format(proxy_geo) assert proxy_geo_parent and tpDcc.Dcc.object_exists( proxy_geo_parent ), 'Proxy geo parent "{}" does not exists in current scene!'.format( proxy_geo_parent) proxy_prefix = proxy_geo.split('_')[0] proxy_geos = tpDcc.Dcc.list_nodes('{}_*'.format(proxy_prefix), node_type='transform') or list() assert len( proxy_geos ) == 1, 'Invalid number ({}) of proxy geometries found in current scene: {}'.format( len(proxy_geos), proxy_geos) proxy_geo = proxy_geos[0] proxy_geo_shapes = tpDcc.Dcc.list_shapes(proxy_geo) assert proxy_geo_shapes, 'No sahpes found in proxy geo geometry!' # We check that all vertex colors have vertices_without_vertex_colors = dict() for proxy_shape in proxy_geo_shapes: proxy_shape_node = node.get_mobject(proxy_shape) proxy_shape_vtx_it = api.IterateVertices(proxy_shape_node) proxy_shape_vertex_colors = proxy_shape_vtx_it.get_vertex_colors( skip_vertices_without_vertex_colors=False) for vtx_id, vtx_color in proxy_shape_vertex_colors.items(): if vtx_color: continue if proxy_shape not in vertices_without_vertex_colors: vertices_without_vertex_colors[proxy_shape] = list() vertices_without_vertex_colors[proxy_shape].append(vtx_id) if vertices_without_vertex_colors: context.data[ 'vertices_without_vertex_colors'] = vertices_without_vertex_colors assert not vertices_without_vertex_colors, 'Some vertices of the proxy shapes have no vertex color ' \ 'applied to them: {}!'.format(vertices_without_vertex_colors)
def update_pixar_maya_usd_environment(): """ Updates current Python environment to setup Pixar Maya USD plugin """ import tpDcc as tp from tpDcc.libs.python import path if not tp.is_maya(): return False usd_dcc_root_path = usdpaths.get_usd_dcc_path() if not usd_dcc_root_path or not os.path.isdir(usd_dcc_root_path): LOGGER.warning( 'Impossible to setup Pixar USD environment. Maya USD is not available!' ) return maya_usd_plugins = path.clean_path( os.path.join(usd_dcc_root_path, 'plugin')) pixar_plugin_path = path.clean_path(os.path.join(maya_usd_plugins, 'pxr')) maya_pixar_lib_path = path.clean_path( os.path.join(pixar_plugin_path, 'lib')) maya_pixar_lib_python_path = path.clean_path( os.path.join(maya_pixar_lib_path, 'python')) maya_pixar_plugin_path = path.clean_path( os.path.join(pixar_plugin_path, 'maya')) maya_pixar_plugin_lib_path = path.clean_path( os.path.join(maya_pixar_plugin_path, 'lib')) maya_pixar_plugin_plugin_path = path.clean_path( os.path.join(maya_pixar_plugin_path, 'plugin')) maya_pixar_plugin_lib_usd_path = path.clean_path( os.path.join(maya_pixar_plugin_lib_path, 'usd')) maya_pixar_plugin_resources = path.clean_path( os.path.join(maya_pixar_plugin_lib_usd_path, 'usdMaya', 'resources')) maya_pixar_surface_resources = path.clean_path( os.path.join(maya_pixar_plugin_plugin_path, 'pxrUsdPreviewSurface', 'resources')) add_to_env('PYTHONPATH', maya_pixar_lib_python_path) add_to_env('PATH', maya_pixar_plugin_lib_path) add_to_env('XBMLANGPATH', maya_pixar_plugin_resources) add_to_env('XBMLANGPATH', maya_pixar_surface_resources) add_to_env('MAYA_SCRIPT_PATH', maya_pixar_plugin_resources) add_to_env('MAYA_SCRIPT_PATH', maya_pixar_surface_resources) add_to_env('MAYA_PLUG_IN_PATH', maya_pixar_plugin_plugin_path) add_to_env('PXR_PLUGINPATH_NAME', maya_pixar_plugin_lib_usd_path) return True
def parse_node_name(self, node_name): """ Parses given node name :param node_name: str :return: list(str) """ if not tpDcc.is_maya(): artellapipe.logger.warning('Parse Node Name by type functionality is only supported in Maya!') return None name_lib = self.naming_lib name_lib.set_active_rule('node') return name_lib.parse(node_name)
def process(self, context, plugin): assert tpDcc.is_maya( ), 'Select Vertices without Vertex Color Action is only available in Maya!' vertices_without_vertex_colors = context.data.get( 'vertices_without_vertex_colors', None) assert vertices_without_vertex_colors, 'No vertices without vertex colors to select' vertices_to_select = list() for shape_node, vertices_ids in vertices_without_vertex_colors.items(): for vertex_id in vertices_ids: vertices_to_select.append('{}.vtx[{}]'.format( shape_node, vertex_id)) assert vertices_to_select, 'No vertices to select' tpDcc.Dcc.select_object(vertices_to_select)