Py4D API Extensions

The C4DDev Py4D API Extensions are a C++ plugin that provide additional functions that can be used from Python.

Note

The C++ component of C4DDev must be installed, otherwise the API extensions will not be available. Pre-compiled binaries are available in the GitHub Releases of C4DDev.

Members

c4ddev.__version__

The installed version of C4DDev.

c4ddev.has_cpp_extensions

True if the C4DDev C++ extensions are installed, otherwise False. Note that if the C++ extensions are not installed and you try to import c4ddev from a Cinema 4D plugin, importing c4ddev could fail if your plugin is executed before the C4DDev Python entrypoint.

Functions

c4ddev.GeListNodeFromAddress(pycobject) -> c4d.GeListNode

Given a PyCObject from which you know it is a Cinema 4D GeListNode instance, you can use this function to get a real Python object out of it. Important: This will result in undefined behaviour (likely a crash) if you pass a wrong PyCObject!

c4ddev.FileSelectPut(path)

This extension allows you to prevent file selection dialogs from popping up and make them return a specific value. This is very useful if you want to streamline Cinema 4D commands that usually do open a file selection dialog.

import c4d
import c4ddev

c4ddev.FileSelectPut('/Users/me/Desktop')
print(c4d.storage.LoadDialog())
# Doesn't open a dialog and prints /Users/me/Desktop

c4ddev.FileSelectPop()

Pop an element from the elements added to the FileSelect Hook queue with FileSelectPut().

c4ddev.FileSelectQueueSize()

Returns the number of elemenets in the queue added to the FileSelect Hook with FileSelectPut().

c4ddev.DocumentInfoData(pycobject) -> dict

Pass the PyCObject received on MSG_DOCUMENTINFO to this function to get a dictionary of the structures data. Important: This will result in undefined behaviour (likely a crash) if you pass a wrong PyCObject!

c4ddev.RenderNotificationData(pycobject) -> dict

Pass the PyCObject received on MSG_MULTI_RENDERNOTIFICATION this this function to get a dictionary of the structures data. Important: This will result in undefined behaviour (likely a crash) if you pass a wrong PyCObject!

c4ddev.GetUserAreaHandle() -> PyCObject

c4ddev.GetClipMapHandle() -> PyCObject

c4ddev.BlitClipMap(dst, src, dx, dy, dw, dh, sx, sy, sw, sh, mode)

This function implements the missing functionality of the GeClipMap to copy another bitmap into another, with the ability to copy only parts and in a different scale and aspect ratio (like GeUserArea.DrawBitmap()).

Parameters Description
dst The destination GeClipMap
src The source GeClipMap
dx The destination X coordinate.
dy The destination Y coordinate.
dw The destination width.
dh The destination height.
sx The destination X coordinate.
sy The destination X coordinate.
sw The destination X coordinate.
sh The destination X coordinate.
mode One of c4ddev.BLIT_NN, BLIT_BILINEAR or BLIT_BICUBIC

Limitation

Currently this function can only accept two GeClipMap objects as we haven’t figured out how to retrieve the actual C pointer to a BaseBitmap from a Python c4d.bitmaps.BaseBitmap object. You can convert a bitmap to a GeClipMap using the following code:

map = c4d.bitmaps.GeClipMap() map.InitWithBitmap(bmp, bmp.GetInternalChannel())

However, keep in mind that this process is relatively slow as it creates a new copy of the image. It is thus recommended to do this operation only once after a bitmap is loaded and keep it as a GeClipMap.

Plugin Messages

The C4DDev C++ component installs a SceneHook that takes special messages which are usually only sent to nodes in a document and redirects them as a global plugin message. Currently, the following messages sent to that scene hook are supported:

There’s a little bit of effort involved in receiving these in Python, though. The data sent will by a PyCObject and you have to use C4DDev to read the data from these objects.

import c4d
import c4ddev

def PluginMessage(msg, data):
  if msg == c4d.MSG_MULTI_RENDERNOTIFICATION:
    data = c4ddev.RenderNotificationData(data)
    print data
  elif msg == c4d.MSG_DOCUMENTINFO:
    data = c4ddev.DocumentInfoData(data)
    print data
  return True

c4ddev.gui.HandleMouseDrag(area, msg, type, data, flags)

This function calls GeUserArea::HandleMouseDrag() on the area object, which is missing in the Py4D API. Currently supported values for type are:

The type of data is specified in the parentheses above. msg must be a c4d.BaseContainer, ideally the one passed from GeUserArea.InputEvent().


c4ddev.am.RegisterMode(id, name, callback)

Registers a new mode in the Attribute Manager. The callback parameter is currently not used. Raises a RuntimeError when the registration failed.

c4ddev.am.SetMode(id, open)

Sets the current attribute manager mode. Optionally opens the attribute manager window.

c4ddev.am.SetObject(id, op, flags, activepage)

Sets the object op as the active object in the attribute manager specified by id. The activepage parameter is currently unused, but must be None or a c4d.DescID object.

c4ddev.am.Open()

Opens the attribute manager window.

c4ddev.am.EditObjectModal(op, title)

Opens a modal attribute manager to edit the object op. The window title will be set to title. Returns True on success, False on failure.

Command-line

-c4ddev-protect-source <filename>

When the C4DDev C++ extensions are available in a Cinema 4D installation, this command-line argument can be used to protect the source code of a Cinema 4D Python Plugin (.pyp), creating a .pype (before R15) or .pypv file (R15 and later). The argument can be specified multiple times to protected multiple files with a single invocation.

The c4ddev source-protector command can be used to protect source files from the Command-line conveniently.

$ c4ddev source-protector myplugin.pyp
...
[c4ddev / INFO]:py4d/Calling Source Protector for 'myplugin.pyp'..html

Alternatively, you can run Cinema 4D directly via the command-line or use the c4ddev run command.

$ /Applications/Cinema 4D R16/plugins/myplugin $ "..\..\CINEMA 4D.exe" -nogui -c4ddev-protect-source myplugin.pyp