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)
This commit is contained in:
Campbell Barton 2009-10-29 09:25:11 +00:00
parent 40731af9d0
commit c508e6198a
6 changed files with 154 additions and 50 deletions

@ -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):
'''

@ -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);

@ -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) {

@ -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.");

@ -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;
}

@ -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 (error_val==0) {
ReportList *reports;
reports= MEM_mallocN(sizeof(ReportList), "wmOperatorReportList");
BKE_reports_init(reports, RPT_STORE);
WM_operator_call_py(C, ot, context, &ptr, reports);
if(BPy_reports_to_error(reports))
error_val = -1;
/* 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(kw && PyDict_Size(kw))
error_val= pyrna_pydict_to_props(&ptr, kw, 0, "Converting py args to operator properties: ");
if(report_str) {
PySys_WriteStdout("%s\n", report_str);
MEM_freeN(report_str);
}
}
if (error_val==0) {
ReportList *reports;
reports= MEM_mallocN(sizeof(ReportList), "wmOperatorReportList");
BKE_reports_init(reports, RPT_STORE);
WM_operator_call_py(C, ot, context, &ptr, reports);
if(BPy_reports_to_error(reports))
error_val = -1;
/* 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;