How to make custom context in maya API 2.0 receive flag/argument?

Hello!,

Currently, I’m working on creating custom context using Maya API 2.0

I already searched from documentation and any tutorial I could find, but I don’t know how to get context command to receive flag/argument.

The only closest reference I can get online is here helixTool.cpp

But it’s C++, and using Maya API 1.0 so I had a problem translating the code.

Below I already attach my progress so far

Any help would be greatly appreciated!

Thanks,
-Aldo

# flag definition
kMeshFlag = "-m"
kMeshLongFlag = "-mesh"

# tell maya plugin produces and need to be passed, maya api 2.0
def maya_useNewAPI():
    pass

class CutomTool(omui.MPxToolCommand):
    mesh = None

    def __init__(self):
        super(CutomTool, self).__init__()

    def syntaxCreator(self):
        syntax = om.MSyntax()

        # (short name, long name, argument data type)
        syntax.addFlag(kMeshFlag,kMeshLongFlag,om.MSyntax.kString)

        return syntax
    
    def argumentParser(self, argList):

        syntax = self.syntax()
        parsedArguments = om.MArgParser(syntax, argList)

        if parsedArguments.isFlagSet(kMeshFlag):
            self.mesh = parsedArguments.flagArgumentString(kMeshFlag, 0)
            return om.MStatus.kSuccess
        if parsedArguments.isFlagSet(kMeshLongFlag):
            self.mesh = parsedArguments.flagArgumentString(kMeshLongFlag, 0)
            return om.MStatus.kSuccess

    def doIt(self, argList):
        self.argumentParser(argList)

        if self.mesh != None:
            self.redoIt()
        return om.MStatus.kSuccess

    def isUndoable(self):
        return True

    # action to undo operation
    def undoIt(self):
        print ("undo")
        return om.MStatus.kSuccess

    def redoIt(self):
        print ("redo")
        return om.MStatus.kSuccess

    def finalize(self):
        command = om.MArgList()
        command.addArg(om.MString(kMeshFlag))
        command.addArg(self.mesh)
        return omui.MPxToolCommand.doFinalize(command)

    @classmethod
    def creator(cls):
        return CutomTool()

class CustomContext(omui.MPxContext):
    TITLE = "Title"
    HELP_TEXT = "Help Text"
    
    def __init__(self):
        super(CustomContext, self).__init__()
        self.setTitleString(CustomContext.TITLE)  

    def helpStateHasChanged(self, event):
        self.setHelpString(CustomContext.HELP_TEXT)
        
    def toolOnSetup(self,event):
        om.MGlobal.selectCommand(om.MSelectionList())  # select nothing, to record undo
        self.reset_context()
        
    def toolOffCleanup(self):
        self.reset_context()

    def doPress(self, event, draw_manager, frame_context):
        print ("do press")

    def completeAction(self):
        print ("complete action")
        
    def deleteAction(self):
        print ("undo")
        
    def abortAction(self):
        print ("abort")

    def reset_context(self):
        print ("reset")
   
class CustomContextCmd(omui.MPxContextCommand):
    
    COMMAND_NAME = "customCommand"   # used as mel command to create context
    
    def __init__(self):
        super(CustomContextCmd, self).__init__()
        
    # required for maya to get instance of context
    def makeObj(self):
        return CustomContext()    # return ribbon spline ctx 
        
    @classmethod
    def creator(cls):
        return CustomContextCmd() # return ribbon spline ctx cmd   

def initializePlugin(plugin):
    
    plugin_fn = om.MFnPlugin(plugin)
    
    try:
        plugin_fn.registerContextCommand(CustomContextCmd.COMMAND_NAME,
                                         CustomContextCmd.creator)
    except:
        om.MGlobal.displayError("Failed to register context command: %s"
                                 %CustomContextCmd.COMMAND_NAME)

    
def uninitializePlugin(plugin):
    plugin_fn = om.MFnPlugin(plugin)
    
    try:
        plugin_fn.deregisterContextCommand(CustomContextCmd.COMMAND_NAME)

    except:
        om.MGlobal.displayError("Failed to deregister context command: %s"
                                 %CustomContextCmd.COMMAND_NAME)

Latest update,
I Tried using “appendSyntax” function inside “CustomContextCmd” class to see if there’s any difference

