@m_adam Oh, that's a little update for many users but huge for tiny developers like me!
Thank you so much for the heads up.
Posts made by beatgram
-
RE: Attribute Manamger Update Delay When Description Added Dynamically
-
RE: Undo Problem For TagData Plugin
@m_magalhaes Thank you for your checking, Manuel.
I didn't think it's a bug! And yep, your workaround with bracket notation works fine.
So these bracket notation style is safer than using BaseContainer in any case?
Anyway, let us know if you confirm this is a bug. -
Undo Problem For TagData Plugin
Hey folks,
I encountered
Undo
related issue again. It might be due to my lack of understanding for undo things but let me ask you a question.I wrote a test tag plugin with python which follows another object's position with a little offset.
It simply works but when I undo, the object that has the tag doesn't react until I click somewhere (see below).Is it possible to solve this issue with python?
I know undo things are prohibited inexecute()
oftagdata
, so is there any workaround?Please help me if you know the solution.
Here's quick code for this.
testtag.pyp
import os import c4d from c4d import plugins, bitmaps, utils PLUGIN_ID = ******* class TestTagData(plugins.TagData): def Init(self, node): pd = c4d.PriorityData() if pd is None: raise MemoryError("Failed to create a priority data.") pd.SetPriorityValue(lValueID = c4d.PRIORITYVALUE_MODE, data = c4d.CYCLE_INITIAL) node[c4d.EXPRESSION_PRIORITY] = pd return True def Execute(self, tag, doc, op, bt, priority, flags): bc = tag.GetDataInstance() obj = bc.GetLink(c4d.TTEST_TARGET_OBJECT) if obj is None: return False mrx = obj.GetMg() mrx.off += c4d.Vector(100, 0, 0) op.SetMg(mrx) return c4d.EXECUTIONRESULT_OK if __name__ == "__main__": plugins.RegisterTagPlugin(id = PLUGIN_ID, str = "Test Tag", info = c4d.TAG_EXPRESSION | c4d.TAG_VISIBLE, g = TestTagData, description = "ttest", icon = None)
ttest.res
CONTAINER Ttest { NAME Ttest; INCLUDE Texpression; GROUP ID_TAGPROPERTIES { LINK TTEST_TARGET_OBJECT { ANIM ON; ACCEPT { Obase; }; } }
-
RE: How To Add Undo For Description Added Dynamically
@Cairyn Thank you so much for helping me again!
-
RE: How To Add Undo For Description Added Dynamically
@m_magalhaes Thank you for your explanation! It's really helpful to know several solutions for a problem.
C4D python doc is not user friendly for newbies like me but this community is super friendly and pretty awesome! -
RE: How To Add Undo For Description Added Dynamically
So I tried some changes based on a hint @Cairyn points out, my code seems to work now.
But I'm still not sure this is a proper way to add undo for dynamic descriptions, so please let me know if you know the correct / better way!test_tag.pyp
import os import c4d from c4d import plugins PLUGIN_ID = ******* LINK_NO = 1100 FLOAT_NO = 1200 class TestTagData(c4d.plugins.TagData): def Init(self, node): self.InitAttr(node, int, c4d.TTESTTAG_CONTROLLERS_NUM) #-------------------- Changed -------------------- bc = node.GetDataInstance() #-------------------- Changed -------------------- bc.SetInt32(c4d.TTESTTAG_CONTROLLERS_NUM, 0) #-------------------- Changed -------------------- pd = c4d.PriorityData() if pd is None: raise MemoryError("Failed to create a priority data.") pd.SetPriorityValue(lValueID = c4d.PRIORITYVALUE_MODE, data = c4d.CYCLE_EXPRESSION) node[c4d.EXPRESSION_PRIORITY] = pd return True def Execute(self, tag, doc, op, bt, priority, flags): return c4d.EXECUTIONRESULT_OK def Message(self, node, type, data): if type == c4d.MSG_DESCRIPTION_COMMAND: if data["id"][0].id == c4d.TTESTTAG_BUTTON_ADD: doc = c4d.documents.GetActiveDocument() doc.StartUndo() doc.AddUndo(c4d.UNDOTYPE_CHANGE_SMALL, node) bc = node.GetDataInstance() #-------------------- Changed -------------------- bc.SetInt32(c4d.TTESTTAG_CONTROLLERS_NUM, bc.GetInt32(c4d.TTESTTAG_CONTROLLERS_NUM) + 1) #-------------------- Changed -------------------- doc.EndUndo() elif data["id"][0].id == c4d.TTESTTAG_BUTTON_REMOVE: bc = node.GetDataInstance() #-------------------- Changed -------------------- controllers_num = bc.GetInt32(c4d.TTESTTAG_CONTROLLERS_NUM) #-------------------- Changed -------------------- if controllers_num > 0: #-------------------- Changed -------------------- doc = c4d.documents.GetActiveDocument() doc.StartUndo() doc.AddUndo(c4d.UNDOTYPE_CHANGE_SMALL, node) bc.SetInt32(c4d.TTESTTAG_CONTROLLERS_NUM, controllers_num - 1) #-------------------- Changed -------------------- doc.EndUndo() return True def GetDDescription(self, node, description, flags): if not description.LoadDescription(node.GetType()): return False singleId = description.GetSingleDescID() groupId = c4d.DescID(c4d.DescLevel(c4d.ID_TAGPROPERTIES)) bc = node.GetDataInstance() #-------------------- Changed -------------------- controllers_num = bc.GetInt32(c4d.TTESTTAG_CONTROLLERS_NUM) #-------------------- Changed -------------------- if controllers_num > 0: for i in range(controllers_num): linkId = c4d.DescID(c4d.DescLevel(LINK_NO + (i + 1))) if singleId is None or linkId.IsPartOf(singleId)[0]: link_bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_BASELISTLINK) link_bc.SetString(c4d.DESC_NAME, "Controller " + str(i + 1)) link_bc.SetString(c4d.DESC_SHORT_NAME, "Controller " + str(i + 1)) link_bc.SetInt32(c4d.DESC_ANIMATE, c4d.DESC_ANIMATE_ON) if not description.SetParameter(linkId, link_bc, groupId): return False floatId = c4d.DescID(c4d.DescLevel(FLOAT_NO + (i + 1))) if singleId is None or floatId.IsPartOf(singleId)[0]: float_bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_REAL) float_bc.SetString(c4d.DESC_NAME, "Rate") float_bc.SetString(c4d.DESC_SHORT_NAME, "Rate") float_bc.SetFloat(c4d.DESC_DEFAULT, 1) float_bc.SetInt32(c4d.DESC_ANIMATE, c4d.DESC_ANIMATE_ON) float_bc.SetInt32(c4d.DESC_UNIT, c4d.DESC_UNIT_PERCENT) float_bc.SetFloat(c4d.DESC_MIN, -1000) float_bc.SetFloat(c4d.DESC_MAX, 1000) float_bc.SetFloat(c4d.DESC_MINSLIDER, -1) float_bc.SetFloat(c4d.DESC_MAXSLIDER, 1) float_bc.SetFloat(c4d.DESC_STEP, 0.01) float_bc.SetInt32(c4d.DESC_CUSTOMGUI, c4d.CUSTOMGUI_REALSLIDER) if not description.SetParameter(floatId, float_bc, groupId): return False separatorId = c4d.DescID(0) if singleId is None or separatorId.IsPartOf(singleId)[0]: separator_bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_SEPARATOR) separator_bc.SetInt32(c4d.DESC_CUSTOMGUI, c4d.CUSTOMGUI_SEPARATOR) separator_bc.SetBool(c4d.DESC_SEPARATORLINE, True) if not description.SetParameter(separatorId, separator_bc, groupId): return False return (True, flags | c4d.DESCFLAGS_DESC_LOADED) if __name__ == "__main__": c4d.plugins.RegisterTagPlugin(id = PLUGIN_ID, str = "Test Tag", info = c4d.TAG_EXPRESSION|c4d.TAG_VISIBLE, g = TestTagData, description = "ttesttag", icon = None)
ttesttag.res
CONTAINER Ttesttag { NAME Ttesttag; INCLUDE Texpression; GROUP ID_TAGPROPERTIES { GROUP TTESTTAG_BUTTONS_GROUP { COLUMNS 2; BUTTON TTESTTAG_BUTTON_ADD { SCALE_H; } BUTTON TTESTTAG_BUTTON_REMOVE { SCALE_H; } LONG TTESTTAG_CONTROLLERS_NUM { HIDDEN; ANIM OFF; MIN 0; MAX 10000; } //-------------------- Added -------------------- } } }
ttesttag.h
#ifndef _TTESTTAG_H_ #define _TTESTTAG_H_ enum { TTESTTAG_BUTTONS_GROUP = 1001, TTESTTAG_BUTTON_ADD = 1002, TTESTTAG_BUTTON_REMOVE = 1003, TTESTTAG_CONTROLLERS_NUM = 1004, //-------------------- Added -------------------- } #endif
ttesttag.str
STRINGTABLE Ttesttag { Ttesttag "Test Tag"; TTESTTAG_BUTTONS_GROUP ""; TTESTTAG_BUTTON_ADD "Add"; TTESTTAG_BUTTON_REMOVE "Remove"; TTESTTAG_CONTROLLERS_NUM ""; //-------------------- Added -------------------- }
-
RE: Attribute Manamger Update Delay When Description Added Dynamically
@m_adam Thank you for confirming.
I already know symbolcache thingy, so it's no problem. -
RE: How To Add Undo For Description Added Dynamically
@Cairyn Thank you for your time!
Hmm, I see. I'm not sure the actual solution for the point you mention but your explanation is so helpful. Anyway, I'll try some workaround on my end.
-
How To Add Undo For Description Added Dynamically
Hi folks,
I'm still trying to make a python tag plugin which has 2 buttons in tag property. It dynamically adds new descriptions to tag property when "add" button is clicked, and it dynamically removes those descriptions when "remove" button is clicked. So far my code works but I noticed undo doesn't work for these descriptions (see below).
Is there a way to add undo for descriptions created dynamically? Please tell me how to add undo for that if it's possible for python plugin.
I tried to insert basic undo things (StartUndo()
,AddUndo()
, andEndUndo()
) intoMessage()
function but it doesn't seem to work.
My goal is it works like "IK-Spline" does. (see below)"Current Status"
"My Goal"
Here's my code.
test_tag.pyp
import os import c4d from c4d import plugins PLUGIN_ID = ******* LINK_NO = 1100 FLOAT_NO = 1200 class TestTagData(c4d.plugins.TagData): def Init(self, node): self.controllers_num = 0 pd = c4d.PriorityData() if pd is None: raise MemoryError("Failed to create a priority data.") pd.SetPriorityValue(lValueID = c4d.PRIORITYVALUE_MODE, data = c4d.CYCLE_EXPRESSION) node[c4d.EXPRESSION_PRIORITY] = pd return True def Execute(self, tag, doc, op, bt, priority, flags): return c4d.EXECUTIONRESULT_OK def Message(self, node, type, data): if type == c4d.MSG_DESCRIPTION_COMMAND: if data["id"][0].id == c4d.TTESTTAG_BUTTON_ADD: doc = c4d.documents.GetActiveDocument() doc.StartUndo() #---------- This doesn't seem to work! ---------- doc.AddUndo(c4d.UNDOTYPE_CHANGE_NOCHILDREN, node) self.controllers_num += 1 doc.EndUndo() #---------------------------------------- node.SetDirty(c4d.DIRTYFLAGS_DESCRIPTION) elif data["id"][0].id == c4d.TTESTTAG_BUTTON_REMOVE: if self.controllers_num > 0: doc = c4d.documents.GetActiveDocument() doc.StartUndo() #---------- This doesn't seem to work! ---------- doc.AddUndo(c4d.UNDOTYPE_CHANGE_NOCHILDREN, node) self.controllers_num -= 1 doc.EndUndo() #---------------------------------------- node.SetDirty(c4d.DIRTYFLAGS_DESCRIPTION) return True def GetDDescription(self, node, description, flags): if not description.LoadDescription(node.GetType()): return False singleId = description.GetSingleDescID() groupId = c4d.DescID(c4d.DescLevel(c4d.ID_TAGPROPERTIES)) controllers_num = self.controllers_num if controllers_num > 0: for i in range(controllers_num): linkId = c4d.DescID(c4d.DescLevel(LINK_NO + (i + 1))) if singleId is None or linkId.IsPartOf(singleId)[0]: link_bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_BASELISTLINK) link_bc.SetString(c4d.DESC_NAME, "Controller " + str(i + 1)) link_bc.SetString(c4d.DESC_SHORT_NAME, "Controller " + str(i + 1)) link_bc.SetInt32(c4d.DESC_ANIMATE, c4d.DESC_ANIMATE_ON) if not description.SetParameter(linkId, link_bc, groupId): return False floatId = c4d.DescID(c4d.DescLevel(FLOAT_NO + (i + 1))) if singleId is None or floatId.IsPartOf(singleId)[0]: float_bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_REAL) float_bc.SetString(c4d.DESC_NAME, "Rate") float_bc.SetString(c4d.DESC_SHORT_NAME, "Rate") float_bc.SetFloat(c4d.DESC_DEFAULT, 1) float_bc.SetInt32(c4d.DESC_ANIMATE, c4d.DESC_ANIMATE_ON) float_bc.SetInt32(c4d.DESC_UNIT, c4d.DESC_UNIT_PERCENT) float_bc.SetFloat(c4d.DESC_MIN, -1000) float_bc.SetFloat(c4d.DESC_MAX, 1000) float_bc.SetFloat(c4d.DESC_MINSLIDER, -1) float_bc.SetFloat(c4d.DESC_MAXSLIDER, 1) float_bc.SetFloat(c4d.DESC_STEP, 0.01) float_bc.SetInt32(c4d.DESC_CUSTOMGUI, c4d.CUSTOMGUI_REALSLIDER) if not description.SetParameter(floatId, float_bc, groupId): return False separatorId = c4d.DescID(0) if singleId is None or separatorId.IsPartOf(singleId)[0]: separator_bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_SEPARATOR) separator_bc.SetInt32(c4d.DESC_CUSTOMGUI, c4d.CUSTOMGUI_SEPARATOR) separator_bc.SetBool(c4d.DESC_SEPARATORLINE, True) if not description.SetParameter(separatorId, separator_bc, groupId): return False return (True, flags | c4d.DESCFLAGS_DESC_LOADED) if __name__ == "__main__": c4d.plugins.RegisterTagPlugin(id = PLUGIN_ID, str = "Test Tag", info = c4d.TAG_EXPRESSION|c4d.TAG_VISIBLE, g = TestTagData, description = "ttesttag", icon = None)
ttesttag.res
CONTAINER Ttesttag { NAME Ttesttag; INCLUDE Texpression; GROUP ID_TAGPROPERTIES { GROUP TTESTTAG_BUTTONS_GROUP { COLUMNS 2; BUTTON TTESTTAG_BUTTON_ADD { SCALE_H; } BUTTON TTESTTAG_BUTTON_REMOVE { SCALE_H; } } } }
ttesttag.h
#ifndef _TTESTTAG_H_ #define _TTESTTAG_H_ enum { TTESTTAG_BUTTONS_GROUP = 1001, TTESTTAG_BUTTON_ADD = 1002, TTESTTAG_BUTTON_REMOVE = 1003, } #endif
ttesttag.str
STRINGTABLE Ttesttag { Ttesttag "Test Tag"; TTESTTAG_BUTTONS_GROUP ""; TTESTTAG_BUTTON_ADD "Add"; TTESTTAG_BUTTON_REMOVE "Remove"; }
---------- User Information ----------
Cinema 4D version: R23
OS: Windows 10
Language: Python -
RE: Attribute Manamger Update Delay When Description Added Dynamically
@Cairyn Interesting investigation but it's a bit difficult for newbie like me.
Anyway, I appreciate your help! -
RE: Attribute Manamger Update Delay When Description Added Dynamically
@Cairyn Okay, here's my .h / .str files. Sorry, I should have shared these in the first post.
ttesttag.h
#ifndef _TTESTTAG_H_ #define _TTESTTAG_H_ enum { TTESTTAG_LINKS_GROUP = 1001, TTESTTAG_ADD_BUTTON = 1002, TTESTTAG_REMOVE_BUTTON = 1003, } #endif
ttesttag.str
STRINGTABLE Ttesttag { Ttesttag "Test Tag"; TTESTTAG_LINKS_GROUP ""; TTESTTAG_ADD_BUTTON "Add"; TTESTTAG_REMOVE_BUTTON "Remove"; }
-
RE: Attribute Manamger Update Delay When Description Added Dynamically
@mp5gosu Oh I couldn't find that. Thank you for your heads up!
-
RE: Attribute Manamger Update Delay When Description Added Dynamically
@Cairyn Thank you again for helping me.
Yep,SetDirty(c4d.DIRTYFLAGS_DESCRIPTION)
works like a charm!def Message(self, node, type, data): if type == c4d.MSG_DESCRIPTION_COMMAND: if data["id"][0].id == c4d.TTESTTAG_ADD_BUTTON: bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_BASELISTLINK) self.links_list.append(bc) node.SetDirty(c4d.DIRTYFLAGS_DESCRIPTION) # This was needed! return True
I'm sorry but I'm not sure what you mean about float fields for 2nd to 4th "Add". On my end, my code adds link fields everytime user click the Add button.
Let me know if my understanding is wrong. -
RE: Attribute Manamger Update Delay When Description Added Dynamically
@Cairyn Thank you for taking your time.
I see. So to execute GetDDescription, "c4d.EventAdd()" should be added to Message() function like below?
def Message(self, node, type, data): if type == c4d.MSG_DESCRIPTION_COMMAND: if data["id"][0].id == c4d.TTESTTAG_ADD_BUTTON: bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_BASELISTLINK) self.links_list.append(bc) c4d.EventAdd() return True
I tried this code but it doesn't work and the result is the same as before.
-
Attribute Manamger Update Delay When Description Added Dynamically
Hello guys,
I'm trying to make a python tag plugin with button. My goal is to add description to tag description when user hit add button in tag property.
I feel my code works properly but Atrribute Manager doesn't update until I move the current time indicator (see below).
Is it possible to add descripton dynamically with button in python plugin?
I searched through this forum and I think "Update button" might be related but I can't understand enough... so please help me if you know the workaround!Here's my code.
test_tag.pypimport os import c4d from c4d import plugins PLUGIN_ID = ******* START_NO = 1100 class TestTagData(c4d.plugins.TagData): def Init(self, node): self.links_list = [] pd = c4d.PriorityData() if pd is None: raise MemoryError("Failed to create a priority data.") pd.SetPriorityValue(lValueID = c4d.PRIORITYVALUE_MODE, data = c4d.CYCLE_EXPRESSION) node[c4d.EXPRESSION_PRIORITY] = pd return True def Execute(self, tag, doc, op, bt, priority, flags): return c4d.EXECUTIONRESULT_OK def Message(self, node, type, data): if type == c4d.MSG_DESCRIPTION_COMMAND: if data["id"][0].id == c4d.TTESTTAG_ADD_BUTTON: bc = c4d.GetCustomDataTypeDefault(c4d.DTYPE_BASELISTLINK) self.links_list.append(bc) return True def GetDDescription(self, node, description, flags): if not description.LoadDescription(node.GetType()): return False singleId = description.GetSingleDescID() groupId = c4d.DescID(c4d.DescLevel(c4d.TTESTTAG_LINKS_GROUP, c4d.DTYPE_GROUP, node.GetType())) links_num = len(self.links_list) if links_num > 0: for i in range(links_num): paramId = c4d.DescID(c4d.DescLevel(START_NO + (i + 1))) if singleId is None or paramId.IsPartOf(singleId)[0]: bc = self.links_list[i] bc.SetString(c4d.DESC_NAME, "Controller " + str(i + 1)) bc.SetString(c4d.DESC_SHORT_NAME, "Controller " + str(i + 1)) bc.SetInt32(c4d.DESC_ANIMATE, c4d.DESC_ANIMATE_ON) if not description.SetParameter(paramId, bc, groupId): return False return (True, flags | c4d.DESCFLAGS_DESC_LOADED) if __name__ == "__main__": c4d.plugins.RegisterTagPlugin(id = PLUGIN_ID, str = "Test Tag", info = c4d.TAG_EXPRESSION|c4d.TAG_VISIBLE, g = TestTagData, description = "ttesttag", icon = None)
ttesttag.res
CONTAINER Ttesttag { NAME Ttesttag; INCLUDE Texpression; GROUP ID_TAGPROPERTIES { GROUP TTESTTAG_LINKS_GROUP { GROUP { COLUMNS 2; BUTTON TTESTTAG_ADD_BUTTON { SCALE_H; } BUTTON TTESTTAG_REMOVE_BUTTON { SCALE_H; } } } } }
---------- User Information ----------
Cinema 4D version: R23
OS: Windows 10
Language: Python -
RE: Python Tag Plugin Doesn't Work Properly With Resource Files
@m_magalhaes Anyway thanks for the support, Manuel! I love this great community.
-
RE: Python Tag Plugin Doesn't Work Properly With Resource Files
I double checked my hierarchy / structure and finally I found my mistake!
The structure below works well and I think it's correct one.Test Plugin/ test_plugin.pyp res/ c4d_symbols.h description/ ttestplugin.h ttestplugin.res strings_en-US/ description -------------------- This folder needed! -------------------- ttestplugin.str
-
RE: Python Tag Plugin Doesn't Work Properly With Resource Files
@m_magalhaes Thank you for checking on your end.
That example code made a same error on my end and I created my own hierarchy on my drive. -
Python Tag Plugin Doesn't Work Properly With Resource Files
Hello guys,
I'm a newbie to C4D python script / plugin development and please allow me to ask you guys some points.I tried to write a python tag plugin based on "py-look_at_camera_r13" example but I encountered the error like below when I attach the tag this plugin created.
I looked through this forum and I keep googling but I can't find the proper answer. I think the problem is related to description resource but I'm not familiar with these things. Python plugin development with resource files is pretty confusing...
So please help me! Any help would be greatly appreciated.My code is below.
text_plugin.pyp
import os import c4d PLUGIN_ID = ******* class TestPlugin(c4d.plugins.TagData): def Init(self, node): self.InitAttr(node, bool, c4d.TESTPLUGIN_ENABLE) node[c4d.TESTPLUGIN_ENABLE] = True pd = c4d.PriorityData() if pd is None: raise MemoryError("Failed to create a priority data.") pd.SetPriorityValue(c4d.PRIORITYVALUE_CAMERADEPENDENT, True) node[c4d.EXPRESSION_PRIORITY] = pd return True def Execute(self, tag, doc, op, bt, priority, flags): return c4d.EXECUTIONRESULT_OK if __name__ == "__main__": register_check = c4d.plugins.RegisterTagPlugin(id = PLUGIN_ID, str = "Test Plugin", info = c4d.TAG_EXPRESSION | c4d.TAG_VISIBLE, g = TestPlugin, description = "Ttestplugin", icon = None) if register_check: print("Test Plugin by beatgram")
c4d_symbols.h
enum { // End of symbol definition _DUMMY_ELEMENT_ };
ttestplugin.h
#ifndef _Ttestplugin_H_ #define _Ttestplugin_H_ enum { TESTPLUGIN_ENABLE = 1000 } #endif
ttestplugin.str
STRINGTABLE Ttestplugin { Ttestplugin "Test Plugin"; TESTPLUGIN_ENABLE "Enable"; }
ttestplugin.res
CONTAINER Ttestplugin { NAME Ttestplugin; INCLUDE Texpression; GROUP ID_TAGPROPERTIES { BOOL TESTPLUGIN_ENABLE { ANIM ON; } } }
Plugin structure
Test Plugin/ test_plugin.pyp res/ c4d_symbols.h description/ ttestplugin.h ttestplugin.res strings_en-US/ ttestplugin.str
---------- User Information ----------
Cinema 4D version: R23
OS: Windows 10
Language: Python