Share

Working With Menus

Note:

In 3ds Max 2025 the menu system has changed. This topic has been updated to reflect these changes. See the Menu System topic in the Developer Guide for more information. In the new system, menus are created at startup either from XML files (.MNX and .QMNX files created using the Menu Editor in 3ds Max), or in scripts that are registered to the cuiRegisterMenus menu event callback. You can also create "dynamic" menus in code at any time. For a more complete overview of the new system, see "Menu System" in the 3ds Max Developer Help.

For more information, see the following topics:

The 3ds Max SDK includes samples that demonstrate how to register and create menus, including some Python samples. The sample below is the same as the pySimpleMenu.py sample under <maxsdk>/howto/ui/menudemo/scripts/. This sample illustrates how to create menus that invoke a macroScript in response to the #cuiRegisterMenus notification in Python:

"""
Creates a simple menu with two menu items which reference actions defined via macroScript.
"""
# pylint: disable=invalid-name,import-error
from pymxs import runtime as rt

# ------------- Definition of MacroScript Actions that can be triggered by your plugin menu items --------------------

# ------------- Define plugin action 1 --------------------
# Create a macroscript that executes our plugin specific code
# (this macroscript defines an action that can be used as an Action menu item)
rt.macros.new(
    "Menu Demo Category",   # Category for the macro script
    "MenuAction1",          # Name of the macro script
    "Action 1 Tooltip Text",  # Tooltip text for the action
    "Action 1 Menu Text",   # Text displayed in the menu
    "executeAction1()"      # Function to execute when this action is triggered
)

# Our plugin specific function that gets called when the user clicks the menu item
def executeAction1():
    print("Action 1 executed")
    
# Expose this function to the maxscript global namespace
rt.executeAction1 = executeAction1

# ------------- Define plugin action 2 --------------------
# Create a second macroscript for another plugin-specific action
rt.macros.new(
    "Menu Demo Category",
    "Menu Action 2",
    "Action 2 Tooltip Text",
    "Action 2 Menu Text",
    "executeAction2()")

# Our plugin specific function that gets called when the user clicks the menu item    
def executeAction2():
    print("Action 2 executed")
    
# Expose this function to the maxscript global namespace
rt.executeAction2 = executeAction2

# ------------- Menu Creation Callback --------------------
# This callback is called every time the menu structure is newly evaluated, such as on 3ds Max startup or when the menu preset changes
def menucallback():
    """Register the a menu an its items.
    This callback is registered on the "cuiRegistermenus" event of 3dsMax and
    is typically called in he startup of 3dsMax.
    """
    menumgr = rt.callbacks.notificationparam()
    mainmenubar = menumgr.mainmenubar
    
    # Create a new submenu at the end of the 3dsMax main menu bar
    # To place the menu at a specific position, use the 'beforeID' parameter with the GUID of the suceeding menu item
    # Note, that every menu item in the menu system needs a persistent Guid for identification and referencing
    submenu = mainmenubar.createsubmenu("F7B07F21-B82C-4A84-8557-1CA9EAB40A9B",
        "Demo Simple Menu Python")
   
    # Add the first macroscript action to the submenu
    macroscriptTableid = 647394 
    submenu.createaction("054B69E1-62D6-46E5-BC39-794E5B34E659",
        macroscriptTableid, "MenuAction1`Menu Demo Category") # Note the action identifier created from the macroscripts name and category

    # Add the second macroscript action to the submenu
    submenu.createaction("6DA06361-4EFC-4A77-9181-213A337BF4AB",
        macroscriptTableid, "Menu_Action_2`Menu Demo Category")

# Make sure menucallback is called on cuiRegisterMenus events
# so that it can register menus at the appropriate moment
MENU_DEMO_SCRIPT = rt.name("pySimpleMenuDemo")
rt.callbacks.removescripts(id=MENU_DEMO_SCRIPT)
rt.callbacks.addscript(rt.name("cuiRegisterMenus"), menucallback, id=MENU_DEMO_SCRIPT)

# For testing your python script in development, you can comment this line in to force a reloading of the menu system when evaluating your script manually from the 3dsMax Scripting Editor.
# Note, that this line needs to be removed again when your menu script gets put into the according 3dsMax startup folder, since the Menu system is automatically
# sending out the 'cuiRegisterMenus' notifications on startup for the menu structure evaluation! 
# The menu notifications are also sent out, when the user switches the menu preset in the menu editor. So you can also toggle this for menu reloading.
# If your script got evaluated successfully your menus should get also listed in the menu editor for customization.
#
# rt.maxops.GetICuiMenuMgr().LoadConfiguration(rt.maxops.GetICuiMenuMgr().GetCurrentConfiguration())

Was this information helpful?