class CustomContextCmd(omui.MPxContextCommand):
    
    COMMAND_NAME = "customCommand"   # used as mel command to create context
    
    def __init__(self):
        super(CustomContextCmd, self).__init__()
        
    # required for maya to get instance of context
    def makeObj(self):
        return CustomContext()    # return ribbon spline ctx 

    def appendSyntax(self):
        syntax = om.MSyntax()
        syntax.addFlag(kMeshFlag, kMeshLongFlag, om.MSyntax.kDouble)
        return syntax
        
    @classmethod
    def creator(cls):
        return CustomContextCmd() # return ribbon spline ctx cmd   

But Maya still didn’t recognize the flag:
“# Error: TypeError: file line 2: Invalid flag ‘m’ #”

Along ‘appendSyntax’, you should override ‘doQueryFlags’ and ‘doEditFlags’ methods of MPxContextCommand class. Something like this:

...

def appendSyntax(self):
    """ ."""

    syntax = self.syntax()
    syntax.addFlag(kMeshFlag, kMeshLongFlag, om.MSyntax.kLong)

def doEditFlags(self):
    """ ."""

    argParser = self.parser()

    if argParser.isFlagSet(kMeshFlag):
        mesh_flag = argParser.flagArgumentInt(kMeshFlag, 0)

        # do something meaningful when in edit mode
        self.mesh_flag = mesh_flag  # like setting a dummy attribute..
        print(f'===>>> Editing flag with value: {mesh_flag} <<<===')

def doQueryFlags(self):
    """ ."""

    argParser = self.parser()

    if argParser.isFlagSet(kMeshFlag):
        # get/do something when in query mode, and call setResult() method with argument of what querying this flag should return
        if hasattr(self, 'mesh_flag'):  # just a check if attribute exists, since we've created it on the fly for this demo, in first edit flag call
            print(f'===>>> Querying flag {kMeshLongFlag}: {self.mesh_flag} <<<===')
            self.setResult(self.mesh_flag)  # setting result to dummy value we stored when editing flag

And you should probably remove both ‘syntaxCreator’ and ‘argumentParser’ methods from your ‘CutomClass’ class inheriting MPxToolCommand (unless you need it for something else).

Hi! Thanks for the reply,

I tried to implement your snippet into my code, but there are a few things I can’t seem to figure out:

  1. what is self.parser() refer to? because the class object can’t identify that attribute
  2. Do I need to include MPxToolCommand, are MPxContextCommand and MPxContext enough to add a custom flag to my context command?

The idea of what I’m trying to achieve is to create my custom context command with a specific argument, to elaborate:

# Step 1: create my custom context instance with unique argument
ctx = cmds.customCommand(flag=argument)

# Step 2 (automatic): context object will fetch the argument and produce different behavior through user actions like doPress or doDrag

# Step 3: set context as active tool
cmds.setToolTo(ctx)

I’ll try to keep it short, and hopefully it would not be confusing (though it probably will be), and help you get a clearer picture of what’s going on.

Question 1 - I guess you’ve just copied code, and methods are left in global scope (no indentation), effectively making them functions - by saying:

It was kinda assumed that you would put them inside ‘CustomContextCmd’ class, where you have placed ‘appendSyntax’ method (that’s how you override methods), which means ‘self’ would be reference to this class instance, like usual.

So, if you place that code in correct place, then you can create your context:

ctx = cmds.customCommand()

After which you could query and edit flags of your context instance like:

cmds.customCommand(ctx, e=True, m=42)
cmds.customCommand(ctx, q=True, m=True)

And of course, use it:

cmds.setToolTo(ctx)

And you need to create context instance first, prior to accessing it. Not really sure if you could pass flags at instantiation time, or would it make any sense (like, why would you pass anything to ‘move’ tool (which is context); maybe gizmo size, or something, but don’t know; haven’t tried it - might be totally valid…).

But I don’t think that’s the idea of how you should use contexts - rather you should have context accompanied by MEL scripts (named same as your context command, with Properties and Values suffixes; so, ‘customCommandProperties’ and ‘customCommandValues’), which will define how Tool Settings UI (UI that pops up when you double-click ‘move’ context, for example), and its UI elements, will interact with your context (here comes querying and editing context command flags).

*Note: you could put all code of these 2 scripts in one MEL file (‘customCommandProperties.mel’), and it will work same as if ‘customCommandValues.mel’ is created also.

