Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Add menu item to existing submenu in Python

    Cinema 4D SDK
    3
    7
    1.1k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • C
      Chris Chris
      last edited by

      Hello,

      I have two C++ plugins which register lots of menu entries. These are not generated under the usual "Extensions" / custom plugin menu, but in an own top level menu and its submenus. That works and look like that:

      MyMenu                                        
        MySubmenu1
          MyMenuItem1
        MySubmenu2
          MyMenuItem2
      

      Now I want to register a Python script to add a menu item after MyMenuItem2, but it appears after MyMenuItem1.

      MyMenu 
        MySubmenu1
          MyMenuItem1
          MyMenuItem3  // here it appears, created from Python
        MySubmenu2
          MyMenuItem2a
          MyMenuItem2b
          MyMenuItem2c 
          *MyMenuItem3*  // here it should appear
      

      I used the official example for building menus and an older forum post How to add a plugin to a toolbar menu?

      My code is

      def EnhanceMenu():  
      
        # getting the main menu resource container
        menu = c4d.gui.GetMenuResource("M_EDITOR")
        
        # find our menu
        customMenu = None
        for bcMenuId, bcMenu in menu:
          #print(" " + str(bcMenuId) + "  " + str(bcMenu))
          if bcMenu != 1: #?
            #print(bcMenu[c4d.MENURESOURCE_SUBTITLE])
            if bcMenu[c4d.MENURESOURCE_SUBTITLE] == "MyMenu":
              customMenu = bcMenu
              #customMenu = menu.GetContainerInstance(bcMenuId)
              break
        print(customMenu)
      
        # find our submenu
        customMenu = None
        menu = customMenu
        for bcMenuId, bcMenu in menu:
          if bcMenu != 1: #?
            #print(" " + str(bcMenuId) + "  " + str(bcMenu))
            #print(bcMenu[c4d.MENURESOURCE_SUBTITLE])
            if bcMenu[c4d.MENURESOURCE_SUBTITLE] == "MySubMenu2":
              customMenu = bcMenu
              break
        print(customMenu)
      
        # test debug output submenu content
        menu = customMenu
        for bcMenuId, bcMenu in menu:
          if bcMenu != 1: #?
            print(" " + str(bcMenuId) + "  " + str(bcMenu))
            print(bcMenu[c4d.MENURESOURCE_SUBTITLE])
            print(bcMenu[c4d.MENURESOURCE_STRING])
      
        # create new menu item
        customMenu.InsData(c4d.MENURESOURCE_COMMAND, "PLUGIN_CMD_" + str(MY_COMMAND_ID))
      
        c4d.gui.UpdateMenus()
      
      
      def PluginMessage(id, data):
        if id == c4d.C4DPL_BUILDMENU:
          EnhanceMenu()
      
      

      I know the code is a bit redundant and I compare the menu text instead of the id, but there was not id set in C++. Worth to mention is that the test debug out submenu content prints

       4  MySubMenu2
      i
      t
       3  PLUGIN_CMD_1000001
      I
      N
       3  PLUGIN_CMD_1000002
      I
      N
       3  PLUGIN_CMD_1000003
      I
      N
      

      The expected strings should be MyMenuItem2a, MyMenuItem2b and MenuItem2c instead of single letters. The commands are correct. And why is MyMenuItem3 within MySubmenu1?

      Has someone any idea?

      Chris

      1 Reply Last reply Reply Quote 0
      • C
        Chris Chris
        last edited by

        I noticed when I add a submenu it is created in the correct place. The code:

        # create new menu item
        #customMenu.InsData(c4d.MENURESOURCE_COMMAND, "PLUGIN_CMD_" + str(MY_COMMAND_ID))
        menu = c4d.BaseContainer()
        menu.InsData(c4d.MENURESOURCE_COMMAND, "PLUGIN_CMD_" + str(MY_COMMAND_ID))
        customMenu.InsData(c4d.MENURESOURCE_SUBMENU, menu)
        

        But it is not what I want.

        1 Reply Last reply Reply Quote 0
        • ManuelM
          Manuel
          last edited by

          Hi,

          In your code, i don't understand those lines, the loop will never work for me.

          customMenu = None
          menu = customMenu
          for bcMenuId, bcMenu in menu:
          

          Look at this thread, which might help you.

          Cheers,
          Manuel

          MAXON SDK Specialist

          MAXON Registered Developer

          1 Reply Last reply Reply Quote 0
          • C
            Chris Chris
            last edited by

            Hello Manuel,

            The line

            customMenu = None
            

            came accidentally in when cleaning up the code for posting. It is not in the actual code. Sorry for the confusion. My current code revision:

            def GetMenuContainer(name, mainMenu=None):
              "searches a submenu in given menu basecontainer. Uses toplevel menu if mainMenu is None. Returns False if not found."
              # get top level menu
              if not mainMenu:
                mainMenu = c4d.gui.GetMenuResource("M_EDITOR")
            
              # search for submenu or entry
              for bcMenuId, bcMenu in mainMenu:
                #print("bcMenuId " + str(bcMenuId) + "  bcMenu " + str(bcMenu))
                if bcMenu != 1:
                  #print(bcMenu[c4d.MENURESOURCE_SUBTITLE])
                  if bcMenu[c4d.MENURESOURCE_SUBTITLE] == name:
                    return bcMenu
            
              #return mainMenu.InsData(c4d.MENURESOURCE_SUBTITLE, name) # create
              return None
            
            def AddMenuEntry(menu, plugin_id):
              "add a menu command to given menu basecontainer and returns True on success, else False."
              plugin_cmd = "PLUGIN_CMD_{0}".format(plugin_id)
              menu.InsData(c4d.MENURESOURCE_COMMAND, plugin_cmd)
              return c4d.gui.SearchMenuResource(menu, plugin_cmd)
            
            
            def EnhanceMenu():
              # search our submenu and add an entry for this script
              ok = False
              menu = GetMenuContainer("MyMenu", None)
              if menu:
                menu = GetMenuContainer("MySubmenu2", menu)
                if menu:
                  ok = AddMenuEntry(menu, MY_COMMAND_ID)
            
              # if that failed because user has moved the (sub)menu then add it to default plugin menu
              if not ok:
                menu = c4d.gui.SearchPluginMenuResource()
                if menu:
                  ok = AddMenuEntry(menu, MY_COMMAND_ID)
            
              if not ok:
                print("Unable to register menu command: " + c4d.plugins.GeLoadString(IDS_XXX))
            
              c4d.gui.UpdateMenus()
            
            
            def PluginMessage(id, data):
              if id == c4d.C4DPL_BUILDMENU:
                EnhanceMenu()
            
            

            I read your linked forum post and had already made experiments with recreating the menu structure and will give it another try.

            I encourage Maxon to simplify things for Python here.

            Kind Regards,
            Chris

            1 Reply Last reply Reply Quote 0
            • C
              Chris Chris
              last edited by

              It works now. Surprisingly I just restarted the PC and I did not changed the python plugin script! I worked for three days on this menu problem and restarted the PC (Windows 64 10 bit) every morning. During that time I did not do any other changes to the OS or Cinema4D (R21 and R23) settings / plugins or Python or similar. Restarting now a couple of times did not go back to the erroneous behavior. So about the final outcome I am half happy and half unhappy.

              1 Reply Last reply Reply Quote 0
              • ManuelM
                Manuel
                last edited by

                I encourage Maxon to simplify things for Python here
                I've noted it, we will see if we can improve things.

                Well pretty strange that it's working now. There's no magic, cache involved there. It's just some BaseContainer and strings.

                MAXON SDK Specialist

                MAXON Registered Developer

                1 Reply Last reply Reply Quote 0
                • ferdinandF
                  ferdinand
                  last edited by ferdinand

                  Hello @Chris-Chris,

                  without further questions or postings, we will consider this topic as solved by Wednesday and flag it accordingly.

                  Thank you for your understanding,
                  Ferdinand

                  MAXON SDK Specialist
                  developers.maxon.net

                  1 Reply Last reply Reply Quote 0
                  • First post
                    Last post