diff options
author | Campbell Barton <ideasman42@gmail.com> | 2008-12-28 11:41:49 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2008-12-28 11:41:49 +0300 |
commit | 625f47755458a1b4b3ad9743aa14ae24700e00b6 (patch) | |
tree | bff2af4e39d919e24943795844556442b0c0d3dd | |
parent | 61b3c4fefc461c2f492e0bf544a0cd7bf2b6e5b3 (diff) |
PyOperator invoke function now receives the wmEvent and default properties as 2 python dictionary args.
the Python invoke function can then edit the properties based on the event, once its finished the properties are copied back to the operator.
python exec and invoke functions can now return RUNNING_MODAL, CANCELLED, FINISHED, PASS_THROUGH flags
Still need to look into how python operators can make use of invoke/exec for a practical case. (Need to bring back the popup menu's)
-rw-r--r-- | source/blender/python/intern/bpy_operator.c | 139 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_operator.h | 3 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_opwrapper.c | 165 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_util.c | 121 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_util.h | 38 |
5 files changed, 383 insertions, 83 deletions
diff --git a/source/blender/python/intern/bpy_operator.c b/source/blender/python/intern/bpy_operator.c index b62693828a6..7d3a253980d 100644 --- a/source/blender/python/intern/bpy_operator.c +++ b/source/blender/python/intern/bpy_operator.c @@ -39,22 +39,75 @@ extern ListBase global_ops; /* evil, temp use */ -/* floats bigger then this are displayed as inf in the docstrings */ -#define MAXFLOAT_DOC 10000000 -#if 0 -void PyObSpit(char *name, PyObject *var) { - fprintf(stderr, "<%s> : ", name); - if (var==NULL) { - fprintf(stderr, "<NIL>"); + +/* This function is only used by operators right now + * Its used for taking keyword args and filling in property values */ +int PYOP_props_from_dict(PointerRNA *ptr, PyObject *kw) +{ + int error_val = 0; + int totkw; + const char *arg_name= NULL; + PyObject *item; + + PropertyRNA *prop, *iterprop; + CollectionPropertyIterator iter; + + iterprop= RNA_struct_iterator_property(ptr); + RNA_property_collection_begin(ptr, iterprop, &iter); + + totkw = kw ? PyDict_Size(kw):0; + + 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; + + if (kw==NULL) { + PyErr_Format( PyExc_AttributeError, "no args, expected \"%s\"", arg_name ? arg_name : "<UNKNOWN>"); + error_val= -1; + break; + } + + item= PyDict_GetItemString(kw, arg_name); + + if (item == NULL) { + PyErr_Format( PyExc_AttributeError, "argument \"%s\" missing", arg_name ? arg_name : "<UNKNOWN>"); + error_val = -1; /* pyrna_py_to_prop sets the error */ + break; + } + + if (pyrna_py_to_prop(ptr, prop, item)) { + error_val= -1; + break; + } + + totkw--; } - else { - PyObject_Print(var, stderr, 0); + + RNA_property_collection_end(&iter); + + if (error_val==0 && totkw > 0) { /* some keywords were given that were not used :/ */ + PyObject *key, *value; + Py_ssize_t pos = 0; + + while (PyDict_Next(kw, &pos, &key, &value)) { + arg_name= _PyUnicode_AsString(key); + if (RNA_struct_find_property(ptr, arg_name) == NULL) break; + arg_name= NULL; + } + + PyErr_Format( PyExc_AttributeError, "argument \"%s\" unrecognized", arg_name ? arg_name : "<UNKNOWN>"); + error_val = -1; } - fprintf(stderr, "\n"); + + return error_val; } -#endif +/* floats bigger then this are displayed as inf in the docstrings */ +#define MAXFLOAT_DOC 10000000 static int pyop_func_compare( BPy_OperatorFunc * a, BPy_OperatorFunc * b ) { @@ -120,14 +173,8 @@ static PyObject * pyop_func_call(BPy_OperatorFunc * self, PyObject *args, PyObje wmOperatorType *ot; int error_val = 0; - int totkw; - const char *arg_name= NULL; - PyObject *item; - PointerRNA ptr; - PropertyRNA *prop, *iterprop; - CollectionPropertyIterator iter; - + if (PyTuple_Size(args)) { PyErr_SetString( PyExc_AttributeError, "All operator args must be keywords"); return NULL; @@ -140,59 +187,11 @@ static PyObject * pyop_func_call(BPy_OperatorFunc * self, PyObject *args, PyObje } RNA_pointer_create(NULL, NULL, ot->srna, &properties, &ptr); - - - iterprop= RNA_struct_iterator_property(&ptr); - RNA_property_collection_begin(&ptr, iterprop, &iter); - - totkw = kw ? PyDict_Size(kw):0; - - 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; - - if (kw==NULL) { - PyErr_Format( PyExc_AttributeError, "no args, expected \"%s\"", arg_name ? arg_name : "<UNKNOWN>"); - error_val= 1; - break; - } - - item= PyDict_GetItemString(kw, arg_name); - - if (item == NULL) { - PyErr_Format( PyExc_AttributeError, "argument \"%s\" missing", arg_name ? arg_name : "<UNKNOWN>"); - error_val = 1; /* pyrna_py_to_prop sets the error */ - break; - } - - if (pyrna_py_to_prop(&ptr, prop, item)) { - error_val= 1; - break; - } - - totkw--; - } - - RNA_property_collection_end(&iter); - - if (error_val==0 && totkw > 0) { /* some keywords were given that were not used :/ */ - PyObject *key, *value; - Py_ssize_t pos = 0; - - while (PyDict_Next(kw, &pos, &key, &value)) { - arg_name= _PyUnicode_AsString(key); - if (RNA_struct_find_property(&ptr, arg_name) == NULL) break; - arg_name= NULL; - } - - PyErr_Format( PyExc_AttributeError, "argument \"%s\" unrecognized", arg_name ? arg_name : "<UNKNOWN>"); - error_val = 1; - } - + + PYOP_props_from_dict(&ptr, kw); + if (error_val==0) { + //WM_operator_name_call(self->C, self->name, WM_OP_INVOKE_DEFAULT, properties); WM_operator_name_call(self->C, self->name, WM_OP_EXEC_DEFAULT, properties); } diff --git a/source/blender/python/intern/bpy_operator.h b/source/blender/python/intern/bpy_operator.h index 55797f6d8cc..19c503c1e8c 100644 --- a/source/blender/python/intern/bpy_operator.h +++ b/source/blender/python/intern/bpy_operator.h @@ -51,4 +51,7 @@ PyObject *BPY_operator_module(bContext *C ); PyObject *pyop_base_CreatePyObject(bContext *C ); PyObject *pyop_func_CreatePyObject(bContext *C, char *name ); +/* fill in properties from a python dict */ +int PYOP_props_from_dict(PointerRNA *ptr, PyObject *kw); + #endif diff --git a/source/blender/python/intern/bpy_opwrapper.c b/source/blender/python/intern/bpy_opwrapper.c index 779f876390b..46ec0f9873e 100644 --- a/source/blender/python/intern/bpy_opwrapper.c +++ b/source/blender/python/intern/bpy_opwrapper.c @@ -37,6 +37,8 @@ #include "bpy_rna.h" #include "bpy_compat.h" +#include "bpy_util.h" +#include "bpy_operator.h" /* for PYOP_props_from_dict() */ typedef struct PyOperatorType { void *next, *prev; @@ -46,8 +48,9 @@ typedef struct PyOperatorType { PyObject *py_exec; } PyOperatorType; -static void pyop_kwargs_from_operator(PyObject *dict, wmOperator *op) +static PyObject *pyop_kwargs_from_operator(wmOperator *op) { + PyObject *dict = PyDict_New(); PyObject *item; PropertyRNA *prop, *iterprop; CollectionPropertyIterator iter; @@ -70,17 +73,130 @@ static void pyop_kwargs_from_operator(PyObject *dict, wmOperator *op) RNA_property_collection_end(&iter); + return dict; } + +static PyObject *pyop_dict_from_event(wmEvent *event) +{ + PyObject *dict= PyDict_New(); + PyObject *item; + char *cstring, ascii[2]; + + /* type */ + item= PyUnicode_FromString(WM_key_event_string(event->type)); + PyDict_SetItemString(dict, "type", item); Py_DECREF(item); + + /* val */ + switch(event->val) { + case KM_ANY: + cstring = "ANY"; + break; + case KM_RELEASE: + cstring = "RELEASE"; + break; + case KM_PRESS: + cstring = "PRESS"; + break; + default: + cstring = "UNKNOWN"; + break; + } + + item= PyUnicode_FromString(cstring); + PyDict_SetItemString(dict, "val", item); Py_DECREF(item); + + /* x, y (mouse) */ + item= PyLong_FromLong(event->x); + PyDict_SetItemString(dict, "x", item); Py_DECREF(item); + + item= PyLong_FromLong(event->y); + PyDict_SetItemString(dict, "y", item); Py_DECREF(item); + + item= PyLong_FromLong(event->prevx); + PyDict_SetItemString(dict, "prevx", item); Py_DECREF(item); + + item= PyLong_FromLong(event->prevy); + PyDict_SetItemString(dict, "prevy", item); Py_DECREF(item); + + /* ascii */ + ascii[0]= event->ascii; + ascii[1]= '\0'; + item= PyUnicode_FromString(ascii); + PyDict_SetItemString(dict, "ascii", item); Py_DECREF(item); + + /* modifier keys */ + item= PyLong_FromLong(event->shift); + PyDict_SetItemString(dict, "shift", item); Py_DECREF(item); + + item= PyLong_FromLong(event->ctrl); + PyDict_SetItemString(dict, "ctrl", item); Py_DECREF(item); + + item= PyLong_FromLong(event->alt); + PyDict_SetItemString(dict, "alt", item); Py_DECREF(item); + + item= PyLong_FromLong(event->oskey); + PyDict_SetItemString(dict, "oskey", item); Py_DECREF(item); + + + + /* modifier */ +#if 0 + item= PyTuple_New(0); + if(event->keymodifier & KM_SHIFT) { + _PyTuple_Resize(&item, size+1); + PyTuple_SET_ITEM(item, size, _PyUnicode_AsString("SHIFT")); + size++; + } + if(event->keymodifier & KM_CTRL) { + _PyTuple_Resize(&item, size+1); + PyTuple_SET_ITEM(item, size, _PyUnicode_AsString("CTRL")); + size++; + } + if(event->keymodifier & KM_ALT) { + _PyTuple_Resize(&item, size+1); + PyTuple_SET_ITEM(item, size, _PyUnicode_AsString("ALT")); + size++; + } + if(event->keymodifier & KM_OSKEY) { + _PyTuple_Resize(&item, size+1); + PyTuple_SET_ITEM(item, size, _PyUnicode_AsString("OSKEY")); + size++; + } + PyDict_SetItemString(dict, "keymodifier", item); Py_DECREF(item); +#endif + + return dict; +} + +static struct BPY_flag_def pyop_ret_flags[] = { + {"RUNNING_MODAL", OPERATOR_RUNNING_MODAL}, + {"CANCELLED", OPERATOR_CANCELLED}, + {"FINISHED", OPERATOR_FINISHED}, + {"PASS_THROUGH", OPERATOR_PASS_THROUGH}, + {NULL, 0} +}; + static int PYTHON_OT_exec(bContext *C, wmOperator *op) { PyOperatorType *pyot = op->type->pyop_data; - PyObject *ret, *args= PyTuple_New(0), *kw= PyDict_New(); - - pyop_kwargs_from_operator(kw, op); + 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) { + PyErr_Print(); + } + else { + if (BPY_flag_from_seq(pyop_ret_flags, ret, &ret_flag) == -1) { + /* the returned value could not be converted into a flag */ + PyErr_Print(); + } + } + Py_DECREF(args); Py_DECREF(kw); @@ -90,16 +206,38 @@ static int PYTHON_OT_exec(bContext *C, wmOperator *op) static int PYTHON_OT_invoke(bContext *C, wmOperator *op, wmEvent *event) { PyOperatorType *pyot = op->type->pyop_data; - PyObject *ret, *args= PyTuple_New(0), *kw= PyDict_New(); + PyObject *args= PyTuple_New(2); + PyObject *kw= pyop_kwargs_from_operator(op); + PyObject *ret; + int ret_flag; - pyop_kwargs_from_operator(kw, op); + PyTuple_SET_ITEM(args, 0, pyop_dict_from_event(event)); + PyTuple_SET_ITEM(args, 1, kw); - ret = PyObject_Call(pyot->py_invoke, args, kw); + ret = PyObject_Call(pyot->py_invoke, args, NULL); - Py_DECREF(args); - Py_DECREF(kw); + if (ret == NULL) { + PyErr_Print(); + } + else { + if (BPY_flag_from_seq(pyop_ret_flags, ret, &ret_flag) == -1) { + /* the returned value could not be converted into a flag */ + PyErr_Print(); + } + else { + /* copy the args back to the prop */ + if (PYOP_props_from_dict(op->ptr, kw) == -1) { + /* one of the dict items didnt convert back to the prop */ + PyErr_Print(); + } + } + } - return OPERATOR_FINISHED; + + + Py_DECREF(args); /* also decref's kw */ + + return ret_flag; } void PYTHON_OT_wrapper(wmOperatorType *ot, void *userdata) @@ -117,7 +255,8 @@ void PYTHON_OT_wrapper(wmOperatorType *ot, void *userdata) 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 */ @@ -216,7 +355,7 @@ static PyObject *pyop_remove(PyObject *self, PyObject *args) return NULL; if (!(ot= WM_operatortype_find(idname))) { - PyErr_Format( PyExc_AttributeError, "Operator \"%s\" alredy exists", idname); + PyErr_Format( PyExc_AttributeError, "Operator \"%s\" does not exists, cant remove", idname); return NULL; } diff --git a/source/blender/python/intern/bpy_util.c b/source/blender/python/intern/bpy_util.c new file mode 100644 index 00000000000..606b9d620c1 --- /dev/null +++ b/source/blender/python/intern/bpy_util.c @@ -0,0 +1,121 @@ +/** + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Contributor(s): Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "bpy_util.h" +#include "BLI_dynstr.h" +#include "MEM_guardedalloc.h" + +PyObject *BPY_flag_to_list(struct BPY_flag_def *flagdef, int flag) +{ + PyObject *list = PyList_New(0); + + PyObject *item; + BPY_flag_def *fd; + + fd= flagdef; + while(fd->name) { + if (fd->flag & flag) { + item = PyUnicode_FromString(fd->name); + PyList_Append(list, item); + Py_DECREF(item); + } + fd++; + } + + return list; + +} + +static char *bpy_flag_error_str(BPY_flag_def *flagdef) +{ + BPY_flag_def *fd= flagdef; + DynStr *dynstr= BLI_dynstr_new(); + char *cstring; + + BLI_dynstr_append(dynstr, "Error converting a sequence of strings into a flag.\n\tExpected only these strings...\n\t"); + + while(fd->name) { + BLI_dynstr_appendf(dynstr, fd!=flagdef?", '%s'":"'%s'", fd->name); + fd++; + } + + cstring = BLI_dynstr_get_cstring(dynstr); + BLI_dynstr_free(dynstr); + return cstring; +} + +int BPY_flag_from_seq(BPY_flag_def *flagdef, PyObject *seq, int *flag) +{ + int i, error_val= 0; + char *cstring; + PyObject *item; + BPY_flag_def *fd; + + if (PySequence_Check(seq)) { + i= PySequence_Length(seq); + + while(i--) { + item = PySequence_ITEM(seq, i); + cstring= _PyUnicode_AsString(item); + if(cstring) { + fd= flagdef; + while(fd->name) { + if (strcmp(cstring, fd->name) == 0) + (*flag) |= fd->flag; + fd++; + } + if (fd==NULL) { /* could not find a match */ + error_val= 1; + } + } else { + error_val= 1; + } + Py_DECREF(item); + } + } + else { + error_val= 1; + } + + if (error_val) { + char *buf = bpy_flag_error_str(flagdef); + PyErr_SetString(PyExc_AttributeError, buf); + MEM_freeN(buf); + return -1; /* error value */ + } + + return 0; /* ok */ +} + +/* for debugging */ +void PyObSpit(char *name, PyObject *var) { + fprintf(stderr, "<%s> : ", name); + if (var==NULL) { + fprintf(stderr, "<NIL>"); + } + else { + PyObject_Print(var, stderr, 0); + } + fprintf(stderr, "\n"); +} diff --git a/source/blender/python/intern/bpy_util.h b/source/blender/python/intern/bpy_util.h new file mode 100644 index 00000000000..82cc602fcab --- /dev/null +++ b/source/blender/python/intern/bpy_util.h @@ -0,0 +1,38 @@ +/** + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Contributor(s): Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <Python.h> + +/* for internal use only, so python can interchange a sequence of strings with flags */ +typedef struct BPY_flag_def { + const char *name; + int flag; +} BPY_flag_def; + + +PyObject *BPY_flag_to_list(BPY_flag_def *flagdef, int flag); +int BPY_flag_from_seq(BPY_flag_def *flagdef, PyObject *seq, int *flag); + +void PyObSpit(char *name, PyObject *var); + |