I think main confusion here is that you’re mixing context command and tool command - they are not same thing. Command can (and should) take flags (and tool command is just a command), because you don’t create it, but run it; context command, which actually creates context tool doesn’t really need flags, since when your tool is active, you can access it via above mentioned tool settings (again, perhaps it’s possible/beneficial to pass something, so you could try it - hopefully, you’ll get enough info after reading this).

**And all arguments parsing logic in your code is in class that you don’t use (read just below).

Question 2 - your code kinda answers it - MPxToolCommand is optional, and in your code (which I’ve noticed later on), you’re not actually using that class at all (could delete entire ‘CutomTool’ class, and won’t make a slightest change) - you must register that command also, along with registering context command (and deregister, of course) in ‘initializePlugin’ function (look code below) - using ‘registerCommand()’ and ‘registerContextCommand()’ methods respectively.

MPxToolCommand have methods that should be used from context tool (like for undo functionality, etc.), otherwise, it would be regular MPxCommand.

If you need tool command, then, like I said, you need to register it, so you need to give it a name, which you would register and use to run it. And you could use flags with it.

Without further delay, here’s some (based on yours) code that will register both context and tool commands, and you could try mixing it up, play with it, and perhaps get what you want:

import maya.api.OpenMaya as om
import maya.api.OpenMayaUI as omui

# flag definition
kMeshFlag = "-m"
kMeshLongFlag = "-mesh"


# tell maya plugin produces and need to be passed, maya api 2.0
def maya_useNewAPI():
    pass


class CutomTool(omui.MPxToolCommand):
    """ ."""

    COMMAND_NAME = 'cutomTool'

    mesh = None

    def __init__(self):
        super().__init__()

    @classmethod
    def syntaxCreator(cls):
        """ ."""

        syntax = om.MSyntax()

        # (short name, long name, argument data type)
        syntax.addFlag(kMeshFlag, kMeshLongFlag, om.MSyntax.kString)

        return syntax

    def argumentParser(self, argList):
        """ ."""

        syntax = self.syntax()
        parsedArguments = om.MArgParser(syntax, argList)

        if parsedArguments.isFlagSet(kMeshFlag):
            self.mesh = parsedArguments.flagArgumentString(kMeshFlag, 0)

        if parsedArguments.isFlagSet(kMeshLongFlag):
            self.mesh = parsedArguments.flagArgumentString(kMeshLongFlag, 0)

    def doIt(self, argList):
        """ ."""

        self.argumentParser(argList)

        if self.mesh:
            self.redoIt()

    def isUndoable(self):
        """ ."""

        return True

    # action to undo operation
    def undoIt(self):
        """ ."""

        print ("undo")

    def redoIt(self):
        """ ."""

        print ("redo")

    def finalize(self):
        """ ."""

        command = om.MArgList()
        command.addArg(om.MString(kMeshFlag))
        command.addArg(self.mesh)

        try:
            omui.MPxToolCommand.doFinalize(self, command)
        except Exception:
            pass

    @classmethod
    def creator(cls):
        """ ."""

        return cls()


class CustomContext(omui.MPxContext):
    """ ."""

    TITLE = "Title"
    HELP_TEXT = "Help Text"

    def __init__(self):
        super().__init__()
        self.setTitleString(CustomContext.TITLE)

    def helpStateHasChanged(self, event):
        self.setHelpString(CustomContext.HELP_TEXT)

    def toolOnSetup(self,event):
        om.MGlobal.selectCommand(om.MSelectionList())  # select nothing, to record undo
        self.reset_context()

    def toolOffCleanup(self):
        self.reset_context()

    def doPress(self, event, draw_manager, frame_context):
        print ("do press")

    def completeAction(self):
        print ("complete action")

    def deleteAction(self):
        print ("undo")

    def abortAction(self):
        print ("abort")

    def reset_context(self):
        print ("reset")


