[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [37260] trunk/blender/source/blender: Support for update callbacks in python defined RNA properties as discussed last meeting .

Campbell Barton ideasman42 at gmail.com
Mon Jun 6 19:50:20 CEST 2011


Revision: 37260
          http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-blender&revision=37260
Author:   campbellbarton
Date:     2011-06-06 17:50:20 +0000 (Mon, 06 Jun 2011)
Log Message:
-----------
Support for update callbacks in python defined RNA properties as discussed last meeting.
This means script authors can perform actions using these callbacks rather then on drawing which puts blender in a readonly state.

Simple example:

import bpy
def up_func(self, context):
    print("test")

bpy.types.Scene.testprop = bpy.props.FloatProperty(update=up_func)
bpy.context.scene.testprop = 11

# prints -> test

Modified Paths:
--------------
    trunk/blender/source/blender/makesrna/RNA_access.h
    trunk/blender/source/blender/makesrna/RNA_define.h
    trunk/blender/source/blender/makesrna/RNA_types.h
    trunk/blender/source/blender/makesrna/intern/rna_access.c
    trunk/blender/source/blender/makesrna/intern/rna_define.c
    trunk/blender/source/blender/makesrna/intern/rna_internal_types.h
    trunk/blender/source/blender/python/intern/bpy_props.c

Modified: trunk/blender/source/blender/makesrna/RNA_access.h
===================================================================
--- trunk/blender/source/blender/makesrna/RNA_access.h	2011-06-06 17:48:29 UTC (rev 37259)
+++ trunk/blender/source/blender/makesrna/RNA_access.h	2011-06-06 17:50:20 UTC (rev 37260)
@@ -650,6 +650,7 @@
 PropertySubType RNA_property_subtype(PropertyRNA *prop);
 PropertyUnit RNA_property_unit(PropertyRNA *prop);
 int RNA_property_flag(PropertyRNA *prop);
+void *RNA_property_py_data_get(PropertyRNA *prop);
 
 int RNA_property_array_length(PointerRNA *ptr, PropertyRNA *prop);
 int RNA_property_array_check(PointerRNA *ptr, PropertyRNA *prop);

Modified: trunk/blender/source/blender/makesrna/RNA_define.h
===================================================================
--- trunk/blender/source/blender/makesrna/RNA_define.h	2011-06-06 17:48:29 UTC (rev 37259)
+++ trunk/blender/source/blender/makesrna/RNA_define.h	2011-06-06 17:50:20 UTC (rev 37260)
@@ -163,6 +163,8 @@
 void RNA_def_property_editable_func(PropertyRNA *prop, const char *editable);
 void RNA_def_property_editable_array_func(PropertyRNA *prop, const char *editable);
 
+void RNA_def_property_update_runtime(PropertyRNA *prop, void *func);
+
 void RNA_def_property_dynamic_array_funcs(PropertyRNA *prop, const char *getlength);
 void RNA_def_property_boolean_funcs(PropertyRNA *prop, const char *get, const char *set);
 void RNA_def_property_int_funcs(PropertyRNA *prop, const char *get, const char *set, const char *range);
@@ -172,6 +174,7 @@
 void RNA_def_property_pointer_funcs(PropertyRNA *prop, const char *get, const char *set, const char *typef, const char *poll);
 void RNA_def_property_collection_funcs(PropertyRNA *prop, const char *begin, const char *next, const char *end, const char *get, const char *length, const char *lookupint, const char *lookupstring);
 void RNA_def_property_srna(PropertyRNA *prop, const char *type);
+void RNA_def_py_data(PropertyRNA *prop, void *py_data);
 
 /* Function */
 

Modified: trunk/blender/source/blender/makesrna/RNA_types.h
===================================================================
--- trunk/blender/source/blender/makesrna/RNA_types.h	2011-06-06 17:48:29 UTC (rev 37259)
+++ trunk/blender/source/blender/makesrna/RNA_types.h	2011-06-06 17:50:20 UTC (rev 37260)
@@ -190,6 +190,7 @@
 
 	/* need context for update function */
 	PROP_CONTEXT_UPDATE = 1<<22,
+	PROP_CONTEXT_PROPERTY_UPDATE = (1<<22)|(1<<27),
 
 	/* Use for arrays or for any data that should not have a referene kept
 	 * most common case is functions that return arrays where the array */

Modified: trunk/blender/source/blender/makesrna/intern/rna_access.c
===================================================================
--- trunk/blender/source/blender/makesrna/intern/rna_access.c	2011-06-06 17:48:29 UTC (rev 37259)
+++ trunk/blender/source/blender/makesrna/intern/rna_access.c	2011-06-06 17:50:20 UTC (rev 37260)
@@ -755,6 +755,11 @@
 	return rna_ensure_property(prop)->flag;
 }
 
