Hi,
full disclaimer: I did not read your code very thoroughly, because it is often pointless with these snippets, as most of it makes little sense without the whole context. I am arguing from a purely formal stand point and have little to no clue what you are trying to do on a bigger semantic scope. So my advice should be taken with a grain of salt, there might be easier solutions.
Initially your statement that this "works flawless" in another plugin had me a bit worried that I told you nonsense, but after a quick test I highly doubt that that other code "works flawless". I expanded my quick test into some narrative code which should illustrate the problem. You will find it at the end of the post.
For the serialising part: If you want to do it manually, you just have to decompose you object (in a pythonics sense) into data types that can be stored in a BaseContainer
. There are no inherently wrong ways to do this. But I only mentioned these things because I did not know what you were trying to do. When you are not interested in making whatever you are trying to cache to be persistent with a document state (i.e. load/save/copy persistent), you should just attach whatever data you want to store to some Python object of your choice. As already mentioned, the NodeData
instance of your plugin would be a good candidate. Here is some mock/pseudo code to illustrate what I mean:
def GVO(self, node, **kwargs):
"""
"""
nid = node.FindUniqueID(c4d.MAXON_CREATOR_ID)
if nid in self.some_cache:
cache = self.some_cache[nid]
# do something
else:
cache = node.GetClone(0)
self.some_cache[nid] = cache
# do something else
Cheers,
zipit
Test code:
import c4d
import gc
def test_container_entry(bc, cid):
"""Pretty prints the data stored at and if it is a BaseLink element for
a given element id of a BaseContainer.
Args:
bc (c4d.BaseContainer): The container to test.
cid (int): The element id.
"""
is_baselink = bc.GetType(cid) == c4d.DA_ALIASLINK
print "Data for id {}: {}".format(cid, bc[cid])
print "Element at id {} is a BaseLink: {}".format(cid, is_baselink)
print "-" * 100
def some_scope_context(node):
"""Demonstrates your problem.
This will create a only locally bound clone of a node and try to "insert"
it into a BaseContainer (it will actually only create a link).
Args:
node (c4d.C4DAtom): Something to cache/clone.
Returns:
c4d.BaseContainer: The "cache" container, which is not really a cache
at all.
"""
# The container.
bc = c4d.BaseContainer()
# A node that is only bound to the local scope, this would be the
# cMesh symbol in your code. If we would pass in node directly, this
# all would not happen.
clone = node.GetClone(0)
# These are IMHO equivalent in Python due to the fact that we have
# no control over how SetData interprets the passed data, i.e. just
# like with __setitem__, we are at the mercy of the Python bindings.
bc[1000] = clone
bc.SetData(1001, clone)
# This will print out that there are some BaseObjects at the given IDs
# and that these elements are BaseLinks. I.e. it behaves like you expect
# the container to behave (minus the element type part).
print "In scope context:"
print "=" * 100
test_container_entry(bc, 1000)
test_container_entry(bc, 1001)
# We push the container out of the scope. The node 'clone' will be
# freed after the interpreter steps to the next instruction.
return bc
def main():
"""Entry point.
"""
# Some dummy node we want to cache.
node = c4d.BaseList2D(c4d.Onull)
# Now we pass our node into another context, this would be GVO in your
# code. It returns a BaseContainer which is meant to hold our "cache",
# which is not really a cache, because it only links to a cache.
bc = some_scope_context(node)
# This line should not be necessary due to the fact that we just left
# a scope when exiting 'some_scope_context', but Python's garbage
# collector is a nasty piece of work, so this is only here to make sure
# to get the point of this example across and the node "clone" freed.
gc.collect()
# When we now poll our returned container it is "empty" (returns None)
# due to the fact that the cloned node is not alive anymore.
# It died with the scope context of the function 'some_scope_context'.
print("After scope context:")
print "=" * 100
test_container_entry(bc, 1000)
test_container_entry(bc, 1001)
if __name__ == "__main__":
main()
Test output:
In scope context:
====================================================================================================
Data for id 1000: <c4d.BaseObject object called 'Null/Null' with ID 5140 at 0x0000021CADCAFA70>
Element at id 1000 is a BaseLink: True
----------------------------------------------------------------------------------------------------
Data for id 1001: <c4d.BaseObject object called 'Null/Null' with ID 5140 at 0x0000021CADCAFA70>
Element at id 1001 is a BaseLink: True
----------------------------------------------------------------------------------------------------
After scope context:
====================================================================================================
Data for id 1000: None
Element at id 1000 is a BaseLink: True
----------------------------------------------------------------------------------------------------
Data for id 1001: None
Element at id 1001 is a BaseLink: True
----------------------------------------------------------------------------------------------------
[Finished in 7.2s]