Hey @vannipo,
Great to hear that you succeeded.
And just to be clear, we are here to answer your questions, that is part of the responsibility of the SDK group. I just have to sometimes moderate a bit the expectations of newcomers (and long timers) regarding what we can do (provide help along the way) and what not (provide full solutions, help with learning a language itself). So, there is no need to thank me every step of the way, we are happy to answer questions, as long as we see that users do not try to use us as a "write my script on demand"-service.
Regarding bringing your script to the next level, there are countless ways you could take, @iplai already pointed out the most straight forward one, assigning them all a hotkey or placing them in a palette (you can directly drag and drop the script cion from the script manager to do that). But I would assume that you are aware of that and rather want to consolidate your nine scripts into less scripts, i.e., bring down the number of hotkeys you require.
- First of all, it is not possible to write a plugin or script that consolidates multiple hotkeys. So, you cannot have a plugin which reacts to all variations of
CTRL + C
, as for example CTRL + C + A
, SHIFT + CTRL + C
, etc., being pressed.
- If you want a 'real' plugin, you could implement a
CommandData
plugin with a GeDialog
as an interface and thereby implement your own little GUI which determines what happens when you invoke the plugin, press its assigned shortcut. The GUI could then have a dropdown where you can select the transformation mode ("position", "rotation", "or "scale") and a dropdown where one can select the component ("x", "y", "z") and when the user has set them to "position" and "z", every invocation of the plugin would set a "position.z" keyframe.
- Another way to reduce the number of shortcuts would be to use a popup menu as a very minmal GUI. You could do this both in a plugin and a script. Find a small example below.
Cheers,
Ferdinand
Result

Code
"""Sets a key for one of the relative position components for all selected objects.
The component which is being set is being determined with a popup menu. This example must be run
from the script manager.
"""
import c4d
import typing
doc: c4d.documents.BaseDocument # The active document.
op: typing.Optional[c4d.BaseObject] # The active object, can be None.
# The parameter IDs this script is targeting.
DID_POS_X: c4d.DescID = c4d.DescID(
c4d.DescLevel(c4d.ID_BASEOBJECT_REL_POSITION, c4d.DTYPE_VECTOR, 0),
c4d.DescLevel(c4d.VECTOR_X, c4d.DTYPE_REAL, 0))
DID_POS_Y: c4d.DescID = c4d.DescID(
c4d.DescLevel(c4d.ID_BASEOBJECT_REL_POSITION, c4d.DTYPE_VECTOR, 0),
c4d.DescLevel(c4d.VECTOR_Y, c4d.DTYPE_REAL, 0))
DID_POS_Z: c4d.DescID = c4d.DescID(
c4d.DescLevel(c4d.ID_BASEOBJECT_REL_POSITION, c4d.DTYPE_VECTOR, 0),
c4d.DescLevel(c4d.VECTOR_Z, c4d.DTYPE_REAL, 0))
# The popup menu container for selecting the component which should be written, x, y, or z. I am using here
# the string mini language to embed an icon in the name of an item, so 'X Channel&i12153&' means for
# example, the label "X Channel" and the icon with the ID 12153. The mini language is documented under
# ShowPopupDialog(). The icons I am using here are documented under:
# https://developers.maxon.net/docs/Cinema4DPythonSDK/html/modules/c4d.bitmaps/RESOURCEIMAGE.html
POPUP_MENU: c4d.BaseContainer = c4d.BaseContainer()
POPUP_MENU.InsData(c4d.VECTOR_X, 'X Channel&i12153&')
POPUP_MENU.InsData(c4d.VECTOR_Y, 'Y Channel&i12154&')
POPUP_MENU.InsData(c4d.VECTOR_Z, 'Z Channel&i12155&')
def main() -> None:
"""Runs the example.
"""
# Do not run the script at all when there is not object selected.
selectedObjects: list[c4d.BaseObject] = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_CHILDREN)
if len(selectedObjects) < 1:
return
# Determine which parameter (ID) to target with a little popup menu.
targetParameter: typing.Optional[c4d.DescID] = None
result: int = c4d.gui.ShowPopupDialog(cd=None, bc=POPUP_MENU, x=c4d.MOUSEPOS, y=c4d.MOUSEPOS,
flags=c4d.POPUP_CENTERHORIZ|c4d.POPUP_CENTERHORIZ)
if result == c4d.VECTOR_X:
targetParameter = DID_POS_X
elif result == c4d.VECTOR_Y:
targetParameter = DID_POS_Y
elif result == c4d.VECTOR_Z:
targetParameter = DID_POS_Z
# The user aborted the popup menu.
if targetParameter is None:
return
# Your code ...
doc.StartUndo()
for node in selectedObjects:
doc.AddUndo(c4d.UNDOTYPE_CHANGE, node)
track: c4d.CTrack = node.FindCTrack(targetParameter)
if track is None:
track = c4d.CTrack(node, targetParameter)
if not track:
raise MemoryError(f"{track = }")
node.InsertTrackSorted(track)
t: c4d.BaseTime = doc.GetTime()
curve: c4d.CCurve = track.GetCurve()
keyData: dict = curve.AddKey(t)
if keyData is None:
raise RuntimeError("Could not add key frame.")
key: c4d.CKey = keyData["key"]
key.SetValue(curve, node[targetParameter])
key[c4d.ID_CKEY_PRESET] = c4d.ID_CKEY_PRESET_AUTO_OVERSHOOTWEIGHTED
doc.EndUndo()
c4d.EventAdd()
if __name__ == "__main__":
main()