Python can now run operators with their own context (data context).
authorCampbell Barton <ideasman42@gmail.com>
Thu, 29 Oct 2009 09:25:11 +0000 (09:25 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Thu, 29 Oct 2009 09:25:11 +0000 (09:25 +0000)
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)

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

index 542f9aabc7e1e6e35e0f56a949a1f3bd7c8bd8b0..fbb4a8b537d51329da85ce746cf95cb1d35518b7 100644 (file)
@@ -113,19 +113,36 @@ class bpy_ops_submodule_op(object):
        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):
                '''
index 09e13c2930e4fc7ba7edaf4a5cd5144a81bf395a..feba39ee11db2de1ef327d6f8c4c0a336a701c8d 100644 (file)
@@ -124,6 +124,9 @@ void CTX_store_free_list(ListBase *contexts);
 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);
index d5cc31d918ace6c0a9ad9698b366f48d0331705e..7f2872c0797fc5b8294893687c897f0fdc2d0db6 100644 (file)
@@ -71,6 +71,7 @@ struct bContext {
 
                int recursion;
                int py_init; /* true if python is initialized */
+               void *py_context;
        } data;
        
        /* data evaluation */
@@ -175,6 +176,15 @@ void CTX_py_init_set(bContext *C, int value)
        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 @@ static int ctx_data_get(bContext *C, const char *member, bContextDataResult *res
 
        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) {
index 1f1caeaa9b56532059c7dbdb72dce18fa90df7fb..995be6d5023bbf57d75ad35f31574803642ff60c 100644 (file)
@@ -757,12 +757,12 @@ static void rna_def_curve(BlenderRNA *brna)
        
        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.");
        
        
index 53c0d591437ed1c61b63a45946d452d79a0f2517..dc7f6947f38025448aafcef19f0339cb856d0e8c 100644 (file)
@@ -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 @@ int BPY_button_eval(bContext *C, char *expr, double *value)
        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;
+}
+
index 87752ca9c58f09be137ba0c9193fde1b3b6727fb..1ae0a40ce16fe0eef8514a40ec8f4ad6ccd43e4e 100644 (file)
@@ -48,6 +48,8 @@ static PyObject *pyop_call( PyObject * self, PyObject * args)
        
        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 @@ static PyObject *pyop_call( PyObject * self, PyObject * args)
        // 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,61 +67,74 @@ static PyObject *pyop_call( PyObject * self, PyObject * args)
                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)
-               {
-                       MEM_freeN(reports);
-               }
-       }
-
-       WM_operator_properties_free(&ptr);
+               WM_operator_properties_free(&ptr);
 
 #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;
-               }
+               /* 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);
-       }
+                       WM_operator_name_call(C, opname, WM_OP_EXEC_DEFAULT, NULL);
+               }
 #endif
+       }
+
+       /* 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 (error_val==-1) {
                return NULL;