diff options
author | Campbell Barton <ideasman42@gmail.com> | 2009-03-16 18:54:43 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2009-03-16 18:54:43 +0300 |
commit | 16fe92f86828d6ccec90ad4d8a1995afaf616964 (patch) | |
tree | 698094db706cd352a484ada3280f18771f9ef0fe /source | |
parent | 133e8827b7e9b76e61e405c6eeac88126a106b09 (diff) |
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
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/python/intern/bpy_interface.c | 16 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_operator.c | 2 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_opwrapper.c | 394 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_rna.c | 132 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_rna.h | 13 | ||||
-rw-r--r-- | source/blender/windowmanager/intern/wm_event_system.c | 7 |
6 files changed, 404 insertions, 160 deletions
diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index fc43d7e5537..54f7bb55cc0 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -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; } diff --git a/source/blender/python/intern/bpy_operator.c b/source/blender/python/intern/bpy_operator.c index 074a6ca499b..d2dbd6c91bb 100644 --- a/source/blender/python/intern/bpy_operator.c +++ b/source/blender/python/intern/bpy_operator.c @@ -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} }; diff --git a/source/blender/python/intern/bpy_opwrapper.c b/source/blender/python/intern/bpy_opwrapper.c index ca6104d087f..727dee01531 100644 --- a/source/blender/python/intern/bpy_opwrapper.c +++ b/source/blender/python/intern/bpy_opwrapper.c @@ -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) -{ - PyOperatorType *pyot = op->type->pyop_data; - PyObject *args= PyTuple_New(2); - PyObject *ret; - int ret_flag; - PyTuple_SET_ITEM(args, 0, pyop_dict_from_event(event)); - PyTuple_SET_ITEM(args, 1, pyop_kwargs_from_operator(op)); - ret = PyObject_Call(pyot->py_invoke, args, NULL); +#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; + 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; + + 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); + 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); } - + return ret_flag; +} + +static int PYTHON_OT_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + return PYTHON_OT_generic(PYOP_INVOKE, C, op, event); +} - Py_DECREF(args); /* also decref's kw */ +static int PYTHON_OT_exec(bContext *C, wmOperator *op) +{ + return PYTHON_OT_generic(PYOP_EXEC, C, op, NULL); +} - return ret_flag; +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; } + + diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index c9cd4414127..193f2a8d3d2 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -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; + } +} diff --git a/source/blender/python/intern/bpy_rna.h b/source/blender/python/intern/bpy_rna.h index 1d49d4d5485..904529b58de 100644 --- a/source/blender/python/intern/bpy_rna.h +++ b/source/blender/python/intern/bpy_rna.h @@ -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 diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 2b08f5a8943..772b308fb62 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -469,7 +469,12 @@ int WM_operator_call_py(bContext *C, wmOperatorType *ot, PointerRNA *properties, { wmWindowManager *wm= CTX_wm_manager(C); wmOperator *op= wm_operator_create(wm, ot, properties, reports); - int retval= op->type->exec(C, op); + int retval= OPERATOR_CANCELLED; + + if (op->type->exec) + retval= op->type->exec(C, op); + else + printf("error \"%s\" operator has no exec function, python cannot call it\n", op->type->name); if (reports) op->reports= NULL; /* dont let the operator free reports passed to this function */ |