Object Plugin position in manager



  • On 30/09/2013 at 06:22, xxxxxxxx wrote:

    when i create my Object , by default it's inserted at the top of the object manager.
    if i try to do this:

    def Message(self, node, type, data) :
            doc = c4d.documents.GetActiveDocument()
      
            if type == c4d.MSG_MENUPREPARE:
                node.InsertUnder(doc.GetActiveObject())
            return True
    

    cinema4d just locks up... Why ?



  • On 30/09/2013 at 08:16, xxxxxxxx wrote:

    the code does look okish, however two ideas:

    1. The return value of Message() methods in c4d does remain a tricky thing. It is easy
    to freeze c4d with missing calls, even though there have been made some changes in
    R14. I cannot remember to have ran into that problem for NodeData yet, but I guess it 
    is worth a try. Replace your return statement with one of the following:

    return super(MyNodeDataType, self).Message(node, type, data)
    return NodeData.Message(self, node, type, data)

    You should also be aware that the Message() return value follows in c4d the consumption logic
    and is not indicating the success of the operation.

    2. I am not sure when MSG_MENUPREPARE is being sent, but c4d.documents.GetActiveDocument()
    is a statement you usually want to avoid in plugins, as it can easily invoke several problems
    (cloned documents, livetime and so on.) Better sources for the document are either the doc 
    parameter or a node we know (or suspect) to be in the targeted document. So if you do change 
    your code to doc = node.GetDocument() and doc is still None, I would not try to insert the object 
    manually at that point. A better solution might be then a small MessageData plugin.



  • On 30/09/2013 at 12:02, xxxxxxxx wrote:

    MSG_MENUPREAPRE is sent when the node is first inserted into a document. Primitive objects for
    instance create a Phong Tag on themselves with this message. You can send it manually when
    creating an object in a script.

    As I already said, the node is already inserted. You can't insert a node twice, that leads to critical
    reference issues. You first need to remove the node, then insert it again.

    You are of course free to do this, but I do not recommend it. In Cinema 4D, the user is free to
    choose at what place the object should be created. If he would like your object to be inserted
    under the selected object, he'd hold the SHIFT key while pressing the menu button.

    Best,
    -Niklas



  • On 30/09/2013 at 12:48, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    MSG_MENUPREAPRE is sent when the node is first inserted into a document. Primitive objects for
    instance create a Phong Tag on themselves with this message. You can send it manually when
    creating an object in a script.

    As I already said, the node is already inserted. You can't insert a node twice, that leads to critical
    reference issues. You first need to remove the node, then insert it again.

    You are of course free to do this, but I do not recommend it. In Cinema 4D, the user is free to
    choose at what place the object should be created. If he would like your object to be inserted
    under the selected object, he'd hold the SHIFT key while pressing the menu button.

    Best,
    -Niklas

    Hey,

    I guess I was a bit unclear about the line ' I am not sure when MSG_MENUPREPARE is being  sent', as
    I did know the occasion and meant I did not know when it is sent, after or before the object
    has been inserted into the document.

    1. At least in python you do not have to invoke Remove() before invoking any of the InsertXYZ()
    methods.

    2. I did a little test with an ObjectData plugin of mine (a SplineObject to be more specific ). These
    lines -

    def Message(self, node, mtype, data) :
        """
        """
        [...]
        if mtype == c4d.MSG_MENUPREPARE:
            print node.GetDocument()
        [...]
    

    will print None to the console for me, indicating that the object is still 'floating' and has not been
    inserted yet. So it is porting towards my assumption, that c4d does not expect the object to be
    inserted at that point yet and doing it manually does not seem to be a good idea ( considering
    the problems the op did report).

    So I am not quite sure if your suggestions do really apply, but I might be wrong.



  • On 30/09/2013 at 13:04, xxxxxxxx wrote:

    Hi Ferdinand,

    Thanks for paying attention, you are correct.

    I somehow had this construct in mind (see below) which leads to Cinema go into an infinite loop.
    But you are absolutely correct about your statement, that it is not  necessary to call the Remove()
    method.

    op.InsertUnder(op) # Invalid, will result in an infinite loop
    

    Seems like I also had #2 wrong in my mind. I thought I was remembering a part where I
    successfully called GetDocument() in MSG_MENUPREPARE, this is why I came to the above
    conclusion which is obviously wrong.

    Drawing a new conclusion from the corrected information, MSG_MENUPREPARE is called before the
    object is inserted into the document, which explains why GetDocument() returns None and why
    Cinema runs into an infinite loop. Cinema probably does not expect you to insert the object in
    MSG_MENUPREPARE, then after it has sent the message, it simply inserts the object and thus
    some objects have invalid references.

    Thanks again Ferdinand,
    Best - Niklas



  • On 30/09/2013 at 13:40, xxxxxxxx wrote:

    Well, for me, it's stuck on the return for sure.

    def Message(self, node, type, data) :
            if type == c4d.MSG_MENUPREPARE:
                doc = c4d.documents.GetActiveDocument()
                print doc
                place = doc.GetActiveObject()
                print place
                node.InsertUnder(place)
                print "success"
            return True
    

    and console prints this:

    <c4d.documents.BaseDocument object called '' with ID 110059 at 0x000000000F1ED670>
    <c4d.BaseObject object called 'Cube/Cube' with ID 5159 at 0x0000000006AE4890>
    success

    and AFTER that it locks. So you can define the active document in the MENUPREPARE for sure.

    but if I use node.GetDocument() it equals to None, so the object itself isn't in the document yet.
    So any ideas how to do it ?
    @ Niklas: I need to insert it before the selected object :) i don't know why i started testing with InsertAfter(). So any ALT or SHIFT will not do it.



  • On 30/09/2013 at 15:09, xxxxxxxx wrote:

    Do not use doc = c4d.documents.GetActiveDocument()in plugin code (except for command data
    plugins). C4d does duplicate the document for rendering which will render that code useless as
    GetActiveDoucment() will always return the active document in the editor (so that you are using
    the wrong document). It also does introduce possible problems with dead objects. If you cannot
    get hold of the document a node is sitting in with GeListNode.GetDocument(), it is a strong 
    indicator that you should look for a later point of interception.

    As I wrote already in my first posing the most straight forward option would be a MessageData
    plugin monitoring your scene structure (or more specifically the top object and the current 
    selection). If you do REALLY want to do it from within your ObjectData plugin you could do the 
    following.

    1. To be extra sure that the whole process is only invoked once I would add a virtual element to 
    the objects description, indicating if the object has already been moved once or not. You will also
    need some boolean flag value in your class indicating that the node has to be moved.

    2. In the Message method ( you could also use Init(), the object isn't yet alive there either ) store 
    the active object selection as a class member, using some clunky GetActiveDocument() code.

    3. One of the first things which will happen after the node has been initialized is that c4d will build
    its cache and call GVO. So check in GVO if your object is flagged for being relocated, if so, use the 
    stored object selection to modify the location of the node passed to GVO.

    Please let me stress again, that this actually rather HACKY and BAD code. The separate
    MessageData plugin would be a much cleaner solution.

    edit : the general flaw in the logic of your code is ( now i do understand what niklas meant with
    op.InsertUnder(op)), that at the point when the object is alive the active selection will be the 
    object itself, as c4d is selecting new objects. that is why you will need that 2 step approach and
    also why a MessageData solution would be much better, as it wouldn't need such hacks.



  • On 30/09/2013 at 15:31, xxxxxxxx wrote:

    thanks for the hints. I'm just starting with python, so obviously i have some digging to do :)
    actually the "data" entry from Message(self, node, type, data) also returns the document, but it seems that this will not help me in anyway. I'll try to figure out the Message plugin solution.



  • On 30/09/2013 at 16:09, xxxxxxxx wrote:

    You mean data is a BaseDocument for MSG_MENUPREPARE ? The type of data actually depends on the 
    type of the message, I didn't  know that it is a BaseDocument for MSG_MENUPREPARE, but it does make 
    sense, as the ID is there to let you do additional setup stuff.

    Just to be clear, using the hacky way wouldn't let the world end, its just rather hacky and you should 
    be aware of it. Generally directly modifying the active scene from within a NodeData plugin is a
    rather unusual thing to do.



  • On 01/10/2013 at 02:02, xxxxxxxx wrote:

    MessageData plugin didn't helped. I couldn't find a message to listen that will suit my needs. But I managed to do it with CommandData plugin. Now it seems so logical to do it that way :) Thanks a lot for the hints. Till now I only made some simple python scripts to help my work, but i got so hooked up with python, that i really want to learn it in depth. I'll show you what is the plugin that i'm trying to make later. Actually I think it will be quite usefull.



  • On 01/10/2013 at 08:01, xxxxxxxx wrote:

    The core message you would use in MessageData plugin would be simply EVMSG_CHANGE.
    A very rough layout would be :

    1. when the class instance is constructed store the active selection and the topmost object.
    2. each time EVMSG_CHANGE occurs, update your topmost object, if it is of the desired type
    and  the current selection move it to your previously stored selection. if not, overwrite your
    selection cache.
    3. to cover some special cases you could also add a general monitoring of your object type
    XYZ so that it doesn't get accidentally moved, when the user does select it and moves it to
    the top of the scene hierarchy.

    A CommandData is of course also a good solution however it won't work if you would instantiate
    your type programmatically (and use the actual object and not directly your CommandData plugin ID).

    Happy rendering,
    Ferdinand



  • On 01/10/2013 at 13:53, xxxxxxxx wrote:

    yes, i that's what I did, but I didn't like that it EVMSG_CHANGE occurs all the time, I didn't feel that's what i need. With the CommandData plugin the code is called only once, on creation of the object, so it's cleaner for me. Also there won't be a problem with the actual ObjectPlugin, cause it has the flag "c4d.PLUGINFLAG_HIDE", so you can't find it as a button and you can't even call it as a command. Or am I wrong ?





  • On 03/10/2013 at 04:51, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    yes, i that's what I did, but I didn't like that it EVMSG_CHANGE occurs all the time, I didn't feel that's what i need. With the CommandData plugin the code is called only once, on creation of the object, so it's cleaner for me. Also there won't be a problem with the actual ObjectPlugin, cause it has the flag "c4d.PLUGINFLAG_HIDE", so you can't find it as a button and you can't even call it as a command. Or am I wrong ?

    no you are absolutely right, it is a perfectly valid way. about EVMSG_CHANGE and that it does
    seem unreasonably computational expensive - it is a bit weird and I was also irritated at first,
    but it is actually the intended way. I did ask here some time ago on how to get notified, if
    the active document has been changed and the answer of one of the devs was EVMSG_CHANGE.
    EVMSG_CHANGE is there to check such stuff like oldx != newx, so it is ok to use it in that way.


Log in to reply