Constant tracking of object position with Python
-
Hello everyone,
I am trying to figure out if it is possible to retrieve the position of an object while you are dragging it around with the mouse. This would mean to pass the message with the instant position to an already running plugin to update dinamically the GUI.
For the moment I thought about getting the position with a button after the mouse movement but is very annoying for the final user.
I looked for posts regarding similar topics and I thought it could be a case for threadding or using a python tag, but I am pretty much lost here.
Any help would be appreciated.
Daniel
-
Hello @DGLopez,
thank you for reaching out to us. We will answer your question more thoroughly, but to be able to do so, we will need more information. Your topic is missing:
- The targeted operating systems expressed as tags on your topic.
- The plugin interface(s) used by the already running plugin.
- Provide code or a description of what the already running plugin is doing.
- Describe more precisely what you want to do exactly: Should the dragging be a tool which can be activated by the user, or should it happen all the time or should it only happen to specific objects?
Please read our Forum Guidelines for details on the support process.
Cheers,
Ferdinand -
Hello @ferdinand, thanks for the quick reply and sorry for the lack of information.
I cannot add tags to the original post but I am using windows as the operating system.
An example of the GUI used by the already running plugin could be somewhat similar to this (I'm working on a simpler GUI but the back-end will be the same):As you can see, when I press "add model" I get the global coordinates of the selected cube and printed in the GUI. However I would like those values (Global x, Global y and Global Z) to be updated when I drag the cube around without the need of refreshing it manually.
I hope this clarifies the topic.
Daniel.
-
Hello @dglopez,
thank you for clarifying your question. There are unfortunately some things still unclear to me, so let me start with a set of assumptions of mine:
- The other plugin uses a
GeDialog
and is most likely someCommandData
plugin. - There is a known set of
C4DAtom
, or more specificallyBaseObject
, you want to track. - There is not necessarily a second plugin in your case.
So, there are basically two major ways you can do this. You can either A. push the information into the other plugin from the action of the objects being moved. This option does necessarily need a second plugin which is pushing the information into your other plugin. Or you can B. grab the information from the objects when they have been moved from within the other plugin. This option does not need necessarily a second plugin. Which might sound like semantics is an important distinction, since you cannot move data without restrictions in the message system of Cinema 4D.
When the the other plugin is some
GeDialog
, option B lends itself best via core messages andEVMSG_CHANGE
, see end of posting for an example. However, when the other plugin is some kind of classic API node, e.g., an object itself, you will have to push the information into that node, e.g., withMSG_BASECONTAINER
. But depending on how you structure all that, you might need a third plugin in between to handle the transfer. Your question is here unfortunately so broad that I cannot give you anything more concrete.I hope this helps and cheers,
FerdinandThe result:
The code:"""Example for tracking object matrices in a GeDialog. This can be run as a script manager script and will create a dialog which tracks the global transform matrices of the objects that have been selected when the dialog was created. As discussed in: https://developers.maxon.net/forum/topic/13643/ """ import c4d class ObjectTrackerDialog (c4d.gui.GeDialog): """Example dialog that tracks the transforms of a set of objects. """ ID_GADGETS_START = 1000 ID_GADGET_GROUP = 0 ID_GADGET_LABEL = 1 ID_GADGET_TEXT = 2 GADGETS_STRIDE = 10 def __init__(self, objects: list[c4d.BaseObject]) -> None: """ """ self._objects = objects def CoreMessage(self, mid: int, msg: c4d.BaseContainer) -> bool: """Receives core messages. We react here to EVMSG_CHANGE to update all our object matrices. EVMSG_CHANGE is the message for EventAdd(), i.e., is being fired ALOT. To make this less throttling, we could also use GeDialog.SetTimer and GeDialog.Timer to update the matrices in a fixed interval. What is better depends on how time-consuming you would consider your updates to be. But the timer approach will also consume process time when nothing changed. So, the best approach would be to mix the two. I.e., only react to EVMSG_CHANGE when a certain time span X has passed since the last time you did react to it. """ if mid == c4d.EVMSG_CHANGE: self.UpdateValues() return True def CreateLayout(self) -> bool: """Creates a static text and a text box for each tacked object. """ self.SetTitle("ObjectTrackerDialog") for i, item in enumerate(self._objects): gid = self.ID_GADGETS_START + i * self.GADGETS_STRIDE name = item.GetName() self.GroupBegin(gid + self.ID_GADGET_GROUP, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT, cols=2) self.AddStaticText(gid + self.ID_GADGET_LABEL, c4d.BFH_LEFT | c4d.BFV_SCALEFIT, name=name) self.AddEditText(gid + self.ID_GADGET_TEXT, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT) self.GroupEnd() return True def InitValues(self) -> bool: """Initializes the dialog values. """ return self.UpdateValues() def UpdateValues(self) -> bool: """Updates the dialog values. """ for i, item in enumerate(self._objects): gid = gid = self.ID_GADGETS_START + i * self.GADGETS_STRIDE self.SetString(gid + self.ID_GADGET_TEXT, item.GetMg()) return True def main(): """Creates an instance of the ObjectTrackerDialog. """ items = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_NONE) if not isinstance(items, list) or len(items) < 1: raise RuntimeError("Please select at least one object.") # Please do not do this in a production environment. This is just a hack # to keep an async dialog alive from the script manager. Which will lead # to problems when used in production. global dialog dialog = ObjectTrackerDialog(items) dialog.Open(c4d.DLG_TYPE_ASYNC) if __name__ == '__main__': main()
- The other plugin uses a
-
Hello @ferdinand ,
this example was perfect for me, thank you very much!