Revision: 19314 http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=19314 Author: campbellbarton Date: 2009-03-16 16:54:43 +0100 (Mon, 16 Mar 2009)
Log Message: ----------- 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. * ?\239?\187?\191Only 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 Modified Paths: -------------- branches/blender2.5/blender/source/blender/python/intern/bpy_interface.c branches/blender2.5/blender/source/blender/python/intern/bpy_operator.c branches/blender2.5/blender/source/blender/python/intern/bpy_opwrapper.c branches/blender2.5/blender/source/blender/python/intern/bpy_rna.c branches/blender2.5/blender/source/blender/python/intern/bpy_rna.h branches/blender2.5/blender/source/blender/windowmanager/intern/wm_event_system.c Modified: branches/blender2.5/blender/source/blender/python/intern/bpy_interface.c =================================================================== --- branches/blender2.5/blender/source/blender/python/intern/bpy_interface.c 2009-03-16 14:52:30 UTC (rev 19313) +++ branches/blender2.5/blender/source/blender/python/intern/bpy_interface.c 2009-03-16 15:54:43 UTC (rev 19314) @@ -57,6 +57,22 @@ 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; } Modified: branches/blender2.5/blender/source/blender/python/intern/bpy_operator.c =================================================================== --- branches/blender2.5/blender/source/blender/python/intern/bpy_operator.c 2009-03-16 14:52:30 UTC (rev 19313) +++ branches/blender2.5/blender/source/blender/python/intern/bpy_operator.c 2009-03-16 15:54:43 UTC (rev 19314) @@ -111,7 +111,7 @@ 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} }; Modified: branches/blender2.5/blender/source/blender/python/intern/bpy_opwrapper.c =================================================================== --- branches/blender2.5/blender/source/blender/python/intern/bpy_opwrapper.c 2009-03-16 14:52:30 UTC (rev 19313) +++ branches/blender2.5/blender/source/blender/python/intern/bpy_opwrapper.c 2009-03-16 15:54:43 UTC (rev 19314) @@ -45,39 +45,9 @@ 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 @@ {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 @@ * 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 @@ * 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; +} - Py_DECREF(args); /* also decref's kw */ +static int PYTHON_OT_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + return PYTHON_OT_generic(PYOP_INVOKE, C, op, event); +} - return ret_flag; +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; + if (PyObject_HasAttrString(py_class, "exec")) + ot->exec= PYTHON_OT_exec; + if (PyObject_HasAttrString(py_class, "poll")) + ot->poll= PYTHON_OT_poll; - ot->exec= PYTHON_OT_exec; - - ot->poll= ED_operator_screenactive; /* how should this work?? */ - /* ot->flag= OPTYPE_REGISTER; */ - 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)); + + 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(); } - 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)) { @@ Diff output truncated at 10240 characters. @@ _______________________________________________ Bf-blender-cvs mailing list Bf-blender-cvs@blender.org http://lists.blender.org/mailman/listinfo/bf-blender-cvs