Group Details Private

administrators

RE: How to import svg in S25 from path

Hello @InterfaceGuy,

thank you for reaching out to us. You can just use the existing loading and merging functions in c4d.documents. The snippet below will open a file dialog where you must select a SVG file, and it will then load that SVG file into a new scene. The example will also suppress the options dialog of the importer. There are many options with c4d.documents.LoadDocument and c4d.documents.MergeDocument; to not suppress these options dialogs, to merge with specific documents, etc. If necessary, you could also get hold of the SVG importer plugin first to set the import settings. See our documentation for details on c4d.documents.LoadDocument. And in our GitHub Python SDK repository, in the Files & Media section, you can find examples for manipulating the im- and exporter plugins.

Cheers,
Ferdinand

The result:
d7a200e5-ae4f-4d9d-97ed-4337d4cda711-image.png

The script:

import c4d
import os

def main():
    """Loads an svg file from a file dialog.
    
    You can just replace `file` with any path you would like.
    """
    file = c4d.storage.LoadDialog(title='Select a file')
    if not file or os.path.splitext(file)[1].lower() != ".svg":
        raise RuntimeError("Please select a svg file.")
        
    doc = c4d.documents.LoadDocument(file, c4d.SCENEFILTER_NONE)
    if doc is None:
        raise RuntimeError("Failed to load svg file.")
    
    c4d.documents.InsertBaseDocument(doc)
    c4d.documents.SetActiveDocument(doc)
    
if __name__=='__main__':
    main()
posted in Plugin Development with Cinema 4D
RE: Best practices for a plugin containing multiple commands

Hello @hexbob6,

thank you for reaching out to us. Please remember to add tags for the targeted operating system(s) and version(s) of Cinema 4D to your topics in the future. You can read more about our support procedures int the Forum Guidelines.

About your question: The answer to your topic title,

Best practices for a plugin containing multiple commands

, slightly reaches into the domain of "out of scope of support" (see Forum Guidelines for details), since such general design decision questions are often hard to answer. So, some stuff in bullet points:

  1. CommandData.ExecuteOptionID() is related to your plugin entry in menus and palettes being split into two buttons. One which will lead to CommandData.Execute() and one which will lead to CommandData.ExecuteOptionID(). This is then rendered out as your plugin icon and a cogwheel next to it. You are somewhat expected to provide an options dialog with the secondary button, but that is just convention and you can do in CommandData.ExecuteOptionID() whatever you want. The Optimize-command of Cinema 4D makes use of that pattern for example: e682c153-adba-43b4-84d9-c9da1a0c12d9-image.png
  2. CommandData.ExecuteSubID() is related to your CommandData being split into multiple menu entries without having to provide multiple plugin ids. You must define these sub-menu entries yourself with CommandData.GetSubContainer() and also define the sub-ids for these sub-commands. ExecuteSubID() will then be called with these ids for the sub-commands you did define and therefore allow you to react to different 'buttons' the user did click on.
  3. At the surface, option No. 2 will be undistinguishable for users from providing multiple CommandData plugins. But plugin ids are not a scarce commodity, we give you as many as you want 😉 ExecuteSubID() is a more complex approach, which at least I would not use when I just want 3, 4, 5, ..., n commands, where n is a fixed number and reasonably small. But it is up to you to make that decision.

On top of that there are several other topics which are tangential to you question of the 'best practice'. There are for once ToolData plugins, which at a quick glance seem more fitting for your task of multiple plugins/commands 'that move the camera around' - since ToolData does provide dedicated keyboard and mouse input methods. There is also the type GeDialog (which you will also need for ToolData), which represents these little dialog windows Cinema 4D does open all over the place. In them you can define your own GUI and with that also your own buttons which do their own stuff. GeDialog is commonly used in conjunction with CommandData. Lastly, there are c4d.gui.GetInputState() and c4d.gui.GetInputEvent(), with which you can poll yourself for keyboard and mouse events and by that branch out from a single plugin into multiple actions.

I cannot really tell you what the best for you under the light of best practice would be, because that is a matter of taste and depends on what you want to do exactly. But for a beginner I would recommend writing multiple CommandData plugins and keeping it as simple as possible.

I hope this helps and cheers,
Ferdinand

posted in Plugin Development with Cinema 4D
RE: R25 - Compile plugin issue

Hey Frank,

I agree, but the problem is that projecttool also does play an automated inhouse-tool role, as you might still be aware of, and there these additional files are likely not welcome. But I will try to find a place for this idea, which then would have to be an option of the tool on a per project basis.

Cheers,
Ferdinand

posted in Plugin Development with Cinema 4D
RE: How to Register "Hot key" plugin in python?

Hello @gheyret,

