Detect new project in Python plugin
-
Hi all, is there a callback or MessageData ID that my Python plugin can attach to detect new projects? I would like to execute some code every time a user executes
File > New Project
.
Thanks! Michael -
Hello @mheberlein,
thank you for reaching out to us. For what you are trying to do there is unfortunately no convenient path for implementation in Python, but it is certainly possible to do.
There is the message family MSG_DOCUMENTINFO being broadcasted to some atoms of in a scene graph. It is related to the sub messages MSG_DOCUMENTINFO_TYPE which will tell you if a document has been loaded, saved and more. To make use of that message, you will need a
NodeData
plugin that receives that message in itsMessage()
method. There are unfortunately no good candidate in Python which you could use in your case. In C++ you would use aSceneHookData
plugin, but that is not available in Python.You could use other NodeData derived plugin interfaces like
ObjectData
orMaterialData
which will also retrieve that message, but that probably does not work very well in your setup. So, the best route is probably a timer. Note that not all NodeData derived plugins will receiveMSG_DOCUMENTINFO
, e.g., you cannot setup shop in aPreferenceData
plugin for example, which is also a node, as it won't receive that message.To take the timer route, implement a
GeDialog
orMessageData
plugin and overwrite their GetTimer() method to receive periodicMSG_TIMER
core messages for that timer interval in theirCoreMessage()
methods. E.g., returning 1000 in GetTimer() will result in a MSG_TIMER core message being sent to that interface in an interval of roughly one second. There you could call a function which manually evaluates if the document has changed by comparing the current document against an identifier for a cached document. Note that I wrote here 'identifier' and not object, as objects get reallocated quite often in Cinema 4D. So, attempting to store an object itself as a reference won't work. SinceBaseDocument
is a type of node itself, you should useC4DAtom.FindUniqueID()
in conjunction with the MAXON_CREATOR_ID id. The snippet below demonstrates the approach in a pseudo-code fashion.import c4d class MyMessageData (c4d.plugins.MessageData): """A message data implementation. I will not show the actual implementation of the message data here, but how to securely store a reference to a node (a document) via its UUID. """ def __init__(self): """Initializes the attribute storing the UUID of the active document. """ # The UUID of the document this message data has seen last. self._cachedDocumentUuid = None @staticmethod def GetActiveDocumentUuid() -> bytes: """Returns the MAXON_CREATOR_ID UUID for the active document. """ doc = c4d.GetActiveDocument() if doc is None: raise RuntimeError("Could not access active document.") uuid = doc.FindUniqueID(c4d.MAXON_CREATOR_ID) if uuid is None: raise RuntimeError(f"{doc} has no maxon creation marker.") return bytes(uuid) def CompareCachedUuid(self, uuid: bytes) -> bool: """Checks if the passed UUID is different than the cached one. Returns #True when they match and #False when they do not. When there is a mismatch, the cached UUID will also be updated to the passed on. """ self._cachedDocumentUuid != uuid: self._cachedDocumentUuid = uuid return False return True def CompareDocuments(self) -> None: """Called by CoreMessage() on a MSG_TIMER event. """ currentUuid = MyMessageData.GetActiveDocumentUuid() if not self.CompareCachedUuid(currentUuid): print ("Active document has changed.") else: print ("Active document is the same.")
You could also mix the MSG_DOCUMENTINFO with the timer approach by injecting a dummy node, a hidden object into the active document via a controlling MessageData to offload the checking work to it and get access to the more elaborate events in the context
MSG_DOCUMENTINFO
. For a simple usage with a moderate timer value the simple approach should be fine. Note that when you set the timer callback to a low value like 40ms, that your code will then really run all the time, possibly impacting the performance of Cinema 4D.Cheers,
Ferdinand -
Thank you for your reply, Ferdinand.
I had a similar timer workaround in mind when I decided that there must be a better, official way to do this.
Will implement it this way now, running every 3-4 seconds should be enough. -
Hello @mheberlein,
without any further questions or postings, we will consider this thread as solved by Friday the 4th, February 2022.
Thank you for your understanding,
Ferdinand