+void *RNA_property_py_data_get(PropertyRNA *prop)
+{
+	return prop->py_data;
+}
+
 int RNA_property_array_length(PointerRNA *ptr, PropertyRNA *prop)
 {
 	return rna_ensure_property_array_length(ptr, prop);
@@ -1344,7 +1349,14 @@
 			/* ideally no context would be needed for update, but there's some
 			   parts of the code that need it still, so we have this exception */
 			if(prop->flag & PROP_CONTEXT_UPDATE) {
-				if(C) ((ContextUpdateFunc)prop->update)(C, ptr);
+				if(C) {
+					if(prop->flag & PROP_CONTEXT_PROPERTY_UPDATE) {
+						((ContextPropUpdateFunc)prop->update)(C, ptr, prop);
+					}
+					else {
+						((ContextUpdateFunc)prop->update)(C, ptr);
+					}
+				}
 			}
 			else
 				prop->update(bmain, scene, ptr);

Modified: trunk/blender/source/blender/makesrna/intern/rna_define.c
===================================================================
--- trunk/blender/source/blender/makesrna/intern/rna_define.c	2011-06-06 17:48:29 UTC (rev 37259)
+++ trunk/blender/source/blender/makesrna/intern/rna_define.c	2011-06-06 17:50:20 UTC (rev 37260)
@@ -1839,6 +1839,11 @@
 	prop->update= (UpdateFunc)func;
 }
 
+void RNA_def_property_update_runtime(PropertyRNA *prop, void *func)
+{
+	prop->update= func;
+}
+
 void RNA_def_property_dynamic_array_funcs(PropertyRNA *prop, const char *getlength)
 {
 	if(!DefRNA.preprocess) {
@@ -2057,6 +2062,11 @@
 	prop->srna= (StructRNA*)type;
 }
 
+void RNA_def_py_data(PropertyRNA *prop, void *py_data)
+{
+	prop->py_data= py_data;
+}
+
 /* Compact definitions */
 
 PropertyRNA *RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, int default_value, const char *ui_name, const char *ui_description)
@@ -2822,6 +2832,7 @@
 		if(prop->identifier) MEM_freeN((void*)prop->identifier);
 		if(prop->name) MEM_freeN((void*)prop->name);
 		if(prop->description) MEM_freeN((void*)prop->description);
