Custom menu in C4D top menu bar with custom Python commands
-
I'm looking to set up a custom menu in the Cinema4D's top menu bar. I found the Enhancing the Main Menu example which shows how to add a custom menu yet it skips to to show how to add a bunch of custom commands as menu items.
So basically this is what I found:
import c4d from c4d import gui main_menu = gui.GetMenuResource('M_EDITOR') # Create custom menu menu = c4d.BaseContainer() menu.InsData(c4d.MENURESOURCE_SUBTITLE, "MyMenu") # Add commands menu.InsData(c4d.MENURESOURCE_COMMAND, "IDM_NEU") menu.InsData(c4d.MENURESOURCE_SEPERATOR, True) menu.InsData(c4d.MENURESOURCE_COMMAND, "IDM_NEU") # Add custom menu to main menu main_menu.InsData(c4d.MENURESOURCE_STRING, menu) # Refresh menu bar gui.UpdateMenus()
Yet now I'm looking to add some menu items that allow me to trigger some custom Python code to show some pipeline tools (Qt widgets). This would be the first step to integrating open-source pipeline Avalon completely into Cinema4D, status for that will be tracked in this Avalon issue.
- Say I wanted to add five menu items, each triggering its own tool. How would I do so? I believe I have to register a command and then add it to the menu item. But a quick search didn't bring me to a concrete example on how to do so. I found this topic but it still was unclear how to proceed and make a simple menu item with some custom commands.
- Would I need a unique plug-in ID for each single command I'll register? Or can this be done differently?
A short to the point example could would be much appreciated.
Thanks in advance!
-
@BigRoy Hi and I am not to sure what you are asking for, but I hope this help you out, I didn't had time to type out a quick code but I hope these pics below help you.
Now from what I know, you have to make a command plugin with a ID to add to your tool to the custom menu.
So basically you need to make it a plugin so c4d can call or load your custom menu that is the (PluginMessage()) when the plugin is loaded. For scripts that's a no.These are how my studio tools menu look in c4d :
This when you press V key button and the c4d pipe menu comes up :
The code in your .pyp file :
Cheers!
If you anymore questions free to contact me I don't mine helping in the best way I can.
Fackbook: https://www.facebook.com/ap.base.1
Email: [email protected] -
Hi Bigroy, thanks for reaching out us.
Sorry for the answer taking longer than expected by holiday season didn't helped
- Say I wanted to add five menu items, each triggering its own tool. How would I do so? I believe I have to register a command and then add it to the menu item. But a quick search didn't bring me to a concrete example on how to do so. I found this topic but it still was unclear how to proceed and make a simple menu item with some custom commands.
The process is pretty straightforward and your initial finding in our documentation is actually the entry point.
Saving the code below in a.pyp
file located in the folder where Cinema 4D looks for plugins should do the trick and show the following entry in the Cinema 4D top-menu bar.
import c4d from c4d import gui PLUGIN1_ID = 999121031 PLUGIN2_ID = 999121032 PLUGIN3_ID = 999121033 PLUGIN4_ID = 999121034 PLUGIN5_ID = 999121035 class PC_12103_1(c4d.plugins.CommandData): def Execute(self, doc): print "Execute the 1st command" return True class PC_12103_2(c4d.plugins.CommandData): def Execute(self, doc): print "Execute the 2nd command" return True class PC_12103_3(c4d.plugins.CommandData): def Execute(self, doc): print "Execute the 3rd command" return True class PC_12103_4(c4d.plugins.CommandData): def Execute(self, doc): print "Execute the 4th command" return True class PC_12103_5(c4d.plugins.CommandData): def Execute(self, doc): print "Execute the 5th command" return True def EnhanceMainMenu(): mainMenu = gui.GetMenuResource("M_EDITOR") # Get main menu resource pluginsMenu = gui.SearchPluginMenuResource() # Get 'Plugins' main menu resource menu = c4d.BaseContainer() # Create a container to hold a new menu information menu.InsData(c4d.MENURESOURCE_SUBTITLE, "Your Menu") # Set the name of the menu menu.InsData(c4d.MENURESOURCE_COMMAND, "PLUGIN_CMD_999121031")# Add registered command identified by ID 999121031 menu.InsData(c4d.MENURESOURCE_COMMAND, "PLUGIN_CMD_999121032")# Add registered command identified by ID 999121032 menu.InsData(c4d.MENURESOURCE_COMMAND, "PLUGIN_CMD_999121033")# Add registered command identified by ID 999121033 menu.InsData(c4d.MENURESOURCE_COMMAND, "PLUGIN_CMD_999121034")# Add registered command identified by ID 999121034 menu.InsData(c4d.MENURESOURCE_COMMAND, "PLUGIN_CMD_999121035")# Add registered command identified by ID 999121035 menu.InsData(c4d.MENURESOURCE_SEPERATOR, True); # Add a separator menu.InsData(c4d.MENURESOURCE_COMMAND, "PLUGIN_CMD_5159") # Add command 'Cube' with ID 5159 to the menu submenu = c4d.BaseContainer() # Create a new submenu container submenu.InsData(c4d.MENURESOURCE_SUBTITLE, "Submenu") # This is a submenu submenu.InsData(c4d.MENURESOURCE_COMMAND, "IDM_NEU") # Add registered default command 'New Scene' to the menu submenu.InsData(c4d.MENURESOURCE_COMMAND, "IDM_SPEICHERN") # Add registered default command 'Save' to the menu menu.InsData(c4d.MENURESOURCE_SUBMENU, submenu) # Add the submenu if pluginsMenu: # Insert menu after 'Plugins' menu mainMenu.InsDataAfter(c4d.MENURESOURCE_STRING, menu, pluginsMenu) else: # Insert menu after the last existing menu ('Plugins' menu was not found) mainMenu.InsData(c4d.MENURESOURCE_STRING, menu) def PluginMessage(id, data): if id==c4d.C4DPL_BUILDMENU: EnhanceMainMenu() if __name__ == "__main__": c4d.plugins.RegisterCommandPlugin(PLUGIN1_ID, "1st Cmd", 0, None, "", PC_12103_1()) c4d.plugins.RegisterCommandPlugin(PLUGIN2_ID, "2nd Cmd", 0, None, "", PC_12103_2()) c4d.plugins.RegisterCommandPlugin(PLUGIN3_ID, "3rd Cmd", 0, None, "", PC_12103_3()) c4d.plugins.RegisterCommandPlugin(PLUGIN4_ID, "4th Cmd", 0, None, "", PC_12103_4()) c4d.plugins.RegisterCommandPlugin(PLUGIN5_ID, "5th Cmd", 0, None, "", PC_12103_5())
- Would I need a unique plug-in ID for each single command I'll register? Or can this be done differently?
Yes, that's the way Cinema 4D registers commands. You can generate your unique plugin IDs on this link.
Looking forward Avalon to be brought on Cinema, give best.
Riccardo -
@r_gigante Thanks, the command examples were exactly what I needed. And someone stating explicitly that I needed to create a command per menu entry made me help realize that too.
I now have an Avalon menu - perfect.
Bug with C4D and PYTHONPATH
I did hit a C4D bug as I started setting up the pipeline integration, whenever there's an empty value in the list on
PYTHONPATH
then Python fails to run correctly in Cinema4D. More details hereIcons. Are they
.tiff
only?I've tried to add an icon to the menu but I failed there. I tried to pass it a
.svg
icon like so:bitmap = c4d.bitmaps.BaseBitmap() bitmap.InitWith(path)
And then providing that to
c4d.plugins.RegisterCommandPlugin
as the 4th argument (where there'sNone
in your example). However, the menu entry showed no icon.
Should this work?
Opening files in C4D with Python (resolved)
I should be separating this out into a new Topic I suppose. But any pointers on how to save/open scenes in C4D Python?
I've tried this:
import os import c4d def file_extensions(): return [".c4d"] def _document(): return c4d.documents.GetActiveDocument() def has_unsaved_changes(): doc = _document() if doc: return doc.GetChanged() def save_file(filepath): doc = _document() if doc: return c4d.documents.SaveDocument(doc, filepath, c4d.SAVEDOCUMENTFLAGS_NONE, c4d.FORMAT_C4DEXPORT) def open_file(filepath): doc = c4d.documents.LoadDocument(filepath, c4d.SCENEFILTER_0, None) if doc: c4d.documents.SetActiveDocument(doc) return doc def current_file(): doc = _document() if doc: root = doc.GetDocumentPath() fname = doc.GetDocumentName() if root and fname: return os.path.join(root, fname)
But whenever I
open_file
a document and set it active, then all functionality in C4D's UIs gets disabled and greyed out. Am I misinterpreting how this should work? The other code seems to do exactly what I need.Edit I should have used
c4d.documents.LoadFile
for opening a file - that does what I need.However, it kept failing with:
Traceback (most recent call last): File "C:\Users\Roy\AppData\Roaming\Maxon\Maxon Cinema 4D R21_64C2B3BD\library\scripts\untitled.py", line 16, in <module> doc = c4d.documents.LoadFile(path) TypeError:unable to convert unicode to @net.maxon.interface.url-C
This was due to the filepath being
unicode
. That's fixed by forcing it tostr
usingstr(path)
. -
@BigRoy said in Custom menu in C4D top menu bar with custom Python commands:
Bug with C4D and PYTHONPATH
I did hit a C4D bug as I started setting up the pipeline integration, whenever there's an empty value in the list on PYTHONPATH then Python fails to run correctly in Cinema4D. More details hereNice find. This is a bug or maybe a limitation.
I've tried to add an icon to the menu but I failed there. I tried to pass it a .svg icon like so:
Yea, that won't work. SVG is not supported. Use TIFF instead (others are possible).
However, it kept failing with:
Traceback (most recent call last):File "C:\Users\Roy\AppData\Roaming\Maxon\Maxon Cinema 4D R21_64C2B3BD\library\scripts\untitled.py", line 16, in <module>
doc = c4d.documents.LoadFile(path)
TypeError:unable to convert unicode to @net.maxon.interface.url-C
This is most likely because you didn't decode it as UTF-8 when giving it to a python-function. (Assuming you used LoadDialog or similar c4d-specific function).
Python uses unicode, Cinema 4D's default coding is utf8. -
@BigRoy In the future, I invite you to open a new topic for new questions no related. This way it helps other readers to understand and find what they need.
It also helps to not pollute a topic with multiple topics.@BigRoy said in Custom menu in C4D top menu bar with custom Python commands:
Bug with C4D and PYTHONPATH
I did hit a C4D bug as I started setting up the pipeline integration, whenever there's an empty value in the list on
PYTHONPATH
then Python fails to run correctly in Cinema4D. More details hereThanks for reporting it, it will be fixed in the next Cinema 4D update.
@BigRoy said in Custom menu in C4D top menu bar with custom Python commands:
Icons. Are they
.tiff
only?I've tried to add an icon to the menu but I failed there. I tried to pass it a
.svg
icon like so:bitmap = c4d.bitmaps.BaseBitmap() bitmap.InitWith(path)
SVG is currently not supported so either use tiff, png, jpg whatever else is supported by Cinema 4D.
If you have further questions about it, please open a new topic and don't continue here.Opening files in C4D with Python (resolved)
I should be separating this out into a new Topic I suppose. But any pointers on how to save/open scenes in C4D Python?
I've tried this:
import os import c4d def file_extensions(): return [".c4d"] def _document(): return c4d.documents.GetActiveDocument() def has_unsaved_changes(): doc = _document() if doc: return doc.GetChanged() def save_file(filepath): doc = _document() if doc: return c4d.documents.SaveDocument(doc, filepath, c4d.SAVEDOCUMENTFLAGS_NONE, c4d.FORMAT_C4DEXPORT) def open_file(filepath): doc = c4d.documents.LoadDocument(filepath, c4d.SCENEFILTER_0, None) if doc: c4d.documents.SetActiveDocument(doc) return doc def current_file(): doc = _document() if doc: root = doc.GetDocumentPath() fname = doc.GetDocumentName() if root and fname: return os.path.join(root, fname)
But whenever I
open_file
a document and set it active, then all functionality in C4D's UIs gets disabled and greyed out.
Am I misinterpreting how this should work? The other code seems to do exactly what I need.If LoadFile works for you it's nice, but I would like to confirm with you since normally LoadDocument should work.
So when you said a document, it's a Cinema 4D document (a *.c4d file)?
If that's the LoadDocument let you load a document but you need to tell what to load e.g. Object, or Material?
Then if you want to expose this document to the user, you need to insert it first with c4d.documents.InsertBaseDocument.# Loads a documents with objects and material in memory newDoc = c4d.documents.LoadDocuments(filePath, c4d.SCENEFILTER_OBJECTS | c4d.SCENEFILTER_MATERIALS, None) if newDoc is None: raise RuntimeError("Failed to load the document") # Exposes it to the user and set it as active c4d.documents.InsertBaseDocument(newDoc) c4d.documents.SetActiveDocument(newDoc) c4d.EventAdd()
If you have further questions about it, please open a new topic and don't continue here.
And as @mp5gosu pointed, Cinema 4D Classic API expects a string and not Unicode.
Cheers,
Maxime. -
The issue about PYTHONPATH is now resolved in R21 Sp2.
Cheers,
Maxime