2.5 PyAPI
Support for subclassing blenders operator, to be registered as a new operator. Still need to... * add constants like Operator.FINISHED * wrap context (with rna?) * poll() cant work right now because there is no way to access the operatorType that holds the python class. * Only float, int and bool properties can be added so far. working example operator. http://wiki.blender.org/index.php/BlenderDev/Blender2.5/WinterCamp/TechnicalDesign#Operator_Example_Code
This commit is contained in:
@@ -57,6 +57,22 @@ static PyObject *CreateGlobalDictionary( bContext *C )
|
||||
PyDict_SetItemString( dict, "__bpy_context__", item );
|
||||
Py_DECREF(item);
|
||||
|
||||
|
||||
// XXX - put somewhere more logical
|
||||
{
|
||||
PyMethodDef *ml;
|
||||
static PyMethodDef bpy_prop_meths[] = {
|
||||
{"FloatProperty", BPy_FloatProperty, METH_VARARGS|METH_KEYWORDS, ""},
|
||||
{"IntProperty", BPy_IntProperty, METH_VARARGS|METH_KEYWORDS, ""},
|
||||
{"BoolProperty", BPy_BoolProperty, METH_VARARGS|METH_KEYWORDS, ""},
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
for(ml = &bpy_prop_meths; ml->ml_name; ml++) {
|
||||
PyDict_SetItemString( dict, ml->ml_name, PyCFunction_New(ml, NULL));
|
||||
}
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ static PyObject *pyop_base_dir(PyObject *self);
|
||||
static struct PyMethodDef pyop_base_methods[] = {
|
||||
{"__dir__", (PyCFunction)pyop_base_dir, METH_NOARGS, ""},
|
||||
{"add", (PyCFunction)PYOP_wrap_add, METH_VARARGS, ""},
|
||||
{"remove", (PyCFunction)PYOP_wrap_remove, METH_VARARGS, ""},
|
||||
{"remove", (PyCFunction)PYOP_wrap_remove, METH_O, ""},
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
|
||||
@@ -45,39 +45,9 @@ typedef struct PyOperatorType {
|
||||
char idname[OP_MAX_TYPENAME];
|
||||
char name[OP_MAX_TYPENAME];
|
||||
char description[OP_MAX_TYPENAME]; // XXX should be longer?
|
||||
PyObject *py_invoke;
|
||||
PyObject *py_exec;
|
||||
PyObject *py_class;
|
||||
} PyOperatorType;
|
||||
|
||||
static PyObject *pyop_kwargs_from_operator(wmOperator *op)
|
||||
{
|
||||
PyObject *dict = PyDict_New();
|
||||
PyObject *item;
|
||||
PropertyRNA *prop, *iterprop;
|
||||
CollectionPropertyIterator iter;
|
||||
const char *arg_name;
|
||||
|
||||
iterprop= RNA_struct_iterator_property(op->ptr);
|
||||
RNA_property_collection_begin(op->ptr, iterprop, &iter);
|
||||
|
||||
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;
|
||||
|
||||
item = pyrna_prop_to_py(op->ptr, prop);
|
||||
PyDict_SetItemString(dict, arg_name, item);
|
||||
Py_DECREF(item);
|
||||
}
|
||||
|
||||
RNA_property_collection_end(&iter);
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
|
||||
static PyObject *pyop_dict_from_event(wmEvent *event)
|
||||
{
|
||||
PyObject *dict= PyDict_New();
|
||||
@@ -191,33 +161,6 @@ static struct BPY_flag_def pyop_ret_flags[] = {
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
/* exec only - no user input */
|
||||
static int PYTHON_OT_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
PyOperatorType *pyot = op->type->pyop_data;
|
||||
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) {
|
||||
pyop_error_report(op->reports);
|
||||
}
|
||||
else {
|
||||
if (BPY_flag_from_seq(pyop_ret_flags, ret, &ret_flag) == -1) {
|
||||
/* the returned value could not be converted into a flag */
|
||||
pyop_error_report(op->reports);
|
||||
}
|
||||
}
|
||||
|
||||
Py_DECREF(args);
|
||||
Py_DECREF(kw);
|
||||
|
||||
return ret_flag;
|
||||
}
|
||||
|
||||
/* This invoke function can take events and
|
||||
*
|
||||
* It is up to the pyot->py_invoke() python func to run pyot->py_exec()
|
||||
@@ -233,26 +176,93 @@ static int PYTHON_OT_exec(bContext *C, wmOperator *op)
|
||||
* op_exec(**prop_defs)
|
||||
*
|
||||
* when there is no invoke function, C calls exec and sets the props.
|
||||
* python class instance is stored in op->customdata so exec() can access
|
||||
*/
|
||||
static int PYTHON_OT_invoke(bContext *C, wmOperator *op, wmEvent *event)
|
||||
|
||||
|
||||
#define PYOP_EXEC 1
|
||||
#define PYOP_INVOKE 2
|
||||
#define PYOP_POLL 3
|
||||
|
||||
static int PYTHON_OT_generic(int mode, bContext *C, wmOperator *op, wmEvent *event)
|
||||
{
|
||||
PyOperatorType *pyot = op->type->pyop_data;
|
||||
PyObject *args= PyTuple_New(2);
|
||||
PyObject *ret;
|
||||
int ret_flag;
|
||||
PyObject *args;
|
||||
PyObject *ret= NULL, *py_class_instance, *item;
|
||||
int ret_flag= (mode==PYOP_POLL ? 0:OPERATOR_CANCELLED);
|
||||
|
||||
args = PyTuple_New(1);
|
||||
PyTuple_SET_ITEM(args, 0, PyObject_GetAttrString(pyot->py_class, "__rna__")); // need to use an rna instance as the first arg
|
||||
py_class_instance = PyObject_Call(pyot->py_class, args, NULL);
|
||||
Py_DECREF(args);
|
||||
|
||||
if (py_class_instance) { /* Initializing the class worked, now run its invoke function */
|
||||
|
||||
|
||||
/* Assign instance attributes from operator properties */
|
||||
{
|
||||
PropertyRNA *prop, *iterprop;
|
||||
CollectionPropertyIterator iter;
|
||||
const char *arg_name;
|
||||
|
||||
PyTuple_SET_ITEM(args, 0, pyop_dict_from_event(event));
|
||||
PyTuple_SET_ITEM(args, 1, pyop_kwargs_from_operator(op));
|
||||
iterprop= RNA_struct_iterator_property(op->ptr);
|
||||
RNA_property_collection_begin(op->ptr, iterprop, &iter);
|
||||
|
||||
ret = PyObject_Call(pyot->py_invoke, args, NULL);
|
||||
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;
|
||||
|
||||
item = pyrna_prop_to_py(op->ptr, prop);
|
||||
PyObject_SetAttrString(py_class_instance, arg_name, item);
|
||||
Py_DECREF(item);
|
||||
}
|
||||
|
||||
RNA_property_collection_end(&iter);
|
||||
}
|
||||
|
||||
|
||||
if (mode==PYOP_INVOKE) {
|
||||
item= PyObject_GetAttrString(pyot->py_class, "invoke");
|
||||
args = PyTuple_New(2);
|
||||
PyTuple_SET_ITEM(args, 1, pyop_dict_from_event(event));
|
||||
}
|
||||
else if (mode==PYOP_EXEC) {
|
||||
item= PyObject_GetAttrString(pyot->py_class, "exec");
|
||||
args = PyTuple_New(1);
|
||||
}
|
||||
else if (mode==PYOP_POLL) {
|
||||
item= PyObject_GetAttrString(pyot->py_class, "poll");
|
||||
args = PyTuple_New(2);
|
||||
//XXX Todo - wrap context in a useful way, None for now.
|
||||
PyTuple_SET_ITEM(args, 1, Py_None);
|
||||
}
|
||||
PyTuple_SET_ITEM(args, 0, py_class_instance);
|
||||
|
||||
ret = PyObject_Call(item, args, NULL);
|
||||
|
||||
Py_DECREF(args);
|
||||
Py_DECREF(item);
|
||||
}
|
||||
|
||||
if (ret == NULL) {
|
||||
pyop_error_report(op->reports);
|
||||
}
|
||||
else {
|
||||
if (BPY_flag_from_seq(pyop_ret_flags, ret, &ret_flag) == -1) {
|
||||
if (mode==PYOP_POLL) {
|
||||
if (PyBool_Check(ret) == 0) {
|
||||
PyErr_SetString(PyExc_ValueError, "Python poll function return value ");
|
||||
pyop_error_report(op->reports);
|
||||
}
|
||||
else {
|
||||
ret_flag= ret==Py_True ? 1:0;
|
||||
}
|
||||
|
||||
} else if (BPY_flag_from_seq(pyop_ret_flags, ret, &ret_flag) == -1) {
|
||||
/* the returned value could not be converted into a flag */
|
||||
pyop_error_report(op->reports);
|
||||
|
||||
}
|
||||
/* there is no need to copy the py keyword dict modified by
|
||||
* pyot->py_invoke(), back to the operator props since they are just
|
||||
@@ -261,131 +271,234 @@ static int PYTHON_OT_invoke(bContext *C, wmOperator *op, wmEvent *event)
|
||||
* If we ever want to do this and use the props again,
|
||||
* it can be done with - PYOP_props_from_dict(op->ptr, kw)
|
||||
*/
|
||||
|
||||
Py_DECREF(ret);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Py_DECREF(args); /* also decref's kw */
|
||||
|
||||
return ret_flag;
|
||||
}
|
||||
|
||||
static int PYTHON_OT_invoke(bContext *C, wmOperator *op, wmEvent *event)
|
||||
{
|
||||
return PYTHON_OT_generic(PYOP_INVOKE, C, op, event);
|
||||
}
|
||||
|
||||
static int PYTHON_OT_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
return PYTHON_OT_generic(PYOP_EXEC, C, op, NULL);
|
||||
}
|
||||
|
||||
static int PYTHON_OT_poll(bContext *C)
|
||||
{
|
||||
// XXX TODO - no way to get the operator type (and therefor class) from the poll function.
|
||||
//return PYTHON_OT_generic(PYOP_POLL, C, NULL, NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void PYTHON_OT_wrapper(wmOperatorType *ot, void *userdata)
|
||||
{
|
||||
PyOperatorType *pyot = (PyOperatorType *)userdata;
|
||||
PyObject *py_class = pyot->py_class;
|
||||
|
||||
/* identifiers */
|
||||
ot->name= pyot->name;
|
||||
ot->idname= pyot->idname;
|
||||
ot->description= pyot->description;
|
||||
|
||||
/* api callbacks */
|
||||
if (pyot->py_invoke != Py_None)
|
||||
/* api callbacks, detailed checks dont on adding */
|
||||
if (PyObject_HasAttrString(py_class, "invoke"))
|
||||
ot->invoke= PYTHON_OT_invoke;
|
||||
|
||||
ot->exec= PYTHON_OT_exec;
|
||||
|
||||
ot->poll= ED_operator_screenactive; /* how should this work?? */
|
||||
/* ot->flag= OPTYPE_REGISTER; */
|
||||
if (PyObject_HasAttrString(py_class, "exec"))
|
||||
ot->exec= PYTHON_OT_exec;
|
||||
if (PyObject_HasAttrString(py_class, "poll"))
|
||||
ot->poll= PYTHON_OT_poll;
|
||||
|
||||
ot->pyop_data= userdata;
|
||||
|
||||
/* inspect function keyword args to get properties */
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
PyObject *var_names= PyObject_GetAttrString(PyFunction_GET_CODE(pyot->py_exec), "co_varnames");
|
||||
PyObject *var_vals = PyFunction_GET_DEFAULTS(pyot->py_exec);
|
||||
PyObject *py_val, *py_name;
|
||||
// TODO - set properties
|
||||
PyObject *props, *item;
|
||||
|
||||
|
||||
if ((props=PyObject_GetAttrString(py_class, "properties"))) {
|
||||
PyObject *dummy_args = PyTuple_New(0);
|
||||
|
||||
int i;
|
||||
char *name;
|
||||
|
||||
if (PyTuple_Size(var_names) != PyTuple_Size(var_vals)) {
|
||||
printf("All args must be keywords");
|
||||
}
|
||||
|
||||
for(i=0; i<PyTuple_Size(var_names); i++) {
|
||||
py_name = PyTuple_GetItem(var_names, i);
|
||||
name = _PyUnicode_AsString(py_name);
|
||||
py_val = PyTuple_GetItem(var_vals, i);
|
||||
|
||||
if (PyBool_Check(py_val)) {
|
||||
prop = RNA_def_property(ot->srna, name, PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_default(prop, PyObject_IsTrue(py_val));
|
||||
}
|
||||
else if (PyLong_Check(py_val)) {
|
||||
prop = RNA_def_property(ot->srna, name, PROP_INT, PROP_NONE);
|
||||
RNA_def_property_int_default(prop, (int)PyLong_AsSsize_t(py_val));
|
||||
}
|
||||
else if (PyFloat_Check(py_val)) {
|
||||
prop = RNA_def_property(ot->srna, name, PROP_FLOAT, PROP_NONE);
|
||||
RNA_def_property_float_default(prop, (float)PyFloat_AsDouble(py_val));
|
||||
}
|
||||
else if (PyUnicode_Check(py_val)) {
|
||||
/* WARNING - holding a reference to the string from py_val is
|
||||
* not ideal since we rely on python keeping it,
|
||||
* however we're also keeping a reference to this function
|
||||
* so it should be OK!. just be careful with changes */
|
||||
prop = RNA_def_property(ot->srna, name, PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_default(prop, _PyUnicode_AsString(py_val));
|
||||
}
|
||||
else {
|
||||
printf("error, python function arg \"%s\" was not a bool, int, float or string type\n", name);
|
||||
}
|
||||
|
||||
for(i=0; i<PyList_Size(props); i++) {
|
||||
item = PyList_GET_ITEM(props, i);
|
||||
|
||||
PyObject *py_func_ptr, *py_kw, *py_srna_cobject, *py_ret;
|
||||
|
||||
if (PyArg_ParseTuple(item, "O!O!", &PyCObject_Type, &py_func_ptr, &PyDict_Type, &py_kw)) {
|
||||
|
||||
PyObject *(*pyfunc)(PyObject *, PyObject *, PyObject *);
|
||||
pyfunc = PyCObject_AsVoidPtr(py_func_ptr);
|
||||
py_srna_cobject = PyCObject_FromVoidPtr(ot->srna, NULL);
|
||||
|
||||
py_ret = pyfunc(py_srna_cobject, dummy_args, py_kw);
|
||||
if (py_ret) {
|
||||
Py_DECREF(py_ret);
|
||||
} else {
|
||||
PyErr_Print();
|
||||
PyErr_Clear();
|
||||
}
|
||||
Py_DECREF(py_srna_cobject);
|
||||
|
||||
} else {
|
||||
/* cant return NULL from here */ // XXX a bit ugly
|
||||
PyErr_Print();
|
||||
PyErr_Clear();
|
||||
}
|
||||
|
||||
// expect a tuple with a CObject and a dict
|
||||
}
|
||||
Py_DECREF(dummy_args);
|
||||
} else {
|
||||
PyErr_Clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* pyOperators - Operators defined IN Python */
|
||||
PyObject *PYOP_wrap_add(PyObject *self, PyObject *args)
|
||||
{
|
||||
// XXX ugly - store the Operator type elsewhere!, probably leaks memory
|
||||
PyObject *optype = PyObject_GetAttrString(PyObject_GetAttrString(PyDict_GetItemString(PyEval_GetGlobals(), "bpy"), "types"), "Operator");
|
||||
PyObject *value, *item;
|
||||
|
||||
PyOperatorType *pyot;
|
||||
|
||||
char *idname= NULL;
|
||||
char *name= NULL;
|
||||
char *description= NULL;
|
||||
PyObject *invoke= NULL;
|
||||
PyObject *exec= NULL;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "sssOO", &idname, &name, &description, &invoke, &exec)) {
|
||||
PyErr_SetString( PyExc_AttributeError, "expected 2 strings and 2 function objects");
|
||||
|
||||
static char *pyop_func_names[] = {"exec", "invoke", "poll", NULL};
|
||||
static int *pyop_func_nargs[] = {1, 2, 2, 0};
|
||||
|
||||
|
||||
if (!PyArg_ParseTuple(args, "O", &value) || !PyObject_IsSubclass(value, optype)) {
|
||||
PyErr_SetString( PyExc_AttributeError, "expected Operator subclass of bpy.types.Operator");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* class name is used for operator ID - this can be changed later if we want */
|
||||
item = PyObject_GetAttrString(value, "__name__");
|
||||
idname = _PyUnicode_AsString(item);
|
||||
Py_DECREF(item);
|
||||
|
||||
if (WM_operatortype_find(idname)) {
|
||||
PyErr_Format( PyExc_AttributeError, "First argument \"%s\" operator alredy exists with this name", idname);
|
||||
PyErr_Format( PyExc_AttributeError, "Operator alredy exists with this name", idname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (((PyFunction_Check(invoke) || invoke==Py_None) && PyFunction_Check(exec)) == 0) {
|
||||
PyErr_SetString( PyExc_AttributeError, "the 2nd arg must be a function or None, the secons must be a function");
|
||||
return NULL;
|
||||
|
||||
/* Operator user readible name */
|
||||
item = PyObject_GetAttrString(value, "name");
|
||||
if (item) {
|
||||
name = _PyUnicode_AsString(item);
|
||||
Py_DECREF(item);
|
||||
}
|
||||
|
||||
if (name == NULL) {
|
||||
name = idname;
|
||||
PyErr_Clear();
|
||||
}
|
||||
|
||||
/* use py docstring for description, should always be None or a string */
|
||||
item = PyObject_GetAttrString(value, "__doc__");
|
||||
if (PyUnicode_Check(item)) {
|
||||
description = _PyUnicode_AsString(item);
|
||||
}
|
||||
else {
|
||||
description = "";
|
||||
}
|
||||
Py_DECREF(item);
|
||||
|
||||
/* Check known functions and argument lengths */
|
||||
int i;
|
||||
int argcount;
|
||||
for (i=0; pyop_func_names[i]; i++) {
|
||||
if (item=PyObject_GetAttrString(value, pyop_func_names[i])) {
|
||||
/* check its callable */
|
||||
if (!PyFunction_Check(item)) {
|
||||
PyErr_Format(PyExc_ValueError, "Cant register operator class - %s.%s() is not a function", idname, pyop_func_names[i]);
|
||||
Py_DECREF(item);
|
||||
return NULL;
|
||||
}
|
||||
/* check the number of args is correct */
|
||||
// MyClass.exec.func_code.co_argcount
|
||||
|
||||
PyObject *pyargcount = PyObject_GetAttrString(PyFunction_GetCode(item), "co_argcount");
|
||||
argcount = PyLong_AsSsize_t(pyargcount);
|
||||
Py_DECREF(pyargcount);
|
||||
|
||||
if (argcount != pyop_func_nargs[i]) {
|
||||
PyErr_Format(PyExc_ValueError, "Cant register operator class - %s.%s() takes %d args, should be %d", idname, pyop_func_names[i], argcount, pyop_func_nargs[i]);
|
||||
Py_DECREF(item);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} else {
|
||||
PyErr_Clear();
|
||||
}
|
||||
Py_XDECREF(item);
|
||||
}
|
||||
|
||||
/* If we have properties set, check its a list of dicts */
|
||||
item = PyObject_GetAttrString(value, "properties");
|
||||
if (item) {
|
||||
if (!PyList_Check(item)) {
|
||||
PyErr_Format(PyExc_ValueError, "Cant register operator class - %s.properties must be a list", idname);
|
||||
Py_DECREF(item);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int i;
|
||||
for(i=0; i<PyList_Size(item); i++) {
|
||||
PyObject *py_args = PyList_GET_ITEM(item, i);
|
||||
PyObject *py_func_ptr, *py_kw; /* place holders */
|
||||
|
||||
if (!PyArg_ParseTuple(py_args, "O!O!", &PyCObject_Type, &py_func_ptr, &PyDict_Type, &py_kw)) {
|
||||
PyErr_Format(PyExc_ValueError, "Cant register operator class - %s.properties must contain values from FloatProperty", idname);
|
||||
Py_DECREF(item);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Py_DECREF(item);
|
||||
}
|
||||
else {
|
||||
PyErr_Clear();
|
||||
}
|
||||
|
||||
pyot= MEM_callocN(sizeof(PyOperatorType), "PyOperatorType");
|
||||
|
||||
strncpy(pyot->idname, idname, sizeof(pyot->idname));
|
||||
strncpy(pyot->name, name, sizeof(pyot->name));
|
||||
strncpy(pyot->description, description, sizeof(pyot->description));
|
||||
pyot->py_invoke= invoke;
|
||||
pyot->py_exec= exec;
|
||||
Py_INCREF(invoke);
|
||||
Py_INCREF(exec);
|
||||
pyot->py_class= value;
|
||||
Py_INCREF(value);
|
||||
|
||||
WM_operatortype_append_ptr(PYTHON_OT_wrapper, pyot);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyObject *PYOP_wrap_remove(PyObject *self, PyObject *args)
|
||||
PyObject *PYOP_wrap_remove(PyObject *self, PyObject *value)
|
||||
{
|
||||
char *idname= NULL;
|
||||
wmOperatorType *ot;
|
||||
PyOperatorType *pyot;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", &idname))
|
||||
if (PyUnicode_Check(value))
|
||||
idname = _PyUnicode_AsString(value);
|
||||
else if (PyCFunction_Check(value)) {
|
||||
PyObject *cfunc_self = PyCFunction_GetSelf(value);
|
||||
if (cfunc_self)
|
||||
idname = _PyUnicode_AsString(cfunc_self);
|
||||
}
|
||||
|
||||
if (idname==NULL) {
|
||||
PyErr_SetString( PyExc_ValueError, "Expected the operator name as a string or the operator function");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(ot= WM_operatortype_find(idname))) {
|
||||
PyErr_Format( PyExc_AttributeError, "Operator \"%s\" does not exists, cant remove", idname);
|
||||
@@ -397,8 +510,7 @@ PyObject *PYOP_wrap_remove(PyObject *self, PyObject *args)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_XDECREF(pyot->py_invoke);
|
||||
Py_XDECREF(pyot->py_exec);
|
||||
Py_XDECREF(pyot->py_class);
|
||||
MEM_freeN(pyot);
|
||||
|
||||
WM_operatortype_remove(idname);
|
||||
@@ -406,3 +518,5 @@ PyObject *PYOP_wrap_remove(PyObject *self, PyObject *args)
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
#include "bpy_compat.h"
|
||||
//#include "blendef.h"
|
||||
#include "BLI_dynstr.h"
|
||||
#include "float.h" /* FLT_MIN/MAX */
|
||||
|
||||
#include "RNA_define.h" /* for defining our own rna */
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
#include "BKE_global.h" /* evil G.* */
|
||||
@@ -65,12 +68,16 @@ static PyObject *pyrna_prop_richcmp(BPy_PropertyRNA * a, BPy_PropertyRNA * b, in
|
||||
/*----------------------repr--------------------------------------------*/
|
||||
static PyObject *pyrna_struct_repr( BPy_StructRNA * self )
|
||||
{
|
||||
return PyUnicode_FromFormat( "[BPy_StructRNA \"%s\"]", RNA_struct_identifier(&self->ptr));
|
||||
char str[512];
|
||||
RNA_string_get(&self->ptr, "identifier", str);
|
||||
return PyUnicode_FromFormat( "[BPy_StructRNA \"%s\" -> \"%s\"]", RNA_struct_identifier(&self->ptr), str);
|
||||
}
|
||||
|
||||
static PyObject *pyrna_prop_repr( BPy_PropertyRNA * self )
|
||||
{
|
||||
return PyUnicode_FromFormat( "[BPy_PropertyRNA \"%s\" -> \"%s\" ]", RNA_struct_identifier(&self->ptr), RNA_property_identifier(&self->ptr, self->prop) );
|
||||
char str[512];
|
||||
RNA_string_get(&self->ptr, "identifier", str);
|
||||
return PyUnicode_FromFormat( "[BPy_PropertyRNA \"%s\" -> \"%s\" -> \"%s\" ]", RNA_struct_identifier(&self->ptr), str, RNA_property_identifier(&self->ptr, self->prop) );
|
||||
}
|
||||
|
||||
static long pyrna_struct_hash( BPy_StructRNA * self )
|
||||
@@ -88,7 +95,7 @@ static void pyrna_struct_dealloc( BPy_StructRNA * self )
|
||||
self->ptr.data= NULL;
|
||||
}
|
||||
|
||||
((PyObject *)self)->ob_type->tp_free(self);
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -516,7 +523,7 @@ static int pyrna_prop_assign_subscript( BPy_PropertyRNA * self, PyObject *key, P
|
||||
char *keyname = NULL;
|
||||
|
||||
if (!RNA_property_editable(&self->ptr, self->prop)) {
|
||||
PyErr_Format( PyExc_AttributeError, "Attribute \"%s\" from \"%s\" is read-only", RNA_property_identifier(&self->ptr, self->prop), RNA_struct_identifier(&self->ptr) );
|
||||
PyErr_Format( PyExc_AttributeError, "PropertyRNA - attribute \"%s\" from \"%s\" is read-only", RNA_property_identifier(&self->ptr, self->prop), RNA_struct_identifier(&self->ptr) );
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -525,26 +532,26 @@ static int pyrna_prop_assign_subscript( BPy_PropertyRNA * self, PyObject *key, P
|
||||
} else if (PyLong_Check(key)) {
|
||||
keynum = PyLong_AsSsize_t(key);
|
||||
} else {
|
||||
PyErr_SetString(PyExc_AttributeError, "invalid key, key must be a string or an int");
|
||||
PyErr_SetString(PyExc_AttributeError, "PropertyRNA - invalid key, key must be a string or an int");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (RNA_property_type(&self->ptr, self->prop) == PROP_COLLECTION) {
|
||||
PyErr_SetString(PyExc_AttributeError, "assignment is not supported for collections (yet)");
|
||||
PyErr_SetString(PyExc_AttributeError, "PropertyRNA - assignment is not supported for collections (yet)");
|
||||
ret = -1;
|
||||
} else if (keyname) {
|
||||
PyErr_SetString(PyExc_AttributeError, "string keys are only supported for collections");
|
||||
PyErr_SetString(PyExc_AttributeError, "PropertyRNA - string keys are only supported for collections");
|
||||
ret = -1;
|
||||
} else {
|
||||
int len = RNA_property_array_length(&self->ptr, self->prop);
|
||||
|
||||
if (len==0) { /* not an array*/
|
||||
PyErr_Format(PyExc_AttributeError, "not an array or collection %d", keynum);
|
||||
PyErr_Format(PyExc_AttributeError, "PropertyRNA - not an array or collection %d", keynum);
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
if (keynum >= len){
|
||||
PyErr_SetString(PyExc_AttributeError, "index out of range");
|
||||
PyErr_SetString(PyExc_AttributeError, "PropertyRNA - index out of range");
|
||||
ret = -1;
|
||||
} else {
|
||||
ret = pyrna_py_to_prop_index(&self->ptr, self->prop, keynum, value);
|
||||
@@ -621,9 +628,11 @@ static PyObject *pyrna_struct_getattro( BPy_StructRNA * self, PyObject *pyname )
|
||||
/* Include this incase this instance is a subtype of a python class
|
||||
* In these instances we may want to return a function or variable provided by the subtype
|
||||
* */
|
||||
ret = PyObject_GenericGetAttr((PyObject *)self, pyname);
|
||||
if (ret) return ret;
|
||||
else PyErr_Clear();
|
||||
if (BPy_StructRNA_CheckExact(self) == 0) {
|
||||
ret = PyObject_GenericGetAttr((PyObject *)self, pyname);
|
||||
if (ret) return ret;
|
||||
else PyErr_Clear();
|
||||
}
|
||||
/* done with subtypes */
|
||||
|
||||
prop = RNA_struct_find_property(&self->ptr, name);
|
||||
@@ -632,7 +641,7 @@ static PyObject *pyrna_struct_getattro( BPy_StructRNA * self, PyObject *pyname )
|
||||
ret = pyrna_prop_to_py(&self->ptr, prop);
|
||||
}
|
||||
else {
|
||||
PyErr_Format( PyExc_AttributeError, "Attribute \"%s\" not found", name);
|
||||
PyErr_Format( PyExc_AttributeError, "StructRNA - Attribute \"%s\" not found", name);
|
||||
ret = NULL;
|
||||
}
|
||||
|
||||
@@ -646,12 +655,17 @@ static int pyrna_struct_setattro( BPy_StructRNA * self, PyObject *pyname, PyObje
|
||||
PropertyRNA *prop = RNA_struct_find_property(&self->ptr, name);
|
||||
|
||||
if (prop==NULL) {
|
||||
PyErr_Format( PyExc_AttributeError, "Attribute \"%s\" not found", name);
|
||||
return -1;
|
||||
if (!BPy_StructRNA_CheckExact(self) && PyObject_GenericSetAttr((PyObject *)self, pyname, value) >= 0) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
PyErr_Format( PyExc_AttributeError, "StructRNA - Attribute \"%s\" not found", name);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!RNA_property_editable(&self->ptr, prop)) {
|
||||
PyErr_Format( PyExc_AttributeError, "Attribute \"%s\" from \"%s\" is read-only", RNA_property_identifier(&self->ptr, prop), RNA_struct_identifier(&self->ptr) );
|
||||
PyErr_Format( PyExc_AttributeError, "StructRNA - Attribute \"%s\" from \"%s\" is read-only", RNA_property_identifier(&self->ptr, prop), RNA_struct_identifier(&self->ptr) );
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1200,3 +1214,89 @@ PyObject *BPY_rna_doc( void )
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
|
||||
/* Orphan functions, not sure where they should go */
|
||||
|
||||
/* Function that sets RNA, NOTE - self is NULL when called from python, but being abused from C so we can pass the srna allong
|
||||
* This isnt incorrect since its a python object - but be careful */
|
||||
PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw)
|
||||
{
|
||||
static char *kwlist[] = {"attribute", "name", "description", "min", "max", "soft_min", "soft_max", "default", NULL};
|
||||
char *id, *name="", *description="";
|
||||
float min=FLT_MIN, max=FLT_MAX, soft_min=FLT_MIN, soft_max=FLT_MAX, def=0.0f;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kw, "s|ssfffff:FloatProperty", kwlist, &id, &name, &description, &min, &max, &soft_min, &soft_max, &def))
|
||||
return NULL;
|
||||
|
||||
if (PyTuple_Size(args) > 0) {
|
||||
PyErr_SetString(PyExc_ValueError, "all args must be keywors"); // TODO - py3 can enforce this.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (self) {
|
||||
StructRNA *srna = PyCObject_AsVoidPtr(self);
|
||||
RNA_def_float(srna, id, def, min, max, name, description, soft_min, soft_max);
|
||||
Py_RETURN_NONE;
|
||||
} else {
|
||||
PyObject *ret = PyTuple_New(2);
|
||||
PyTuple_SET_ITEM(ret, 0, PyCObject_FromVoidPtr((void *)BPy_FloatProperty, NULL));
|
||||
PyTuple_SET_ITEM(ret, 1, kw);
|
||||
Py_INCREF(kw);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw)
|
||||
{
|
||||
static char *kwlist[] = {"attribute", "name", "description", "min", "max", "soft_min", "soft_max", "default", NULL};
|
||||
char *id, *name="", *description="";
|
||||
int min=INT_MIN, max=INT_MAX, soft_min=INT_MIN, soft_max=INT_MAX, def=0;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kw, "s|ssiiiii:IntProperty", kwlist, &id, &name, &description, &min, &max, &soft_min, &soft_max, &def))
|
||||
return NULL;
|
||||
|
||||
if (PyTuple_Size(args) > 0) {
|
||||
PyErr_SetString(PyExc_ValueError, "all args must be keywors"); // TODO - py3 can enforce this.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (self) {
|
||||
StructRNA *srna = PyCObject_AsVoidPtr(self);
|
||||
RNA_def_int(srna, id, def, min, max, name, description, soft_min, soft_max);
|
||||
Py_RETURN_NONE;
|
||||
} else {
|
||||
PyObject *ret = PyTuple_New(2);
|
||||
PyTuple_SET_ITEM(ret, 0, PyCObject_FromVoidPtr((void *)BPy_IntProperty, NULL));
|
||||
PyTuple_SET_ITEM(ret, 1, kw);
|
||||
Py_INCREF(kw);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw)
|
||||
{
|
||||
static char *kwlist[] = {"attribute", "name", "description", "default", NULL};
|
||||
char *id, *name="", *description="";
|
||||
int def=0;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kw, "s|ssi:IntProperty", kwlist, &id, &name, &description, &def))
|
||||
return NULL;
|
||||
|
||||
if (PyTuple_Size(args) > 0) {
|
||||
PyErr_SetString(PyExc_ValueError, "all args must be keywors"); // TODO - py3 can enforce this.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (self) {
|
||||
StructRNA *srna = PyCObject_AsVoidPtr(self);
|
||||
RNA_def_boolean(srna, id, def, name, description);
|
||||
Py_RETURN_NONE;
|
||||
} else {
|
||||
PyObject *ret = PyTuple_New(2);
|
||||
PyTuple_SET_ITEM(ret, 0, PyCObject_FromVoidPtr((void *)BPy_IntProperty, NULL));
|
||||
PyTuple_SET_ITEM(ret, 1, kw);
|
||||
Py_INCREF(kw);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,8 +33,10 @@
|
||||
extern PyTypeObject pyrna_struct_Type;
|
||||
extern PyTypeObject pyrna_prop_Type;
|
||||
|
||||
#define BPy_StructRNA_Check(v) (PyObject_TypeCheck(v, &pyrna_struct_Type))
|
||||
#define BPy_PropertyRNA_Check(v) (PyObject_TypeCheck(v, &pyrna_prop_Type))
|
||||
#define BPy_StructRNA_Check(v) (PyObject_TypeCheck(v, &pyrna_struct_Type))
|
||||
#define BPy_StructRNA_CheckExact(v) (Py_TYPE(v) == &pyrna_struct_Type)
|
||||
#define BPy_PropertyRNA_Check(v) (PyObject_TypeCheck(v, &pyrna_prop_Type))
|
||||
#define BPy_PropertyRNA_CheckExact(v) (Py_TYPE(v) == &pyrna_prop_Type)
|
||||
|
||||
//XXX add propper accessor function, we know this is just after next/prev pointers
|
||||
|
||||
@@ -69,4 +71,11 @@ PyObject *pyrna_prop_CreatePyObject( PointerRNA *ptr, PropertyRNA *prop );
|
||||
/* operators also need this to set args */
|
||||
int pyrna_py_to_prop(PointerRNA *ptr, PropertyRNA *prop, PyObject *value);
|
||||
PyObject * pyrna_prop_to_py(PointerRNA *ptr, PropertyRNA *prop);
|
||||
|
||||
/* functions for setting up new props - experemental */
|
||||
PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw);
|
||||
PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw);
|
||||
PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user