Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2009-04-01 16:43:07 +0400
committerCampbell Barton <ideasman42@gmail.com>2009-04-01 16:43:07 +0400
commit3224efc38419201b44503982541aca8eaeb29486 (patch)
tree77a09bed4e1668427c48ceac7fdb8021eaa7f729 /source/blender/python/intern/bpy_operator_wrap.c
parent3a28a7450596f46281431bd163b9a237eb481055 (diff)
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.
Diffstat (limited to 'source/blender/python/intern/bpy_operator_wrap.c')
-rw-r--r--source/blender/python/intern/bpy_operator_wrap.c477
1 files changed, 477 insertions, 0 deletions
diff --git a/source/blender/python/intern/bpy_operator_wrap.c b/source/blender/python/intern/bpy_operator_wrap.c
new file mode 100644
index 00000000000..1db1f547fa6
--- /dev/null
+++ b/source/blender/python/intern/bpy_operator_wrap.c
@@ -0,0 +1,477 @@
+
+/**
+ * $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_operator_wrap.h"
+#include "BLI_listbase.h"
+#include "BKE_context.h"
+#include "BKE_report.h"
+#include "DNA_windowmanager_types.h"
+#include "MEM_guardedalloc.h"
+#include "WM_api.h"
+#include "WM_types.h"
+#include "ED_screen.h"
+
+#include "RNA_define.h"
+
+#include "bpy_rna.h"
+#include "bpy_compat.h"
+#include "bpy_util.h"
+
+#define PYOP_ATTR_PROP "__props__"
+#define PYOP_ATTR_UINAME "__label__"
+#define PYOP_ATTR_IDNAME "__name__" /* use pythons class name */
+#define PYOP_ATTR_DESCRIPTION "__doc__" /* use pythons docstring */
+
+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;
+}
+
+/* TODO - a whole traceback would be ideal */
+static void pyop_error_report(ReportList *reports)
+{
+ PyObject *exception, *v, *tb;
+ PyErr_Fetch(&exception, &v, &tb);
+ if (exception == NULL)
+ return;
+ /* Now we know v != NULL too */
+ BKE_report(reports, RPT_ERROR, _PyUnicode_AsString(v));
+
+ PyErr_Print();
+}
+
+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}
+};
+
+/* This invoke function can take events and
+ *
+ * It is up to the pyot->py_invoke() python func to run pyot->py_exec()
+ * the invoke function gets the keyword props as a dict, but can parse them
+ * to py_exec like this...
+ *
+ * def op_exec(x=-1, y=-1, text=""):
+ * ...
+ *
+ * def op_invoke(event, prop_defs):
+ * prop_defs['x'] = event['x']
+ * ...
+ * 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
+ */
+
+
+#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)
+{
+ 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(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 */
+
+
+ /* Assign instance attributes from operator properties */
+ {
+ 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);
+ PyObject_SetAttrString(py_class_instance, arg_name, item);
+ Py_DECREF(item);
+ }
+
+ RNA_property_collection_end(&iter);
+ }
+
+
+ if (mode==PYOP_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(py_class, "exec");
+ args = PyTuple_New(1);
+ }
+ else if (mode==PYOP_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);
+ }
+ PyTuple_SET_ITEM(args, 0, py_class_instance);
+
+ ret = PyObject_Call(item, args, NULL);
+
+ Py_DECREF(args);
+ Py_DECREF(item);
+ }
+
+ if (ret == NULL) { /* covers py_class_instance failing too */
+ pyop_error_report(op->reports);
+ }
+ else {
+ 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
+ * thrown away anyway
+ *
+ * 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);
+ }
+
+ PyGILState_Release(gilstate);
+
+ 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)
+{
+ PyObject *py_class = (PyObject *)userdata;
+ PyObject *props, *item;
+
+ /* identifiers */
+ 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;
+ 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;
+
+ props= PyObject_GetAttrString(py_class, PYOP_ATTR_PROP);
+
+ if (props) {
+ PyObject *dummy_args = PyTuple_New(0);
+ int i;
+
+ Py_DECREF(props);
+
+ for(i=0; i<PyList_Size(props); i++) {
+ PyObject *py_func_ptr, *py_kw, *py_srna_cobject, *py_ret;
+ item = PyList_GET_ITEM(props, i);
+
+ 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 *py_class)
+{
+ PyObject *base_class, *item;
+
+
+ char *idname= NULL;
+ 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");
+ base_class = PyObject_GetAttrStringArgs(PyDict_GetItemString(PyEval_GetGlobals(), "bpy"), 2, "types", "Operator");
+ Py_DECREF(base_class);
+
+ 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(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;
+ }
+
+ /* If we have properties set, check its a list of dicts */
+ item= PyObject_GetAttrString(py_class, PYOP_ATTR_PROP);
+ if (item) {
+ Py_DECREF(item);
+ 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);
+ return NULL;
+ }
+ }
+ }
+ else {
+ PyErr_Clear();
+ }
+
+ 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;
+
+
+ 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);
+ return NULL;
+ }
+
+ if (!(py_class= (PyObject *)ot->pyop_data)) {
+ PyErr_Format( PyExc_AttributeError, "Operator \"%s\" was not created by python", idname);
+ return NULL;
+ }
+
+ Py_XDECREF(py_class);
+
+ WM_operatortype_remove(idname);
+
+ Py_RETURN_NONE;
+}
+
+
+