2.5 PyAPI

Support for subclassing blenders operator, to be registered as a new operator.

Still need to... 
* add constants like Operator.FINISHED
* wrap context (with rna?)
* poll() cant work right now because there is no way to access the operatorType that holds the python class.
* Only float, int and bool properties can be added so far.

working example operator.
http://wiki.blender.org/index.php/BlenderDev/Blender2.5/WinterCamp/TechnicalDesign#Operator_Example_Code
This commit is contained in:
Campbell Barton
2009-03-16 15:54:43 +00:00
parent 133e8827b7
commit 16fe92f868
6 changed files with 404 additions and 160 deletions

View File

@@ -57,6 +57,22 @@ static PyObject *CreateGlobalDictionary( bContext *C )
PyDict_SetItemString( dict, "__bpy_context__", item );
Py_DECREF(item);
// XXX - put somewhere more logical
{
PyMethodDef *ml;
static PyMethodDef bpy_prop_meths[] = {
{"FloatProperty", BPy_FloatProperty, METH_VARARGS|METH_KEYWORDS, ""},
{"IntProperty", BPy_IntProperty, METH_VARARGS|METH_KEYWORDS, ""},
{"BoolProperty", BPy_BoolProperty, METH_VARARGS|METH_KEYWORDS, ""},
{NULL, NULL, 0, NULL}
};
for(ml = &bpy_prop_meths; ml->ml_name; ml++) {
PyDict_SetItemString( dict, ml->ml_name, PyCFunction_New(ml, NULL));
}
}
return dict;
}

View File

@@ -111,7 +111,7 @@ static PyObject *pyop_base_dir(PyObject *self);
static struct PyMethodDef pyop_base_methods[] = {
{"__dir__", (PyCFunction)pyop_base_dir, METH_NOARGS, ""},
{"add", (PyCFunction)PYOP_wrap_add, METH_VARARGS, ""},
{"remove", (PyCFunction)PYOP_wrap_remove, METH_VARARGS, ""},
{"remove", (PyCFunction)PYOP_wrap_remove, METH_O, ""},
{NULL, NULL, 0, NULL}
};

View File

