Your approach is not necessarily worse, one could even argue that it is better. I personally would always avoid manually binding to an OS DLL via ctypes, but that is more a personal preference.
administrators
Posts
-
RE: How to select multiple files using "c4d.storage.LoadDialog()"?
-
RE: Is it okay to release a free plugin that overlaps with paid alternatives?
Hey @Anlv,
to make it short: You can release whatever you want, there is no etiquette that prevents you from releasing your software because it rains on the parade of someone else. That is just part of software development that you always outshine someone else in some shape or form.
But the Cinema community is very polite, I would be surprised if someone would take offsense in any shape or form.
Cheers,
Ferdinand -
RE: How to select multiple files using "c4d.storage.LoadDialog()"?
Yes, that is a very low level way to do it, I assume you had help from an AI in writing it?
I personally meant more to use a GUI kit such as tkinter or QT. We deliberately do not ship our Python with tkinter (the builtin GUI lib of Python), but you could just install it yourself. The advantage of tkinter or a similar lib is that things like
filedialog.askopenfilenameswill work on multiple OS's and versions of it, the disadvantge is that you have to install the dependency. Doing it manually yourself means that you have to support each OS yourself and also have to track versions of the API. But at least GetOpenFileNameW is probably so stable that you never have to care about the latter on WIndows.Cheers,
Ferdinand -
RE: Effector Objects written in Python
Hey @Dimitris_Derm. ,
no there is currently no dedicated plugin class for MoGraph effectors and fields in Python, only the scripting objects exist at the moment.
Cheers,
Ferdinand -
RE: Reading Immutable Selection Tags
Hello @Dimitris_Derm. ,
Thank you for reaching out to us. Please follow our Support Procedures, especially regarding providing code and scene data alongside your questions. Because otherwise things can become a guessing game for us. It also often the case that things are more complicated than it seems, as the case here.
About your Question
When you talk about 'selection tags', I assume from the context that you actually mean selection proxy tags (denoted by the little link overlay icon). E.g., as here generated by the extrude object.

Proxy selection tags exist because selection tags need a point object to work, when a selection tag sits on a generator, it becomes ambiguous to which point object inside the cache of the generator they belong. Proxies solve this by pointing to one or many actual selection tags in a cache (it is often the case that a proxy tag does not resolve to a singular discrete tag in the cache but to multiple tags).
The proxy tag type class/interface is private as declared in the C++ API, i.e., one cannot interact directly with them (as a third party). But one can send
MSG_GETREALTAGDATAto find the discrete tag(s) it points to.
The data returned by MSG_GETREALTAGDATAis by design data in caches. You can read data in caches, but you should never attempt to write data in caches, as caches are static by design, and violating this rule can lead to crashes.
What makes this more complicated in this case is that you use Neutron (Scene Nodes) which adds another layer of complexity. We would need here your existing code and a concrete scene to give an answer, as the approach shown below will likely not work there.Cheers,
FerdinandResult
An extrude object which holds a proxy selection tag. In the console we can see the partial scene graph for the object. I have highlighted the line which is the actual selection tag inside the cache of the extrude object the proxy points to.