class CustomContextCmd(omui.MPxContextCommand):
    """ ."""

    COMMAND_NAME = "testTA"   # used as mel command to create context

    def __init__(self):
        super().__init__()

    # required for maya to get instance of context
    def makeObj(self):
        return CustomContext()    # return ribbon spline ctx

    @classmethod
    def creator(cls):
        return CustomContextCmd() # return ribbon spline ctx cmd

    def appendSyntax(self):
        """ ."""

        syntax = self.syntax()
        syntax.addFlag(kMeshFlag, kMeshLongFlag, om.MSyntax.kLong)

    def doEditFlags(self):
        """ ."""

        argParser = self.parser()

        if argParser.isFlagSet(kMeshFlag):
            mesh_flag = argParser.flagArgumentInt(kMeshFlag, 0)

            # do something meaningful when in edit mode
            self.mesh_flag = mesh_flag  # like setting a dummy attribute..
            print(f'===>>> Editing flag with value: {mesh_flag} <<<===')

    def doQueryFlags(self):
        """ ."""

        argParser = self.parser()

        if argParser.isFlagSet(kMeshFlag):
            # get something when in query mode, and call setResult() method with argument of what querying this flag should return
            if hasattr(self, 'mesh_flag'):  # just a check if attribute exists, since we've created it on the fly for this demo, in first edit flag call
                print(f'===>>> Querying flag {kMeshLongFlag}: {self.mesh_flag} <<<===')
                self.setResult(self.mesh_flag)  # setting result to dummy value we stored when editing flag


def initializePlugin(plugin):

    plugin_fn = om.MFnPlugin(plugin)

    try:
        plugin_fn.registerCommand(CutomTool.COMMAND_NAME, CutomTool.creator, CutomTool.syntaxCreator)
        plugin_fn.registerContextCommand(CustomContextCmd.COMMAND_NAME, CustomContextCmd.creator)
    except:
        om.MGlobal.displayError(f"Failed to register context command: {CustomContextCmd.COMMAND_NAME}")

def uninitializePlugin(plugin):
    plugin_fn = om.MFnPlugin(plugin)

    try:
        plugin_fn.deregisterContextCommand(CustomContextCmd.COMMAND_NAME)
        plugin_fn.deregisterCommand(CutomTool.COMMAND_NAME)
    except:
        om.MGlobal.displayError(f"Failed to deregister context command: {CustomContextCmd.COMMAND_NAME}")

I’ve changed name of context command to ‘testTA’, so, either create it with ctx = cmds.testTA(), or rename it (‘customCommand’ was too generic, and was overriding something).
Also, I left the name of class ‘CutomTool’, though I think it’s a typo.

You create context tool as mentioned above. And you can run your command with:

cmds.cutomTool()
cmds.cutomTool(m='blah')

So, these are two distinct things: ‘customCommand’ context command, and ‘cutomTool’ command. And you don’t have to create context first, in order to run command.

P.S. For steps 1 and 2 - maybe that’s not the best approach, to pass arguments, but you should utilize context’s tool settings (explained above) for producing different behaviour (like, I think, it’s meant to be used).
Or you may try to pass arguments to tool command, and that tool command creates context, and sets what needs to be set, etc…

Hi bjelDark,

I appreciate your time and explanation. I’m getting a better understanding of context class (it’s frustrating how little documentation out there :expressionless:)

So from your answer, it’s better to have two commands for context (create context and modify context). But I’ll figure out later if I need to add that extra command, Because currently, I attach my context to some custom UI, and the UI will handle all the flag data (again, this might not be the best approach).

I already include your snippet inside “CustomContextCmd” class but the error persist (self.parser inside the query and edit function and self.syntax inside appendSyntax still unrecognized), for detail here is my full “CustomContextCmd” class snippet

class CustomContextCmd(omui.MPxContextCommand):
    
    COMMAND_NAME = "customContext"   # used as mel command to create context
    
    def __init__(self):
        super(CustomContextCmd, self).__init__()
        
    def makeObj(self):
        return CustomContext()    # return custom ctx instance
    
    def appendSyntax(self):
        syntax = self.syntax()   
        syntax.addFlag(kMeshFlag, kMeshLongFlag, om.MSyntax.kDouble)
    
    def doEditFlags(self):
        argParser = self.parser()

        if argParser.isFlagSet(kMeshFlag):
            mesh_flag = argParser.flagArgumentInt(kMeshFlag, 0)
            self.mesh_flag = mesh_flag 
            print (self.mesh_flag)

    def doQueryFlags(self):
        argParser = self.parser()

        # check if attribute exist
        if argParser.isFlagSet(kMeshFlag):
            if self.mesh_flag: 
                self.setResult(self.mesh_flag)  # setting result to dummy value we stored when editing flag
                print (self.mesh_flag)

    @classmethod
    def creator(cls):
        return RibbonSplineContextCmd() # return ribbon spline ctx cmd          

Along with the customContext class, so far I can create my custom context. The problem is, the context still doesn’t recognize my custom flag (KMeshFlag)

