UNSOLVED Python Generator: Handles

Hi all,
Is it possible to create a simple square spline parametric with handles in a Python Generator or it's works only with plugins via DRAWPASS_HANDLES like at this example Py-DoubleCircle

A Python generator creates a virtual hierarchy of objects. You can not select any of these objects, only the generator itself. Therefore, the hypothetical handles will never come to pass. In other words: no.
You can feed the Python generator with User Data to control certain input values, or you can read e.g. the position of helper objects outside the generator to make the generator react to. But to my knowledge, no handles. For that you will need a full plugin.

This is what I was afraid, thanks for the answer!

Hello @inevestenko,

thank you for reaching out to us. And thank your @Cairyn for providing an answer. @Cairyn's answer is correct in the sense that a Python Generator object does not provide a callback for the viewport hook, i.e., a draw() function with which one could draw handle information.

It is however possible to use a Python Generator Object and Interaction Tag combination to mimic a handles interface like provided by plugins. There are however a few concessions to make, most importantly that one cannot draw into the DRAWPASS_HANDLES with an Interaction Tag. One could use just a plain Python Programming Tag instead, which can draw into any pass, but there one would have to implement the event loop oneself. At the end you can find an example file and code for how to implement a cube object where the size can be manipulated via the viewport.


The result:

The example file:

The Python Generator Object code:

"""Python Generator Object example for mimicking a handle interface with 
Python Scripting objects.

As discussed in:

import c4d

def main():
    size = op[c4d.ID_USERDATA, 1]
    cube = c4d.BaseObject(c4d.Ocube)
    cube[PRIM_CUBE_LEN] = c4d.Vector(size)
    return cube

The Python Interaction Tag code:

"""Python Interaction Tag example for mimicking a handle interface with 
Python Scripting objects.

As discussed in:

import c4d
from c4d import gui

def mouseDown():
    """Executed when the user clicks into the object.
    # We just cache the size of our cube at the moment, global attributes are
    # evil, this should be done more formally in a production environment, one
    # could for example use a userdata parameter to store this value or just
    # store it in the BaseContainer of a node.
    global startSize
    startSize = op[c4d.ID_USERDATA, 1]

def mouseDrag():
    """Executed while dragging.
    # Here we are going to be extra lazy and just use the tags delta parameter
    # to compute the drag distance, this could also be done more formally,
    # yielding better results.
    global startSize
    op[c4d.ID_USERDATA, 1] = startSize + delta.GetLength()

def draw(bd):
    # Bail when op is None, i.e., the tag is not attached to an object. 
    # Technically, we would also like to test here something like
    #   bd.GetDrawPass() != c4d.DRAWPASS_HANDLES:
    #       ...
    # But this draw() is only being called for DRAWPASS_OBJECT, so we
    # cannot draw into the "proper" handles pass.
    if op is None:

    # Setup the drawing matrix in the space of the object.
    bd.SetMatrix_Matrix(op, op.GetMg())

    # We are going to draw the size handle as a gizmo on the z-axis.
    # We have to halve the value because the cube object size is effectively
    # used as a radius and we nudge it a little, so that it is not "colliding"
    # with the polygons of the object due to us being in the object draw pass.
    size = op[c4d.ID_USERDATA, 1] * .5 + 1
    bd.SetPen(c4d.Vector(1, 1, 0))
    p = c4d.Vector(0, 0, size)

    bd.DrawHandle(p, c4d.DRAWHANDLE_BIG, 0)