[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [19314] branches/blender2.5/blender/source /blender: 2.5 PyAPI

Campbell Barton ideasman42 at gmail.com
Mon Mar 16 16:54:43 CET 2009


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. @@




More information about the Bf-blender-cvs mailing list