Additional question for further possible research,

  1. Do I need to use MPxToolCommand when modifying context, or is it fine with regular MPxCommand?
  1. About tool settings for custom context, do I need to create my layout and UI (Separated window class) then dock and parent it to Maya?

Thank you so much!

No problem.

You should be more careful when selectively doing copy/paste of code, and watch out what you omit or change - for example, your ‘creator’ method returns ‘RibbonSplineContextCmd’ class (guess that’s what you’ll name it in the end, but for now, that class is nowhere to be found in code shared here):

    ...

    @classmethod
    def creator(cls):
        return RibbonSplineContextCmd() # return ribbon spline ctx cmd 

Change that to return correct class (‘CustomContextCmd’ in this case), and try editing/querying context tool’s flag like explained above. It should work.

Not really - you must have two classes for context (MPxContext and MPxContextCommand), and one command (which is that class MPxContextCommand), for it to work. This context command is used to create your context tool, and to edit/query its flags.
Other command is MPxToolCommand, which, again, is not necessary for context to work.
Maybe, for starters, you should completely ignore tool command, remove it from code, and focus on context itself, til you get the hang of it.

Question 1: You don’t need any of those - MPxContextCommand is enough to edit/query its own flags.

Question 2: You DO need to create UI, but not in the way you’re describing it in this question - you define UI layout (tool property sheet) in MEL script (with naming explained in previous reply), and it’s managed by Maya, with its internal mechanism, meaning, it will pop up UI window with layout specified by you, when you double-click context tool’s icon - you don’t create window that will hold your UI (called toolPropertyWindow), its creation is done by Maya, and you’re just parenting your UI layout to it inside that ‘…Properties.mel’ file.

If it’s still confusing what’s that tool property thingy:

Here are move and rotate tools, and UI when you double-click on them.
In MEL code, you set how all that in yellow should look and what should be there, and Maya creates this surrounding window, called Tool Settings, that will be parent to your layout. When you double-click tool’s icon, querying context’s flags should happen, and UI should update to match it; when you change something in UI, it should edit context’s flags to match.

3 Likes

Hi bjelDark, thanks for the reply!

I think I still have some problems here, specifically with RibbonContextCmd class inside appendSyntax, doEditFlags, and doQueryFlags functions.

I’m referring to your code and Maya can’t identify self.parser and self.syntax attribute, what it might refer to? Without further delay, here’s my full context code:

import maya.api.OpenMaya as om
import maya.api.OpenMayaUI as omui

# flag definition
kMeshFlag = "-m"
kMeshLongFlag = "-mesh"

# maya api 2.0
def maya_useNewAPI():
    pass

class RibbonContext(omui.MPxContext):

    def __init__(self):
        super(RibbonContext, self).__init__()
        
    def toolOnSetup(self,event):
        om.MGlobal.selectCommand(om.MSelectionList()) 
        self.reset_context()
        
    def toolOffCleanup(self):
        self.reset_context()

    def doPress(self, event, draw_manager, frame_context):
        print ("do press")
          
    def completeAction(self):
        print ("complete action")
        
    def deleteAction(self):
        print ("delete action")
        
    def abortAction(self):
        print ("abort action")

    def reset_context(self):
        print ("reset context")
   
class RibbonContextCmd(omui.MPxContextCommand):
    
    COMMAND_NAME = "zRibbonCtx"   
    
    def __init__(self):
        super(RibbonContextCmd, self).__init__()
        
    def makeObj(self):
        return RibbonContext() 
    
    @classmethod
    def creator(cls):
        return RibbonContextCmd()

    def appendSyntax(self):
        syntax = self.syntax()
        syntax.addFlag(kMeshFlag, kMeshLongFlag, om.MSyntax.kDouble)

    def doEditFlags(self):
        argParser = self.parser()

        if argParser.isFlagSet(kMeshFlag):
            mesh_flag = argParser.flagArgumentInt(kMeshFlag, 0)
            print (mesh_flag) 

    def doQueryFlags(self):
        argParser = self.parser()

        if argParser.isFlagSet(kMeshFlag):
            print ("query mesh flag")

def initializePlugin(plugin):
    
    plugin_fn = om.MFnPlugin(plugin)
    
    try:
        plugin_fn.registerContextCommand(RibbonContextCmd.COMMAND_NAME,
                                         RibbonContextCmd.creator)
    except:
        om.MGlobal.displayError("Failed to register context command: %s"
                                 %RibbonContextCmd.COMMAND_NAME)
    
