Tag plugin general issues (crash, threading, message)
-
Hello ,
at the moment I try to create a tag plugin but it seems it is a bit more complicated than with an object plugin :-).I added a small snippet of the plugin as testtrans.zip zipfile.
In the res-folder is the Cinema4d scene file (crashes) and a json-file.In the layout res-file I created a simple Baselink, Filename , Button(I want avoid) and BaseTime
Function of the test plugin:
- The user has to drag a pose morph tag into the Baselink.
- Then he has to load the json1.json file as link
The user loads the *.json file (res-folder), I catch this via c4d.MSG_DESCRIPTION_POSTSETPARAMETER and this triggers the function load_transcript() and loads the *.json file content into a member variable self.data.
Questions:
-
Main Question: In the Execute(self, tag, doc, op, bt, priority, flags) method I want to to some actions. But here Cinema is crashing. Probably it has something to do with threading and that it asks the tags description.:-).
I commented out the position where it is crashing. So if I replace the tag[PY_SYNC_START] simply with an integer it works -
Is the method I used in the Message() method correct to detect the changes in the filename link? It doesn't work so I added a button to load it , then it works, but I want it to load as soon as the user changes the link to the json file. It calls the print() function in the POSTSETPARAMETER thing, but it doesn't trigger the load_transfile() method.
-
Another small bug maybe in Cinema 4D. When I set the ACCEPT parameter in the Baselink in the res-file to {Tposemorph;} he gives me a ressource error but when I type Tdisplay it works ??????
Here is the code:
import c4d import os from c4d import bitmaps, gui, plugins import json # be sure to use a unique ID obtained from www.plugincafe.com PLUGIN_ID = 5554443 # just a test ID # ID's PY_MORPH_LINK = 10000 PY_TRANSCRIPT_LINK = 10001 PY_SYNC_START = 10018 PY_LOAD_TRANSCRIPT = 10021 # The Plugin Class class TestTrans(plugins.TagData): def __init__(self): self.data = None def Init(self, node): self.InitAttr(node, c4d.BaseList2D, PY_MORPH_LINK) # node[PY_MORPH_LINK] = None self.InitAttr(node, str, PY_TRANSCRIPT_LINK) # node[PY_TRANSCRIPT_LINK] = "" self.InitAttr(node, c4d.BaseTime, PY_SYNC_START) node[PY_SYNC_START] = c4d.BaseTime() return True def load_transcript(self, op): if op[PY_TRANSCRIPT_LINK] != "": with open(op[PY_TRANSCRIPT_LINK], "r") as transfile: self.data = json.load(transfile) def move2(self, tag, frame, value): print("2. Hello in function") def Execute(self, tag, doc, op, bt, priority, flags): if self.data: pose_morph = tag[PY_MORPH_LINK] if not pose_morph: return c4d.EXECUTIONRESULT_OK morph_count = pose_morph.GetMorphCount() print(pose_morph) print(self.data["words"]) frame = doc.GetTime().GetFrame(doc.GetFps()) - tag[PY_SYNC_START] #Here it is crashing print(frame) if frame == 10: print("1. Hello") self.move2(tag, frame, 1) return c4d.EXECUTIONRESULT_OK def Message(self, node, type, data): if type == c4d.MSG_DESCRIPTION_COMMAND: if data["id"][0].id == PY_LOAD_TRANSCRIPT: self.load_transcript(node) if type == c4d.MSG_DESCRIPTION_POSTSETPARAMETER: if data["descid"][0].id == PY_TRANSCRIPT_LINK: print("POSTSETPARAMETER") self.load_transcript(node) return True # main if __name__ == "__main__": # load icon.tif from res into bmp bmp = bitmaps.BaseBitmap() dir, file = os.path.split(__file__) fn = os.path.join(dir, "res", "icon.tif") bmp.InitWith(fn) new_bmp = c4d.bitmaps.InitResourceBitmap(1051133) # register the plugin c4d.plugins.RegisterTagPlugin(id=PLUGIN_ID, str="Test-Trans", info=c4d.TAG_EXPRESSION | c4d.TAG_VISIBLE, description="testtrans", g=TestTrans, icon=bmp)
res-file:
CONTAINER Testtrans { NAME TESTTRANS; INCLUDE Texpression; GROUP ID_TAGPROPERTIES { LINK PY_MORPH_LINK { ACCEPT { Tbase; } } FILENAME PY_TRANSCRIPT_LINK { ANIM ON; } BASETIME PY_SYNC_START {} } }
header-file:
#ifndef _TESTTRANS_H_ #define _TESTTRANS_H_ enum { PY_MORPH_LINK = 10000, PY_TRANSCRIPT_LINK = 10001, PY_SYNC_START = 10018, //GV_XPEMIT_NODE_DUMMY }; #endif // ${FILE_NAME}
string-file:
STRINGTABLE Testtrans // derselbe Name wie res-file { TESTTRANS "Test-Trans"; PY_MORPH_LINK "Pose-Morph-Tag"; PY_TRANSCRIPT_LINK "Transcript"; PY_SYNC_START "Start"; }
Best Regards
Tom -
Hey @ThomasB,
Thank you for reaching out to us. Thank you for your very clear posting, but your three questions should have been at least two or better three topics because they are all entirely different subjects. I did not fork your topic in this case because you did put so much work into your posting, but I would want to give you a gentle reminder that topics should only contain a singular topic.
About Your Questions
Crash in TagData.Execute
No, this has nothing to do with threading and your code is (mostly) correct. There is a bug in 2023 in BaseTime.__sub__, it has already been fixed in the API of the upcoming release.
Note that we support
BaseTime.__sub__
withother
being anint
; this has been added in2023
and is the cause of the bug. But we do not support__rsub__
. I.e.,BaseTime - int
(__sub__
) is supported butint - BaseTime
(__rsub__
) is not. Theint
support has also not yet been documented.When you call
frame = doc.GetTime().GetFrame(doc.GetFps()) - tag[PY_SYNC_START]
you invoke__rsub__
which then defaults to__sub__
and triggers the bug. When subtracting anint
from aBaseTime
, theint
is also interpreted as a value in seconds and not as frame value, because aBaseTime
has no inherent fps.>> import c4d >> bt: c4d.BaseTime = c4d.BaseTime(50, 25) >> bt.Get() 2.0 >> # 1 will be subtracted from the value in seconds which represented by #bt. >> (bt - 1).Get() 1.0
So, you probably meant here:
fps: int = doc.GetFps() frame: int = doc.GetTime().GetFrame(fps) - tag[PY_SYNC_START].GetFrame(fps)
Does not Invoke load_transcript
Well, when your statement
print("POSTSETPARAMETER")
is being hit, so will be the line after it. The interpreter will not magically not call something. Please also do understand that we cannot debug your code for you.What can happen here is that it looks like that
load_transcript
is not running because:- Your condition
op[PY_TRANSCRIPT_LINK] != ""
is not being met. - Whatever relies on
self.data
only later reacts to it being updated or never reacts at all. - You get on
NodeData.Message
calls that are not on the main thread. All file operations are not thread-safe, you cannot have two things accessing one file at the same time. You should wrap all file operations in ac4d.threading.GeIsMainThread
condition.
In the end, you probably have to do a bit more debugging here.
That
MSG_DESCRIPTION_POSTSETPARAMETER
is not broadcasted for parameter changes of a node that is linked is normal, because you listen here to the messages of your node and not the messages of the linked node.Tposemorph Symbol
Yes, you are right, the symbol is not exposed in resources, that is an oversight or bug if you will. You must use the numeric value of
Tposemorph
instead:LINK PY_MORPH_LINK { ANIM OFF; ACCEPT { 1024237; } // Value for Tposemorph }
Cheers,
Ferdinand - Your condition
-
Thank you Ferdinand for your leniency regarding the three questions..
Will take note of that in the future.=====BaseTime=====
Which just surprises me about the BaseTime problem is, that it worked in the Python tag. I didn't get an error message and the prototype worked very well.
So I also tried instead of BaseTime int type value in the plugin, but it crashed. It confused me a bit. So I try your suggestion.# In the python tag it worked with int
# starf_frame was an int frame = doc.GetTime().GetFrame(doc.GetFps()) - op[c4d.ID_USERDATA,start_frame]
====PoseMorph Symbol====
Regarding the posmorph ID, I should have figured that out myselfThanks for your quick respond
Tom
-
This post is deleted!