Python Panels WIP

- Register python panels
- Added a generic class checking function BPY_class_validate() for panels/operators.
- No button drawing yet

Brecht, Added RNA_enum_value_from_id() and RNA_enum_id_from_value() to rna_access.c to do lookups between identifiers and values of EnumPropertyItem's, Not sure if these should go here.
This commit is contained in:
2009-04-01 12:43:07 +00:00
parent 3a28a74505
commit 3224efc384
18 changed files with 299 additions and 153 deletions

View File

@@ -139,7 +139,8 @@ extern "C" {
void BPY_scripts_clear_pyobjects( void );
void error_pyscript( void );
void BPY_DECREF(void *pyob_ptr); /* Py_DECREF() */
/* void BPY_Err_Handle(struct Text *text); */
/* void BPY_clear_bad_scriptlink(struct ID *id, struct Text *byebye); */
/* void BPY_clear_bad_scriptlist(struct ListBase *, struct Text *byebye); */

View File

@@ -241,6 +241,11 @@ int BPY_run_script_space_listener(bContext *C, SpaceScript * sc)
return 1;
}
void BPY_DECREF(void *pyob_ptr)
{
Py_DECREF((PyObject *)pyob_ptr);
}
#if 0
/* called from the the scripts window, assume context is ok */
int BPY_run_python_script_space(const char *modulename, const char *func)

View File

@@ -24,7 +24,7 @@
*/
#include "bpy_operator.h"
#include "bpy_opwrapper.h"
#include "bpy_operator_wrap.h"
#include "bpy_rna.h" /* for setting arg props only - pyrna_py_to_prop() */
#include "bpy_compat.h"

View File

