Interaction Tag - def message seems to not work
-
Hi,
def message (id,data) in Interaction Tag ( scripting tab ) seems to not work :
None of the message id's from here doesn't work : https://developers.maxon.net/docs/py/2023_2/manuals/application_development/manual_message_system.html like c4d.MSG_POLYGONS_CHANGED ( id 2 )
-
Hello @Smolak,
Welcome to the Plugin Café forum and the Cinema 4D development community, it is great to have you with us!
Getting Started
Before creating your next postings, we would recommend making yourself accustomed with our Forum and Support Guidelines, as they line out details about the Maxon SDK Group support procedures. Of special importance are:
- Support Procedures: Scope of Support: Lines out the things we will do and what we will not do.
- Support Procedures: Confidential Data: Most questions should be accompanied by code but code cannot always be shared publicly. This section explains how to share code confidentially with Maxon.
- Forum Structure and Features: Lines out how the forum works.
- Structure of a Question: Lines out how to ask a good technical question. It is not mandatory to follow this exactly, but you follow the idea of keeping things short and mentioning your primary question in a clear manner.
About your First Question
Your posting is missing the version you are using, but with
2023.2
I do not have any problems, including retrievingMSG_POLYGONS_CHANGED
when the polygons of aPolygonObject
hosting the interaction tag have changed.import c4d def message(mid: int, data: any) -> bool: """Called by Cinema 4D to convey events to the tag. """ if mid == c4d.MSG_POLYGONS_CHANGED: print ("MSG_POLYGONS_CHANGED") return True
The major question for me would be what you are trying to do to invoke the message. The purpose of
MSG_POLYGONS_CHANGED
is as declared in the docs:Sent to notify an object its polygons have changed.
What is meant with that is that you will only receive it when polygons are added or removed. The same goes for
MSG_POINTS_CHANGED
. When you want to catch point values being changed, e.g., by moving around a polygon in the editor, you usually listen forMSG_UPDATE
which has a bit cryptic description:Must be sent when the bounding box must be recalculated.
However, this message is not being propagated to children of a node (probably for performance reasons). We must remember that we are here inside the Python Interaction tag and listening to its node message stream, i.e., we only receive messages sent to the tag. Only when messages are sent deliberately also to all related nodes with C4DAtom.MultiMessage, something like
MSG_POLYGONS_CHANGED
can bubble up inTagData.Message
(which is what is effectively being called here) which has no polygons and therefore is never the direct recipient of such message.In the case of manipulating the values of existing points, e.g., moving a polygon, there are two messages being broadcasted to a hosting Python Interaction Tag:
MSG_INTERACTIONTAG_INITTWEAKINFO
(440000186): The message symbol is internal and neither exposed in C++ nor Python. It is a special message of the interaction tag and is sent every time the user starts dragging in the viewport.c4d.MSG_GETCUSTOMICON
(1001090): This message is sent very often and is a common candidate when one wants to catch update events for things one is not explicitly informed about as the message indirectly indicates changes.
import c4d c4d.MSG_INTERACTIONTAG_INITTWEAKINFO: int = 440000186 def message(mid: int, data: any) -> bool: """Called by Cinema 4D to convey events to the tag. """ if mid == c4d.MSG_POLYGONS_CHANGED: print ("MSG_POLYGONS_CHANGED") # Sent when the user clicks into the viewport while the object which hosts this tag is selected. # This will then also include point values changes. elif mid == c4d.MSG_INTERACTIONTAG_INITTWEAKINFO: print ("MSG_INTERACTIONTAG_INITTWEAKINFO") # Sent all the time and therefore only useful for piggy backing onto when one does not care # about when something does happen. elif mid == c4d.MSG_GETCUSTOMICON: print ("MSG_GETCUSTOMICON") return True
You can make things more precise with
BaseObject.IsDirty
, but if you really want to narrow down things, you will have to cache things yourself (see end of posting).Cheers,
FerdionandCode:
import c4d tag: c4d.BaseTag# The Python interaction tag op: c4d.BaseObject # The object owning the tag # Add the unexposed message ID for an interaction tag preparing for a tweak event. c4d.MSG_INTERACTIONTAG_INITTWEAKINFO: int = 440000186 # A plugin ID to store point cache data under in an interaction tag, you can use that ID if you # want to, I registered it just for the example. ID_POINTDATA_CACHE: int = 1060897 def IsPointDataDirty(tag: c4d.BaseTag, obj: c4d.PointObject) -> bool: """Determines if the point values of the hosting point object #obj of #tag have changed. This is done by caching the last known point values state in #tag itself. """ # Get out when #obj is not a point object or not the host of #tag. if not isinstance(obj, c4d.PointObject) or tag.GetMain() != obj: return False # The general idea is to simply copy the point values of #obj into #tag as a BaseContainer # under the custom parameter ID ID_POINTDATA_CACHE defined above. # Get the BaseContainer parameter at #ID_POINTDATA_CACHE in #tag or create an empty container # when there is None. Also give such container itself the ID #ID_POINTDATA_CACHE to be sure # we retrieve the correct data. cache: c4d.BaseContainer = (tag.GetParameter(ID_POINTDATA_CACHE, c4d.DESCFLAGS_GET_NONE) or c4d.BaseContainer(ID_POINTDATA_CACHE)) if not cache.GetId() == ID_POINTDATA_CACHE: raise RuntimeError(f"Found unexpected data at ID_POINTDATA_CACHE.") # Now iterate over all point data of #obj and compare/update it with the cache. isDirty: bool = False for i, liveValue in enumerate(obj.GetAllPoints()): cachedValue: c4d.Vector | None = cache.GetVector(i) if cache.GetIndexId(i) == c4d.NOTOK or cachedValue != liveValue: cache.SetVector(i, liveValue) isDirty = True # Write the cache back and report if the cache differed from the current point values of #obj. tag.SetParameter(ID_POINTDATA_CACHE, cache, c4d.DESCFLAGS_SET_NONE) return isDirty def message(mid: int, data: any) -> bool: """Called by Cinema 4D to convey events to a tag. """ if mid == c4d.MSG_INTERACTIONTAG_INITTWEAKINFO: # This will narrow things down to cases where the hosting object is data dirty, which # includes but is not limited to point value changes. There is no better dirty flag or # message for this and when we want a more precise event, we must track things ourselves. if op.IsDirty(c4d.DIRTYFLAGS_DATA): # print ("Interaction event with the host object being data dirty.") pass # Which is done by this function here. if IsPointDataDirty(tag, op): print ("The point data of the hosting point object has changed.") return True
-
Thank you very much for depth explanation. I didn't know that "polygons have changed" means that they need to be deleted. I thought that it will works with polygons modification too.
But using one of the code I have this error :
Regards,
Sebastian -
Hey @Smolak,
You must expose the ID as explained in the posting and shown in the second code listing. Forgot to do it in the second one. I have updated the listing.
Cheers,
Ferdinand