[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [24151] trunk/blender: Python can now run operators with their own context (data context).

Campbell Barton ideasman42 at gmail.com
Thu Oct 29 10:25:11 CET 2009


Revision: 24151
          http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=24151
Author:   campbellbarton
Date:     2009-10-29 10:25:11 +0100 (Thu, 29 Oct 2009)

Log Message:
-----------
Python can now run operators with their own context (data context).
The aim of this is to avoid having to set the selection each time before running an operator from python.

At the moment this is set as a python dictionary with string keys and rna values... eg.

C = {}
C["active_object"] = bpy.data.objects['SomeOb']
bpy.ops.object.game_property_new(C)

# ofcourse this works too..
bpy.ops.object.game_property_new({"active_object":ob})

# or...
C = {"main":bpy.data, "scene":bpy.data.scenes[0], "active_object":bpy.data.objects['SomeOb'], "selected_editable_objects":list(bpy.data.objects)}
bpy.ops.object.location_apply(C)

Modified Paths:
--------------
    trunk/blender/release/scripts/modules/bpy_ops.py
    trunk/blender/source/blender/blenkernel/BKE_context.h
    trunk/blender/source/blender/blenkernel/intern/context.c
    trunk/blender/source/blender/makesrna/intern/rna_curve.c
    trunk/blender/source/blender/python/intern/bpy_interface.c
    trunk/blender/source/blender/python/intern/bpy_operator.c

Modified: trunk/blender/release/scripts/modules/bpy_ops.py
===================================================================
--- trunk/blender/release/scripts/modules/bpy_ops.py	2009-10-29 09:14:20 UTC (rev 24150)
+++ trunk/blender/release/scripts/modules/bpy_ops.py	2009-10-29 09:25:11 UTC (rev 24151)
@@ -113,19 +113,36 @@
 	def __call__(self, *args, **kw):
 		
 		# Get the operator from blender
-		if len(args) > 1:
-			raise ValueError("only one argument for the execution context is supported ")
+		if len(args) > 2:
+			raise ValueError("only 1 or 2 arguments for the execution context is supported")
 		
+		C_dict = None
+		
 		if args:
+			
+			C_exec = 'EXEC_DEFAULT'
+			
+			if len(args) == 2:
+				C_exec = args[0]
+				C_dict = args[1]
+			else:
+				if type(args[0]) != str:
+					C_dict= args[0]
+				else:
+					C_exec= args[0]
+			
 			try:
-				context = context_dict[args[0]]
+				context = context_dict[C_exec]
 			except:
 				raise ValueError("Expected a single context argument in: " + str(list(context_dict.keys())))
 			
-			return op_call(self.idname(), kw, context)
+			if len(args) == 2:
+				C_dict= args[1]
+			
+			return op_call(self.idname() , C_dict, kw, context)
 		
 		else:
-			return op_call(self.idname(), kw)
+			return op_call(self.idname(), C_dict, kw)
 	
 	def get_rna(self):
 		'''

Modified: trunk/blender/source/blender/blenkernel/BKE_context.h
===================================================================
--- trunk/blender/source/blender/blenkernel/BKE_context.h	2009-10-29 09:14:20 UTC (rev 24150)
+++ trunk/blender/source/blender/blenkernel/BKE_context.h	2009-10-29 09:25:11 UTC (rev 24151)
@@ -124,6 +124,9 @@
 int CTX_py_init_get(bContext *C);
 void CTX_py_init_set(bContext *C, int value);
 
+void *CTX_py_dict_get(bContext *C);
+void CTX_py_dict_set(bContext *C, void *value);
+
 /* Window Manager Context */
 
 struct wmWindowManager *CTX_wm_manager(const bContext *C);

Modified: trunk/blender/source/blender/blenkernel/intern/context.c
===================================================================
--- trunk/blender/source/blender/blenkernel/intern/context.c	2009-10-29 09:14:20 UTC (rev 24150)
+++ trunk/blender/source/blender/blenkernel/intern/context.c	2009-10-29 09:25:11 UTC (rev 24151)
@@ -71,6 +71,7 @@
 
 		int recursion;
 		int py_init; /* true if python is initialized */
+		void *py_context;
 	} data;
 	
 	/* data evaluation */
@@ -175,6 +176,15 @@
 	C->data.py_init= value;
 }
 
+void *CTX_py_dict_get(bContext *C)
+{
+	return C->data.py_context;
+}
+void CTX_py_dict_set(bContext *C, void *value)
+{
+	C->data.py_context= value;
+}
+
 /* window manager context */
 
 wmWindowManager *CTX_wm_manager(const bContext *C)
@@ -401,6 +411,10 @@
 
 	memset(result, 0, sizeof(bContextDataResult));
 
+	if(CTX_py_dict_get(C)) {
+		return bpy_context_get(C, member, result);
+	}
+
 	/* we check recursion to ensure that we do not get infinite
 	 * loops requesting data from ourselfs in a context callback */
 	if(!done && recursion < 1 && C->wm.store) {

Modified: trunk/blender/source/blender/makesrna/intern/rna_curve.c
===================================================================
--- trunk/blender/source/blender/makesrna/intern/rna_curve.c	2009-10-29 09:14:20 UTC (rev 24150)
+++ trunk/blender/source/blender/makesrna/intern/rna_curve.c	2009-10-29 09:25:11 UTC (rev 24151)
@@ -757,12 +757,12 @@
 	
 	prop= RNA_def_property(srna, "render_resolution_u", PROP_INT, PROP_NONE);
 	RNA_def_property_int_sdna(prop, NULL, "resolu_ren");
-	RNA_def_property_ui_range(prop, 1, 1024, 1, 0);
+	RNA_def_property_ui_range(prop, 0, 1024, 1, 0);
 	RNA_def_property_ui_text(prop, "Render Resolution U", "Surface resolution in U direction used while rendering. Zero skips this property.");
 	
 	prop= RNA_def_property(srna, "render_resolution_v", PROP_INT, PROP_NONE);
 	RNA_def_property_int_sdna(prop, NULL, "resolv_ren");
-	RNA_def_property_ui_range(prop, 1, 1024, 1, 0);
+	RNA_def_property_ui_range(prop, 0, 1024, 1, 0);
 	RNA_def_property_ui_text(prop, "Render Resolution V", "Surface resolution in V direction used while rendering. Zero skips this property.");
 	
 	

Modified: trunk/blender/source/blender/python/intern/bpy_interface.c
===================================================================
--- trunk/blender/source/blender/python/intern/bpy_interface.c	2009-10-29 09:14:20 UTC (rev 24150)
+++ trunk/blender/source/blender/python/intern/bpy_interface.c	2009-10-29 09:25:11 UTC (rev 24151)
@@ -63,6 +63,7 @@
 #include "BKE_context.h"
 #include "BKE_fcurve.h"
 #include "BKE_text.h"
+#include "BKE_context.h"
 
 #include "BPY_extern.h"
 
@@ -948,3 +949,57 @@
 	return error_ret;
 }
 
+
+
+int bpy_context_get(bContext *C, const char *member, bContextDataResult *result)
+{
+	PyObject *pyctx= (PyObject *)CTX_py_dict_get(C);
+	PyObject *item= PyDict_GetItemString(pyctx, member);
+	PointerRNA *ptr= NULL;
+	int done= 0;
+
+	if(item==NULL) {
+		/* pass */
+	}
+	else if(item==Py_None) {
+		/* pass */
+	}
+	else if(BPy_StructRNA_Check(item)) {
+		ptr= &(((BPy_StructRNA *)item)->ptr);
+
+		//result->ptr= ((BPy_StructRNA *)item)->ptr;
+		CTX_data_pointer_set(result, ptr->id.data, ptr->type, ptr->data);
+		done= 1;
+	}
+	else if (PyList_Check(item)) {
+		int len= PyList_Size(item);
+		int i;
+		for(i = 0; i < len; i++) {
+			PyObject *list_item = PyList_GET_ITEM(item, i); // XXX check type
+
+			if(BPy_StructRNA_Check(list_item)) {
+				/*
+				CollectionPointerLink *link= MEM_callocN(sizeof(CollectionPointerLink), "bpy_context_get");
+				link->ptr= ((BPy_StructRNA *)item)->ptr;
+				BLI_addtail(&result->list, link);
+				*/
+				ptr= &(((BPy_StructRNA *)list_item)->ptr);
+				CTX_data_list_add(result, ptr->id.data, ptr->type, ptr->data);
+			}
+			else {
+				printf("List item not a valid type\n");
+			}
+
+		}
+
+		done= 1;
+	}
+
+	if(done==0) {
+		if (item)	printf("Context '%s' not found\n", member);
+		else		printf("Context '%s' not a valid type\n", member);
+	}
+
+	return done;
+}
+

Modified: trunk/blender/source/blender/python/intern/bpy_operator.c
===================================================================
--- trunk/blender/source/blender/python/intern/bpy_operator.c	2009-10-29 09:14:20 UTC (rev 24150)
+++ trunk/blender/source/blender/python/intern/bpy_operator.c	2009-10-29 09:25:11 UTC (rev 24151)
@@ -48,6 +48,8 @@
 	
 	char		*opname;
 	PyObject	*kw= NULL; /* optional args */
+	PyObject	*context_dict= NULL; /* optional args */
+	PyObject	*context_dict_back;
 
 	/* note that context is an int, python does the conversion in this case */
 	int context= WM_OP_EXEC_DEFAULT;
@@ -55,7 +57,7 @@
 	// XXX Todo, work out a better solution for passing on context, could make a tuple from self and pack the name and Context into it...
 	bContext *C = BPy_GetContext();
 	
-	if (!PyArg_ParseTuple(args, "s|O!i:bpy.__ops__.call", &opname, &PyDict_Type, &kw, &context))
+	if (!PyArg_ParseTuple(args, "sO|O!i:bpy.__ops__.call", &opname, &context_dict, &PyDict_Type, &kw, &context))
 		return NULL;
 
 	ot= WM_operatortype_find(opname, TRUE);
@@ -65,62 +67,75 @@
 		return NULL;
 	}
 	
+	if(!PyDict_Check(context_dict))
+		context_dict= NULL;
+
+	context_dict_back= CTX_py_dict_get(C);
+
+	CTX_py_dict_set(C, (void *)context_dict);
+	Py_XINCREF(context_dict); /* so we done loose it */
+
 	if(WM_operator_poll((bContext*)C, ot) == FALSE) {
 		PyErr_SetString( PyExc_SystemError, "bpy.__ops__.call: operator poll() function failed, context is incorrect");
-		return NULL;
+		error_val= -1;
 	}
+	else {
+		/* WM_operator_properties_create(&ptr, opname); */
+		/* Save another lookup */
+		RNA_pointer_create(NULL, ot->srna, NULL, &ptr);
 
-	/* WM_operator_properties_create(&ptr, opname); */
-	/* Save another lookup */
-	RNA_pointer_create(NULL, ot->srna, NULL, &ptr);
-	
-	if(kw && PyDict_Size(kw))
-		error_val= pyrna_pydict_to_props(&ptr, kw, 0, "Converting py args to operator properties: ");
+		if(kw && PyDict_Size(kw))
+			error_val= pyrna_pydict_to_props(&ptr, kw, 0, "Converting py args to operator properties: ");
 
-	
-	if (error_val==0) {
-		ReportList *reports;
 
-		reports= MEM_mallocN(sizeof(ReportList), "wmOperatorReportList");
-		BKE_reports_init(reports, RPT_STORE);
+		if (error_val==0) {
+			ReportList *reports;
 
-		WM_operator_call_py(C, ot, context, &ptr, reports);
+			reports= MEM_mallocN(sizeof(ReportList), "wmOperatorReportList");
+			BKE_reports_init(reports, RPT_STORE);
 
-		if(BPy_reports_to_error(reports))
-			error_val = -1;
+			WM_operator_call_py(C, ot, context, &ptr, reports);
 
-		/* operator output is nice to have in the terminal/console too */
-		if(reports->list.first) {
-			char *report_str= BKE_reports_string(reports, 0); /* all reports */
+			if(BPy_reports_to_error(reports))
+				error_val = -1;
 
-			if(report_str) {
-				PySys_WriteStdout("%s\n", report_str);
-				MEM_freeN(report_str);
+			/* operator output is nice to have in the terminal/console too */
+			if(reports->list.first) {
+				char *report_str= BKE_reports_string(reports, 0); /* all reports */
+	
+				if(report_str) {
+					PySys_WriteStdout("%s\n", report_str);
+					MEM_freeN(report_str);
+				}
 			}
+	
+			BKE_reports_clear(reports);
+			if ((reports->flag & RPT_FREE) == 0)
+			{
+				MEM_freeN(reports);
+			}
 		}
 
-		BKE_reports_clear(reports);
-		if ((reports->flag & RPT_FREE) == 0)
+		WM_operator_properties_free(&ptr);
+
+#if 0
+		/* if there is some way to know an operator takes args we should use this */
 		{
-			MEM_freeN(reports);
+			/* no props */
+			if (kw != NULL) {
+				PyErr_Format(PyExc_AttributeError, "Operator \"%s\" does not take any args", opname);
+				return NULL;
+			}
+
+			WM_operator_name_call(C, opname, WM_OP_EXEC_DEFAULT, NULL);
 		}
+#endif
 	}
 
-	WM_operator_properties_free(&ptr);
+	/* restore with original context dict, probably NULL but need this for nested operator calls */
+	Py_XDECREF(context_dict);
+	CTX_py_dict_set(C, (void *)context_dict_back);
 
-#if 0
-	/* if there is some way to know an operator takes args we should use this */
-	{
-		/* no props */
-		if (kw != NULL) {
-			PyErr_Format(PyExc_AttributeError, "Operator \"%s\" does not take any args", opname);
-			return NULL;
-		}
-
-		WM_operator_name_call(C, opname, WM_OP_EXEC_DEFAULT, NULL);
-	}
-#endif
-
 	if (error_val==-1) {
 		return NULL;
 	}





More information about the Bf-blender-cvs mailing list