+		if(prop->py_data) MEM_freeN(prop->py_data);
 
 		switch(prop->type) {
 			case PROP_BOOLEAN: {

Modified: trunk/blender/source/blender/makesrna/intern/rna_internal_types.h
===================================================================
--- trunk/blender/source/blender/makesrna/intern/rna_internal_types.h	2011-06-06 17:48:29 UTC (rev 37259)
+++ trunk/blender/source/blender/makesrna/intern/rna_internal_types.h	2011-06-06 17:50:20 UTC (rev 37260)
@@ -62,6 +62,7 @@
 /* Function Callbacks */
 
 typedef void (*UpdateFunc)(struct Main *main, struct Scene *scene, struct PointerRNA *ptr);
+typedef void (*ContextPropUpdateFunc)(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop);
 typedef void (*ContextUpdateFunc)(struct bContext *C, struct PointerRNA *ptr);
 typedef int (*EditableFunc)(struct PointerRNA *ptr);
 typedef int (*ItemEditableFunc)(struct PointerRNA *ptr, int index);
@@ -177,6 +178,9 @@
 	 * any property can have this but should only be used for collections and arrays
 	 * since python will convert int/bool/pointer's */
 	struct StructRNA *srna;	/* attributes attached directly to this collection */
+
+	/* python handle to hold all callbacks in a tuple */
+	void *py_data;
 };
 
 /* Property Types */

Modified: trunk/blender/source/blender/python/intern/bpy_props.c
===================================================================
--- trunk/blender/source/blender/python/intern/bpy_props.c	2011-06-06 17:48:29 UTC (rev 37259)
+++ trunk/blender/source/blender/python/intern/bpy_props.c	2011-06-06 17:50:20 UTC (rev 37260)
@@ -47,6 +47,11 @@
 
 #include "../generic/py_capi_utils.h"
 
+/* initial definition of callback slots we'll probably have more then 1 */
+#define BPY_DATA_CB_SLOT_SIZE 1
+
+#define BPY_DATA_CB_SLOT_UPDATE 0
+
 extern BPy_StructRNA *bpy_context_module;
 
 static EnumPropertyItem property_flag_items[]= {
@@ -110,7 +115,46 @@
 static PyObject *pymeth_CollectionProperty= NULL;
 static PyObject *pymeth_RemoveProperty= NULL;
 
+PyObject *pyrna_struct_as_instance(PointerRNA *ptr)
+{
+	PyObject *self= NULL;
+	/* first get self */
+	/* operators can store their own instance for later use */
+	if(ptr->data) {
+		void **instance= RNA_struct_instance(ptr);
 
+		if(instance) {
+			if(*instance) {
+				self= *instance;
+				Py_INCREF(self);
+			}
+		}
+	}
+
+	/* in most cases this will run */
+	if(self == NULL) {
+		self= pyrna_struct_CreatePyObject(ptr);
+	}
+
+	return self;
+}
+
+/* could be moved into bpy_utils */
+static void printf_func_error(PyObject *py_func)
+{
+	/* since we return to C code we can't leave the error */
+	PyCodeObject *f_code= (PyCodeObject *)PyFunction_GET_CODE(py_func);
+	PyErr_Print();
+	PyErr_Clear();
+
+	/* use py style error */
+	fprintf(stderr, "File \"%s\", line %d, in %s\n",
+			_PyUnicode_AsString(f_code->co_filename),
+			f_code->co_firstlineno,
+			_PyUnicode_AsString(((PyFunctionObject *)py_func)->func_name)
+			);
+}
+
 /* operators and classes use this so it can store the args given but defer
  * running it until the operator runs where these values are used to setup
  * the default args for that operator instance */
@@ -130,6 +174,87 @@
 	return ret;
 }
 
+/* callbacks */
+void bpy_prop_update_cb(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop)
+{
+	PyGILState_STATE gilstate;
+	PyObject **py_data= (PyObject **)RNA_property_py_data_get(prop);
+	PyObject *py_func;
+	PyObject *args;
+	PyObject *self;
+	PyObject *ret;
+
+	BLI_assert(py_data != NULL);
+
+	bpy_context_set(C, &gilstate);
+
+	py_func= py_data[BPY_DATA_CB_SLOT_UPDATE];
+
+	args= PyTuple_New(2);
+	self= pyrna_struct_as_instance(ptr);
+	PyTuple_SET_ITEM(args, 0, self);
+
+	PyTuple_SET_ITEM(args, 1, (PyObject *)bpy_context_module);
+	Py_INCREF(bpy_context_module);
+
+	ret= PyObject_CallObject(py_func, args);
+
+	Py_DECREF(args);
+
+	if(ret == NULL) {
+		printf_func_error(py_func);
+	}
+	else {
+		if(ret != Py_None) {
+			PyErr_SetString(PyExc_ValueError, "the return value must be None");
+			printf_func_error(py_func);
+		}
+
+		Py_DECREF(ret);
+	}
+
+	bpy_context_clear(C, &gilstate);
+}
+
+static int bpy_prop_callback_check(PyObject *py_func, int argcount)
+{
+	if(py_func) {
+		if(!PyFunction_Check(py_func)) {
+			PyErr_Format(PyExc_TypeError,
+			             "update keyword: expected a function type, not a %.200s",
+			             Py_TYPE(py_func)->tp_name);
+			return -1;
+		}
+		else {
+			PyCodeObject *f_code= (PyCodeObject *)PyFunction_GET_CODE(py_func);
+			if (f_code->co_argcount != argcount) {
+				PyErr_Format(PyExc_TypeError,
+				             "update keyword: expected a function taking %d arguments, not %d",
+				             argcount, f_code->co_argcount);
+				return -1;
+			}
+		}
+	}
+
+	return 0;
+}
+
+
+static int bpy_prop_callback_assign(struct PropertyRNA *prop, PyObject *update_cb)
+{
+	/* assume this is already checked for type and arg length */
+	if(update_cb) {
+		PyObject **py_data= MEM_callocN(sizeof(PyObject *) * BPY_DATA_CB_SLOT_SIZE, "bpy_prop_callback_assign");
+		RNA_def_property_update_runtime(prop, (void *)bpy_prop_update_cb);
+		py_data[BPY_DATA_CB_SLOT_UPDATE]= update_cb;
+		RNA_def_py_data(prop, py_data);
+
+		RNA_def_property_flag(prop, PROP_CONTEXT_PROPERTY_UPDATE);
+	}
+
+	return 0;
+}
+
 /* this define runs at the start of each function and deals with 
  * returning a deferred property (to be registered later) */
 #define BPY_PROPDEF_HEAD(_func)	\
@@ -184,6 +309,11 @@
 "   :type description: string\n" \
 
 
+#define BPY_PROPDEF_UPDATE_DOC \
+"   :arg update: function to be called when this value is modified,\n" \

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list