Add menu item to existing submenu in Python
-
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
-
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.
-
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 -
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 -
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.
-
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.
-
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