def _redraw__Desc(self, verbose=0): dprint('REDRAW DESC', verbose=verbose) _type = get_single(self.ui.Type.selected_text(), catch=True) _char = get_single(self.ui.Character.selected_text(), catch=True) _name = get_single(self.ui.Name.selected_text(), catch=True) _descs = sorted( set([ _work.desc for _work in self.o_works if _work.type_ == _type and _work.asset == _char and _work.name == _name ])) # Populate list self.ui.Desc.blockSignals(True) self.ui.Desc.clear() for _desc in _descs: self.ui.Desc.addItem(_desc) if _descs: self.ui.Desc.setCurrentRow(0) self.ui.Desc.blockSignals(False) self.ui.DescLabel.setText({'Name': 'Desc'}.get(_type, 'Desc')) self._redraw__Iteration()
def read_shd(shp, allow_base=False, verbose=1): """Read shader from the given geo shape node. Args: shp (str): shape node to read allow_base (bool): return BaseShader objects for unhandled shaders verbose (int): print process data Returns: (_BaseShader): shader object """ _shp = shp if cmds.objectType(_shp) == 'transform': _shp = get_shp(_shp) _se = get_single(cmds.listConnections(_shp, source=False, type='shadingEngine'), catch=True) if not _se: lprint('No shading engine found:', _shp, verbose=verbose) return None lprint('Shading engine:', _se, verbose=verbose > 1) _shd = get_single(cmds.listConnections(_se + '.surfaceShader', destination=False), catch=True) if not _shd: return None _shd = find_shd(_shd, allow_base=allow_base) _shd.set_se(hom.HFnDependencyNode(_se)) return _shd
def _callback__PlaySeq(self): _work = get_single(self.ui.Work.selected_data(), catch=True) if not _work: return _c_work = self.c_works[_work] _viewables = [ _out for _out in _c_work.find_outputs() if _out.format in ['mov', 'jpg'] ] if len(_viewables) == 1: _viewable = get_single(_viewables) _seq = get_single(_viewable.find_files()) _seq.view() return _menu = qt.HMenu(self.ui.PlaySeq) for _viewable in _viewables: _seq = get_single(_viewable.find_files()) _menu.add_action('View ' + _viewable.output_type, func=_seq.view, icon=icons.EMOJI.find('Blue Circle')) _pos = qt.get_p(self.ui.PlaySeq.size() / 2) _menu.exec_(self.ui.PlaySeq.mapToGlobal(_pos))
def _redraw__Step(self): # Get step root _mode = self.ui.RootTabs.cur_text() if _mode == 'Assets': _root = get_single(self.ui.Asset.selected_data(), catch=True) elif _mode == 'Shots': _root = get_single(self.ui.Shot.selected_data(), catch=True) else: raise ValueError(_mode) _steps = _root.find_step_roots() if _root else [] # Populate list _cur = self.ui.Step.selected_text(single=True) self.ui.Step.blockSignals(True) self.ui.Step.clear() _sel = _cur if _cur in _steps else None for _step in _steps: _work_area = _step.get_work_area(dcc=hb_utils.cur_dcc()) _col = 'grey' if os.path.exists(_work_area.yaml): _col = 'white' _sel = _sel or _step _item = qt.HListWidgetItem(_step.step, data=_step) _item.set_col(_col) self.ui.Step.addItem(_item) if _sel: self.ui.Step.select_data([_sel]) else: self.ui.Step.setCurrentRow(0) self.ui.Step.blockSignals(False) self._callback__Step()
def _redraw__Work(self, verbose=0): dprint('POPULATE WORK', verbose=verbose) _type = get_single(self.ui.Type.selected_text(), catch=True) _char = get_single(self.ui.Character.selected_data(), catch=True) _name = get_single(self.ui.Name.selected_text(), catch=True) _desc = get_single(self.ui.Desc.selected_text(), catch=True) _iter = get_single(self.ui.Iteration.selected_data(), catch=True) _works = [ _work for _work in self.o_works if _work.type_ == _type and _work.get_root() == _char and _work.iter == _iter and _work.name == _name and _work.desc == _desc ] # Populate list self.ui.Work.setSpacing(2) self.ui.Work.blockSignals(True) self.ui.Work.clear() for _o_work in reversed(_works): _c_work = self.c_works[_o_work] _item = hive_bro.create_work_item(_c_work) _item.set_data(_o_work) self.ui.Work.addItem(_item) if _works: self.ui.Work.setCurrentRow(0) self.ui.Work.blockSignals(False) self._callback__Work()
def _redraw__Name(self, verbose=0): dprint('REDRAW NAME', verbose=verbose) # Get names _type = get_single(self.ui.Type.selected_text(), catch=True) _char = get_single(self.ui.Character.selected_data(), catch=True) _names = sorted( set([ _work.name for _work in self.o_works if _work.type_ == _type and _work.get_root() == _char ])) # Populate list self.ui.Name.blockSignals(True) self.ui.Name.clear() for _item in _names: self.ui.Name.addItem(_item) if _names: self.ui.Name.select_text(_names[0]) self.ui.Name.blockSignals(False) self.ui.NameLabel.setText(_type) self._redraw__Desc()
def _redraw__Iteration(self): # Get iterations _type = get_single(self.ui.Type.selected_text(), catch=True) _char = get_single(self.ui.Character.selected_data(), catch=True) _name = get_single(self.ui.Name.selected_text(), catch=True) _desc = get_single(self.ui.Desc.selected_text(), catch=True) _iters = sorted( set([ _work.iter for _work in self.o_works if _work.type_ == _type and _work.get_root() == _char and _work.name == _name and _work.desc == _desc ])) # Populate list self.ui.Iteration.blockSignals(True) self.ui.Iteration.clear() for _iter in _iters: _item = qt.HListWidgetItem('{:02d}'.format(_iter), data=_iter) self.ui.Iteration.addItem(_item) if _iters: self.ui.Iteration.setCurrentRow(0) self.ui.Iteration.blockSignals(False) self._redraw__Work()
def test_find(self): _test_dir = '{}/psyhive/testing/blah'.format(tempfile.gettempdir()) _test_file = abs_path('{}/test.txt'.format(_test_dir)) if os.path.exists(_test_dir): shutil.rmtree(_test_dir) touch(_test_file) assert get_single(find(_test_dir)) == _test_file assert get_single(find(_test_dir, full_path=False)) == 'test.txt'
def get_metadata(self, data=None, catch=True, verbose=0): """Get metadata for this work file. This can be expensive - it should read at work area level and then passed using the data arg. Args: data (dict): override data dict rather than read from disk catch (bool): no error on work file missing from metadata verbose (int): print process data """ dprint('Reading metadata', self.path, verbose=verbose) _work_area = self.get_work_area() if data: _data = data else: dprint('Reading work area metadata (slow)', _work_area.path, verbose=verbose) _data = _work_area.get_metadata() if not _data: return {} # Apply task filter _task_files = [ _data for _data in _data['workfiles'] if _data['name'] == self.task.lower() ] if not _task_files: if catch: return {} raise ValueError('Missing task {} from metadata {}'.format( self.task.lower(), self.path)) _work_files = get_single(_task_files) lprint("MATCHED {:d} WORK FILES IN TASK {}".format( len(_work_files), self.task.lower()), verbose=verbose > 1) lprint(pprint.pformat(_work_files), verbose=verbose > 1) # Find this version if 'versions' not in _work_files: raise ValueError("Missing versions key in metadata " + _work_area.path) _versions = [ _data for _data in _work_files['versions'] if _data['version'] == self.version ] if not _versions and catch: return {} _version = get_single(_versions, fail_message='Missing version in metadata ' + self.path) return _version
def get_sg_data(self, verbose=0): """Find shotgun data for this publish. Args: verbose (int): print process data Returns: (dict): shoutgun data """ from psyhive import tk2 _proj = tk2.get_project_sg_data(pipe.Project(self.path)) _root = tk2.TTRoot(self.path) _task = tk2.get_sg_data( 'Task', content=self.task, project=_proj, entity=_root.get_sg_data()) _data = tk2.get_sg_data( 'PublishedFile', version_number=self.ver_n, sg_format=self.extn, project=_proj, task=_task, limit=2, code=self.filename.replace('%04d', '####'), fields=['task', 'code', 'short_name']) if verbose: pprint.pprint(_data) if len(_data) > 1: raise RuntimeError(self.path) return get_single(_data, catch=True)
def find_ref(namespace=None, filter_=None, catch=False, class_=None, prefix=None, extn=None, verbose=0): """Find reference with given namespace. Args: namespace (str): namespace to match filter_ (str): apply filter to names list catch (bool): no error on fail to find matching ref class_ (FileRef): override FileRef class prefix (str): match reference by prefix (prefix references don't use namespaces) extn (str): filter by extension verbose (int): print process data Returns: (FileRef): matching ref """ _refs = find_refs(namespace=namespace, filter_=filter_, class_=class_, prefix=prefix, extn=extn) lprint('Found {:d} refs'.format(len(_refs)), _refs, verbose=verbose) return get_single(_refs, catch=catch, name='ref')
def _add_work_ctx_output_files(work, menu): """Add work file output context options. Args: work (TTWork): work file menu (QMenu): menu to add to """ _files = work.find_output_files() menu.addSeparator() # No outputs found if not _files: menu.add_label("No outputs found") return menu.add_label("Outputs") # Show individual files if small number if len(_files) < 10: for _file in _files: _add_work_ctx_output_file(menu=menu, file_=_file) return # Organise into names _names = sorted(set([_file.output_name for _file in _files])) for _name in _names: _name_files = [_file for _file in _files if _file.output_name == _name] if len(_name_files) == 1: _add_work_ctx_output_file(menu=menu, file_=get_single(_name_files)) else: _add_work_ctx_output_name(menu=menu, name=_name, files=_name_files)
def get_default(self): """Get default value of this plug. Returns: (any): default value """ return get_single(self.attribute_query(listDefault=True), catch=True)
def __init__(self, node, verbose=0): """Constructor. Args: node (str): tranform node name verbose (int): print process data """ from maya_psyhive import open_maya as hom super(BaseTransform, self).__init__(node) # Get shape (if any) _shps = cmds.listRelatives( self.node, shapes=True, path=True, noIntermediate=True) or [] _shp = get_single([str(_shp) for _shp in _shps], catch=True) self.shp = hom.HFnDependencyNode(_shp) if _shp else None lprint('SHAPE', self.shp, _shps, verbose=verbose) # Create plugs for _param in 'trs': for _axis in 'xyz': _attr = _param + _axis _plug = HPlug(self.node + '.' + _attr) setattr(self, _attr, _plug) self.translate = HPlug(self.node + '.translate') self.rotate = HPlug(self.node + '.rotate') self.scale = HPlug(self.node + '.scale') self.visibility = HPlug(self.node + '.visibility')
def _script_editor_find_file_menu(verbose=0): """Find script editor file menu. Args: verbose (int): print process data Returns: (str): script editor file menu name """ # Find script editor file menu _menus = [] for _menu in cmds.lsUI(menus=True): if cmds.menu(_menu, query=True, label=True) != 'File': continue lprint('TESTING', _menu, verbose=verbose) _post_cmd = cmds.menu(_menu, query=True, postMenuCommand=True) lprint(' - POST CMD', _post_cmd, verbose=verbose) if not _post_cmd or 'ScriptEditor' not in _post_cmd: continue lprint(' - MATCHED', verbose=verbose) _menus.append(_menu) _menu = get_single(_menus, catch=True) if not _menu: print ' - PSYHIVE FAILED TO FIND SCRIPT EDITOR MENU' return None # Init menu if it has no children if not cmds.menu(_menu, query=True, itemArray=True): _init_cmd = cmds.menu(_menu, query=True, postMenuCommand=True) mel.eval(_init_cmd) assert cmds.menu(_menu, query=True, itemArray=True) return _menu
def map_tag_to_shot(tag): """Map the given tag to a shot in the current project. Args: tag (str): tag to match Returns: (TTShot): shot root """ # Try existing shot on disk _shot = tk2.find_shot(tag, catch=True, mode='sg') if _shot: return _shot # Try shot from sg _data = get_single(tk2.get_sg_data('Shot', code=tag, fields=['sg_sequence']), catch=True) if _data: _path = '{}/sequences/{}/{}'.format(pipe.cur_project().path, _data['sg_sequence']['name'], tag) return tk2.TTShot(_path) return None
def _script_editor_save_to_project(*xargs): """Execute save to project.""" del xargs # Maya callbacks require args # Get current editor _cur_editor = [ _ui for _ui in cmds.lsUI(dumpWidgets=True, long=False) if cmds.cmdScrollFieldExecuter(_ui, query=True, exists=True) and not cmds.cmdScrollFieldExecuter(_ui, query=True, isObscured=True) ][0] # Get file path _src_type = cmds.cmdScrollFieldExecuter(_cur_editor, query=True, sourceType=True) _extn = {'mel': 'mel', 'python': 'py'}[_src_type] _text = cmds.cmdScrollFieldExecuter(_cur_editor, query=True, text=True) _file = get_single( cmds.fileDialog2( fileMode=0, # Single file doesn't need to exist caption="Save Script", okCaption='Save', startingDirectory=pipe.cur_project().maya_scripts_path, fileFilter='{} Files (*.{})'.format(_extn.upper(), _extn)), catch=True) # Write file to disk if _file: write_file(file_=_file, text=_text)
def _add_elements_to_psyop_menu(verbose=0): """Add elements to psyop menu. Args: verbose (int): print process data """ dprint('ADDING {:d} TO PSYOP MENU'.format(len(_BUTTONS)), verbose=verbose) _menu = ui.obtain_menu('Psyop') _children = cmds.menu(_menu, query=True, itemArray=True) or [] _anim = get_single([ _child for _child in _children if cmds.menuItem(_child, query=True, label=True) == 'Animation' ], catch=True) if _anim: for _name, _data in _BUTTONS.items(): _mi_name = 'HIVE_' + _name if cmds.menuItem(_mi_name, query=True, exists=True): cmds.deleteUI(_mi_name) lprint(' - ADDING', _mi_name, verbose=verbose) cmds.menuItem(_mi_name, parent=_anim, command=_data['cmd'], image=_data['image'], label=_data['label'])
def _clean_unused_uv_sets(mesh, verbose=0): """Clean unused uv sets from the given mesh. Args: mesh (HFnMesh): mesh to clean verbose (int): print process data """ _all = cmds.polyUVSet(mesh, query=True, allUVSets=True) or [] if len(_all) <= 1: lprint('NO UV CLEAN REQUIRED', verbose=verbose) return _cur = get_single(cmds.polyUVSet(mesh, query=True, currentUVSet=True)) lprint(' - CLEANING UVS {} cur="{}" all={}'.format(mesh, _cur, _all), verbose=verbose) # Reorder if current is not default set (can't delete first set) if _all.index(_cur): lprint(' - SWITCHING SETS', _cur, _all[0], verbose=verbose) cmds.polyUVSet(mesh, reorder=True, uvSet=_cur, newUVSet=_all[0]) for _idx, _set in enumerate(_all): if _set != _cur: lprint(' - DELETE SET', _idx, _set, verbose=verbose) cmds.polyUVSet(mesh, delete=True, uvSet=_set)
def build_loc(name='locator', scale=None, col=None): """Build locator at this array's position. Args: name (str): name for locator scale (str): locator scale col (str): locator colour Returns: (str): locator name """ from maya_psyhive import open_maya as hom from maya_psyhive.utils import set_col _loc = cmds.spaceLocator(name=get_unique(name))[0] # Apply scale _scale = scale or hom.LOC_SCALE if _scale != 1.0: _shp = get_single(cmds.listRelatives(_loc, shapes=True)) cmds.setAttr(_shp + '.localScale', _scale, _scale, _scale) # Apply colour _col = col or hom.LOC_COL set_col(_loc, _col) return hom.HFnTransform(_loc)
def find_output_file(self, format_=None, extn=None, version=None, task=None, output_type=None, catch=False, verbose=0): """Find a specific output file in this step root. Args: format_ (str): match format (eg. maya) extn (str): match extension (eg. mb) version (str): match version (eg. v003 or latest) task (str): match task output_type (str): match output type catch (bool): no error on fail verbose (int): print process data Returns: (TTOutputFileBase): matching output file """ _files = self.find_output_files(format_=format_, extn=extn, version=version, task=task, output_type=output_type) return get_single(_files, catch=catch, verbose=verbose)
def export_img_plane(camera, abc): """Export image plane preset data for the given camera/abc. Args: camera (str): camera shape node name abc (str): path to output abc """ _cam = hom.HFnCamera(get_parent(str(camera))) lprint(' - CAM', _cam) # Read image plane _img_plane = get_single(_cam.shp.list_connections(type='imagePlane'), catch=True) if not _img_plane: lprint(' - NO IMAGE PLANE FOUND') return _img_plane = hom.HFnTransform(_img_plane.split('->')[-1]) # Export preset for each shape for _shp in [_cam.shp, _img_plane.shp]: _preset = '{}/{}.preset'.format(os.path.dirname(abc), _shp.object_type()) lprint(' - SAVING', _preset) try: _shp.save_preset(_preset) except RuntimeError: lprint(' - FAILED TO SAVE')
def _add_displacement_override(shd, aip, verbose=0): """Add displacement override if applicable. Args: shd (HFnDepedencyNode): shader node aip (HFnDepedencyNode): aiSetParameter node verbose (int): print process data """ _shd = tex.find_shd(str(shd), catch=True) if not _shd: return lprint(' - SHD', _shd, verbose=verbose) if not _shd.get_se(): return lprint(' - SE', _shd.get_se(), verbose=verbose) _displ = get_single( _shd.get_se().plug('displacementShader').list_connections(), catch=True) if not _displ: return lprint(' - DISPL', _displ, verbose=verbose) _val = "disp_map = '{}'".format(_displ) _get_next_idx(aip.plug('assignment')).set_val(_val)
def _get_latest_abc(abc): """Get latest path for an abc. This is the latest version of any output file with a special case added to handle to off-pipeline rest cache abcs. Args: abc (str): path to abc Returns: (str): path to latest version """ # Handle regular output file try: _out_file = tk2.TTOutputFile(abc) except ValueError: pass else: return _out_file.find_latest().path # Special handling for rest cache try: _output = tk2.TTOutput(abc) except ValueError: pass else: return get_single(_output.find_latest().find(extn=File(abc).extn))
def _context__WorkJumpTo(self, menu): _work = get_single(self.ui.Work.selected_data(), catch=True) # Add jump to recent options menu.add_label("Jump to") for _work in hb_work.get_recent_work(): _work = _work.find_latest() if not _work: continue _label = hb_work.get_work_label(_work) _icon = hb_work.get_work_icon(_work, mode='basic') _fn = wrap_fn(self.jump_to, _work.path) menu.add_action(_label, _fn, icon=_icon) menu.addSeparator() # Jump to clipboard work _clip_work = tk2.get_work(qt.get_application().clipboard().text()) if _clip_work: _label = hb_work.get_work_label(_clip_work) _fn = wrap_fn(self.jump_to, _clip_work.path) menu.add_action('Jump to '+_label, _fn, icon=icons.COPY) else: menu.add_label('No work in clipboard', icon=icons.COPY) # Add current work to recent if _work: menu.add_action( 'Add selection to recent', _work.add_to_recent, icon=icons.EMOJI.find('Magnet'))
def _callback__WorkSave(self): self.ui.WorkSave.setEnabled(False) _work = get_single(self.ui.Work.selected_data()) _comment = qt.read_input( 'Enter comment:', title='Save new version', parent=self) _work.save_inc(comment=_comment) self.ui.WorkSave.setEnabled(True)
def _callback__WorkLoad(self): _ver = get_single(self.ui.Work.selected_data(), catch=True) self.ui.WorkLoad.setEnabled(False) host.refresh() _ver.load() self.ui.WorkLoad.setEnabled(True) self._callback__Work()
def _redraw__WorkPath(self): _work = get_single(self.ui.Work.selected_data(), catch=True) _task = self.ui.TaskEdit.text() _step = get_single(self.ui.Step.selected_data(), catch=True) if not _work and _task and _step: _dcc = hb_utils.cur_dcc() _hint = '{dcc}_{area}_work'.format(dcc=_dcc, area=_step.area) try: _work = _step.map_to( hint=_hint, class_=tk2.TTWork, Task=_task, extension=tk2.get_extn(_dcc), version=1) except ValueError: _work = None self.ui.WorkPath.setText(_work.path if _work else '')
def get_plugs(self): """Get name of plug which this curve is driving. Returns: (str): plug being driven """ return get_single( self.output.list_connections(source=False, plugs=True))
def is_static(self): """Test whether this is a static anim. Returns: (bool): whether anim is static """ _ktv = get_single(cmds.getAttr(self.plug('keyTimeValue'))) return bool(_ktv)