def set_section(self, section, verbose=0): """Set current section (implemented in subclass). Args: section (_Section): section to apply verbose (int): print process data """ _section = _SectionHeader( section.label, col=self.section_col, collapse=section.collapse) _section.setMinimumSize(100, 22) _policy = _section.sizePolicy() _policy.setHorizontalPolicy(_policy.Expanding) _policy.setHorizontalStretch(100) _section.setSizePolicy(_policy) self.main_layout.addWidget(_section) self.read_settings_fns['section'][section.label] = {} self.read_settings_fns['section'][section.label]['collapse'] = wrap_fn( getattr, _section, 'collapse') self.set_settings_fns['section'][section.label] = {} self.set_settings_fns['section'][section.label]['collapse'] = wrap_fn( _section.set_collapse) self.section = _section
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 _context__submit(self, menu): menu.add_action("Print renders + work files", chain_fns( wrap_fn(pprint.pprint, self._renders), wrap_fn(pprint.pprint, self._work_files.keys()), wrap_fn(pprint.pprint, self._passes))) menu.add_action("Print frame ranges", wrap_fn( self._read_frame_ranges, sorted(self._work_files), verbose=1))
def delete(self): """Delete this interface.""" for _mthd in (wrap_fn(self.closeEvent, None), self.deleteLater): try: _mthd() except RuntimeError: pass
def get_work_ctx_opts(work, menu, redraw_work, parent): """Add context options for the given work file. Args: work (TTWork): work file menu (QMenu): menu to add options too redraw_work (fn): function for redrawing work items parent (QDialog): parent dialog """ _add_path_menu_items(menu=menu, obj=work) menu.addSeparator() _set_comment_fn = chain_fns( wrap_fn(_set_work_comment, work, parent=parent), redraw_work) menu.add_action('Set comment', _set_comment_fn, icon=icons.EDIT) # Add output options _add_work_ctx_output_files(work=work, menu=menu) # Add increments _incs = work.find_increments() menu.addSeparator() if _incs: _menu = menu.add_menu('Increments') _icon = get_work_icon(work) for _inc in _incs: _inc_menu = _menu.add_menu(_inc.basename, icon=_icon) _add_path_menu_items(menu=_inc_menu, obj=_inc) else: menu.add_label('No increments found')
def connect_widgets(self, catch_error_=False, track_usage_=True, disable_btns_on_exec=True, verbose=0): """Connect widgets with redraw/callback methods. Only widgets with override types are linked. Args: catch_error_ (bool): apply catch error decorator to callbacks track_usage_ (bool): apply track usage decorator to callbacks disable_btns_on_exec (bool): disable push buttons while they are being executed - can interfere with enabling/disabling buttons on the fly verbose (int): print process data """ for _widget in self.widgets: _name = _widget.objectName() lprint('CHECKING', _name, verbose=verbose > 1) # Connect callback _callback = getattr(self, '_callback__' + _name, None) if _callback: if isinstance(_widget, QtWidgets.QPushButton): if track_usage_: from psyhive.tools import track_usage _callback = track_usage(_callback) if catch_error_: from psyhive.tools import get_error_catcher _catcher = get_error_catcher(exit_on_error=False) _callback = _catcher(_callback) if disable_btns_on_exec: _callback = _disable_while_executing(func=_callback, btn=_widget) _callback = wrap_fn(_callback) # To lose args from hook lprint(' - CONNECTING', _widget, verbose=verbose) for _hook_name in [ 'clicked', 'currentTextChanged', 'textChanged', ]: _hook = getattr(_widget, _hook_name, None) if _hook: _hook.connect(_callback) # Connect context _context = getattr(self, '_context__' + _name, None) if _context: _widget.customContextMenuRequested.connect( _build_context_fn(_context, widget=_widget)) _widget.setContextMenuPolicy(Qt.CustomContextMenu) # Connect redraw callback _redraw = getattr(self, '_redraw__' + _name, None) if _redraw: lprint(' - CONNECTING REDRAW', _widget, verbose=verbose) _mthd = _build_redraw_method(_redraw) _widget.redraw = types.MethodType(_mthd, _widget)
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 build_aistandin_from_shade(archive, shade=None, animated=True, name=None, deferred=True, verbose=0): """Create aiStandIn from selected shade asset. The shader is read from all mesh nodes in the shade asset, and then this is used to create an aiSetParameter node on the standin for each shader. If all the meshes using the shader has matching values for ai attrs, these values are applied as overrides on the aiSetParameter node. Args: archive (str): path to archive to apply to standin shade (FileRef): shade asset to build overrides from animated (bool): whether this archive is animated name (str): base name for nodes (normally shade namespace) deferred (bool): apply deferrred changes - this allows the standin to be generated with no error message as maya makes deferred callbacks on aiStandIn create; however this cannot be used if running code as part of a publish verbose (int): print process data Returns: (HFnTransform) standin transform """ _name = name or shade.namespace # Create standin _standin = hom.CMDS.createNode('aiStandIn') _standin.plug('dso').set_val(archive) _standin.plug('useFrameExtension').set_val(animated) _merge = hom.CMDS.createNode('aiMerge', name='{}_mergeOperators'.format(_name)) _merge.plug('out').connect(_standin.plug('operators[0]')) _build_col_switches_aip(shade=shade, merge=_merge, name=_name) _build_shader_overrides(shade=shade, merge=_merge, verbose=verbose) # Init updates to happen after abc load _standin.select() _rng = _get_abc_range_from_sg(archive) if animated else None _ais_name = get_unique(name or '{}_AIS'.format(shade.namespace)) _finalise_fn = wrap_fn(_finalise_standin, node=_standin, range_=_rng, name=_ais_name) if deferred: cmds.evalDeferred(_finalise_fn, lowestPriority=True) _parent = None else: _parent = _finalise_fn() print 'NOT DEFERRED', _parent print 'CREATED', _standin, _parent return _parent
def setup_ctrls(self): """Set up system controls.""" _bones = { Limb.ARM: ('Shoulder', 'Elbow', 'Wrist'), Limb.LEG: ('Hip', 'Knee', 'Ankle') } _names = { 'side': { Side.LEFT: 'L', Side.RIGHT: 'R' }[self.side], 'limb': { Limb.ARM: 'Arm', Limb.LEG: 'Leg' }[self.limb], 'gimbal': { Limb.ARM: 'wrist', Limb.LEG: 'ankle' }[self.limb], 'offset': { Limb.ARM: 'Elbow', Limb.LEG: 'Knee' }[self.limb] } self.fk_ctrls = [ self.rig.get_node('FK{bone}_{side}'.format(bone=_bone, **_names), class_=hom.HFnTransform) for _bone in _bones[self.limb] ] self.ik_jnts = [ self.rig.get_node('IKX{bone}_{side}'.format(bone=_bone, **_names)) for _bone in _bones[self.limb] ] self.ik_ = self.rig.get_node('IK{limb}_{side}'.format(**_names), class_=hom.HFnTransform) self.ik_pole = self.rig.get_node('Pole{limb}_{side}'.format(**_names)) self.ik_pole_rp = self.ik_pole.plug('rotatePivot') self.ik_offs = [] self.gimbal = self.rig.get_node('FKIK{limb}_{side}'.format(**_names)) self.ik_fk_attr = self.gimbal.plug('FKIKBlend') self.set_to_ik = wrap_fn(self.ik_fk_attr.set_val, 10) self.set_to_fk = wrap_fn(self.ik_fk_attr.set_val, 0)
def _context__Keyframe(self, menu): menu.setStyleSheet('background-color:DimGrey; color:white') try: _system = system.get_selected_system(class_=self.system) except ValueError as _exc: menu.add_label(_exc.message) else: _nodes = _system.get_ctrls() menu.add_action('Select nodes', wrap_fn(cmds.select, _nodes))
def setup_ctrls(self): """Set up system controls.""" _names = { 'side': {Side.LEFT: 'Lf', Side.RIGHT: 'Rt'}[self.side], 'limb': {Limb.ARM: 'arm', Limb.LEG: 'leg'}[self.limb], 'gimbal': {Limb.ARM: 'wrist', Limb.LEG: 'ankle'}[self.limb], 'offset': {Limb.ARM: 'Elbow', Limb.LEG: 'Knee'}[self.limb]} self.fk_ctrls = [ self.rig.get_node('{side}_{limb}Fk_{idx}_Ctrl'.format( idx=_idx, **_names)) for _idx in range(1, 4)] self.fk_jnts = [ self.rig.get_node('{side}_{limb}Fk_{idx}_Jnt'.format( idx=_idx, **_names)) for _idx in range(1, 4)] self.ik_jnts = [ self.rig.get_node('{side}_{limb}Ik_{idx}_Jnt'.format( idx=_idx, **_names)) for _idx in range(1, 4)] self.ik_ = self.rig.get_node( '{side}_{limb}Ik_Ctrl'.format(**_names)) self.ik_pole = self.rig.get_node( '{side}_{limb}Pole_Ctrl'.format(**_names)) self.ik_pole_rp = self.ik_pole.plug('rotatePivot') self.ik_offs = ['{}.{offset}_Offset'.format(self.ik_, **_names)] if self.limb == 'leg': self.ik_offs += [ "{}.Heel_Roll".format(self.ik_), "{}.Heel_Pivot".format(self.ik_), "{}.Toe_Rotate".format(self.ik_), "{}.Foot_Rock".format(self.ik_), ] self.gimbal = self.rig.get_node( '{side}_{gimbal}Gimbal_Ctrl'.format(**_names)) self.ik_fk_attr = self.gimbal.plug('FK_IK') self.set_to_ik = wrap_fn(self.ik_fk_attr.set_val, 1) self.set_to_fk = wrap_fn(self.ik_fk_attr.set_val, 0)
def _add_path_menu_items(menu, obj): """Add menu items for the given path object. Args: menu (QMenu): menu to add items to obj (Path): path object """ # Add label if isinstance(obj, Seq): _start, _end = obj.find_range() _join = '...' if obj.has_missing_frames() else '-' _label = 'Seq {:d}{}{:d}'.format(_start, _join, _end) else: _label = 'File' menu.add_label(_label) menu.add_action('Copy path', wrap_fn(copy_text, obj.path), icon=icons.COPY) _browser = wrap_fn(launch_browser, obj.dir) menu.add_action('Show in explorer', _browser, icon=icons.BROWSER) if obj.extn in ['mb', 'ma', 'abc']: # Open scene _open = wrap_fn(host.open_scene, obj.path) menu.add_action('Open scene', _open, icon=icons.OPEN) # Reference scene _namespace = obj.basename if isinstance(obj, tk2.TTWork): _namespace = obj.task elif isinstance(obj, tk2.TTOutputFile): _namespace = obj.output_name _ref = wrap_fn(host.reference_scene, obj.path, namespace=_namespace) _pix = qt.HPixmap(icons.OPEN) _pix.add_overlay(icons.EMOJI.find('Diamond With a Dot'), pos=_pix.size(), resize=80, anchor='BR') menu.add_action('Reference scene', _ref, icon=_pix) # Reference asset if isinstance(obj, tk2.TTOutputFile): _fn = wrap_fn(tk2.reference_publish, obj.path) menu.add_action('Reference publish', _fn, icon=_pix) if isinstance(obj, Seq): _icon = icons.EMOJI.find('Play button') menu.add_action('View images', obj.view, icon=_icon) elif obj.extn == 'mov': _icon = icons.EMOJI.find('Play button') _view = wrap_fn(system, 'djv_view ' + obj.path) menu.add_action('View images', _view, icon=_icon)
def test_wrap_fn(self): def _test(a=1, b=2, c=3): return a, b, c assert wrap_fn(_test, b=3)() == (1, 3, 3) assert wrap_fn(_test, arg_to_kwarg='c')(4) == (1, 2, 4) # Test pass_data def _test(*args, **kwargs): return args, kwargs assert _test() == ((), {}) assert _test(a=1) == ((), {'a': 1}) assert wrap_fn(_test)() == ((), {}) assert wrap_fn(_test, a=1)() == ((), {'a': 1}) assert wrap_fn(_test)(a=1) == ((), {}) assert wrap_fn(_test, arg_to_kwarg='test')(1) == ((), {'test': 1}) assert wrap_fn(_test, pass_data=True)(a=1) == ((), {'a': 1})
def open_scene(file_, func=None, force=False, lazy=False): """Open the given scene file. A warning is raised if the current scene has been modified. Args: file_ (str): file to open func (fn): override save function force (bool): lose current scene with no warning lazy (bool): abandon open scene if file is already open """ _file = get_path(file_) if lazy and cur_scene() == _file: print 'SCENE ALREADY OPEN', _file return if not force and _scene_modified(): handle_unsaved_changes() _func = func or wrap_fn(_force_open_scene, _file) _func()
def user_setup(verbose=0): """User setup. Args: verbose (int): print process data """ dprint('Executing PsyHive user setup') if cmds.about(batch=True): return _install_psyhive_elements(verbose=verbose) # Fix logging level (pymel sets to debug) _fix_fn = wrap_fn(logging.getLogger().setLevel, logging.WARNING) cmds.evalDeferred(_fix_fn, lowestPriority=True) # Add elements to psyop menu (deferred to make sure exists) cmds.evalDeferred(_add_elements_to_psyop_menu, lowestPriority=True) # Add script editor save to project cmds.evalDeferred(script_editor_add_project_opts)
def _context__MakeTicket(self, menu): _url = _make_ticket( summary='[PSYHIVE] Error: {}'.format(self.message), description=self.summary, open_=False) _fn = wrap_fn(copy_text, _url) menu.add_action('Copy ticket url', icon=icons.COPY, func=_fn)
def refresh(): """Refresh current dcc interface.""" try: from maya import cmds except ImportError: pass else: from maya_psyhive import ref, ui from maya_psyhive.utils import (get_fps, save_as, save_scene, open_scene as open_scene_, set_start, set_end) NAME = 'maya' batch_mode = wrap_fn(cmds.about, batch=True) _get_cur_scene = wrap_fn(cmds.file, query=True, location=True) _force_open_scene = lambda file_: open_scene_(file_, force=True) _force_new_scene = wrap_fn(cmds.file, new=True, force=True) refresh = cmds.refresh reference_scene = ref.create_ref _scene_modified = wrap_fn(cmds.file, query=True, modified=True) t_start = wrap_fn(cmds.playbackOptions, query=True, minTime=True) t_end = wrap_fn(cmds.playbackOptions, query=True, maxTime=True) save = wrap_fn(cmds.file, save=True) get_main_window_ptr = ui.get_main_window_ptr if not NAME: try: import hou except ImportError:
def _context__Step(self, menu): _step = get_single(self.ui.Step.selected_data(), catch=True) if _step: menu.add_action( 'Copy path', wrap_fn(copy_text, _step.path), icon=icons.COPY)
def _connect_widget(self, widget, track_usage_=True, catch_error_=True, disable_btns_on_exec=True, verbose=0): """Connect a widget to callbacks on the parent object. Args: widget (QWidget): widget to connect track_usage_ (bool): apply track usage decorator catch_error_ (bool): apply error catcher decorator disable_btns_on_exec (bool): disable push buttons while executing (this can interfere with custom on the fly enabling/disabling) verbose (int): print process data """ _name = widget.objectName() # See if this element needs connecting if not _name: return _callback = getattr(self, '_callback__' + _name, None) _context = getattr(self, '_context__' + _name, None) _redraw = getattr(self, '_redraw__' + _name, None) if not (_callback or _context or _redraw): return lprint('CONNECTING', _name, verbose=verbose) # Connect callback if _callback: # Wrap callback if isinstance(widget, QtWidgets.QPushButton): if track_usage_: from psyhive.tools import track_usage _callback = track_usage(_callback) if catch_error_: from psyhive.tools import get_error_catcher _catcher = get_error_catcher(exit_on_error=False) _callback = _catcher(_callback) if disable_btns_on_exec: _callback = _disable_while_executing(func=_callback, btn=widget) _callback = wrap_fn(_callback) # To lose args from hook lprint(' - CONNECTING', widget, verbose=verbose) # Find signals to connect to for _hook_name in [ 'clicked', 'currentTextChanged', 'textChanged', ]: _hook = getattr(widget, _hook_name, None) if _hook: _hook.connect(_callback) # Connect context if _context: widget.customContextMenuRequested.connect( _build_context_fn(_context, widget=widget)) widget.setContextMenuPolicy(Qt.CustomContextMenu) # Connect redraw callback if _redraw: lprint(' - CONNECTING REDRAW', widget, verbose=verbose) _mthd = _build_redraw_method(_redraw) widget.redraw = types.MethodType(_mthd, widget)
def _context__SendEmail(self, menu): _fn = wrap_fn(copy_text, self.summary) menu.add_action('Copy email text', icon=icons.COPY, func=_fn)