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)
This commit is contained in:
@@ -39,23 +39,76 @@
|
||||
|
||||
extern ListBase global_ops; /* evil, temp use */
|
||||
|
||||
|
||||
|
||||
/* 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--;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return error_val;
|
||||
}
|
||||
|
||||
/* 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>");
|
||||
}
|
||||
else {
|
||||
PyObject_Print(var, stderr, 0);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static int pyop_func_compare( BPy_OperatorFunc * a, BPy_OperatorFunc * b )
|
||||
{
|
||||
return (strcmp(a->name, b->name)==0) ? 0 : -1;
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
121
source/blender/python/intern/bpy_util.c
Normal file
121
source/blender/python/intern/bpy_util.c
Normal file
@@ -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");
|
||||
}
|
38
source/blender/python/intern/bpy_util.h
Normal file
38
source/blender/python/intern/bpy_util.h
Normal file
@@ -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);
|
||||
|
Reference in New Issue
Block a user