How to expose an object function done in C++
-
On 08/07/2016 at 03:03, xxxxxxxx wrote:
User Information:
Cinema 4D Version: R17
Platform: Windows ;
Language(s) : C.O.F.F.E.E ; C++ ; PYTHON ;---------
I've made my own kind of object and it is working fine, now I wanna to expose a function from my object to be called from Python and Coffee.Is there any example?
Thank you in advance.
-
On 08/07/2016 at 06:02, xxxxxxxx wrote:
Howdy,
Well, you can create Coffee functions with the SDK's Coffee class, but I don't know if you can do it for Python, as I don't see a Python class in the SDK documentation.
EDIT:
Take a look at this thread:
https://developers.maxon.net/forum/topic/4558/4341_extending-coffee-with-c&KW=AddGlobalFunctionAdios,
Cactus Dan -
On 08/07/2016 at 08:55, xxxxxxxx wrote:
Sorry I didn't explain properly I mean a function relative to my object, never used COFFEE but I mean something like
MyObject.saveDataTo ("c:\\whatever.txt")
being MyObject a instance of an object of my own class.
-
On 08/07/2016 at 09:30, xxxxxxxx wrote:
Howdy,
Well, you could do something like this:
class MyObject : public ObjectData { public: Bool setting; Bool GetSetting(void); // other class functions... }; Bool GetSetting(void) { return setting; } // coffee function to access object setting void MyObjectGetSetting(Coffee *cof, Value*&sp, Int32 argc) { Bool res = false; if(argc == 1 && sp[argc-1].IsType(COFFEE_VTYPE_OBJECT)) { GeData gData; GeCoffeeValue2GeData(cof,&sp[argc-1],&gData); BaseLink* bl = gData.GetBaseLink(); BaseObject *op = static_cast<BaseObject*>(bl->GetLink(GetActiveDocument())); if(op) { MyObject *mOp = op->GetNodeData(); if(mOp) res = mOp->GetSetting(); } } GeCoffeeGeData2Value(cof, res, &sp[argc]); sp += argc; }
... and then in your coffee script call it like this:
var setng = MyObjectGetSetting(op);
Is that similar to what you want to do?
Adios,
Cactus Dan -
On 08/07/2016 at 09:48, xxxxxxxx wrote:
This part ...
_// coffee function to access object setting_ _void MyObjectGetSetting(Coffee \*cof, Value\* &sp, Int32 argc)_ _{_ _ Bool res = false;_ _ _ _ if(argc == 1 && sp[argc-1].IsType(COFFEE_VTYPE_OBJECT))_ _ {_ _ GeData gData;_ _ GeCoffeeValue2GeData(cof,&sp[argc-1],&gData);_ _ BaseLink\* bl = gData.GetBaseLink();_ _ _ _ BaseObject \*op = static_cast<BaseObject\*>(bl->GetLink(GetActiveDocument()));_ _ if(op)_ _ {_ _ MyObject \*mOp = op->GetNodeData();_ _ if(mOp) res = mOp->GetSetting();_ _ }_ _ }_ _ _ _ GeCoffeeGeData2Value(cof, res, &sp[argc]);_ _ sp += argc;_ _}_
...is inside my object (C++) or this is pure COFFEE?
Thank you so much
-
On 08/07/2016 at 10:03, xxxxxxxx wrote:
Howdy,
That would be a function outside of your object class. You can add it to the bottom of your myobject.cpp file, and declare it in the main header file.
Then in your PluginStart() function in your main.cpp file, you add your function to the coffee master like this:
Coffee *cofM = GetCoffeeMaster(); if(cofM) { cofM->AddGlobalFunction("MyObjectGetSetting",MyObjectGetSetting); }
Adios,
Cactus Dan -
On 15/07/2016 at 07:13, xxxxxxxx wrote:
Hi!
Sorry for the late reply. It's possible to define new Python functions using the library defined in c4d_libs/lib_py.h
This library definitions are not documented currenly and many are meant for internal use only.To expose a Python function a new module has to be initialized with PythonLibrary::InitModule().
Here is some code:// Global table of c4d.extendpyapi functions for PythonLibrary::InitModule() static maxon::BaseArray<PythonMethodData>* g_extendpyapi_functions = nullptr; // Prints "Hello from Python!" to the console static _PyObject *extendpyapi_HelloPython(_PyObject *self) { PythonLibrary pylib; GePrint("Hello from Python!"); return pylib.ReturnPyNone(); } // Initializes c4d.extendpyapi module void InitExtendPython() { PythonLibrary pylib; // Allocates c4d.extendpyapi module functions g_extendpyapi_functions = NewObjClear(maxon::BaseArray<PythonMethodData>); if (g_extendpyapi_functions == nullptr) return; // Initializes c4d.extendpyapi module functions g_extendpyapi_functions->Resize(2); PythonMethodData* moduleFunctions = g_extendpyapi_functions->GetFirst(); moduleFunctions[0].Init("HelloPython", (PyFn)extendpyapi_HelloPython, PYFN_FLAGS_NOARGS, "HelloPython() - Extend Python API"); moduleFunctions[1].Init(String(), nullptr, PYFN_FLAGS_0, String()); // Last dummy element! // Initializes c4d.extendpyapi module if (pylib.InitModule("c4d.extendpyapi", moduleFunctions, "Extend Python API")) GePrint("\'c4d.extendpyapi\' module successfully initialized"); } // Frees the module global function table void FreeExtendPython() { DeleteObj(g_extendpyapi_functions); }
InitExtendPython()/FreeExtendPython() have to called from within PluginMessage() C4DPL_PYINITTYPES/C4DPL_PYFINALIZE:
Bool PluginMessage(Int32 id, void* data) { case C4DPL_PYINITTYPES: { InitExtendPython(); return true; } case C4DPL_PYFINALIZE: { FreeExtendPython(); return true; } return false; }
To pass basic parameters like string, integer or float use PythonLibrary::ParseTupleAndKeywords() like shown in the following code snippet:
static _PyObject *extendpyapi_PassParameters(_PyObject *self, _PyObject *args, _PyObject *keywords) { PythonLibrary pylib; String str; Int32 integer = 0; Float real = 0.0f; const Char *kwlist[] = {"str", "integer", "real", nullptr}; if (!pylib.ParseTupleAndKeywords(args, keywords, "$if", kwlist, &str, &integer, &real)) return nullptr; if (str.Content()) GePrint("Parameter str: " + str); GePrint("Parameter integer: " + String::IntToString(integer)); GePrint("Parameter real: " + String::FloatToString(real)); return pylib.ReturnPyNone(); }
If a function accepts parameters pass PYFN_FLAGS_KEYWORDS instead of PYFN_FLAGS_NOARGS for the Init() call.
Finally, to parse a GeData/baselist object use 'G' format:
static _PyObject *extendpyapi_PassBaseList(_PyObject *self, _PyObject *args, _PyObject *keywords) { PythonLibrary pylib; GeData data; const Char *kwlist[] = {"baselist", nullptr}; if (!pylib.ParseTupleAndKeywords(args, keywords, "G", kwlist, &data)) return nullptr; if (data.GetType() == DA_ALIASLINK) { BaseLink* link = data.GetBaseLink(); if (link == nullptr) return pylib.ReturnPyNone(); BaseList2D* baseList = link->GetLink(nullptr); if (baseList == nullptr) return pylib.ReturnPyNone(); GePrint("Passed baselist: " + baseList->GetName()); } return pylib.ReturnPyNone(); }
-
On 15/07/2016 at 10:15, xxxxxxxx wrote:
Howdy,
Wow, thanks Yannick!
I've been working on making aspects of my plugins scriptable through cofffee and wanted to included python scriptability, too. This gives me a place to start.
Adios,
Cactus Dan -
On 28/07/2016 at 16:15, xxxxxxxx wrote:
Howdy,
OK, I got the passing a BaseList as a prameter with the "G" format, but suppose I want to pass both a BaseList and an interger or real in the same function? How would that be formatted?
Adios,
Cactus Dan -
On 29/07/2016 at 02:37, xxxxxxxx wrote:
Originally posted by xxxxxxxx
OK, I got the passing a BaseList as a prameter with the "G" format, but suppose I want to pass both a BaseList and an interger or real in the same function? How would that be formatted?
Like the argument parsing in the function example extendpyapi_PassParameters() I posted above, concatenate the characters for each parameter.
For instance:GeData data; Int32 integer = 0; Float real = 0.0f; const Char *kwlist[] = {"baselist", "integer", "real", nullptr}; if (!pylib.ParseTupleAndKeywords(args, keywords, "Gif", kwlist, &data, &integer, &real)) return nullptr;
-
On 29/07/2016 at 04:53, xxxxxxxx wrote:
Howdy,
DOH! It's the strings that always throw me off when they're used for anything other than printing.
But I get it now:
"$" = string format
"i" = integer format
"f" = float format
"G" = GeData formatPossibly if the example had the float first like "$fi" then I might have been able to figure that out. But, with it the other way around, no matter how many times I looked at it, I still saw the word "if", and got confused.
Thanks for the clarification.
Adios,
Cactus Dan -
On 29/07/2016 at 05:53, xxxxxxxx wrote:
You're welcome
"C" can also be used to parse BaseContainer* too (note only pointer to BaseContainer, initialize with nullptr before calling ParseTupleAndKeywords()).
-
On 29/07/2016 at 06:07, xxxxxxxx wrote:
Howdy,
Thanks. Is there a complete list of formatting letters that you could post?
Also, is there a way to add global symbols in python like in coffee, and if so, can you post an example?
Adios,
Cactus Dan -
On 26/04/2017 at 00:05, xxxxxxxx wrote:
Hello,
I know this is a bit older topic but the last question from Cactus Dan was not answered and I also need it. Especially I need to transfer BaseObject to and from python script.
So again, we just need a table of all formatting letters and types that Cinema python binding API uses.
Just for completeness, python build-in formatting letters seems to work and are available here: https://docs.python.org/2.7/c-api/arg.html?highlight=parsetupleandkeywords#c.PyArg_ParseTupleAndKeywords
-
On 26/04/2017 at 02:38, xxxxxxxx wrote:
Hi Miro,
Originally posted by xxxxxxxx
I know this is a bit older topic but the last question from Cactus Dan was not answered and I also need it. Especially I need to transfer BaseObject to and from python script.
To parse a BaseList based object, use 'G' format character as show in my above code snippet's function extendpyapi_PassBaseList(). The object can be casted to the most interesting type after retrieving it.
Originally posted by xxxxxxxx
So again, we just need a table of all formatting letters and types that Cinema python binding API uses.
PythonLibrary::ParseTupleAndKeywords() accepts the following format characters for Cinema 4D C++ API classes:
- $: String
- %: Filename
- M: Matrix
- V: Vector
- Q: Quaternion
- BaseContainer*
- G: GeData (versatile: can parse a BaseList, a custom data, a time, etc.)
- T: BaseTime
- X: BaseThreadOriginally posted by xxxxxxxx
Just for completeness, python build-in formatting letters seems to work and are available here: https://docs.python.org/2.7/c-api/arg.html?highlight=parsetupleandkeywords#c.PyArg_ParseTupleAndKeywords
PythonLibrary::ParseTupleAndKeywords()also accepts the following standard format characters: b, B, j, h, i, I, v, l, L, r, f, d, c
-
On 27/04/2017 at 23:34, xxxxxxxx wrote:
That's perfect! Thank you.
-
On 24/09/2017 at 13:07, xxxxxxxx wrote:
Hello all,
It seems to be that i's only possible to make a python extension library as a plugin. Which plugin then is best suited? A command plugin, I would guess, or is there another plugin that loads completely automatic at startup?
I need something that is always available, so that I don't have to think about loading the library. Much like the c++ example here: page_creating_libraries.html
Regards,
Hermen
-
On 25/09/2017 at 01:29, xxxxxxxx wrote:
Hello,
a "_ plugin_" is just a custom module loaded by Cinema. So a "plugin" does not need to include anything e.g. a command data extension. You only have to implement PluginStart()/PluginMessage()/PluginEnd(), see Plugin Functions Manual. In such a PluginMessage () function you have to register your Python extension as shown above.
best wishes,
Sebastian -
On 25/09/2017 at 02:53, xxxxxxxx wrote:
Aha!
Now I understand, thanks for clearing that up!regards,
Hermen