@@ -24,7 +24,7 @@
*/
#include "bpy_opwrapper.h"
#include "bpy_operator_wrap.h"
#include "BLI_listbase.h"
#include "BKE_context.h"
#include "BKE_report.h"
@@ -45,14 +45,6 @@
#define PYOP_ATTR_IDNAME "__name__" /* use pythons class name */
#define PYOP_ATTR_DESCRIPTION "__doc__" /* use pythons docstring */
typedef struct PyOperatorType {
void *next, *prev;
char idname[OP_MAX_TYPENAME];
char name[OP_MAX_TYPENAME];
char description[OP_MAX_TYPENAME]; // XXX should be longer?
PyObject *py_class;
} PyOperatorType;
static PyObject *pyop_dict_from_event(wmEvent *event)
{
PyObject *dict= PyDict_New();
@@ -191,14 +183,16 @@ static struct BPY_flag_def pyop_ret_flags[] = {
static int PYTHON_OT_generic(int mode, bContext *C, wmOperator *op, wmEvent *event)
{
PyOperatorType *pyot = op->type->pyop_data;
PyObject *py_class = op->type->pyop_data;
PyObject *args;
PyObject *ret= NULL, *py_class_instance, *item;
int ret_flag= (mode==PYOP_POLL ? 0:OPERATOR_CANCELLED);
PyGILState_STATE gilstate = PyGILState_Ensure();
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);
PyTuple_SET_ITEM(args, 0, PyObject_GetAttrString(py_class, "__rna__")); // need to use an rna instance as the first arg
py_class_instance = PyObject_Call(py_class, args, NULL);
Py_DECREF(args);
if (py_class_instance) { /* Initializing the class worked, now run its invoke function */
@@ -229,16 +223,16 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperator *op, wmEvent *eve
if (mode==PYOP_INVOKE) {
item= PyObject_GetAttrString(pyot->py_class, "invoke");
item= PyObject_GetAttrString(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");
item= PyObject_GetAttrString(py_class, "exec");
args = PyTuple_New(1);
}
else if (mode==PYOP_POLL) {
item= PyObject_GetAttrString(pyot->py_class, "poll");
item= PyObject_GetAttrString(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);
@@ -251,7 +245,7 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperator *op, wmEvent *eve
Py_DECREF(item);
}
if (ret == NULL) {
if (ret == NULL) { /* covers py_class_instance failing too */
pyop_error_report(op->reports);
}
else {
@@ -280,6 +274,8 @@ static int PYTHON_OT_generic(int mode, bContext *C, wmOperator *op, wmEvent *eve
Py_DECREF(ret);
}
PyGILState_Release(gilstate);
return ret_flag;
}
@@ -302,15 +298,29 @@ static int PYTHON_OT_poll(bContext *C)
void PYTHON_OT_wrapper(wmOperatorType *ot, void *userdata)
{
PyOperatorType *pyot = (PyOperatorType *)userdata;
PyObject *py_class = pyot->py_class;
PyObject *py_class = (PyObject *)userdata;
PyObject *props, *item;
/* identifiers */
ot->name= pyot->name;
ot->idname= pyot->idname;
ot->description= pyot->description;
item= PyObject_GetAttrString(py_class, PYOP_ATTR_IDNAME);
Py_DECREF(item);
ot->idname= _PyUnicode_AsString(item);
item= PyObject_GetAttrString(py_class, PYOP_ATTR_UINAME);
if (item) {
Py_DECREF(item);
ot->name= _PyUnicode_AsString(item);
}
else {
ot->name= ot->idname;
PyErr_Clear();
}
item= PyObject_GetAttrString(py_class, PYOP_ATTR_DESCRIPTION);
Py_DECREF(item);
ot->description= (item && PyUnicode_Check(item)) ? _PyUnicode_AsString(item):"";
/* api callbacks, detailed checks dont on adding */
if (PyObject_HasAttrString(py_class, "invoke"))
ot->invoke= PYTHON_OT_invoke;
@@ -364,105 +374,48 @@ void PYTHON_OT_wrapper(wmOperatorType *ot, void *userdata)
/* pyOperators - Operators defined IN Python */
PyObject *PYOP_wrap_add(PyObject *self, PyObject *value)
PyObject *PYOP_wrap_add(PyObject *self, PyObject *py_class)
{
PyObject *optype, *item;
PyObject *base_class, *item;
PyOperatorType *pyot;
char *idname= NULL;
char *name= NULL;
char *description= NULL;
static char *pyop_func_names[] = {"exec", "invoke", "poll", NULL};
static int pyop_func_nargs[] = {1, 2, 2, 0};
PyObject *pyargcount;
int i, argcount;
int i;
static struct BPY_class_attr_check pyop_class_attr_values[]= {
{PYOP_ATTR_IDNAME, 's', 0, 0},
{PYOP_ATTR_UINAME, 's', 0, BPY_CLASS_ATTR_OPTIONAL},
{PYOP_ATTR_PROP, 'l', 0, BPY_CLASS_ATTR_OPTIONAL},
{PYOP_ATTR_DESCRIPTION, 's', 0, BPY_CLASS_ATTR_NONE_OK},
{"exec", 'f', 1, BPY_CLASS_ATTR_OPTIONAL},
{"invoke", 'f', 2, BPY_CLASS_ATTR_OPTIONAL},
{"poll", 'f', 2, BPY_CLASS_ATTR_OPTIONAL},
{NULL, 0, 0, 0}
};
// in python would be...
//PyObject *optype = PyObject_GetAttrString(PyObject_GetAttrString(PyDict_GetItemString(PyEval_GetGlobals(), "bpy"), "types"), "Operator");
optype = PyObject_GetAttrStringArgs(PyDict_GetItemString(PyEval_GetGlobals(), "bpy"), 2, "types", "Operator");
Py_DECREF(optype);
base_class = PyObject_GetAttrStringArgs(PyDict_GetItemString(PyEval_GetGlobals(), "bpy"), 2, "types", "Operator");
Py_DECREF(base_class);
if (!PyObject_IsSubclass(value, optype)) {
PyErr_SetString( PyExc_AttributeError, "expected Operator subclass of bpy.types.Operator");
return NULL;
if(BPY_class_validate("Operator", py_class, base_class, pyop_class_attr_values, NULL) < 0) {
return NULL; /* BPY_class_validate sets the error */
}
/* class name is used for operator ID - this can be changed later if we want */
item = PyObject_GetAttrString(value, PYOP_ATTR_IDNAME);
item= PyObject_GetAttrString(py_class, PYOP_ATTR_IDNAME);
Py_DECREF(item);
idname = _PyUnicode_AsString(item);
if (WM_operatortype_find(idname)) {
PyErr_Format( PyExc_AttributeError, "Operator alredy exists with this name \"%s\"", idname);
return NULL;
}
/* Operator user readible name */
item = PyObject_GetAttrString(value, PYOP_ATTR_UINAME);
if (item) {
Py_DECREF(item);
name = _PyUnicode_AsString(item);
}
if (name == NULL) {
name = idname;
PyErr_Clear();
}
/* use py docstring for description, should always be None or a string */
item = PyObject_GetAttrString(value, PYOP_ATTR_DESCRIPTION);
Py_DECREF(item);
if (PyUnicode_Check(item)) {
description = _PyUnicode_AsString(item);
}
else {
description = "";
}
/* Check known functions and argument lengths */
for (i=0; pyop_func_names[i]; i++) {
item=PyObject_GetAttrString(value, pyop_func_names[i]);
if (item) {
Py_DECREF(item);
/* 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]);
return NULL;
}
/* check the number of args is correct */
/* MyClass.exec.func_code.co_argcount */
pyargcount = PyObject_GetAttrString(PyFunction_GET_CODE(item), "co_argcount");
Py_DECREF(pyargcount);
argcount = PyLong_AsSsize_t(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]);
return NULL;
}
} else {
PyErr_Clear();
}
}
/* If we have properties set, check its a list of dicts */
item = PyObject_GetAttrString(value, PYOP_ATTR_PROP);
item= PyObject_GetAttrString(py_class, PYOP_ATTR_PROP);
if (item) {
Py_DECREF(item);
if (!PyList_Check(item)) {
PyErr_Format(PyExc_ValueError, "Cant register operator class - %s.properties must be a list", idname);
return NULL;
}
for(i=0; i<PyList_Size(item); i++) {
PyObject *py_args = PyList_GET_ITEM(item, i);
PyObject *py_func_ptr, *py_kw; /* place holders */
@@ -477,24 +430,18 @@ PyObject *PYOP_wrap_add(PyObject *self, PyObject *value)
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_class= value;
Py_INCREF(value);
WM_operatortype_append_ptr(PYTHON_OT_wrapper, pyot);
Py_INCREF(py_class);
WM_operatortype_append_ptr(PYTHON_OT_wrapper, py_class);
Py_RETURN_NONE;
}
PyObject *PYOP_wrap_remove(PyObject *self, PyObject *value)
{
PyObject *py_class;
char *idname= NULL;
wmOperatorType *ot;
PyOperatorType *pyot;
if (PyUnicode_Check(value))
idname = _PyUnicode_AsString(value);
@@ -514,13 +461,12 @@ PyObject *PYOP_wrap_remove(PyObject *self, PyObject *value)
return NULL;
}
if (!(pyot= (PyOperatorType *)ot->pyop_data)) {
if (!(py_class= (PyObject *)ot->pyop_data)) {
PyErr_Format( PyExc_AttributeError, "Operator \"%s\" was not created by python", idname);
return NULL;
}
Py_XDECREF(pyot->py_class);
MEM_freeN(pyot);
Py_XDECREF(py_class);
WM_operatortype_remove(idname);

View File

@@ -22,8 +22,8 @@
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef BPY_OPWRAPPER_H
#define BPY_OPWRAPPER_H
#ifndef BPY_OPERATOR_WRAP_H
#define BPY_OPERATOR_WRAP_H
#include <Python.h>

View File

@@ -0,0 +1,36 @@
/**
* $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 *****
*/
#ifndef BPY_PANEL_WRAP_H
#define BPY_PANEL_WRAP_H
#include <Python.h>
/* these are used for operator methods, used by bpy_operator.c */
PyObject *PyPanel_wrap_add(PyObject *self, PyObject *args);
PyObject *PyPanel_wrap_remove(PyObject *self, PyObject *args);
#endif

View File

@@ -128,20 +128,9 @@ static char *pyrna_enum_as_string(PointerRNA *ptr, PropertyRNA *prop)
{
const EnumPropertyItem *item;
int totitem, i;
DynStr *dynstr= BLI_dynstr_new();
char *cstring;
RNA_property_enum_items(ptr, prop, &item, &totitem);
for (i=0; i<totitem; i++) {
BLI_dynstr_appendf(dynstr, i?", '%s'":"'%s'", item[i].identifier);
}
cstring = BLI_dynstr_get_cstring(dynstr);
BLI_dynstr_free(dynstr);
return cstring;
RNA_property_enum_items(ptr, prop, &item, &totitem);
return BPy_enum_as_string(item);
}
PyObject * pyrna_prop_to_py(PointerRNA *ptr, PropertyRNA *prop)

View File

@@ -27,6 +27,8 @@
#include "bpy_rna.h" /* for rna buttons */
#include "bpy_operator.h" /* for setting button operator properties */
#include "bpy_compat.h"
#include "bpy_panel_wrap.h" /* for setting button operator properties */
#include "WM_types.h" /* for WM_OP_INVOKE_DEFAULT & friends */
#include "BLI_dynstr.h"
@@ -423,6 +425,10 @@ static struct PyMethodDef ui_methods[] = {
{"getScreenPtr", (PyCFunction)Method_getScreenPtr, METH_NOARGS, ""},
{"getSpacePtr", (PyCFunction)Method_getSpacePtr, METH_NOARGS, ""},
{"getWindowPtr", (PyCFunction)Method_getWindowPtr, METH_NOARGS, ""},
/* Adding panels should be moved, at the moment there is no obvious place as there is with operators */
{"addPanel", (PyCFunction)PyPanel_wrap_add, METH_VARARGS, ""},
{"removePanel", (PyCFunction)PyPanel_wrap_remove, METH_VARARGS, ""},
{NULL, NULL, 0, NULL}
};

View File

@@ -26,7 +26,6 @@
#include "bpy_util.h"
#include "BLI_dynstr.h"
#include "MEM_guardedalloc.h"
#include "bpy_compat.h"
PyObject *BPY_flag_to_list(struct BPY_flag_def *flagdef, int flag)
{
@@ -241,3 +240,91 @@ PyObject *PyObject_GetAttrStringArgs(PyObject *o, Py_ssize_t n, ...)
Py_XINCREF(item); /* final value has is increfed, to match PyObject_GetAttrString */
return item;
}
int BPY_class_validate(const char *class_type, PyObject *class, PyObject *base_class, BPY_class_attr_check* class_attrs, PyObject **py_class_attrs)
{
PyObject *item;
PyObject *py_arg_count;
int i, arg_count;
if (base_class) {
if (!PyObject_IsSubclass(class, base_class)) {
PyObject *name= PyObject_GetAttrString(base_class, "__name__");
PyErr_Format( PyExc_AttributeError, "expected %s subclass of class \"%s\"", class_type, name ? _PyUnicode_AsString(name):"<UNKNOWN>");
Py_XDECREF(name);
return -1;
}
}
for(i= 0;class_attrs->name; class_attrs++, i++) {
item = PyObject_GetAttrString(class, class_attrs->name);
if (py_class_attrs)
py_class_attrs[i]= item;
if (item==NULL) {
if ((class_attrs->flag & BPY_CLASS_ATTR_OPTIONAL)==0) {
PyErr_Format( PyExc_AttributeError, "expected %s class to have an \"%s\" attribute", class_type, class_attrs->name);
return -1;
}
PyErr_Clear();
}
else {
Py_DECREF(item); /* no need to keep a ref, the class owns it */
if((item==Py_None) && (class_attrs->flag & BPY_CLASS_ATTR_NONE_OK)) {
/* dont do anything, this is ok, dont bother checking other types */
}
else {
switch(class_attrs->type) {
case 's':
if (PyUnicode_Check(item)==0) {
PyErr_Format( PyExc_AttributeError, "expected %s class \"%s\" attribute to be a string", class_type, class_attrs->name);
return -1;
}
break;
case 'l':
if (PyList_Check(item)==0) {
PyErr_Format( PyExc_AttributeError, "expected %s class \"%s\" attribute to be a list", class_type, class_attrs->name);
return -1;
}
break;
case 'f':
if (PyFunction_Check(item)==0) {
PyErr_Format( PyExc_AttributeError, "expected %s class \"%s\" attribute to be a function", class_type, class_attrs->name);
return -1;
}
if (class_attrs->arg_count >= 0) { /* -1 if we dont care*/
py_arg_count = PyObject_GetAttrString(PyFunction_GET_CODE(item), "co_argcount");
arg_count = PyLong_AsSsize_t(py_arg_count);
Py_DECREF(py_arg_count);
if (arg_count != class_attrs->arg_count) {
PyErr_Format( PyExc_AttributeError, "expected %s class \"%s\" function to have %d args", class_type, class_attrs->name, class_attrs->arg_count);
return -1;
}
}
break;
}
}
}
}
return 0;
}
char *BPy_enum_as_string(EnumPropertyItem *item)
{
DynStr *dynstr= BLI_dynstr_new();
EnumPropertyItem *e;
char *cstring;
for (e= item; item->identifier; item++) {
BLI_dynstr_appendf(dynstr, (e==item)?"'%s'":", '%s'", item->identifier);
}
cstring = BLI_dynstr_get_cstring(dynstr);
BLI_dynstr_free(dynstr);
return cstring;
}

View File

@@ -28,6 +28,9 @@
#define BPY_UTIL_H
#include "bpy_compat.h"
#include "RNA_types.h" /* for EnumPropertyItem only */
struct EnumPropertyItem;
/* for internal use only, so python can interchange a sequence of strings with flags */
typedef struct BPY_flag_def {
@@ -46,4 +49,22 @@ void BPY_getFileAndNum(char **filename, int *lineno);
/* own python like utility function */
PyObject *PyObject_GetAttrStringArgs(PyObject *o, Py_ssize_t n, ...);
/* Class type checking, use for checking classes can be added as operators, panels etc */
typedef struct BPY_class_attr_check {
const char *name; /* name of the class attribute */
char type; /* 's' = string, 'f' = function, 'l' = list, (add as needed) */
int arg_count; /* only for function types, -1 for undefined, includes self arg */
int flag; /* other options */
} BPY_class_attr_check;
/* BPY_class_attr_check, flag */
#define BPY_CLASS_ATTR_OPTIONAL 1
#define BPY_CLASS_ATTR_NONE_OK 2
int BPY_class_validate(const char *class_type, PyObject *class, PyObject *base_class, BPY_class_attr_check* class_attrs, PyObject **py_class_attrs);
char *BPy_enum_as_string(struct EnumPropertyItem *item);
#endif