@@ -45,39 +45,9 @@ typedef struct PyOperatorType {
char idname[OP_MAX_TYPENAME];
char name[OP_MAX_TYPENAME];
char description[OP_MAX_TYPENAME]; // XXX should be longer?
PyObject *py_invoke;
PyObject *py_exec;
PyObject *py_class;
} PyOperatorType;
static PyObject *pyop_kwargs_from_operator(wmOperator *op)
{
PyObject *dict = PyDict_New();
PyObject *item;
PropertyRNA *prop, *iterprop;
CollectionPropertyIterator iter;
const char *arg_name;
iterprop= RNA_struct_iterator_property(op->ptr);
RNA_property_collection_begin(op->ptr, iterprop, &iter);
for(; iter.valid; RNA_property_collection_next(&iter)) {
prop= iter.ptr.data;
arg_name= RNA_property_identifier(&iter.ptr, prop);
if (strcmp(arg_name, "rna_type")==0) continue;
item = pyrna_prop_to_py(op->ptr, prop);
PyDict_SetItemString(dict, arg_name, item);
Py_DECREF(item);
}
RNA_property_collection_end(&iter);
return dict;
}
static PyObject *pyop_dict_from_event(wmEvent *event)
{
PyObject *dict= PyDict_New();
@@ -191,33 +161,6 @@ static struct BPY_flag_def pyop_ret_flags[] = {
{NULL, 0}
};
/* exec only - no user input */
static int PYTHON_OT_exec(bContext *C, wmOperator *op)
{
PyOperatorType *pyot = op->type->pyop_data;
PyObject *args= PyTuple_New(0);
PyObject *kw= pyop_kwargs_from_operator(op);
PyObject *ret;
int ret_flag;
ret = PyObject_Call(pyot->py_exec, args, kw);
if (ret == NULL) {
pyop_error_report(op->reports);
}
else {
if (BPY_flag_from_seq(pyop_ret_flags, ret, &ret_flag) == -1) {
/* the returned value could not be converted into a flag */
pyop_error_report(op->reports);
}
}
Py_DECREF(args);
Py_DECREF(kw);
return ret_flag;
}
/* This invoke function can take events and
*
* It is up to the pyot->py_invoke() python func to run pyot->py_exec()
@@ -233,26 +176,93 @@ static int PYTHON_OT_exec(bContext *C, wmOperator *op)
* op_exec(**prop_defs)
*
* when there is no invoke function, C calls exec and sets the props.
* python class instance is stored in op->customdata so exec() can access
*/
static int PYTHON_OT_invoke(bContext *C, wmOperator *op, wmEvent *event)
#define PYOP_EXEC 1
#define PYOP_INVOKE 2
#define PYOP_POLL 3
static int PYTHON_OT_generic(int mode, bContext *C, wmOperator *op, wmEvent *event)
{
PyOperatorType *pyot = op->type->pyop_data;
PyObject *args= PyTuple_New(2);
PyObject *ret;
int ret_flag;
PyObject *args;
PyObject *ret= NULL, *py_class_instance, *item;
int ret_flag= (mode==PYOP_POLL ? 0:OPERATOR_CANCELLED);
args = PyTuple_New(1);
PyTuple_SET_ITEM(args, 0, PyObject_GetAttrString(pyot->py_class, "__rna__")); // need to use an rna instance as the first arg
py_class_instance = PyObject_Call(pyot->py_class, args, NULL);
Py_DECREF(args);
if (py_class_instance) { /* Initializing the class worked, now run its invoke function */
/* Assign instance attributes from operator properties */
{
PropertyRNA *prop, *iterprop;
CollectionPropertyIterator iter;
const char *arg_name;
PyTuple_SET_ITEM(args, 0, pyop_dict_from_event(event));
PyTuple_SET_ITEM(args, 1, pyop_kwargs_from_operator(op));
iterprop= RNA_struct_iterator_property(op->ptr);
RNA_property_collection_begin(op->ptr, iterprop, &iter);
ret = PyObject_Call(pyot->py_invoke, args, NULL);
for(; iter.valid; RNA_property_collection_next(&iter)) {
prop= iter.ptr.data;
arg_name= RNA_property_identifier(&iter.ptr, prop);
if (strcmp(arg_name, "rna_type")==0) continue;
item = pyrna_prop_to_py(op->ptr, prop);
PyObject_SetAttrString(py_class_instance, arg_name, item);
Py_DECREF(item);
}
RNA_property_collection_end(&iter);
}
if (mode==PYOP_INVOKE) {
item= PyObject_GetAttrString(pyot->py_class, "invoke");
args = PyTuple_New(2);
PyTuple_SET_ITEM(args, 1, pyop_dict_from_event(event));
}
else if (mode==PYOP_EXEC) {
item= PyObject_GetAttrString(pyot->py_class, "exec");
args = PyTuple_New(1);
}
else if (mode==PYOP_POLL) {
item= PyObject_GetAttrString(pyot->py_class, "poll");
args = PyTuple_New(2);
//XXX Todo - wrap context in a useful way, None for now.
PyTuple_SET_ITEM(args, 1, Py_None);
}
PyTuple_SET_ITEM(args, 0, py_class_instance);
ret = PyObject_Call(item, args, NULL);
Py_DECREF(args);
Py_DECREF(item);
}
if (ret == NULL) {
pyop_error_report(op->reports);
}
else {
if (BPY_flag_from_seq(pyop_ret_flags, ret, &ret_flag) == -1) {
if (mode==PYOP_POLL) {
if (PyBool_Check(ret) == 0) {
PyErr_SetString(PyExc_ValueError, "Python poll function return value ");
pyop_error_report(op->reports);
}
else {
ret_flag= ret==Py_True ? 1:0;
}
} else if (BPY_flag_from_seq(pyop_ret_flags, ret, &ret_flag) == -1) {
/* the returned value could not be converted into a flag */
pyop_error_report(op->reports);
}
/* there is no need to copy the py keyword dict modified by
* pyot->py_invoke(), back to the operator props since they are just
@@ -261,131 +271,234 @@ static int PYTHON_OT_invoke(bContext *C, wmOperator *op, wmEvent *event)
* If we ever want to do this and use the props again,
* it can be done with - PYOP_props_from_dict(op->ptr, kw)
*/
Py_DECREF(ret);
}
Py_DECREF(args); /* also decref's kw */
return ret_flag;
}
static int PYTHON_OT_invoke(bContext *C, wmOperator *op, wmEvent *event)
{
return PYTHON_OT_generic(PYOP_INVOKE, C, op, event);
}
static int PYTHON_OT_exec(bContext *C, wmOperator *op)
{
return PYTHON_OT_generic(PYOP_EXEC, C, op, NULL);
}
static int PYTHON_OT_poll(bContext *C)
{
// XXX TODO - no way to get the operator type (and therefor class) from the poll function.
//return PYTHON_OT_generic(PYOP_POLL, C, NULL, NULL);
return 1;
}
void PYTHON_OT_wrapper(wmOperatorType *ot, void *userdata)
{
PyOperatorType *pyot = (PyOperatorType *)userdata;
PyObject *py_class = pyot->py_class;
/* identifiers */
ot->name= pyot->name;
ot->idname= pyot->idname;
ot->description= pyot->description;
/* api callbacks */
if (pyot->py_invoke != Py_None)
/* api callbacks, detailed checks dont on adding */
if (PyObject_HasAttrString(py_class, "invoke"))
ot->invoke= PYTHON_OT_invoke;
ot->exec= PYTHON_OT_exec;
ot->poll= ED_operator_screenactive; /* how should this work?? */
/* ot->flag= OPTYPE_REGISTER; */
if (PyObject_HasAttrString(py_class, "exec"))
ot->exec= PYTHON_OT_exec;
if (PyObject_HasAttrString(py_class, "poll"))
ot->poll= PYTHON_OT_poll;
ot->pyop_data= userdata;
/* inspect function keyword args to get properties */
{
PropertyRNA *prop;
PyObject *var_names= PyObject_GetAttrString(PyFunction_GET_CODE(pyot->py_exec), "co_varnames");
PyObject *var_vals = PyFunction_GET_DEFAULTS(pyot->py_exec);
PyObject *py_val, *py_name;
// TODO - set properties
PyObject *props, *item;
if ((props=PyObject_GetAttrString(py_class, "properties"))) {
PyObject *dummy_args = PyTuple_New(0);
int i;
char *name;
if (PyTuple_Size(var_names) != PyTuple_Size(var_vals)) {
printf("All args must be keywords");
}
for(i=0; i<PyTuple_Size(var_names); i++) {
py_name = PyTuple_GetItem(var_names, i);
name = _PyUnicode_AsString(py_name);
py_val = PyTuple_GetItem(var_vals, i);
if (PyBool_Check(py_val)) {
prop = RNA_def_property(ot->srna, name, PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_default(prop, PyObject_IsTrue(py_val));
}
else if (PyLong_Check(py_val)) {
prop = RNA_def_property(ot->srna, name, PROP_INT, PROP_NONE);
RNA_def_property_int_default(prop, (int)PyLong_AsSsize_t(py_val));
}
else if (PyFloat_Check(py_val)) {
prop = RNA_def_property(ot->srna, name, PROP_FLOAT, PROP_NONE);
RNA_def_property_float_default(prop, (float)PyFloat_AsDouble(py_val));
}
else if (PyUnicode_Check(py_val)) {
/* WARNING - holding a reference to the string from py_val is
* not ideal since we rely on python keeping it,
* however we're also keeping a reference to this function
* so it should be OK!. just be careful with changes */
prop = RNA_def_property(ot->srna, name, PROP_STRING, PROP_NONE);
RNA_def_property_string_default(prop, _PyUnicode_AsString(py_val));
}
else {
printf("error, python function arg \"%s\" was not a bool, int, float or string type\n", name);
}
for(i=0; i<PyList_Size(props); i++) {
item = PyList_GET_ITEM(props, i);
PyObject *py_func_ptr, *py_kw, *py_srna_cobject, *py_ret;
if (PyArg_ParseTuple(item, "O!O!", &PyCObject_Type, &py_func_ptr, &PyDict_Type, &py_kw)) {
PyObject *(*pyfunc)(PyObject *, PyObject *, PyObject *);
pyfunc = PyCObject_AsVoidPtr(py_func_ptr);
py_srna_cobject = PyCObject_FromVoidPtr(ot->srna, NULL);
py_ret = pyfunc(py_srna_cobject, dummy_args, py_kw);
if (py_ret) {
Py_DECREF(py_ret);
} else {
PyErr_Print();
PyErr_Clear();
}
Py_DECREF(py_srna_cobject);
} else {
/* cant return NULL from here */ // XXX a bit ugly
PyErr_Print();
PyErr_Clear();
}
// expect a tuple with a CObject and a dict
}
Py_DECREF(dummy_args);
} else {
PyErr_Clear();
}
}
/* pyOperators - Operators defined IN Python */
PyObject *PYOP_wrap_add(PyObject *self, PyObject *args)
{
// XXX ugly - store the Operator type elsewhere!, probably leaks memory
PyObject *optype = PyObject_GetAttrString(PyObject_GetAttrString(PyDict_GetItemString(PyEval_GetGlobals(), "bpy"), "types"), "Operator");
PyObject *value, *item;
PyOperatorType *pyot;
char *idname= NULL;
char *name= NULL;
char *description= NULL;
PyObject *invoke= NULL;
PyObject *exec= NULL;
if (!PyArg_ParseTuple(args, "sssOO", &idname, &name, &description, &invoke, &exec)) {
PyErr_SetString( PyExc_AttributeError, "expected 2 strings and 2 function objects");
static char *pyop_func_names[] = {"exec", "invoke", "poll", NULL};
static int *pyop_func_nargs[] = {1, 2, 2, 0};
if (!PyArg_ParseTuple(args, "O", &value) || !PyObject_IsSubclass(value, optype)) {
PyErr_SetString( PyExc_AttributeError, "expected Operator subclass of bpy.types.Operator");
return NULL;
}
/* class name is used for operator ID - this can be changed later if we want */
item = PyObject_GetAttrString(value, "__name__");
idname = _PyUnicode_AsString(item);
Py_DECREF(item);
if (WM_operatortype_find(idname)) {
PyErr_Format( PyExc_AttributeError, "First argument \"%s\" operator alredy exists with this name", idname);
PyErr_Format( PyExc_AttributeError, "Operator alredy exists with this name", idname);
return NULL;
}
if (((PyFunction_Check(invoke) || invoke==Py_None) && PyFunction_Check(exec)) == 0) {
PyErr_SetString( PyExc_AttributeError, "the 2nd arg must be a function or None, the secons must be a function");
return NULL;
/* Operator user readible name */
item = PyObject_GetAttrString(value, "name");
if (item) {
name = _PyUnicode_AsString(item);
Py_DECREF(item);
}
if (name == NULL) {
name = idname;
PyErr_Clear();
}
/* use py docstring for description, should always be None or a string */
item = PyObject_GetAttrString(value, "__doc__");
if (PyUnicode_Check(item)) {
description = _PyUnicode_AsString(item);
}
else {
description = "";
}
Py_DECREF(item);
/* Check known functions and argument lengths */
int i;
int argcount;
for (i=0; pyop_func_names[i]; i++) {
if (item=PyObject_GetAttrString(value, pyop_func_names[i])) {
/* check its callable */
if (!PyFunction_Check(item)) {
PyErr_Format(PyExc_ValueError, "Cant register operator class - %s.%s() is not a function", idname, pyop_func_names[i]);
Py_DECREF(item);
return NULL;
}
/* check the number of args is correct */
// MyClass.exec.func_code.co_argcount
PyObject *pyargcount = PyObject_GetAttrString(PyFunction_GetCode(item), "co_argcount");
argcount = PyLong_AsSsize_t(pyargcount);
Py_DECREF(pyargcount);
if (argcount != pyop_func_nargs[i]) {
PyErr_Format(PyExc_ValueError, "Cant register operator class - %s.%s() takes %d args, should be %d", idname, pyop_func_names[i], argcount, pyop_func_nargs[i]);
Py_DECREF(item);
return NULL;
}
} else {
PyErr_Clear();
}
Py_XDECREF(item);
}
/* If we have properties set, check its a list of dicts */
item = PyObject_GetAttrString(value, "properties");
if (item) {
if (!PyList_Check(item)) {
PyErr_Format(PyExc_ValueError, "Cant register operator class - %s.properties must be a list", idname);
Py_DECREF(item);
return NULL;
}
int i;
for(i=0; i<PyList_Size(item); i++) {
PyObject *py_args = PyList_GET_ITEM(item, i);
PyObject *py_func_ptr, *py_kw; /* place holders */
if (!PyArg_ParseTuple(py_args, "O!O!", &PyCObject_Type, &py_func_ptr, &PyDict_Type, &py_kw)) {
PyErr_Format(PyExc_ValueError, "Cant register operator class - %s.properties must contain values from FloatProperty", idname);
Py_DECREF(item);
return NULL;
}
}
Py_DECREF(item);
}
else {
PyErr_Clear();
}
pyot= MEM_callocN(sizeof(PyOperatorType), "PyOperatorType");
strncpy(pyot->idname, idname, sizeof(pyot->idname));
strncpy(pyot->name, name, sizeof(pyot->name));
strncpy(pyot->description, description, sizeof(pyot->description));
pyot->py_invoke= invoke;
pyot->py_exec= exec;
Py_INCREF(invoke);
Py_INCREF(exec);
pyot->py_class= value;
Py_INCREF(value);
WM_operatortype_append_ptr(PYTHON_OT_wrapper, pyot);
Py_RETURN_NONE;
}
PyObject *PYOP_wrap_remove(PyObject *self, PyObject *args)
PyObject *PYOP_wrap_remove(PyObject *self, PyObject *value)
{
char *idname= NULL;
wmOperatorType *ot;
PyOperatorType *pyot;
if (!PyArg_ParseTuple(args, "s", &idname))
if (PyUnicode_Check(value))
idname = _PyUnicode_AsString(value);
else if (PyCFunction_Check(value)) {
PyObject *cfunc_self = PyCFunction_GetSelf(value);
if (cfunc_self)
idname = _PyUnicode_AsString(cfunc_self);
}
if (idname==NULL) {
PyErr_SetString( PyExc_ValueError, "Expected the operator name as a string or the operator function");
return NULL;
}
if (!(ot= WM_operatortype_find(idname))) {
PyErr_Format( PyExc_AttributeError, "Operator \"%s\" does not exists, cant remove", idname);
@@ -397,8 +510,7 @@ PyObject *PYOP_wrap_remove(PyObject *self, PyObject *args)
return NULL;
}
Py_XDECREF(pyot->py_invoke);
Py_XDECREF(pyot->py_exec);
Py_XDECREF(pyot->py_class);
MEM_freeN(pyot);
WM_operatortype_remove(idname);
@@ -406,3 +518,5 @@ PyObject *PYOP_wrap_remove(PyObject *self, PyObject *args)
Py_RETURN_NONE;
}

View File

@@ -26,6 +26,9 @@
#include "bpy_compat.h"
//#include "blendef.h"
#include "BLI_dynstr.h"
#include "float.h" /* FLT_MIN/MAX */
#include "RNA_define.h" /* for defining our own rna */
#include "MEM_guardedalloc.h"
#include "BKE_global.h" /* evil G.* */
@@ -65,12 +68,16 @@ static PyObject *pyrna_prop_richcmp(BPy_PropertyRNA * a, BPy_PropertyRNA * b, in
/*----------------------repr--------------------------------------------*/
static PyObject *pyrna_struct_repr( BPy_StructRNA * self )
{
return PyUnicode_FromFormat( "[BPy_StructRNA \"%s\"]", RNA_struct_identifier(&self->ptr));
char str[512];
RNA_string_get(&self->ptr, "identifier", str);
return PyUnicode_FromFormat( "[BPy_StructRNA \"%s\" -> \"%s\"]", RNA_struct_identifier(&self->ptr), str);
}
static PyObject *pyrna_prop_repr( BPy_PropertyRNA * self )
{
return PyUnicode_FromFormat( "[BPy_PropertyRNA \"%s\" -> \"%s\" ]", RNA_struct_identifier(&self->ptr), RNA_property_identifier(&self->ptr, self->prop) );
char str[512];
RNA_string_get(&self->ptr, "identifier", str);
return PyUnicode_FromFormat( "[BPy_PropertyRNA \"%s\" -> \"%s\" -> \"%s\" ]", RNA_struct_identifier(&self->ptr), str, RNA_property_identifier(&self->ptr, self->prop) );
}
static long pyrna_struct_hash( BPy_StructRNA * self )
@@ -88,7 +95,7 @@ static void pyrna_struct_dealloc( BPy_StructRNA * self )
self->ptr.data= NULL;
}
((PyObject *)self)->ob_type->tp_free(self);
Py_TYPE(self)->tp_free(self);
return;
}
@@ -516,7 +523,7 @@ static int pyrna_prop_assign_subscript( BPy_PropertyRNA * self, PyObject *key, P
char *keyname = NULL;
if (!RNA_property_editable(&self->ptr, self->prop)) {
PyErr_Format( PyExc_AttributeError, "Attribute \"%s\" from \"%s\" is read-only", RNA_property_identifier(&self->ptr, self->prop), RNA_struct_identifier(&self->ptr) );
PyErr_Format( PyExc_AttributeError, "PropertyRNA - attribute \"%s\" from \"%s\" is read-only", RNA_property_identifier(&self->ptr, self->prop), RNA_struct_identifier(&self->ptr) );
return -1;
}
@@ -525,26 +532,26 @@ static int pyrna_prop_assign_subscript( BPy_PropertyRNA * self, PyObject *key, P
} else if (PyLong_Check(key)) {
keynum = PyLong_AsSsize_t(key);
} else {
PyErr_SetString(PyExc_AttributeError, "invalid key, key must be a string or an int");
PyErr_SetString(PyExc_AttributeError, "PropertyRNA - invalid key, key must be a string or an int");
return -1;
}
if (RNA_property_type(&self->ptr, self->prop) == PROP_COLLECTION) {
PyErr_SetString(PyExc_AttributeError, "assignment is not supported for collections (yet)");
PyErr_SetString(PyExc_AttributeError, "PropertyRNA - assignment is not supported for collections (yet)");
ret = -1;
} else if (keyname) {
PyErr_SetString(PyExc_AttributeError, "string keys are only supported for collections");
PyErr_SetString(PyExc_AttributeError, "PropertyRNA - string keys are only supported for collections");
ret = -1;
} else {
int len = RNA_property_array_length(&self->ptr, self->prop);
if (len==0) { /* not an array*/
PyErr_Format(PyExc_AttributeError, "not an array or collection %d", keynum);
PyErr_Format(PyExc_AttributeError, "PropertyRNA - not an array or collection %d", keynum);
ret = -1;
}
if (keynum >= len){
PyErr_SetString(PyExc_AttributeError, "index out of range");
PyErr_SetString(PyExc_AttributeError, "PropertyRNA - index out of range");
ret = -1;
} else {
ret = pyrna_py_to_prop_index(&self->ptr, self->prop, keynum, value);
@@ -621,9 +628,11 @@ static PyObject *pyrna_struct_getattro( BPy_StructRNA * self, PyObject *pyname )
/* Include this incase this instance is a subtype of a python class
* In these instances we may want to return a function or variable provided by the subtype
* */
ret = PyObject_GenericGetAttr((PyObject *)self, pyname);
if (ret) return ret;
else PyErr_Clear();
if (BPy_StructRNA_CheckExact(self) == 0) {
ret = PyObject_GenericGetAttr((PyObject *)self, pyname);
if (ret) return ret;
else PyErr_Clear();
}
/* done with subtypes */
prop = RNA_struct_find_property(&self->ptr, name);
@@ -632,7 +641,7 @@ static PyObject *pyrna_struct_getattro( BPy_StructRNA * self, PyObject *pyname )
ret = pyrna_prop_to_py(&self->ptr, prop);
}
else {
PyErr_Format( PyExc_AttributeError, "Attribute \"%s\" not found", name);
PyErr_Format( PyExc_AttributeError, "StructRNA - Attribute \"%s\" not found", name);
ret = NULL;
}
@@ -646,12 +655,17 @@ static int pyrna_struct_setattro( BPy_StructRNA * self, PyObject *pyname, PyObje
PropertyRNA *prop = RNA_struct_find_property(&self->ptr, name);
if (prop==NULL) {
PyErr_Format( PyExc_AttributeError, "Attribute \"%s\" not found", name);
return -1;
if (!BPy_StructRNA_CheckExact(self) && PyObject_GenericSetAttr((PyObject *)self, pyname, value) >= 0) {
return 0;
}
else {
PyErr_Format( PyExc_AttributeError, "StructRNA - Attribute \"%s\" not found", name);
return -1;
}
}
if (!RNA_property_editable(&self->ptr, prop)) {
PyErr_Format( PyExc_AttributeError, "Attribute \"%s\" from \"%s\" is read-only", RNA_property_identifier(&self->ptr, prop), RNA_struct_identifier(&self->ptr) );
PyErr_Format( PyExc_AttributeError, "StructRNA - Attribute \"%s\" from \"%s\" is read-only", RNA_property_identifier(&self->ptr, prop), RNA_struct_identifier(&self->ptr) );
return -1;
}
@@ -1200,3 +1214,89 @@ PyObject *BPY_rna_doc( void )
return mod;
}
/* Orphan functions, not sure where they should go */
/* Function that sets RNA, NOTE - self is NULL when called from python, but being abused from C so we can pass the srna allong
* This isnt incorrect since its a python object - but be careful */
PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw)
{
static char *kwlist[] = {"attribute", "name", "description", "min", "max", "soft_min", "soft_max", "default", NULL};
char *id, *name="", *description="";
float min=FLT_MIN, max=FLT_MAX, soft_min=FLT_MIN, soft_max=FLT_MAX, def=0.0f;
if (!PyArg_ParseTupleAndKeywords(args, kw, "s|ssfffff:FloatProperty", kwlist, &id, &name, &description, &min, &max, &soft_min, &soft_max, &def))
return NULL;
if (PyTuple_Size(args) > 0) {
PyErr_SetString(PyExc_ValueError, "all args must be keywors"); // TODO - py3 can enforce this.
return NULL;
}
if (self) {
StructRNA *srna = PyCObject_AsVoidPtr(self);
RNA_def_float(srna, id, def, min, max, name, description, soft_min, soft_max);
Py_RETURN_NONE;
} else {
PyObject *ret = PyTuple_New(2);
PyTuple_SET_ITEM(ret, 0, PyCObject_FromVoidPtr((void *)BPy_FloatProperty, NULL));
PyTuple_SET_ITEM(ret, 1, kw);
Py_INCREF(kw);
return ret;
}
}
PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw)
{
static char *kwlist[] = {"attribute", "name", "description", "min", "max", "soft_min", "soft_max", "default", NULL};
char *id, *name="", *description="";
int min=INT_MIN, max=INT_MAX, soft_min=INT_MIN, soft_max=INT_MAX, def=0;
if (!PyArg_ParseTupleAndKeywords(args, kw, "s|ssiiiii:IntProperty", kwlist, &id, &name, &description, &min, &max, &soft_min, &soft_max, &def))
return NULL;
if (PyTuple_Size(args) > 0) {
PyErr_SetString(PyExc_ValueError, "all args must be keywors"); // TODO - py3 can enforce this.
return NULL;
}
if (self) {
StructRNA *srna = PyCObject_AsVoidPtr(self);
RNA_def_int(srna, id, def, min, max, name, description, soft_min, soft_max);
Py_RETURN_NONE;
} else {
PyObject *ret = PyTuple_New(2);
PyTuple_SET_ITEM(ret, 0, PyCObject_FromVoidPtr((void *)BPy_IntProperty, NULL));
PyTuple_SET_ITEM(ret, 1, kw);
Py_INCREF(kw);
return ret;
}
}
PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw)
{
static char *kwlist[] = {"attribute", "name", "description", "default", NULL};
char *id, *name="", *description="";
int def=0;
if (!PyArg_ParseTupleAndKeywords(args, kw, "s|ssi:IntProperty", kwlist, &id, &name, &description, &def))
return NULL;
if (PyTuple_Size(args) > 0) {
PyErr_SetString(PyExc_ValueError, "all args must be keywors"); // TODO - py3 can enforce this.
return NULL;
}
if (self) {
StructRNA *srna = PyCObject_AsVoidPtr(self);
RNA_def_boolean(srna, id, def, name, description);
Py_RETURN_NONE;
} else {
PyObject *ret = PyTuple_New(2);
PyTuple_SET_ITEM(ret, 0, PyCObject_FromVoidPtr((void *)BPy_IntProperty, NULL));
PyTuple_SET_ITEM(ret, 1, kw);
Py_INCREF(kw);
return ret;
}
}

View File

@@ -33,8 +33,10 @@
extern PyTypeObject pyrna_struct_Type;
extern PyTypeObject pyrna_prop_Type;
#define BPy_StructRNA_Check(v) (PyObject_TypeCheck(v, &pyrna_struct_Type))
#define BPy_PropertyRNA_Check(v) (PyObject_TypeCheck(v, &pyrna_prop_Type))
#define BPy_StructRNA_Check(v) (PyObject_TypeCheck(v, &pyrna_struct_Type))
#define BPy_StructRNA_CheckExact(v) (Py_TYPE(v) == &pyrna_struct_Type)
#define BPy_PropertyRNA_Check(v) (PyObject_TypeCheck(v, &pyrna_prop_Type))
#define BPy_PropertyRNA_CheckExact(v) (Py_TYPE(v) == &pyrna_prop_Type)
//XXX add propper accessor function, we know this is just after next/prev pointers
@@ -69,4 +71,11 @@ PyObject *pyrna_prop_CreatePyObject( PointerRNA *ptr, PropertyRNA *prop );
/* operators also need this to set args */
int pyrna_py_to_prop(PointerRNA *ptr, PropertyRNA *prop, PyObject *value);
PyObject * pyrna_prop_to_py(PointerRNA *ptr, PropertyRNA *prop);
/* functions for setting up new props - experemental */
PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw);
PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw);
PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw);
#endif