My scene: extrude.c4d
Code
"""Demonstrates how to find the discrete selection tags which are pointed to by a selection proxy tag on a generator. """ import c4d import mxutils doc: c4d.documents.BaseDocument # The currently active document. op: c4d.BaseObject | None # The primary selected object in `doc`. Can be `None`. # The types of proxy tags which we want to find on the selected object. PROXY_TAG_TYPES: tuple[int, ...] = ( c4d.Tcacheproxytagpointselection, c4d.Tcacheproxytagedgeselection, c4d.Tcacheproxytagpolyselection ) def main() -> None: """Called by Cinema 4D when the script is being executed. """ if op is None: return c4d.gui.MessageDialog('Please select an object.') # The proxy tags of #op. Proxy tags exist because selections (and some caches) only work directly # on a point object. A selection tag on a generator can be ambiguous, because it might not be # clear which of the multiple point objects in the cache of the generator it should be applied # to. Proxy tags solve this problem, as they point to one or multiple discrete selection tags # in the cache of a generator, which should be used in place of the proxy. proxyTags: list[c4d.BaseTag] = [t for t in op.GetTags() if t.GetType() in PROXY_TAG_TYPES] if not proxyTags: return c4d.gui.MessageDialog('The selected object has no proxy tags.') # Print the partial scene graph of #op, so that we can see what is going on in the cache tree # of it.We should see there the discrete selection tag(s) which are pointed to by the proxy tags # of #op. print(mxutils.GetSceneGraphString(op)) print("\n", "-" * 80, "\n") # Iterate over all proxy tags and find the discrete selection tags they point to, using the # message c4d.MSG_GETREALTAGDATA. The result of this message is a dictionary, which contains # references to the discrete selection tags in the cache of the generator, which are pointed # to by the proxy tag. for proxyTag in proxyTags: res: dict = {} proxyTag.Message(c4d.MSG_GETREALTAGDATA, res) tagSymbol: str = mxutils.g_c4d_symbol_translation_cache.Get(proxyTag.GetType())[0] print(f"{proxyTag.GetName()}({tagSymbol}) points to: {res}") # NEVER modify the returned tags, as caches are by design static (but that is not physically # enforced by Cinema 4D). Changing the content of caches can very easily lead to crashes, unless # you know exactly what you are doing. Reading data from caches is perfectly fine, though. if __name__ == '__main__': main() -
RE: How to select multiple files using "c4d.storage.LoadDialog()"?
Hello @Anlv,
Welcome to the Maxon developers forum and its community, it is great to have you with us!
Getting Started
Before creating your next postings, we would recommend making yourself accustomed with our forum and support procedures. You did not do anything wrong, we point all new users to these rules.
- Forum Overview: Provides a broad overview of the fundamental structure and rules of this forum, such as the purpose of the different sub-forums or the fact that we will ban users who engage in hate speech or harassment.
- Support Procedures: Provides a more in detail overview of how we provide technical support for APIs here. This topic will tell you how to ask good questions and limits of our technical support.
- Forum Features: Provides an overview of the technical features of this forum, such as Markdown markup or file uploads.
It is strongly recommended to read the first two topics carefully, especially the section Support Procedures: How to Ask Questions.
About your First Question
What you are asking for is not possible in this form at the moment. The Cinema (C++) API has two functions for opening the native OS file and folder dialogs: FileSelect and FileSelectMultiple. The Python API makes this more complicated that it has to be with
c4d.storage.LoadDialogandc4d.storage.SaveDialogbut both just wrapFileSelect.With
FileSelectMultipleyou can do what you want, select multiple files in one go, but the Python API currently does not wrap this function. You must either open multiple dialogs or use a third party GUI API which can open such native OS dialog for you.Cheers,
Ferdinand -
RE: Can I have access to the Loop and Ring Selection algorithm ?
Hey,
well, "intended" would be the wrong word, but I am aware. The whole resource parsing situation is a bit of a mess at the moment, both in C++ and the Python API.
Why is it like this?
A description is defined by a
resfile (more or less its GUI declaration) and anhfile which declares symbols for parameter IDs. The resource for the loop tool looks like this:CONTAINER ToolLoopSelection { NAME ToolLoopSelection; INCLUDE ToolBase; GROUP MDATA_MAINGROUP { BOOL MDATA_LOOP_SEL_STOP_AT_SELECTIONS { } BOOL MDATA_LOOP_SEL_STOP_AT_NON_QUADS { } BOOL MDATA_LOOP_SEL_STOP_AT_POLES { } } HIDE MDATA_COMMANDGROUP; }I.e., it indeed only defines three parameters. But the header file looks like this:
#ifndef TOOLLOOPSELECTION_H__ #define TOOLLOOPSELECTION_H__ enum { MDATA_LOOP_SEL_STOP_AT_SELECTIONS = 1100, // BOOL MDATA_LOOP_SEL_STOP_AT_NON_QUADS = 1110, // BOOL MDATA_LOOP_SEL_STOP_AT_POLES = 1120, // BOOL MDATA_LOOP_FIRST_VERTEX = 1130, // LONG MDATA_LOOP_SECOND_VERTEX = 1131, // LONG MDATA_LOOP_POLYGON_INDEX = 1132, // LONG MDATA_LOOP_BOTH_SIDES = 1133, // BOOL MDATA_LOOP_SWAP_SIDES = 1134, // BOOL MDATA_LOOP_SELECTION_TYPE = 1140, // LONG (must be SELECTION_NEW, SELECTION_ADD or SELECTION_SUB) MDATA_LOOP_SEL_POLYGON_OBJECT = 1150, // LINK }; #endif // TOOLLOOPSELECTION_H__I.e., it not only defines these three parameters, but also all the others. Because there are all these "hidden" parameters which are written into the data container of the tool, but never show up in the GUI. What collides here is (a) the a bit irregular (but not illegal) behavior of a resource to define more parameters in its header file than are used in the resource and (b) the questionable decision of our resource parser to ignore hidden parameters.
Our resource parsing is automated, i.e., I cannot just go into a file and just add these parameters for a docs build. I could touch the resource parsers for Python and C++, but I do not have the time for that right now as they has been written in a very opaque manner.
My advice is simply what the docs suggest: Search in the header files directly. Go to your Cinema folder, make sure that the
resource.zipis either unpacked to the local folder (the folder existing does not mean necessarily that it has been fully unpacked) or an external folder of your choice. And then simply search in that folder with an editor of your choice.
At some point I will replace the resource and symbols parsing for Python and C++, because we made there quite a mess in the past with questionable parsers and manually curated lists. But for now this cannot be changed and using the files directly is the way to go when you want to look at descriptions.
Cheers,
FerdinandPS: The C++ docs are NOT inherently better in that regard. The parser shares there the same flaws. The reason why you find some symbols there is because developers have duplicated them from the resources into the frameworks.
-
RE: Can I have access to the Loop and Ring Selection algorithm ?
Hello @Dimitris_Derm.,
Welcome to the Maxon developers forum and its community, it is great to have you with us!
Getting Started
Before creating your next postings, we would recommend making yourself accustomed with our forum and support procedures. You did not do anything wrong, we point all new users to these rules.
- Forum Overview: Provides a broad overview of the fundamental structure and rules of this forum, such as the purpose of the different sub-forums or the fact that we will ban users who engage in hate speech or harassment.
- Support Procedures: Provides a more in detail overview of how we provide technical support for APIs here. This topic will tell you how to ask good questions and limits of our technical support.
- Forum Features: Provides an overview of the technical features of this forum, such as Markdown markup or file uploads.
It is strongly recommended to read the first two topics carefully, especially the section Support Procedures: How to Ask Questions.
About your First Question
I am not quite sure how your question is meant, but the loop and ring selection tools are accessible in the API. When you are literally asking for the algorithm, we cannot share that, as declared in our Support Procedures:
We will not reveal non-public functionalities of our APIs.
When you just want to use the tools programmatically, SendModelingCommand is the way to go. We also have a dedicated section in our examples for them.
Cheers,
FerdinandResult

