Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Constant tracking of object position with Python

    Cinema 4D SDK
    python r23
    2
    5
    762
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • D
      DGLopez
      last edited by DGLopez

      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

      1 Reply Last reply Reply Quote 0
      • ferdinandF
        ferdinand
        last edited by ferdinand

        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

        MAXON SDK Specialist
        developers.maxon.net

        D 1 Reply Last reply Reply Quote 0
        • D
          DGLopez @ferdinand
          last edited by

          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):

          60f2491b-a6f9-47e2-8a4c-106b2d3d66f5-image.png

          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.

          ferdinandF 1 Reply Last reply Reply Quote 0
          • ferdinandF
            ferdinand @DGLopez
            last edited by ferdinand

            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 some CommandData plugin.
            • There is a known set of C4DAtom, or more specifically BaseObject, 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 and EVMSG_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., with MSG_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,
            Ferdinand

            The result:
            234243.gif
            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()
            

            MAXON SDK Specialist
            developers.maxon.net

            1 Reply Last reply Reply Quote 0
            • D
              DGLopez
              last edited by

              Hello @ferdinand ,

              this example was perfect for me, thank you very much!

              1 Reply Last reply Reply Quote 0
              • First post
                Last post