def find_work(self, force=False, verbose=1): """Find work files within this work area. Args: force (bool): force reread work files from disk verbose (int): print process data Returns: (_CTTWorkFileBase): list of cachable work files """ dprint('FINDING WORK', self.path, verbose=verbose) # Get work dir _tmpl = get_template(self.maya_work_type.hint) _data = copy.copy(self.data) _data['Task'] = 'blah' _data['extension'] = 'mb' _data['version'] = 1 _tmp_path = _tmpl.apply_fields(_data) _tmp_work = self.maya_work_type(_tmp_path) # Find work files + make cachable _works = [] for _file in find(_tmp_work.dir, depth=1, type_='f'): try: _tmp_work = self.maya_work_type(_file) except ValueError: continue lprint(' - ADDING', _file, verbose=verbose > 1) _work = obtain_work(_tmp_work.path) _works.append(_work) return _works
def _read_frame_ranges(self, work_files, verbose=0): """Read frame range for each work file. This reads the frame range from all the selected passes for that work files and then takes the overall range from that. Args: work_files (TTWorkFileBase list): list of work files verbose (int): print process data Returns: (tuple list): list of start/end frames """ _ranges = [] for _work_file in qt.progress_bar( work_files, 'Reading {:d} frame range{}'): dprint('READING', _work_file) _start, _end = None, None for _render in self._work_files[_work_file]: lprint(' - TESTING RENDER', _render, verbose=verbose) for _seq in _render.find_files(class_=tk2.TTOutputFileSeq): _sstart, _send = _seq.find_range() _start = (_sstart if _start is None else min(_start, _sstart)) _end = (_send if _end is None else max(_end, _send)) lprint( ' - {:d}-{:d} {}'.format( _sstart, _send, _seq.path), verbose=verbose) print ' - RANGE {:d}-{:d} {}'.format( _start, _end, _work_file.path) _ranges.append((_start, _end)) lprint(verbose=verbose) return _ranges
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 _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 read_settings(self, verbose=0): """Read settings from disk. Args: verbose (int): print process data """ if not self.settings: return dprint('READ SETTINGS', self.settings.fileName(), verbose=verbose) # Apply window settings _pos = self.settings.value('window/pos') if _pos: lprint(' - APPLYING POS', _pos, verbose=verbose) self.move(_pos) _size = self.settings.value('window/size') if _size: lprint(' - APPLYING SIZE', _size, verbose=verbose) self.resize(_size) # Apply widget settings for _widget in self.findChildren(QtWidgets.QWidget): _name = _widget.objectName() _val = self.settings.value(_name) if _val is None: continue lprint(' - APPLY', _name, _val, verbose=verbose) self._apply_setting(widget=_widget, value=_val)
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 write_settings(self, verbose=0): """Write settings to disk. Args: verbose (int): print process data """ if not self.settings: return dprint('WRITING SETTINGS', self.settings.fileName(), verbose=verbose) for _widget in self.widgets: if isinstance(_widget, QtWidgets.QLineEdit): _val = _widget.text() elif isinstance(_widget, (QtWidgets.QRadioButton, QtWidgets.QCheckBox)): _val = _widget.isChecked() elif (isinstance(_widget, QtWidgets.QPushButton) and _widget.isCheckable()): _val = _widget.isChecked() elif isinstance(_widget, QtWidgets.QListWidget): _val = [str(_item.text()) for _item in _widget.selectedItems()] elif isinstance(_widget, QtWidgets.QTabWidget): _val = _widget.currentIndex() elif isinstance(_widget, QtWidgets.QSplitter): _val = _widget.sizes() else: continue lprint(' - SAVING', _widget.objectName(), _val, verbose=verbose) self.settings.setValue(_widget.objectName(), _val) self.settings.setValue('window/pos', self.ui.pos()) lprint(' - SAVING POS', self.ui.pos(), verbose=verbose) self.settings.setValue('window/size', self.ui.size()) lprint(' - SAVING SIZE', self.ui.size(), verbose=verbose)
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 _update_assets(verbose=0): """Update scene assets to latest version. Args: verbose (int): print process data """ dprint('UPDATING ASSETS') for _ref in qt.progress_bar(ref.find_refs(), 'Updating {:d} asset{}'): # Find asset _asset = tk2.TTOutputFile(_ref.path) if not _asset: continue # Make sure asset is latest if not _asset.is_latest(): lprint(' - CURRENT FILE: {}'.format(_asset.path), verbose=verbose) _latest = _asset.find_latest() lprint(' - UPDATING TO LATEST: {}'.format(_latest.path), verbose=verbose) _ref.swap_to(_latest.path) _status = 'updated' else: _status = 'no update needed' print ' - {:25} {:20} {}'.format(_ref.namespace, _status, _ref.path) print
def _print_exports(self): """Print exports.""" print dprint('EXPORTS:') for _work_file in sorted(self._exports): print ' - {:30} {}'.format(_work_file.basename, self._exports[_work_file])
def update_libs(check_root, mod_names=None, sort=None, close_interfaces=True, verbose=1): """Update a list of modules to a new location. Args: check_root (str): location to check paths match to mod_names (str list): list of modules to update sort (fn): module sort function close_interfaces (bool): close interfaces before refresh verbose (int): print process data """ for _idx in range(6): dprint('Updating modules - attempt', _idx + 1) _result = reload_libs(catch=True, check_root=check_root, mod_names=mod_names, sort=sort, close_interfaces=close_interfaces, verbose=verbose) dprint('Updating modules:', 'success' if _result else 'failed') if _result: break
def _update_abcs(shot='rnd0080', verbose=0): """Update abcs to point to the given shot. Args: shot (str): name of shot to update to (eg. rnd0080) verbose (int): print process data """ dprint('CHECKING ABCS') _refs_to_remove = set() for _exo in qt.progress_bar(hom.CMDS.ls(type='ExocortexAlembicFile'), 'Updating {:d} abc{}'): lprint('CHECKING EXO', _exo, verbose=verbose) _path = _exo.plug('fileName').get_val() _status, _to_remove = _update_abc(exo=_exo, shot=shot) if _to_remove: _refs_to_remove.add(_to_remove) print ' - {:60} {:30} {}'.format(_exo, _status, _path) print if _refs_to_remove: dprint('REMOVING {:d} REFS WITH NO {} CACHES'.format( len(_refs_to_remove), shot)) for _ref in _refs_to_remove: _ref.remove(force=True)
def _save_fbx(file_, force=False): """Save fbx file. Args: file_ (str): fbx path force (bool): replace without confirmation """ _file = File(get_path(file_)) _file.delete(wording='Replace', force=force) for _mel in [ 'FBXExportUpAxis z', 'FBXExportFileVersion -v FBX201800', 'FBXExportSmoothingGroups -v true', 'FBXExportSmoothMesh -v true', 'FBXExportTangents -v true', 'FBXExportSkins -v true', 'FBXExportShapes -v true', 'FBXExportEmbeddedTextures -v false', 'FBXExportApplyConstantKeyReducer -v true', 'FBXExportSplitAnimationIntoTakes -c', 'FBXExport -f "{}"'.format(_file.path), ]: mel.eval(_mel) dprint('Wrote file', _file.nice_size(), _file.path) assert _file.exists()
def init_ui(self, rebuild_fn=None, verbose=1): """Initiate ui. Args: rebuild_fn (func): override rebuild function verbose (int): print process data """ dprint('Building ui {} ({})'.format(self.ui_name, self.base_col), verbose=verbose) # Add menu bar _interface = self.add_menu('Interface') self.add_menu_item(_interface, label='Collapse all', image=icons.EMOJI.find('Sponge', catch=True), command=self.collapse_all) self.add_menu_item(_interface, label='Rebuild', image=icons.EMOJI.find('Hammer', catch=True), command=rebuild_fn or self.rebuild) _settings = self.add_menu('Settings') self.add_menu_item(_settings, label='Save', image=icons.EMOJI.find('Floppy disk', catch=True), command=self.save_settings) self.add_menu_item(_settings, label='Reset', image=icons.EMOJI.find('Shower', catch=True), command=wrap_fn( self.reset_settings)) # Wrap to discard args self._save_on_close = self.add_menu_item(_settings, label='Save on close', checkbox=False)
def toggle_file_errors(): """Toggle error catcher decorator.""" if os.environ.get('EXC_DISABLE_FILE_ERROR'): del os.environ['EXC_DISABLE_FILE_ERROR'] dprint("Enabled file errors") else: os.environ['EXC_DISABLE_FILE_ERROR'] = '1' dprint("Disabled file errors")
def read_settings(self, verbose=0): """Read settings from disk. Args: verbose (int): print process data """ if not self.settings: return dprint('READ SETTINGS', self.settings.fileName(), verbose=verbose) # Apply window settings _pos = self.settings.value('window/pos') if _pos: lprint(' - APPLYING POS', _pos, verbose=verbose) self.move(_pos) _size = self.settings.value('window/size') if _size: lprint(' - APPLYING SIZE', _size, verbose=verbose) self.resize(_size) # Apply widget settings for _widget in self.widgets: _name = _widget.objectName() _val = self.settings.value(_name) if _val is None: continue lprint(' - APPLY', _name, _val, verbose=verbose) if isinstance(_widget, QtWidgets.QLineEdit): _widget.setText(_val) elif isinstance(_widget, (QtWidgets.QRadioButton, QtWidgets.QCheckBox, QtWidgets.QPushButton)): if isinstance(_val, six.string_types): _val = {'true': True, 'false': False}[_val] if isinstance(_val, bool): _widget.setChecked(_val) else: print ' - FAILED TO APPLY:', _widget, _val, type(_val) elif isinstance(_widget, QtWidgets.QListWidget): for _row in range(_widget.count()): _item = _widget.item(_row) _item.setSelected(_item.text() in _val) elif isinstance(_widget, QtWidgets.QTabWidget): try: _widget.setCurrentIndex(_val) except TypeError: print ' - FAILED TO APPLY TAB', _val elif isinstance(_widget, QtWidgets.QSplitter): _val = [int(_item) for _item in _val] lprint('SET SPLITTER SIZE', _val, verbose=verbose) _widget.setSizes(_val) elif isinstance(_widget, QtWidgets.QLabel): pass else: print 'WIDGET', _name, _widget raise ValueError('Error reading settings ' + self.settings.fileName())
def cache_work_file(work_file, namespaces, confirm=False, new_scene=False, farm=True, parent=None): """Recache the given work file. The work file is opened, versioned up and the recached. Args: work_file (TTWorkFileBase): work file to recache namespaces (str list): list of assets to recache confirm (bool): confirm before execute new_scene (bool): new scene after recache farm (bool): submit recache to farm parent (QDialog): parent interface (for dialog positioning) """ dprint('RECACHING', work_file.path) _engine = tank.platform.current_engine() _fileops = _engine.apps['psy-multi-fileops'] # Load the scene work_file.load() maya.utils.processIdleEvents() _fileops.init_app() # Update assets _updated = [] for _ns in qt.progress_bar(namespaces, 'Updating {:d} asset{}', col='LightSteelBlue', parent=parent): _ref = ref.find_ref(_ns, class_=m_pipe.OutputRef) if not _ref.is_loaded(): _ref.load() if _ref.update_to_latest(): _updated.append(_ref.namespace) # Version up _fileops.init_app() maya.utils.processIdleEvents() _engine = tank.platform.current_engine() _fileops = _engine.apps['psy-multi-fileops'] _fileops.version_up_workfile() maya.utils.processIdleEvents() _cur_work = tk2.cur_work(class_=BCWork) _cur_work.set_comment('Versioned up by batch cache tool') _cur_work.read_dependencies(new_scene=False) _exec_cache(namespaces=namespaces, new_scene=new_scene, confirm=confirm, farm=farm) cmds.file(new=True, force=True)
def print_eta(self): """Print expected time remaining.""" _n_remaining = len(self.items) - self.counter + 1 _durs = self.durs[-5:] _avg_dur = sum(_durs) / len(_durs) _etr = _avg_dur * _n_remaining _eta = time.time() + _etr dprint('Beginning {}/{}, frame_t={:.02f}s, etr={:.00f}s, ' 'eta={}{}'.format(self.counter, len(self.items), _avg_dur, _etr, time.strftime('%H:%M:%S', get_time_t(_eta)), self.info))
def save_settings(self, verbose=0): """Save dialog settings. Args: verbose (int): print process data """ dprint('SAVE SETTINGS', self.settings.fileName(), verbose=verbose) self.settings.setValue('window/pos', self.pos()) lprint(' - SAVING POS', self.pos(), verbose=verbose) self.settings.setValue('window/size', self.size()) lprint(' - SAVING SIZE', self.size(), verbose=verbose)
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 save_settings(self, verbose=0): """Save current settings to disk. Args: verbose (int): print process data """ _settings = self._read_settings() if verbose > 1: pprint.pprint(_settings) dprint('Saved settings', self.settings_file, verbose=verbose) write_yaml(file_=self.settings_file, data=_settings, force=True)
def get_metadata(self, verbose=1): """Read this work area's metadata yaml file. Args: verbose (int): print process data Returns: (dict): work area metadata """ dprint("Reading metadata", self.path, verbose=verbose) if not os.path.exists(self.yaml): return {} return read_yaml(self.yaml)
def restart_tank(force=True, verbose=0): """Restart shotgun toolkit (and remove unused modules). Args: force (bool): remove leftover libs with no confirmation verbose (int): print process data """ _start = time.time() tank.platform.restart() _init_tank() _clean_leftover_modules(force=force, verbose=verbose) dprint("RESTARTED TANK ({:.02f}s)".format(time.time() - _start))
def add_to_dlayer(obj, layer, verbose=0): """Add the specified object to a display layer, creating it if needed. Args: obj (str): object to add layer (str): layer to add to verbose (int): print process data """ if not cmds.objExists(layer): set_namespace(":") dprint("Creating displaylayer", layer, verbose=verbose) cmds.createDisplayLayer(name=layer, number=True, empty=True) cmds.editDisplayLayerMembers(layer, obj, noRecurse=1)
def _finalise_standin(node, name, range_, verbose=0): """Finalise new aiStandIn node. Executes updates to be run after abc has loaded (abc loads using deferred evaluation). This includes renaming the transform/shape - if they are renamed before abc load the auto generated abc frame expression errors. Also the frame expression is regenenerated to make the abc loop - if this is generated before abc load then the auto generated expression also errors. Args: node (HFnDependencyNode): aiStandIn node (shape) name (str): intended node name (of transform) range_ (tuple|None): range to loop (if any) verbose (int): print process data """ dprint('FINALISE STANDIN', node, verbose=verbose) lprint(' - RANGE', range_, verbose=verbose) # Fix names _parent = node.get_parent() lprint(' - RENAMING', name, _parent, verbose=verbose) _parent = cmds.rename(_parent, name) lprint(' - PARENT', _parent, verbose=verbose) _node = node.rename(name + "Shape") _plug = _node.plug('frameNumber') # Apply range expression if range_: # Clean frame expression lprint(' - PLUG', _plug, _plug.find_driver(), verbose=verbose) lprint(' - BREAKING CONNECTIONS', verbose=verbose) _plug.break_connections() # Build expression if range_: lprint(' - BUILDING EXPRESSION', verbose=verbose) _str = ('{plug} = ((frame - {start}) % ({end} - {start} + 1)) + ' '{start};').format(start=range_[0], end=range_[1], plug=_plug) lprint(_str, verbose=verbose) _expr = cmds.expression(string=_str, timeDependent=True) lprint(' - CREATED EXPRESSION', _expr, verbose=verbose) return hom.HFnTransform(_parent)
def load_settings(self, verbose=1): """Read settings from disk. Args: verbose (int): print process data """ dprint('LOAD SETTINGS', self.settings.fileName(), verbose=verbose) # Apply window settings _pos = self.settings.value('window/pos') if _pos: lprint(' - APPLYING POS', _pos, verbose=verbose) self.move(_pos) _size = self.settings.value('window/size') if _size: lprint(' - APPLYING SIZE', _size, verbose=verbose) self.resize(_size)
def _write_usage_to_kibana(name=None, catch=True, args=None, verbose=0): """Write usage data to kibana index. Args: name (str): override function name catch (bool): on fail continue and disable usage tracking args (tuple): args data to write to usage verbose (int): print process data """ # Don't track farm/dev usage if os.environ.get('USER') == 'render' or dev_mode(): return try: from elasticsearch import Elasticsearch except ImportError: return _start = time.time() _index_name = 'psyhive-' + datetime.datetime.utcnow().strftime('%Y.%m') _usage = _build_usage_dict(name=name, args=args) if verbose > 1: print _index_name pprint.pprint(_usage) # Send to kibana _conn = Elasticsearch([_ELASTIC_URL]) if not _conn.ping(): if catch: dprint('Failed to make connection to Elasticsearch') os.environ['PSYHIVE_DISABLE_USAGE'] = '1' return raise RuntimeError('Cannot connect to Elasticsearch database.') if not _conn.indices.exists(_index_name): _conn.indices.create(index=_index_name, body=_INDEX_MAPPING) _res = _conn.index(index=_index_name, doc_type=_ES_DATA_TYPE, body=_usage, id=_usage.pop('_id', None)) _usage['_id'] = _res['_id'] _dur = time.time() - _start dprint('Wrote usage to kibana ({:.02f}s)'.format(_dur), verbose=verbose)
def set_items(self, items, select=None, verbose=0): """Populate this list with the given items. This allows the list to be populated and changed with a single itemSelectionChanged signal emission. Args: items (str|QListWidgetItem list): items to add select (str|QListWidgetItem): item to select (if any) verbose (int): print process data """ dprint('SET ITEMS', self, items, select, verbose=verbose) if select: _select = select if isinstance(select, list) else [select] _text_list = isinstance(_select[0], six.string_types) else: _select = self.selected_text() # Match current selection _text_list = True self.blockSignals(True) self.clear() # Populate list _selected = False for _idx, _item in enumerate(items): if isinstance(_item, six.string_types): _item = HListWidgetItem(_item) self.addItem(_item) if ((not _text_list and _item in _select) or (_text_list and _item.text() in _select)): lprint(' - MATCHED SELECT', select, _idx, verbose=verbose) _item.setSelected(True) _selected = True if not _selected: lprint(' - APPLYING ROW 0', verbose=verbose) self.setCurrentRow(0) self.blockSignals(False) self.itemSelectionChanged.emit()
def _exec_fn(*xargs): _start = time.time() dprint('############ Start {} ##############'.format(def_.name)) del xargs if not disable_reload: reload(_mod) _kwargs = {} for _arg_name, _arg_fn in read_arg_fns.items(): if _arg_fn: _kwargs[_arg_name] = _arg_fn() _fn = getattr(_mod, def_.name) if catch_error_: _fn = catch_error(_fn) if track_usage_: _fn = track_usage(_fn) interface.save_settings() _fn(**_kwargs) _dur = time.time() - _start dprint('############ Complete {} ({}) ############'.format( def_.name, nice_age(_dur)))
def build_aistandin_output(output): """Build aiStandIn ma file output. Args: output (str): path to aiStandIn output Returns: (str): path to output file """ print 'BUILD aiStandIn MA', output # Get paths for standin + rest cache + shade _out = tk2.TTOutput(output) assert _out.format == 'aistandin' _standin = _out.map_to(tk2.TTOutputFile, extension='ma') print ' - STANDIN', _standin assert _standin.extn == 'ma' _ver = tk2.TTOutputVersion(output) print ' - VER', _ver _rest_cache = get_single(_ver.find(extn='abc', filter_='restCache')) print ' - REST CACHE', _rest_cache _shade = _ver.find_file(extn='mb', format_='maya') print ' - SHADE', _shade assert not _shade == _out.path # Build aiStandIn node dprint('OPENING SHADE SCENE') host.open_scene(_shade.path, force=True) build_aistandin_from_shade(archive=_rest_cache, shade=_ShadeScene(), animated=False, name='AIS', deferred=False) # Strip out scene cmds.delete('GEO') host.save_as(file_=_standin.path, force=True) return _standin.path