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()
, and EndUndo()
) into Message()
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