Does XRef or XRef simple are now accessible with Python?
-
Hi all,
After some research on plugincafe, I found many old threads saying that XRef is not scriptable with Python. They are all from 2018, so, maybe in between the situation has changed and is it possible now to:
• Change the c4d.ID_CA_XREF_FILE file referenced on the fly with Python
• Change the Pivot vectors c4d.ID_CA_XREF_PIVOT_POS, c4d. c4d.ID_CA_XREF_PIVOT_ROT and c4d.ID_CA_XREF_PIVOT_SCL
• Force a reload with c4d.ID_CA_XREF_REFRESH if the file referenced has changed?I do know that's a lot of question, but prior investigated and spending time on this subject I would prefer to be sure that is possible. @ferdinand, your inputs on this are more than welcome!
Thanks a lot,
Cheers,Christophe
-
Hey @mocoloco,
Thank you for reaching out to us. I am not quite sure where you found the information that
Oxref
is 'not scriptable', but that information does not seem quite right to me. You just have to be a bit more verbose when setting parameters. Find an example below.Cheers,
FerdinandResult:
Code:
"""Provides an example for setting Xref object parameters. """ import c4d doc: c4d.documents.BaseDocument # The active document. def main() -> None: """ """ xref: c4d.BaseObject = c4d.BaseObject(c4d.Oxref) if not xref: raise MemoryError("Could not allocate Xref object.") # Add the Xref to the document and define the url for the 'Pillow Decorative' object asset. doc.InsertObject(xref) assetUrl: str = "asset:///file_0de9c0c9d02f0d77~.c4d" # The sort of trick is here, that you must be a bit more verbose with setting parameters, # and and cannot use the high level __setitem__, e.g., `node[myId]` access. Instead, you must # use the lower level C4DAtom.Get/SetParameter access and simulate a user interaction. The Xref # object is a bit special :D # The DescID for the things you want to write, Get/SetParameter only accepts fully formed DescID. fileId: c4d.DescID = c4d.DescID(c4d.DescLevel(c4d.ID_CA_XREF_FILE, c4d.DTYPE_FILENAME, 0)) posId: c4d.DescID = c4d.DescID(c4d.DescLevel(c4d.ID_CA_XREF_PIVOT_POS, c4d.DTYPE_VECTOR, 0)) rotId: c4d.DescID = c4d.DescID(c4d.DescLevel(c4d.ID_CA_XREF_PIVOT_ROT, c4d.DTYPE_VECTOR, 0)) # Set the Xref to the asset and write some pivot data. xref.SetParameter(fileId, assetUrl, c4d.DESCFLAGS_SET_USERINTERACTION) xref.SetParameter(posId, c4d.Vector(0, 100, 0), c4d.DESCFLAGS_SET_USERINTERACTION) xref.SetParameter(rotId, c4d.Vector(c4d.utils.DegToRad(30)), c4d.DESCFLAGS_SET_USERINTERACTION) c4d.EventAdd() # Clicking the button is not necessary the xref is already correctly setup after the event. if __name__ == "__main__": main()
-
Hello @ferdinand,
Thanks a lot for your reactivity. Like said is was through old post here from 2018 and nothing more relevant to go through it.
Does the FileName have to start with
asset::///
?I'm on MacOSX and when I'm writing the following below the file parameter in XRef remains blank without raising any error on console. Even if it seems I understood the DescID formatting.
xRefFile = "Assets/default_xref.c4d" xRefPivot = baseDoc.SearchObject('mPivot') if xRefPivot is not None: print ("mPivot found") if (baseDoc.SearchObject('mXRef')) is None: mXRefObj = c4d.BaseObject(c4d.Oxref) #c4d.BaseObject(c4d.Oxrefsimple) # R2023 only mXRefObj.InsertUnder(xRefPivot) mXRefObj.SetName("mXRef") mXRefObj = baseDoc.SearchObject('mXRef') # Assign paramters on mXRef print(f"File: { XRefFile }" ) descID = c4d.DescID(c4d.DescLevel(c4d.ID_CA_XREF_FILE, c4d.DTYPE_FILENAME, 0)) mXRefObj.SetParameter(descID, XRefFile, c4d.DESCFLAGS_SET_PARAM_SET) print('Model successfully imported') c4d.EventAdd() else: print("Model can't be imported")
The only difference was about
c4d.DESCFLAGS_SET_PARAM_SET
while you are usingc4d.DESCFLAGS_SET_USERINTERACTION
. But in that case, could we consider this as a User interaction?Thanks
-
Indeed, the problem was caused by using
c4d.DESCFLAGS_SET_PARAM_SET
instead ofc4d.DESCFLAGS_SET_USERINTERACTION
.After reading the SDK documentation again, as far as I understand the XRef Filename is considered as a User Interactive Element. That way, it seems mandatory to have
c4d.DESCFLAGS_SET_USERINTERACTION
to set the parameter. I assume that's the case for all the parameter accessible by the user in a Ge.Thanks for the example @ferdinand, it helped me to point this out!
One more question; I'm continuing to explore XRef and I can't really find if there returned value or flag by XRef when the object is correctly loaded.
Is there such a thing, or do I need to check if the Xref object have childs which means somehow that the load happened correctly?Cheers,
Christophe -
Hi @mocoloco,
Does the FileName have to start with asset::///?
No, I only used an asset here so that example runs on any machine (and does not require a specific file on a local volume).
The string
assetUrl
is deliberately named this way, because we effectively define amaxon.Url
here. The parameterID_CA_XREF_FILE
is of typeFilename
. The type does not explicitly exist in the Python API and is instead represented bystr
. Internally, allFilename
are however converted to and represented asmaxon::Url
.Filename
is just a classic API thin wrapper formaxon::Url
.asset:///
is the scheme of this URL andmaxon::Url
supports many schemes;asset
andfile
are just two of them. To define a URL in the file scheme you can either do it implicitly, provide no scheme, or be explicit.assetUrl: str = r"E:/cube.c4d" # Implicitly in the file scheme assetUrl: str = r"file:///E:/cube.c4d"
it seems mandatory to have c4d.DESCFLAGS_SET_USERINTERACTION to set the parameter. I assume that's the case for all the parameters accessible by the user in a Ge.
I am not sure what you meant with 'Ge', but in general that is not the case. In most cases you can use
GeListNode.__getitem__
andGeListNode.__setitem__
to access parameters. For more complex parameter access you sometimes needC4DAtom.Get/SetParameter
, usually with the flagsDESCFLAGS_GET_NONE
andDESCFLAGS_SET_NONE
. It is only in very rare cases that you have to simulate a user interaction withDESCFLAGS_SET_USERINTERACTION
. This happens when the internal data of a node is much more complex than its parameters make it look like and the node must manage the data on each parameter change.Oxref
is an example and also the newOrscamera
, i.e., the new standard camera.One more question; I'm continuing to explore XRef and I can't really find if there returned value or flag by XRef when the object is correctly loaded.
I do not think so, that would be very uncharacteristic for the classic API. Message streams are there sealed, i.e., not accessible for outside observers. Sometimes nodes write a state into a hidden parameter, but looking at the Xref description I cannot see such parameter.
Where would that be relevant for you? Once you have passed the
EventAdd()
in my script, the object should be finalized. You could technically also sendxref.Message(c4d.MSG_CHANGE)
to the atom, but this is a bit pointless since you do not get a response for this message and the node is updating anyways.Cheers,
Ferdinand -
Good morning @ferdinand,
Thanks a lot for all the explanation, I really appreciate, it clarify a lot of stuff and fill the gap. I consider this thread Solved. Thanks.
"Where would that be relevant for you? Once you have passed the EventAdd() in my script, the object should be finalized"
Should, that's why I asked. Maybe the XRef plugin raise an error or a dialog box saying that something wrong happened. I also had a look on the XRef description, and thought at first that
LOADED
parameter what was I was looking, but it is not.PS: I used 'Ge' to design all the user interactive elements such as Sliders, Input for value vector etc. It was maybe a wrong designation, sorry.
Cheer,
Christophe -
Hey @mocoloco,
Should, that's why I asked.
Yeah, that is not the style of the classic API, there are only little error messages and things like that. What you can also do, is use the return value of
SetParameter
, a boolean. It will beFalse
when setting the parameter failed.I used 'Ge' to design all [...]
Eh, I understand, you mean from GeDialog, or what? I think it just stands for GenericDialog, it also pops up in thing like
GeListNode
orGeClipMap
. There are some really old entities in the classic API and naming conventions were different then Ge is at least not a name for interface gadgets in our APIs.Cheers,
Ferdinand -