Capture the dragging and release of a marker via python
-
I have an idea, where I want something to happen when I click and drag on a marker in the timeline, but I am not sure where to begin with getting that sort of event trigger, or if it is even possible in Python.
Simplest example would be to print the new frame number of the marker's location once I release it.
Would I need to like, enter a tool/context in order to track that?
For more context, I wrote a script that Based on whatever marker I had selected(if the key was within the length of the marker), and I thought it might be cool to be able to shift those keys when I moved the marker as well, but I got a bit lost.
-
Hello @BretBays,
Thank you for reaching out to us. Doing what you want to do will be tricky.
- You cannot implement handling drag events for things (a scene element, a dialog, a UI element) you do not implement yourself in Cinema 4D.
- The GUI event streams are sealed, i.e., you cannot just hook into the event stream of a dialog or UI element you do not own - which would contain the mouse interaction messages.
What you can relatively easy do, is figure out which markers are selected. The caveat is here as always that asking for the selection state of an element in "the" timeline is an ambiguous question as there can be up to four timelines.
"""Prints out the name of all timeline markers contained in a document and their selection state in the first timeline. """ import c4d 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. """ # Get the first marker in #doc and loop over them. marker: c4d.BaseList2D = c4d.documents.GetFirstMarker(doc) while marker: # Print out the name of the marker and its selection state in timeline 1. print(f"Name: {marker.GetName()}, selected: {bool(marker.GetNBit(c4d.NBIT_TL1_SELECT))}") marker = marker.GetNext() if __name__ == '__main__': main()
What you could then do, is write a
MessageData
plugin and register a timer with a stride of100ms
. You then would have to capture drag events yourself by the conditionsisSelected
andchanged time since the last check
.What you could also do, is hook into the message stream of each marker in a scene with
BaseList2D.AddEventNotification
and then listen forMSG_DESCRIPTION_POSTSETPARAMETER
as the marker will likely receive these messages while being dragged for itsTLMARKER_TIME
parameter. To attach yourself to the marker message streams in a document, you would need aMessageData
plugin which would check on eachEVMSG_CHANGE
if there is a new marker and then attach your callbacks. But it will likely be very hard to detect the end of a drag event with this message alone and we will need a timer too. But we could do this here:- Via an event notification capture that the time value of a marker is changing.
- Turn on your
MessageData
timer event to further check on markers. - Once we concluded all markers are being not dragged anymore because their time value has not changed anymore for a certain time or the markers are not selected anymore, we can turn off the timer again, saving resources.
BaseList2D.AddEventNotification
is private for a reason, you can easily crash Cinema 4D with it especially withNOTIFY_EVENT::MESSAGE
. We cannot share additional information on this method other than that what can already be found on the forum.The second approach hinges on
MSG_DESCRIPTION_POSTSETPARAMETER
being sent to markers while being dragged (seems likely at least for the case when the marker is released) but bears the risk of misusingAddEventNotification
. You would also need a node to which you can route the messages which is a bit tricky in Python because there are noSceneHookData
plugins there. You could try to abuse aPreferenceData
plugin as they must not be instantiated manually and are also nodes (i.e., have aMessage
function you could route your events). But be aware that some nodes are being excluded from certain parts of a message stream they technically should receive. E.g., aPreferenceData
plugin won't receive document and rendering notifications. But I doubt that aMSG_DESCRIPTION_POSTSETPARAMETER
event notification will be filtered. Otherwise you would have to inject and manage a hidden dummy object into the scene to which you route all event notifications.The first approach is a bit iffy performance wise due to the constantly running timer, but you would be here on less shaky ground. It is also less technical implementationswise.
Cheers,
Ferdinand -
Hey Ferdinand,
Thank you for your detailed response. It sounds like it may be a bit overkill for what I was thinking, and thats ok. I will read over it more and evaluate from there.
-
Follow up but slightly unrelated question. If I have a GeDialog plugin with an edit field(for changing the Frame of a marker for example), is it possible to have it update while dragging vs on release? Is that some sort of CoreMessage thing? Right now I have a call inside of my Command function that basically says if the ID matches the edit field then set the marker frame. But I would like to be able to click and drag and see the marker's position move as I drag. Is that possible