Source code for pickrunner.mayarunner

#!/usr/bin/env python
# -*- coding: utf-8 -*-

'''A Maya implementation for Pickrunner.

The user, usually an animator or rigger, can use this GUI to assign
object-to-object relationships. Once those relationships are defined, they
can use the arrow-keys on their keyboard to move between those objects.

Functionally, this is exactly the same as Maya's built-in pickWalk command.
The difference here however is that pickWalk is notoriously useless because
it relies on Maya's DAG hierarchy, which doesn't always make navigation easy.

Pickrunner doesn't care about hierarchy. It can even be used for DG nodes.
Take that, pickWalk!

'''

# IMPORT STANDARD LIBRARIES
import json

# IMPORT THIRD-PARTY LIBRARIES
from Qt import QtCore
from maya import cmds
import pymel.core as pm

# IMPORT LOCAL LIBRARIES
from . import gui
from . import mui

WINDOW_TITLE = 'Pickrunner'


[docs]class MayaBehaviorControl(gui.BehaviorControl): '''A controller that implements Maya-specific functions to Pickrunner.''' reserved_attribute_name = '__mayarunner_info' def __init__(self): '''Initialize the object and do nothing else.''' super(MayaBehaviorControl, self).__init__() @classmethod def _create_hidden_metadata_attribute(cls, node): '''Get the hidden attribute that we store Pickrunner values onto. If the attribute doesn't exist, create it and hide it from the user so they can't mess with it easily. Args: node (<pm.general.PyNode>): The node to add the attribute onto. ''' try: node.attr(cls.reserved_attribute_name) except pm.MayaAttributeError: node.addAttr(cls.reserved_attribute_name, dataType='string') attr = node.attr(cls.reserved_attribute_name) pm.setAttr(attr, keyable=False, channelBox=False) attr.setLocked(True)
[docs] @staticmethod def get_selection(): '''list[<pm.general.PyNode>]: The selected objects in the Maya scene.''' return pm.selected()
[docs] @classmethod def get_settings(cls, node): '''dict[str]: Get the settings for the given node, if any.''' known_exceptions = ( # If the node isn't a PyMEL node AttributeError, # If the Maya node doesn't have the reserved attribute pm.MayaAttributeError, # If the value retrieved from our reserved attribute isn't a string TypeError, # If the JSON string found has syntax errors or is empty ValueError, ) try: value = json.loads(node.attr(cls.reserved_attribute_name).get()) except known_exceptions: # pylint: disable=E0712 return dict() return value
[docs] @staticmethod def get_object_name(obj): '''str: Find the unique-name of the given object.''' try: return obj.nodeName() except AttributeError: pass try: obj = pm.ls(obj)[0] except IndexError: return obj return obj.nodeName()
[docs] @classmethod def assign(cls, from_object, direction, to_object, settings=None): '''Set an object to be remapped to another object, given some direction. Once an object is remapped to another object, we can use that to move Maya's selection around whenever the user asks to. Args: from_object: The object that will have the direction and to_object stored onto. direction: Some unique key to store onto from_object. This direction should always point towards to_object. (How direction points to to_object is up to the developer to implement). to_object: The object to remap to when direction and from_object are given to :func:`BehaviorControl.do_motion`. ''' if not settings: settings = cls.get_settings(from_object) settings[direction] = get_uuid(to_object) # Dump settings onto the node cls._create_hidden_metadata_attribute(from_object) attr = from_object.attr(cls.reserved_attribute_name) is_locked = attr.isLocked() attr.setLocked(False) attr.set(json.dumps(settings)) attr.setLocked(is_locked)
[docs] @classmethod def do_motion(cls, direction, obj): '''Change selection to an associated node of obj, given some direction. Args: direction (str): The direction to move to. obj (<pm.general.PyNode>): The object to get the associated object from. ''' uuid_of_the_node_to_select = cls.get_settings(obj).get(direction) try: node = pm.ls(uuid_of_the_node_to_select)[0] except IndexError: return pm.select(node) return node
class PickrunnerMayaWindow(gui.AssignmentManagerWidget): '''A GUI implementation of Pickrunner, for Maya.''' def __init__(self, parent=None): '''Create the window and its default widgets. Args: parent (:obj:`<QtCore.QObject>`, optional): Qt-based associated object. Default is None. ''' super(PickrunnerMayaWindow, self).__init__( controller=MayaBehaviorControl(), parent=parent) # Whenever the user changes selection, try to update the GUI self.jobs = [] selection_job_id = pm.scriptJob( event=['SelectionChanged', self.update_appearance]) new_scene_job_id = pm.scriptJob( event=['deleteAll', self.update_appearance]) self.jobs.append(selection_job_id) self.jobs.append(new_scene_job_id) selection = self.controller.get_selection() if selection: self.set_loaded_object(selection[0]) def init_default_settings(self): '''Set the window size to be larger, by default.''' super(PickrunnerMayaWindow, self).init_default_settings() self.toggle_mode() # Place into "Assignment Mode" by default self.resize(320, 100) def closeEvent(self, event): '''When the window is closed, stop trying to update the GUI.''' for job_id in self.jobs: pm.scriptJob(kill=job_id) super(PickrunnerMayaWindow, self).closeEvent(event)
[docs]def get_uuid(node): '''str: Get the UUID of the given node, if the node exists.''' try: node = node.nodeName() except AttributeError: pass try: return cmds.ls(node, uuid=True)[0] except IndexError: return ''
[docs]def do_pickrun_motion(direction): '''Try to pickrun in a given direction. Otherwise, pickWalk. Args: direction (str): The direction to walk. Options are: ("up", "down", "left", "right"). ''' try: node = pm.selected()[-1] except IndexError: pm.pickWalk(direction=direction) return new_node = MayaBehaviorControl.do_motion(direction, node) if not new_node: # Pickrun failed for some reason so lets pickWalk, instead pm.pickWalk(direction=direction)
@mui.delete_ui_if_exists(WINDOW_TITLE) def show(): '''Create and show the Pickrunner GUI for Maya.''' window = PickrunnerMayaWindow(mui.get_main_window()) window.setWindowFlags(QtCore.Qt.Window) window.setWindowTitle(WINDOW_TITLE) window.setObjectName(WINDOW_TITLE) window.manager.main_widget.setFocus() window.show()