thank you for reaching out to us and please excuse the slight delay. You are misunderstanding the purpose of PLUGINFLAG_COMMAND_HOTKEY, which admittedly is badly explained, and I also had to do some digging myself to understand how this is meant to be used. In bullet points:

  1. First of all, you do not need any special flags to assign a shortcut to your CommandData plugin. A simple PLUGINFLAG_SMALLNODE will be enough.
  2. Then there are these two flags PLUGINFLAG_COMMAND_HOTKEY and PLUGINFLAG_COMMAND_STICKY which are often used in conjunction internally. When you only pass PLUGINFLAG_COMMAND_HOTKEY, a command with your plugin id will be emitted continuously while the user is pressing the shortcut assigned to your plugin which did register with that flag. If also PLUGINFLAG_COMMAND_STICKY is being passed, there will only an opening and closing event for the key down and key up events.
  3. The Move Parent plugin you want to imitate is internally not a CommandData plugin, it simply registers its ID as an hotkey in a way not available to the public. But there are some CommandData plugins of ours which register as 'PLUGINFLAG_COMMAND_HOTKEY' or 'PLUGINFLAG_COMMAND_HOTKEY| PLUGINFLAG_COMMAND_STICKY'. And all these CommandData plugins then have one thing in common: They do nothing, or nothing related to the hotkey, as their Execute method will never be called or at least never be called in the context of the hotkey.
  4. So, hotkey means here that when you assign a shortcut to your plugin and press that shortcut key, a global hotkey message will be emitted. Which then can then be polled in other places. E.g., with EditorWindow.IsHotkeyDown or GeUserArea.IsHotkeyDown. Which is also why CommandData.Execute() will not be called when you pass the PLUGINFLAG_COMMAND_HOTKEY flag, since Cinema does assume you handle the associated logic in another place.
  5. So, to use PLUGINFLAG_COMMAND_HOTKEY, you will usually need at least two plugins. One that registers the hotkey and one that makes use of it. You could technically imagine a CommandData plugin which registers as a hotkey and the also makes use of the hotkey in its dialog window in some shape or form. But that will have the side effect that you cannot open the dialog with that hotkey, because CommandData.Execute() will never be called for that plugin when pressing the shortcut/hotkey. But you could still open the dialog by clicking with the mouse.
  6. If you want just some continuous execution, e.g., do XYZ while Shift + A are being pressed, you should just implement a normal CommandData and then poll in your CommandData.Execute() for these keys being pressed and do stuff while they are and stop doing when not. You can poll for the input state with c4d.gui.GetInputState and you can figure out the shortcuts assigned to your plugin with c4d.gui.GetShortcut, as shown in the code at the end of the posting.

The description for PLUGINFLAG_COMMAND_STICKY was either always wrong or is outdated, as it does indicate that 'PLUGINFLAG_COMMAND_HOTKEY | PLUGINFLAG_COMMAND_STICKY' will cause CommandData.Execute() to be called - which is not true.

I hope this helps,
Ferdinand

"""Example for retrieving the shortcuts for a plugin id.
"""

import c4d

def GetPluginShortcuts(pid: int) -> list[list[tuple[int]]]:
    """Retrieves the shortcuts for a plugin-id.
    
    Args:
        pid (int): The plugin id.
    
    Returns:
        list[list[tuple[int]]]: The shortcut sequences for the plugin.
    """
    # Get all shortcut containers for the plugin id.
    count = c4d.gui.GetShortcutCount()
    matches = [c4d.gui.GetShortcut(i) for i in range(count)
               if c4d.gui.GetShortcut(i)[c4d.SHORTCUT_PLUGINID] == pid]

    # build the shortcut data.
    result = []
    for item in matches:
        sequence = []
        for i in range(0, c4d.SHORTCUT_PLUGINID, 10):
            a, b = item[i], item[i+1]
            if isinstance(a, (int, float)):
                sequence.append((a, b))

        if sequence != []:
            result.append(sequence)
    return result


if __name__ == "__main__":
    # The shortcuts for your plugin id.
    for item in GetPluginShortcuts(1208170):
        for a, b in item:
            print (a, b, c4d.gui.Shortcut2String(a, b))
    # The shortcuts for the dissolve tool (multiple shortcuts)
    for item in GetPluginShortcuts(440000043):
        for a, b in item:
            print (a, b, c4d.gui.Shortcut2String(a, b))
posted in Plugin Development with Cinema 4D
RE: R25 doesn't register the [c4d.ID_BASEOBJECT_COLOR] parameter

Hi @reubenlara, I'm not able to reproduce here ID_BASEOBJECT_COLOR is correctly highlighted in R25.010 in Windows and Mac.

Would it be possible to share your full script with us? And also the complete error log.
However looking at the error the issue come later at the and this is the CallCommand(12298) which fail, moreover according to your screen you do have an object called 3D_Cursor so this branch will never be taken (at least not in your case).

Just to be sure would it be possible for you to write in the console c4d.ID_BASEOBJECT_COLOR and press enter. 908 should be written.

Thanks in advance,
Cheers,
Maxime.

posted in Plugin Development with Cinema 4D
RE: Run command, after c4d fully loaded...

