Search code examples
pythonmayamelpymel

Setting a hotkey for a custom runTimeCommand in Maya


My goal: Set up a simple class to add some keyboard macros to Maya at startup.

My Issue: The intended key mappings are not set in the hotkey editor. Editing them manually does work, but as these are default commands, they need to be set programmatically.

-thanks

class Macros(object):
    '''
    '''
    def __init__(self):
        '''
        '''

    def setMacro(self, name=None, k=None, cat=None, ann=None):
        '''
        Sets a default runtime command with a keyboard shotcut.
        args:
            name = 'string' - The command name you provide must be unique. The name itself must begin with an alphabetic character or underscore followed by alphanumeric characters or underscores.
            cat = 'string' - catagory - Category for the command.
            ann = 'string' - annotation - Description of the command.
            k = 'string' - keyShortcut - Specify what key is being set.
                            modifiers: alt, ctl, sht - Modifier values are set by adding a '+' between chars. ie. 'sht+z'.
        '''
        command = "from macros import Macros; Macros.{0}();".format(name)

        #set runTimeCommand
        pm.runTimeCommand(
                name,
                annotation=ann,
                category=cat,
                command=command,
                default=True,
        )

        #set hotkey
        #modifiers
        ctl=False; alt=False; sht=False
        for char in k.split('+'):
            if char=='ctl':
                ctl = True
            elif char=='alt':
                alt = True
            elif char=='sht':
                sht = True
            else:
                key = char

        pm.hotkey(keyShortcut=key, name=name, ctl=ctl, alt=alt, sht=sht) #set only the key press.




    #  ------------------------------------------------------

    @staticmethod
    def hk_back_face_culling():
        '''
        hk_back_face_culling
        Toggle Back-Face Culling
        1
        '''
        print 'hk_back_face_culling()'

Call:

m = Macros()
m.setMacro(name='hk_back_face_culling', k='1', cat='Display', ann='Toggle back-face culling')

Edit: I got it halfway working by adding a nameCommand.

#set command
nameCommand = pm.nameCommand(
        '{0}Command'.format(name),
        annotation=ann,
        command='python("{0}")'.format(command),
)

Solution

  • Here is A fully working example. Perhaps it can help someone in the future.

    import pymel.core as pm
    
    class Macros(object):
        '''Macro functions with assigned hotkeys.
        
        ex. usage: in startup script call: from macros import Macros; Macros().setMacros()
        '''
        def __init__(self, *args, **kwargs):
            '''
            '''
    
        def setMacros(self, macros={}):
            '''Extends setMacro to accept a dictionary.
    
            :Parameters:
                macros (dict) = Command names as keys, with dict values containing any keyword args for 'setMacro'. ex. {'m_group':{'k':'ctl+g', 'cat':'Edit'}}
            '''
            if not macros:
                macros = {
                    'm_back_face_culling':      {'k':'1', 'cat':'Display'},
                }
    
            for name, kwargs in macros.items():
                self.setMacro(name, **kwargs)
    
    
        def setMacro(self, name, k=None, cat=None, ann=None, default=False, deleteExisting=True):
            '''Sets a default runtime command with a keyboard shortcut.
    
            :Parameters:
                name (str) = The command name you provide must be unique. (alphanumeric characters, or underscores)
                cat (str) = catagory - Category for the command.
                ann (str) = annotation - Description of the command.
                k (str) = keyShortcut - Specify what key is being set.
                            key modifier values are set by adding a '+' between chars. ie. 'sht+z'.
                            modifiers:
                                alt, ctl, sht
                            additional valid keywords are:
                                Up, Down, Right, Left,
                                Home, End, Page_Up, Page_Down, Insert
                                Return, Space
                                F1 to F12
                                Tab (Will only work when modifiers are specified)
                                Delete, Backspace (Will only work when modifiers are specified)
                default (bool) = Indicate that this run time command is a default command. Default run time commands will not be saved to preferences.
                deleteExisting = Delete any existing (non-default) runtime commands of the given name.
            '''
            command = "if 'macros' not in globals(): from macros import Macros; global macros; macros = Macros();\nmacros.{}();".format(name)
    
            if not ann: #if no ann is given, try using the method's docstring.
                method = getattr(self, name)
                ann = method.__doc__.split('\n')[0] #use only the first line.
    
            if pm.runTimeCommand(name, exists=True):
                if pm.runTimeCommand(name, query=True, default=True):
                    return #can not delete default runtime commands.
                elif deleteExisting:#delete any existing (non-default) runtime commands of that name.
                    pm.runTimeCommand(name, edit=True, delete=True)
    
            try: #set runTimeCommand
                pm.runTimeCommand(
                    name,
                    annotation=ann,
                    category=cat,
                    command=command,
                    default=default,
                )
            except RuntimeError as error:
                print ('# Error: {}: {} #'.format(__file__, error))
                return error
    
            #set command
            nameCommand = pm.nameCommand(
                    '{0}Command'.format(name),
                    annotation=ann,
                    command=name,
            )
    
            #set hotkey
            #modifiers
            ctl=False; alt=False; sht=False
            for char in k.split('+'):
                if char=='ctl':
                    ctl = True
                elif char=='alt':
                    alt = True
                elif char=='sht':
                    sht = True
                else:
                    key = char
    
            # print(name, char, ctl, alt, sht)
            pm.hotkey(keyShortcut=key, name=nameCommand, ctl=ctl, alt=alt, sht=sht) #set only the key press.
    
    
        @staticmethod
        def m_back_face_culling():
            '''Toggle Back-Face Culling.
            '''
            print ('<call to: {}.m_back_face_culling>'.format(__name__))