Code
"""Demonstrates how to use the loop selection command in the Python API. The script creates a cylinder, makes it editable, and then performs a loop selection based on two vertices and a polygon index. Finally, it inserts the modified object into the active document and updates the scene. """ import c4d import mxutils doc: c4d.documents.BaseDocument # The currently active document. op: c4d.BaseObject | None # The primary selected object in `doc`. Can be `None`. def main() -> None: """Called by Cinema 4D when the script is being executed. """ # Instantiate a cylinder and insert it into a temporary working document. cylinder: c4d.BaseObject = mxutils.CheckType(c4d.BaseObject(c4d.Ocylinder)) temp: c4d.documents.BaseDocument = mxutils.CheckType(c4d.documents.BaseDocument()) temp.InsertObject(cylinder) # Make the object editable. We could also build the caches and get the cache, but since the # subject is here SMC, we can also use SendModelingCommand and MODELINGCOMMAND_MAKEEDITABLE. res: list[c4d.BaseObject] | bool = c4d.utils.SendModelingCommand( c4d.MCOMMAND_MAKEEDITABLE, list=[cylinder], mode=c4d.MODELINGCOMMANDMODE_ALL, doc=temp) if not res: raise RuntimeError("Failed to make object editable.") # The cylinder is now editable. cylinder: c4d.PolygonObject = res[0] # Now we are going to do the loop selection. These are the IDs defined in toolloopselection.h: # MDATA_LOOP_SEL_STOP_AT_SELECTIONS = 1100, // BOOL # MDATA_LOOP_SEL_STOP_AT_NON_QUADS = 1110, // BOOL # MDATA_LOOP_SEL_STOP_AT_POLES = 1120, // BOOL # MDATA_LOOP_FIRST_VERTEX = 1130, // LONG # MDATA_LOOP_SECOND_VERTEX = 1131, // LONG # MDATA_LOOP_POLYGON_INDEX = 1132, // LONG # MDATA_LOOP_BOTH_SIDES = 1133, // BOOL # MDATA_LOOP_SWAP_SIDES = 1134, // BOOL # MDATA_LOOP_SELECTION_TYPE = 1140, // LONG (must be SELECTION_NEW, SELECTION_ADD or SELECTION_SUB) # MDATA_LOOP_SEL_POLYGON_OBJECT = 1150, // LINK settings: c4d.BaseContainer = c4d.BaseContainer() # Define the major input, two vertices and a polygon index. You need all three, regardless of the output mode # you choose in SMC. settings[c4d.MDATA_LOOP_FIRST_VERTEX] = 2 settings[c4d.MDATA_LOOP_SECOND_VERTEX] = 3 settings[c4d.MDATA_LOOP_POLYGON_INDEX] = 1 # Define the selection mode, this is more cosmetic, as SELECTION_NEW is the default. settings[c4d.MDATA_LOOP_SELECTION_TYPE] = c4d.SELECTION_NEW # Carry out the loop selection, targeting edge mode. res: list[c4d.BaseObject] | bool = c4d.utils.SendModelingCommand( c4d.ID_MODELING_LOOP_TOOL, list=[cylinder], mode=c4d.MODELINGCOMMANDMODE_EDGESELECTION, bc=settings, doc=temp) if not res: raise RuntimeError("Failed to perform loop selection.") # Scene elements can only be inserted once, the Python API removes nodes automatically when you call InsertObject, but # C++ does not and there is no guarantee that the Python API babysits you in all cases where you can insert something, # so it is good practice to ensure scene integrity yourself. (#cylinder is currently part of #temp). cylinder.Remove() # Insert the object into our scene, enable edge mode, make the object selected, and push an update event. doc.InsertObject(cylinder) doc.SetMode(c4d.Medges) doc.SetActiveObject(cylinder) c4d.EventAdd() if __name__ == '__main__': main() -
RE: GetEffectiveRenderData does not return per-take overrides for RDATA_FRAMEFROM / RDATA_FRAMETO
Hello @vaishhg,
Thank you for reaching out to us and your comprehensive yet sparse problem description, this really makes things easier for us.
The problem is a bit that you somewhat forgot a crucial part: The scene data :). You provided this:
Create a Cinema 4D scene with a Main take (frame range 0-6)
Create child takes (V, E, R, A) and override the render data's frame range on each (e.g., V: 0-9, E: 0-23, R: 0-13, A: 0-17)But I cannot recreate a scene with these instructions, unless you meant the second step in a rather metaphorical way (switching the render data itself via takes). I also talked with some of our support specialists, and the consensus seems to be that what you claim to be the case - a scene with take overwritten FROM/TO values - is not possible. What you can do, is create multiple render settings and then activate them via takes.
But Takes are a tricky subject, and I would not rule out that you somehow found a way to create take overrides for parameters of a render data instance. If so, please share the scene. Find also a scene attached which demonstrates what I discussed above: A scene that switches the active render data based on takes. It will print this with your script:
Take 'Main': FRAMEFROM=0, FRAMETO=90 Take '0_45': FRAMEFROM=0, FRAMETO=45 Take '0_90': FRAMEFROM=0, FRAMETO=90So, to formally answer your main question: When my assumptions above are true, your scene is printing the same values because each take does have the same values, as these two parameters are not directly overridable.
Cheers,
Ferdinand -
RE: NETWORK_CONNECTTIMEOUT with SSL
Timeouts should not happen unless the endpoint is indeed timing out. But as I said, I cannot help you unless you give me compilable code (i.e., a little self contained function) with which I can reproduce your claims. If possible, please also share your endpoint as it obviously plays a major role in this problem.