Finding Control ID in ObjectData Plugin Message() Override



  • Hello,
    I have an Integer control that I want to use for presets in my ObjectData plugin. I'm currently storing a variable _preset in my __init__ override and then checking for a different value when I detect a MSG_DESCRIPTION_USERINTERACTION_END message in my Message override. It works, but it is currently checking this value with every control change since the UserInteraction Messages do not pass IDs (data is None) and there is no Command method as in a GeDialog. I have a lot of controls, so for efficiency, how can I only check this when the control ID is the Preset Integer in my ObjectData plugin? Thank you!

        def __init__(self, *args):
            super(Demo, self).__init__(*args)
            self._preset = 1
    
        def Message(self, node, type, data):
            if type == c4d.MSG_DESCRIPTION_USERINTERACTION_END:
                if self._preset != node[c4d.DEMO_PRESETS]:
                    self._preset = node[c4d.DEMO_PRESETS]
                    self.ApplyPreset()                
    


  • Hi @blastframe,

    thank you for reaching out to us. Regarding your question, one way to do it, is to listen to MSG_DESCRIPTION_POSTSETPARAMETER, where the corresponding message data will be the DesscID of the attribute this message has been raised for. Below you will find both a script and a scene which demonstrate that. I am aware that you are in ObjectData scenario, but I am confident that you get the gist of it ;)

    Cheers,
    Ferdinand

    PC13001_scene.c4d

    """Demonstrates how to react to specific attribute changes in a node for: 
        https://plugincafe.maxon.net/topic/13001
    
        The node has one user data attribute called "Monitored Attribute".
        Changing that attribute will cause a different print out in the console
        than any other attribute (user data or not).
    """
    
    import c4d
    
    
    def main():
        return c4d.BaseObject(c4d.Ocube)
    
    def message(mid, data):
        """Reacts to parameter changes of the node.
        
        Limits the reaction to specific DescIds by unpacking the message data.
    
        Note:
            Since this was written for scripting object, the signature of the
            message function differs slightly from NodeData.Message(), but
            I think you got that ;)
        
        Args:
            mid (int): The message id.
            data (any): The message data.
        """
        # Listen for parameter changes.
        if mid == c4d.MSG_DESCRIPTION_POSTSETPARAMETER:
            # Unpack the message data which contain the DescID of the attribute,
            # this message has been raised for.
            descid = data["descid"]
            # When the message data points to our monitored attribute. This here
            # is a rather special case, since user data are inherently represented
            # as multilevel DescIds. For your run of the mill attribute it will
            # be just descid[0].id == ID_YOU_ARE_INTERESTED_IN.
            if descid[0].id == c4d.ID_USERDATA and descid[1].id == 1:
                print ("Monitored Attribute")
            # All other cases.
            else:
                print ("Ignored Attribute")
    


  • Hi:

    Assuming that you are creating a Python plug-in instead of a C++ plug-in, there are two solutions.

    The first is to compare the variable data after the control is updated after the plug-in's name stores the previous variable data.If the data is not the same, it is executed to avoid repeated runs.

    The second is to create a polygon cache, for example by storing the previous variable data with the local coordinates of the first point, and comparing variable data after the control is updated.If the data is not the same, it is executed to avoid repeated runs.

    Python plug-ins actually update automatically, even with every click of the mouse, which often leads to very heavy delays.All dirty systems can be used to detect if an update has occurred.

    There should be a better solution to your problem.

    Here are examples of Python tags and links to examples : link text

    import c4d
    #e-mail: xiuziye@qq.com
    
    def main():
           
        Name = op.GetName()
    
        Objects = op.GetObject()
        Changed = Objects.GetDirty(c4d.DIRTYFLAGS_SELECT)
    
        Text = ["xit" + str(Changed)[-1]]
        
        if str(Name).count(Text[0][:-1]) != 0 :
            
            if str(Name).find(Text[0][:-1]) != str(Name).rfind(Text[0][:-1]) :
                
                if str(Name)[str(Name).rfind(Text[0][:-1]):] == Text[0] :
                
                    if str(Name).find(Text[0][:-1]) <= 0:
                        
                        #Do not execute, exit the program.
                        print ("Does not perform.")
                        op.SetName(str(Text[0]))
                        
                        return
                    
                    else:
                        #Do not execute, exit the program.
                        op.SetName(str(Name)[:str(Name).find(Text[0][:-1])] + str(Text[0]))
                        print ("Does not perform.")
                        
                        return
                        
                else:
                    if str(Name).find(Text[0][:-1]) <= 0:
                        
                        op.SetName(str(Text[0]))
                        print ("Perform.")
                        
                    else:
                        
                        op.SetName(str(Name)[:str(Name).find(Text[0][:-1])] + str(Text[0]))
                        print ("Perform.")
    
            else:
                
                if str(Name)[str(Name).rfind(Text[0][:-1]):] == Text[0] :
                    #Do not execute, exit the program.
                
                    print ("Does not perform.")
                    return
    
                else:
    
                    op.SetName(str(Name)[:str(Name).find(Text[0][:-1])] + str(Text[0]))
                    print ("Perform.")
    
        else:
            print ("Perform.")
            op.SetName(str(Name) + str(Text[0]))
        
    
    
        print ("pass")
        #The next thing to execute.
    
    


  • @x_nerve Thank you for the reply, but I don't think my question was understood. First, I tagged this post with Python, so yes, it is a Python plugin.

    Second, as mentioned in the original post, I am already able to compare whether or not the control variable has changed. As mentioned in the topic title, I'm looking to determine the control's ID from NodeData.Message so I don't have to do this check every time a control is edited.

    Finally, I'm creating an ObjectData plugin and the Python tag example didn't make sense to me. How is this related to detecting a Control ID from User Interaction?

    I do appreciate the effort, thank you. I hope my intentions are clearer now.



  • Hi @blastframe,

    thank you for reaching out to us. Regarding your question, one way to do it, is to listen to MSG_DESCRIPTION_POSTSETPARAMETER, where the corresponding message data will be the DesscID of the attribute this message has been raised for. Below you will find both a script and a scene which demonstrate that. I am aware that you are in ObjectData scenario, but I am confident that you get the gist of it ;)

    Cheers,
    Ferdinand

    PC13001_scene.c4d

    """Demonstrates how to react to specific attribute changes in a node for: 
        https://plugincafe.maxon.net/topic/13001
    
        The node has one user data attribute called "Monitored Attribute".
        Changing that attribute will cause a different print out in the console
        than any other attribute (user data or not).
    """
    
    import c4d
    
    
    def main():
        return c4d.BaseObject(c4d.Ocube)
    
    def message(mid, data):
        """Reacts to parameter changes of the node.
        
        Limits the reaction to specific DescIds by unpacking the message data.
    
        Note:
            Since this was written for scripting object, the signature of the
            message function differs slightly from NodeData.Message(), but
            I think you got that ;)
        
        Args:
            mid (int): The message id.
            data (any): The message data.
        """
        # Listen for parameter changes.
        if mid == c4d.MSG_DESCRIPTION_POSTSETPARAMETER:
            # Unpack the message data which contain the DescID of the attribute,
            # this message has been raised for.
            descid = data["descid"]
            # When the message data points to our monitored attribute. This here
            # is a rather special case, since user data are inherently represented
            # as multilevel DescIds. For your run of the mill attribute it will
            # be just descid[0].id == ID_YOU_ARE_INTERESTED_IN.
            if descid[0].id == c4d.ID_USERDATA and descid[1].id == 1:
                print ("Monitored Attribute")
            # All other cases.
            else:
                print ("Ignored Attribute")
    


  • @zipit Thank you, Ferdinand, for the answer and the example scene.

    So when one control is changed, it appears that every attribute sends a c4d.MSG_DESCRIPTION_POSTSETPARAMETER message, is that correct?

    If I print the descid from your code in my plugin, it prints descids for all of my attributes. I must not have understood this message correctly: I thought it was for one attribute's change.

    d8957bbf-4245-452e-8072-070783e896d9-image.png



  • Hi @blastframe,

    could provide a code snippet for your NodeData.Mesage() which produces the output shown above? It would help to simplify the communication a bit.

    Cheers,
    Ferdinand



  • @zipit Hi Ferdinand. It's almost the same as yours, just with the other NodeData.Message() parameters and a print statement for the descid.

        def Message(self, node, type, data):
            if type == c4d.MSG_DESCRIPTION_POSTSETPARAMETER:
                descid = data["descid"]
                print(descid)
    


  • Hi @blastframe,

    well, you aren't doing anything with your DescID, so of course it will print everything ;)

    Cinema's message system can be thought of as a more old school version of an event system you might be more familiar with. You have certain events, i.e. message channels, which are used to transport/convey certain information. Some messages are only used to convey that something has happened which broadly falls within the scope of that message. Many messages however, send along some message data which specify the circumstances of that message.

    So in your NodeData.Message(), there will arrive multiple types of messages with multiple types of message data attached to them. One of them is MSG_DESCRIPTION_POSTSETPARAMETER, which is raised after a parameter has been changed. Along with it will be sent the DescID of the parameter which has been modified. When you now want to react to the modification of a specific parameter, you have to evaluate both the message and the message data, like I have shown above in my example. Below you will find a snippet specific to your scenario, unless I am completely misunderstanding you.

    I hope this helps and cheers,
    Ferdinand

    def Message(self, node, type, data):
        """Pretty much the same thing as the previous example, just less
         abstract.
        """
        if type == c4d.MSG_DESCRIPTION_POSTSETPARAMETER:
            # Should not happen, but being a bit paranoid can be good thing ;)
            if (not isinstance(data, dict) or 
                "descid" not in data or
                not isinstance(data["descid"], c4d.DescID)):
                msg = "Malformed message data."
                raise ValueError(msg)
    
            descid = data["descid"]
            # Your DEMO_PRESETS parameter has been modified.
            if descid[0].id == c4d.DEMO_PRESETS:
                print ("c4d.DEMO_PRESETS has been modified")
            # React to other stuff ...
        return True
    


  • @zipit Hi, thank you for the message. Yes, I believe I already understood everything you explained and I do appreciate your efforts. I knew I wasn't doing anything with the descid in my code, I was just confused as to why when I change one attribute, I was getting MSG_DESCRIPTION_POSTSETPARAMETER messages from all of the attributes as opposed to just the one attribute. Perhaps it is something else in my code that is sending those. Regardless, it isn't hugely important, I was trying to be as efficient as possible.

    Thank you.