def uninitializePlugin(plugin):
    plugin_fn = om.MFnPlugin(plugin)
    
    try:
       plugin_fn.deregisterContextCommand(RibbonContextCmd.COMMAND_NAME)
    except:
        om.MGlobal.displayError("Failed to deregister context command: %s"
                                 %RibbonContextCmd.COMMAND_NAME)

Code looks fine now, and it works for me (Maya 2023/2024). But, noticing that you have changed some lines to ancient string formatting, I figure that you’ve probably on Maya 2020?

And looking in API docs, MPxContextCommand really didn’t have syntax and parser methods - seems like they were added starting from 2022 version. So, you might need to use MPxToolCommand, after all, for parsing arguments, and be creative. Unfortunately, I can not help you further with that, and would suggest to move to newer Maya, if possible - no reason, really, spending time developing something that is (becoming) obsolete (of course, if not forced to use explicitly Maya 2020; and python 2).

1 Like

You are right I’ve been working on Maya 2020 and I just tested the code above inside Maya 2022 and it works :sweat_smile:

I see, that might explain why some examples I found parse the arguments inside MpxToolCommand class like helixTool.cpp

Thank you so much once again bjelDark, I appreciate your time! Hopefully, someone who has the same problem as me in the future will find this topic helpful :raised_hands:

Below is the full code, tested in Maya 2022 using Python 3.xx

import maya.api.OpenMaya as om
import maya.api.OpenMayaUI as omui

# flag definition
kMeshFlag = "-m"
kMeshLongFlag = "-mesh"

# maya api 2.0
def maya_useNewAPI():
    pass

class RibbonContext(omui.MPxContext):

    def __init__(self):
        super(RibbonContext, self).__init__()
        
    def toolOnSetup(self,event):
        om.MGlobal.selectCommand(om.MSelectionList()) 
        self.reset_context()
        
    def toolOffCleanup(self):
        self.reset_context()

    def doPress(self, event, draw_manager, frame_context):
        print ("do press")
          
    def completeAction(self):
        print ("complete action")
        
    def deleteAction(self):
        print ("delete action")
        
    def abortAction(self):
        print ("abort action")

    def reset_context(self):
        print ("reset context")
   
class RibbonContextCmd(omui.MPxContextCommand):
    
    COMMAND_NAME = "zRibbonCtx"   
    
    def __init__(self):
        super(RibbonContextCmd, self).__init__()
        
    def makeObj(self):
        return RibbonContext() 
    
    @classmethod
    def creator(cls):
        return RibbonContextCmd()

    def appendSyntax(self):
        syntax = self.syntax()
        syntax.addFlag(kMeshFlag, kMeshLongFlag, om.MSyntax.kDouble)

    def doEditFlags(self):
        argParser = self.parser()

        if argParser.isFlagSet(kMeshFlag):
            mesh_flag = argParser.flagArgumentInt(kMeshFlag, 0)
            self.mesh_flag = mesh_flag  # setting a dummy attribute..
            print(f'===>>> Editing flag with value: {mesh_flag} <<<===')      

    def doQueryFlags(self):
        argParser = self.parser()

        if argParser.isFlagSet(kMeshFlag):
            # get something when in query mode, and call setResult() method with argument of what querying this flag should return
            if hasattr(self, 'mesh_flag'):  # check if attribute exists, since we've created it on the fly for this demo, in first edit flag call
                print(f'===>>> Querying flag {kMeshLongFlag}: {self.mesh_flag} <<<===')
                self.setResult(self.mesh_flag)  # setting result to dummy value we stored when editing flag      

def initializePlugin(plugin):
    
    plugin_fn = om.MFnPlugin(plugin)
    
    try:
        plugin_fn.registerContextCommand(RibbonContextCmd.COMMAND_NAME,
                                         RibbonContextCmd.creator)
    except:
        om.MGlobal.displayError("Failed to register context command: %s"
                                 %RibbonContextCmd.COMMAND_NAME)
    
def uninitializePlugin(plugin):
    plugin_fn = om.MFnPlugin(plugin)
    
    try:
       plugin_fn.deregisterContextCommand(RibbonContextCmd.COMMAND_NAME)
    except:
        om.MGlobal.displayError("Failed to deregister context command: %s"
                                 %RibbonContextCmd.COMMAND_NAME)
1 Like