Hello @ymoon,

thank you for reaching out to us and please excuse the slight delay in my answer. While the answers given here are all helpful (thanks guys!), I would press for taking a few steps back.

  1. It is neither normal, nor acceptable that Cinema 4D does crash. I would turn to customer support with your problem, to see if either your bug/problem is already known and/or if there does exist a workaround. This is primarily important because that there is no guarantee that turning to Python will make your problem go away. Please note that we are not customer support here, you can get it in the support center of ours.
  2. If your files are so heavy that your machine starts to buckle under the load of running a full Cinema 4D and rendering your scene, you can use one of the two GUI-less Cinema 4D instances we do offer, the Commandline and the c4dpy executables.
  3. In Python you can run the render queue. You can find the documentation for it under c4d.documents.BatchRender.
  4. I would normally either use c4dpy or just a normal Script Manager script for invoking a batch render, but if this is really the only option which fits your case, you could also write a plugin that listens in PluginMessage() for one of the early plugin messages, e.g., C4DPL_STARTACTIVITY as pointed out by Frank. But this solution of writing a full plugin seems unnecessarily complex.

Cheers,
Ferdinand

posted in Plugin Development with Cinema 4D
RE: R25 - Compile plugin issue

Hello @jwzegelaar,

thank you for reaching out to us. Your screenshot, build error message, and the structure of the solution explorer as well as the fact that there are no frameworks in your solution explorer indicate that you have:

  1. Either opened a project in your solution instead of the solution itself. So, if you have a directory \myFancysolution\, then there should be a file \myFancysolution\plugins\project\plugins.sln, which is the one you have to always open to build or debug a solution. Your screenshot does cut unfortunately the title bar and with that the file path of the opened "thing", but it looks very much like that you have opened something along the line of \myFancysolution\plugins\customcommandline\project\customcommandline.vcxproj. I.e., a .vcxproj project file instead of a .sln solution file.
  2. Or that your solution has been somehow corrupted. In this case I would recommend rebuilding the solution with the project tool. There are two projectdefinition.txt files which your solution should contain at least. One in \myFancysolution\plugins\project\ which should define the build targets (Win64, OSX) and the included projects (at least your customcommandline project) and one in \myFancysolution\plugins\customcommandline\project\ which must include the frameworks your project is using, again the target platforms, the project type (likely DLL in your case) and other things. More information on the projecttool and its features can be found in the Project Tool Help Page.

Cheers,
Ferdinand

posted in Plugin Development with Cinema 4D
RE: What's really happening inside a CKey?

Hi @Cairyn,

I do not really know where this has been claimed, but there was a note in our internal systems which summarized this thread as that the data types DTYPE_REAL, DTYPE_LONG, and DTYPE_BOOL should be written with .SetValue(). For which our documentation currently makes the slightly misleading claim that is should be used for float values. Which I believe was at least partially the starting point of this thread.

I just wanted to make sure that no unclarities remain for future readers, DTYPE_BOOL keys/tracks should be written with SetGeData() and .SetValue() can be used for DTYPE_REAL and DTYPE_LONG type keys.

Cheers,
Ferdinand

posted in Plugin Development with Cinema 4D
RE: Python "undo" changes the original selection order

hi,

well, first it needs to be fixed, then release on a cinema4D's update (hotfix, service pack, release)

Cheers,
Manuel

posted in Plugin Development with Cinema 4D
RE: Larger BaseMaterial Preview Renders

hi,

there's also RenderMaterialPreview that allow you to do this. Mark as private but still usable.

Progress hook can be defined to the basebitmap, BASEBITMAP_DATA_PROGRESSHOOK and BASEBITMAP_DATA_PROGRESSHOOK_CTX must be defined.

BaseMaterial* mat = doc->GetFirstMaterial();
if (mat)
{
  BaseMaterial* clone = (BaseMaterial*)mat->GetClone(COPYFLAGS::NO_HIERARCHY, nullptr); // copy without children
  if (clone)
  {
    finally
    {
      BaseMaterial::Free(clone);
    };

    BaseBitmap* bm = BaseBitmap::Alloc();
    if (bm)
    {
      if (bm->Init(1024, 1024) == IMAGERESULT::OK) // initialize preview bitmap with the preferred size
      {

        RENDERRESULT render_result = RENDERRESULT::PROJECTNOTFOUND;
      

        render_result = RenderMaterialPreview(doc->GetDocumentPath(), clone, mat, nullptr, bm, nullptr, 0, RENDER_PREVIEW_USE_BMP_SIZE);
        
        if ((render_result != RENDERRESULT::OK) && (render_result != RENDERRESULT::SAVINGFAILED)) // not ok and not only missing texture?
          BaseBitmap::Free(bm);
        else
          ShowBitmap(bm);
      }
      else
      {
        BaseBitmap::Free(bm);
      }
    }
  }
}

Hope that helped.
Cheers,
Manuel

posted in Plugin Development with Cinema 4D